-
Notifications
You must be signed in to change notification settings - Fork 558
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2120 from liangzai006/master
support 'cluster-info dump' command
- Loading branch information
Showing
11 changed files
with
838 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package v1alpha2 | ||
|
||
import ( | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
) | ||
|
||
type MultiClusterSpec struct { | ||
// Join cluster as a kubefed cluster | ||
JoinFederation bool `json:"joinFederation,omitempty"` | ||
|
||
// Desired state of the cluster | ||
Enable bool `json:"enable,omitempty"` | ||
|
||
// Provider of the cluster, this field is just for description | ||
Provider string `json:"provider,omitempty"` | ||
|
||
// Connection holds info to connect to the member cluster | ||
Connection Connection `json:"connection,omitempty"` | ||
|
||
// ExternalKubeAPIEnabled export kubeapiserver to public use a lb type service if connection type is proxy | ||
ExternalKubeAPIEnabled bool `json:"externalKubeAPIEnabled,omitempty"` | ||
} | ||
|
||
type ConnectionType string | ||
|
||
const ( | ||
ConnectionTypeDirect ConnectionType = "direct" | ||
ConnectionTypeProxy ConnectionType = "proxy" | ||
) | ||
|
||
type Connection struct { | ||
|
||
// type defines how host cluster will connect to host cluster | ||
// ConnectionTypeDirect means direct connection, this requires | ||
// kubeconfig and kubesphere apiserver endpoint provided | ||
// ConnectionTypeProxy means using kubesphere proxy, no kubeconfig | ||
// or kubesphere apiserver endpoint required | ||
Type ConnectionType `json:"type,omitempty"` | ||
|
||
// KubeSphere API Server endpoint. Example: http://10.10.0.11:8080 | ||
// Should provide this field explicitly if connection type is direct. | ||
// Will be populated by ks-apiserver if connection type is proxy. | ||
KubeSphereAPIEndpoint string `json:"kubesphereAPIEndpoint,omitempty"` | ||
|
||
// Kubernetes API Server endpoint. Example: https://10.10.0.1:6443 | ||
// Should provide this field explicitly if connection type is direct. | ||
// Will be populated by ks-apiserver if connection type is proxy. | ||
KubernetesAPIEndpoint string `json:"kubernetesAPIEndpoint,omitempty"` | ||
|
||
// External Kubernetes API Server endpoint | ||
// Will be populated by ks-apiserver if connection type is proxy and ExternalKubeAPIEnabled is true. | ||
ExternalKubernetesAPIEndpoint string `json:"externalKubernetesAPIEndpoint,omitempty"` | ||
|
||
// KubeConfig content used to connect to cluster api server | ||
// Should provide this field explicitly if connection type is direct. | ||
// Will be populated by ks-proxy if connection type is proxy. | ||
KubeConfig []byte `json:"kubeconfig,omitempty"` | ||
|
||
// Token used by agents of member cluster to connect to host cluster proxy. | ||
// This field is populated by apiserver only if connection type is proxy. | ||
Token string `json:"token,omitempty"` | ||
|
||
// KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic | ||
// Only applicable when connection type is proxy. | ||
KubernetesAPIServerPort uint16 `json:"kubernetesAPIServerPort,omitempty"` | ||
|
||
// KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic | ||
// Only applicable when connection type is proxy. | ||
KubeSphereAPIServerPort uint16 `json:"kubesphereAPIServerPort,omitempty"` | ||
} | ||
type MultiClusterStatus struct { | ||
|
||
// Represents the latest available observations of a cluster's current state. | ||
Conditions []ClusterCondition `json:"conditions,omitempty"` | ||
|
||
// GitVersion of the kubernetes cluster, this field is populated by cluster controller | ||
KubernetesVersion string `json:"kubernetesVersion,omitempty"` | ||
|
||
// GitVersion of the /kapis/version api response, this field is populated by cluster controller | ||
KubeSphereVersion string `json:"kubeSphereVersion,omitempty"` | ||
|
||
// Count of the kubernetes cluster nodes | ||
// This field may not reflect the instant status of the cluster. | ||
NodeCount int `json:"nodeCount,omitempty"` | ||
|
||
// Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'. | ||
// +optional | ||
Zones []string `json:"zones,omitempty"` | ||
|
||
// Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'. | ||
// +optional | ||
Region *string `json:"region,omitempty"` | ||
|
||
// Configz is status of components enabled in the member cluster. This is synchronized with member cluster | ||
// every amount of time, like 5 minutes. | ||
// +optional | ||
Configz map[string]bool `json:"configz,omitempty"` | ||
|
||
// UID is the kube-system namespace UID of the cluster, which represents the unique ID of the cluster. | ||
UID types.UID `json:"uid,omitempty"` | ||
} | ||
type ClusterConditionType string | ||
|
||
const ( | ||
// Cluster agent is initialized and waiting for connecting | ||
ClusterInitialized ClusterConditionType = "Initialized" | ||
|
||
// Cluster agent is available | ||
ClusterAgentAvailable ClusterConditionType = "AgentAvailable" | ||
|
||
// Cluster has been one of federated clusters | ||
ClusterFederated ClusterConditionType = "Federated" | ||
|
||
// Cluster external access ready | ||
ClusterExternalAccessReady ClusterConditionType = "ExternalAccessReady" | ||
|
||
// Cluster is all available for requests | ||
ClusterReady ClusterConditionType = "Ready" | ||
|
||
// Openpitrix runtime is created | ||
ClusterOpenPitrixRuntimeReady ClusterConditionType = "OpenPitrixRuntimeReady" | ||
|
||
// ClusterKubeConfigCertExpiresInSevenDays indicates that the cluster certificate is about to expire. | ||
ClusterKubeConfigCertExpiresInSevenDays ClusterConditionType = "KubeConfigCertExpiresInSevenDays" | ||
) | ||
|
||
type ClusterCondition struct { | ||
// Type of the condition | ||
Type ClusterConditionType `json:"type"` | ||
// Status of the condition, one of True, False, Unknown. | ||
Status corev1.ConditionStatus `json:"status"` | ||
// The last time this condition was updated. | ||
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` | ||
// Last time the condition transitioned from one status to another. | ||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` | ||
// The reason for the condition's last transition. | ||
Reason string `json:"reason,omitempty"` | ||
// A human readable message indicating details about the transition. | ||
Message string `json:"message,omitempty"` | ||
} | ||
type MultiCluster struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec MultiClusterSpec `json:"spec,omitempty"` | ||
Status MultiClusterStatus `json:"status,omitempty"` | ||
} | ||
|
||
type MultiClusterList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []MultiCluster `json:"items"` | ||
} | ||
|
||
const HostClusterLabel = "cluster-role.kubesphere.io/host" | ||
|
||
func (m MultiCluster) IsHostCluster() bool { | ||
_, host := m.Labels[HostClusterLabel] | ||
return host | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package clusterinfo | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// NewCmdClusterInfo creates a new clusterinfo command | ||
func NewCmdClusterInfo() *cobra.Command { | ||
|
||
cmd := &cobra.Command{ | ||
Use: "cluster-info", | ||
Short: "display cluster information", | ||
} | ||
|
||
cmd.AddCommand(NewCmdClusterInfoDump()) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package clusterinfo | ||
|
||
import ( | ||
"fmt" | ||
"github.com/kubesphere/kubekey/v3/cmd/kk/cmd/util" | ||
"github.com/kubesphere/kubekey/v3/cmd/kk/pkg/clusterinfo" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
type ClusterInfoDumpOptions struct { | ||
Options clusterinfo.DumpOption | ||
} | ||
|
||
func NewClusterInfoDumpOptions() *ClusterInfoDumpOptions { | ||
return &ClusterInfoDumpOptions{} | ||
} | ||
|
||
func NewCmdClusterInfoDump() *cobra.Command { | ||
o := NewClusterInfoDumpOptions() | ||
cmd := &cobra.Command{ | ||
Use: "dump", | ||
Short: "Dumping key cluster configurations and files", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
|
||
util.CheckErr(o.Validate()) | ||
util.CheckErr(o.Run()) | ||
|
||
}, | ||
} | ||
|
||
o.addFlag(cmd) | ||
return cmd | ||
} | ||
|
||
func (o *ClusterInfoDumpOptions) Validate() error { | ||
switch o.Options.Type { | ||
case "yaml", "YAML", "json", "JSON": | ||
default: | ||
return fmt.Errorf("unsupport output content format [%s]", o.Options.Type) | ||
} | ||
return nil | ||
} | ||
|
||
func (o *ClusterInfoDumpOptions) addFlag(cmd *cobra.Command) { | ||
DefaultDumpNamespaces := []string{"kubesphere-system", "kubesphere-logging-system", "kubesphere-monitoring-system", "openpitrix-system", "kube-system", "istio-system", "kubesphere-devops-system", "porter-system"} | ||
cmd.Flags().StringArrayVar(&o.Options.Namespace, "namespaces", DefaultDumpNamespaces, "Namespaces to be dumped, separated by commas.") | ||
cmd.Flags().StringVar(&o.Options.KubeConfig, "kube-config", "", "Path to the kube-config file") | ||
cmd.Flags().BoolVarP(&o.Options.AllNamespaces, "all-namespaces", "A", false, "dump all namespaces.") | ||
cmd.Flags().StringVar(&o.Options.OutputDir, "output-dir", "", "output the dump result to the specified directory directory.") | ||
cmd.Flags().StringVarP(&o.Options.Type, "output", "o", "json", "output file content format. support in json,yaml") | ||
cmd.Flags().BoolVarP(&o.Options.Tar, "tar", "t", false, "build the dump result into a tar") | ||
cmd.Flags().IntVar(&o.Options.Queue, "queue", 5, "dump queue size") | ||
cmd.Flags().BoolVar(&o.Options.Logger, "log", false, "output the dump result to the log console") | ||
} | ||
|
||
func (o *ClusterInfoDumpOptions) Run() error { | ||
fmt.Println("dumping cluster info...") | ||
return clusterinfo.Dump(o.Options) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package clusterinfo | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/kubesphere/kubekey/v3/cmd/kk/apis/kubekey/v1alpha2" | ||
"github.com/kubesphere/kubekey/v3/cmd/kk/pkg/utils" | ||
"github.com/xuri/excelize/v2" | ||
) | ||
|
||
type DumpOption struct { | ||
Namespace []string | ||
KubeConfig string | ||
AllNamespaces bool | ||
OutputDir string | ||
Type string | ||
Tar bool | ||
Queue int | ||
Logger bool | ||
} | ||
|
||
func Dump(option DumpOption) error { | ||
client, err := utils.NewClient(option.KubeConfig) | ||
if err != nil { | ||
return err | ||
} | ||
dump := NewDumpOption(client) | ||
|
||
cluster, err := dump.GetMultiCluster() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fileChan := NewFileChan(option, excelize.NewFile()) | ||
queue := make(chan struct{}, option.Queue) | ||
go func() { | ||
err = fileChan.WriteFile() | ||
if err != nil { | ||
fmt.Printf("failed to write file %s", err.Error()) | ||
fileChan.WaitGroup.Done() | ||
} | ||
}() | ||
|
||
for _, multiCluster := range cluster { | ||
fileChan.WaitGroup.Add(1) | ||
queue <- struct{}{} | ||
go func(multiCluster v1alpha2.MultiCluster) { | ||
defer func() { | ||
<-queue | ||
}() | ||
if multiCluster.IsHostCluster() { | ||
fileChan.ReadData(dump, multiCluster.Name) | ||
} else { | ||
clusterClient, err := utils.NewClientForCluster(multiCluster.Spec.Connection.KubeConfig) | ||
if err != nil { | ||
fmt.Printf("failed to create cluster %s", multiCluster.Name) | ||
fileChan.WaitGroup.Done() | ||
return | ||
} | ||
fileChan.ReadData(NewDumpOption(clusterClient), multiCluster.Name) | ||
} | ||
}(multiCluster) | ||
} | ||
|
||
defer func() { | ||
if err := fileChan.Excel.Close(); err != nil { | ||
fmt.Println(err) | ||
} | ||
close(fileChan.OutFileChan) | ||
close(queue) | ||
}() | ||
|
||
fileChan.WaitGroup.Wait() | ||
|
||
fileChan.Excel.DeleteSheet("Sheet1") | ||
fileChan.Excel.SaveAs(fmt.Sprintf("%s/%s", option.GetOutputDir(), "cluster_dump.xlsx")) | ||
|
||
if option.Tar { | ||
err = NewTar(option).Run() | ||
if err != nil { | ||
fmt.Printf("failed to tar file %s", err.Error()) | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourcesClassification(resources interface{}) map[string][]interface{} { | ||
|
||
var resourcesMap []map[string]interface{} | ||
if marshal, err := json.Marshal(resources); err != nil { | ||
fmt.Println(err, "marshal resources error") | ||
return nil | ||
} else { | ||
decoder := json.NewDecoder(bytes.NewReader(marshal)) | ||
if err = decoder.Decode(&resourcesMap); err != nil { | ||
fmt.Println(err, "Decode resources error") | ||
return nil | ||
} | ||
} | ||
|
||
var completeMap = make(map[string][]interface{}) | ||
for _, m := range resourcesMap { | ||
namespace, ok := m["metadata"].(map[string]interface{})["namespace"] | ||
if ok { | ||
completeMap[namespace.(string)] = append(completeMap[namespace.(string)], m) | ||
} else { | ||
completeMap[""] = append(completeMap[""], m) | ||
} | ||
} | ||
return completeMap | ||
} | ||
|
||
func (c *DumpOption) GetOutputDir() string { | ||
if c.OutputDir == "" { | ||
return "cluster_dump" | ||
} | ||
return c.OutputDir | ||
} |
Oops, something went wrong.