diff --git a/kubectl-plugin/common/installer.go b/kubectl-plugin/common/installer.go new file mode 100644 index 0000000..a97b73c --- /dev/null +++ b/kubectl-plugin/common/installer.go @@ -0,0 +1,34 @@ +package common + +// KSInstaller is the installer for KubeSphere +type KSInstaller struct { + Spec KSInstallerSpec `yaml:"spec"` +} + +// KSInstallerSpec is ks-installer +type KSInstallerSpec struct { + Version string + ImageNamespace string + + Servicemesh ComponentStatus + Openpitrix ComponentStatus + Notification ComponentStatus + NetworkPolicy ComponentStatus + MetricsServer ComponentStatus + Logging ComponentStatus + Events ComponentStatus + DevOps ComponentStatus + Auditing ComponentStatus + Alerting ComponentStatus + Multicluster Multicluster `yaml:"multicluster"` +} + +// ComponentStatus is a common status +type ComponentStatus struct { + Enabled bool +} + +// Multicluster represents multi-cluster +type Multicluster struct { + ClusterRole string `yaml:"clusterRole"` +} diff --git a/kubectl-plugin/config/cluster.go b/kubectl-plugin/config/cluster.go new file mode 100644 index 0000000..1f88558 --- /dev/null +++ b/kubectl-plugin/config/cluster.go @@ -0,0 +1,124 @@ +package config + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "github.com/linuxsuren/ks/kubectl-plugin/common" + kstypes "github.com/linuxsuren/ks/kubectl-plugin/types" + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "sigs.k8s.io/yaml" +) + +func newClusterCmd(client dynamic.Interface) (cmd *cobra.Command) { + opt := clusterOption{ + client: client, + } + + cmd = &cobra.Command{ + Use: "cluster", + Short: "Set multi cluster", + RunE: opt.runE, + PostRunE: opt.postRunE, + } + + flags := cmd.Flags() + flags.StringVarP(&opt.role, "role", "r", "", + "Set current KubeSphere cluster as your desired role (none, host, member)") + flags.StringVarP(&opt.jwtSecret, "jwtSecret", "", "", + "Need this if you want to set the cluster as the member role") + + _ = cmd.RegisterFlagCompletionFunc("role", common.ArrayCompletion("none", "host", "member")) + return +} + +func (o *clusterOption) runE(cmd *cobra.Command, args []string) (err error) { + switch o.role { + case "member": + if o.jwtSecret == "" { + err = errors.New("please provide the jwtSecret from the host cluster") + } else { + err = o.updateJwtSecret() + } + + if err != nil { + return + } + fallthrough + case "none", "host": + err = o.updateClusterRole() + case "": + default: + err = fmt.Errorf("invalid cluster role: %s", o.role) + } + return +} + +func (o *clusterOption) postRunE(cmd *cobra.Command, args []string) (err error) { + err = o.showClusterRole() + return +} + +func (o *clusterOption) updateJwtSecret() (err error) { + patch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/authentication/jwtSecret", "value": "%s"}]`, o.jwtSecret) + ctx := context.TODO() + _, err = o.client.Resource(kstypes.GetClusterConfiguration()).Namespace("kubesphere-system").Patch(ctx, + "ks-installer", types.JSONPatchType, + []byte(patch), + metav1.PatchOptions{}) + return +} + +func (o *clusterOption) updateClusterRole() (err error) { + patch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/multicluster/clusterRole", "value": "%s"}]`, o.role) + ctx := context.TODO() + _, err = o.client.Resource(kstypes.GetClusterConfiguration()).Namespace("kubesphere-system").Patch(ctx, + "ks-installer", types.JSONPatchType, + []byte(patch), + metav1.PatchOptions{}) + return +} + +func (o *clusterOption) showClusterRole() (err error) { + ctx := context.TODO() + var rawData *unstructured.Unstructured + if rawData, err = o.client.Resource(kstypes.GetClusterConfiguration()).Namespace("kubesphere-system"). + Get(ctx, "ks-installer", metav1.GetOptions{}); err == nil { + var data []byte + buf := bytes.NewBuffer(data) + enc := json.NewEncoder(buf) + if err = enc.Encode(rawData); err != nil { + return + } + + var yamlData []byte + if yamlData, err = yaml.JSONToYAML(buf.Bytes()); err != nil { + return + } + + installer := common.KSInstaller{} + if err = yaml.Unmarshal(yamlData, &installer); err == nil { + fmt.Printf("cluster role: %s\n", installer.Spec.Multicluster.ClusterRole) + } + } + + var rawConfigMap *unstructured.Unstructured + if rawConfigMap, err = o.client.Resource(kstypes.GetConfigMapSchema()).Namespace("kubesphere-system"). + Get(context.TODO(), "kubesphere-config", metav1.GetOptions{}); err == nil { + data := rawConfigMap.Object["data"] + dataMap := data.(map[string]interface{}) + kubeSphereCfg := dataMap["kubesphere.yaml"] + + cfg := kubeSphereConfig{} + if err = yaml.Unmarshal([]byte(fmt.Sprintf("%v", kubeSphereCfg)), &cfg); err == nil { + fmt.Printf("jwtSecret: %s\n", cfg.Authentication.JwtSecret) + } + } + return +} diff --git a/kubectl-plugin/config/config_root.go b/kubectl-plugin/config/config_root.go new file mode 100644 index 0000000..8d8048d --- /dev/null +++ b/kubectl-plugin/config/config_root.go @@ -0,0 +1,18 @@ +package config + +import ( + "github.com/spf13/cobra" + "k8s.io/client-go/dynamic" +) + +// NewConfigRootCmd returns the config command +func NewConfigRootCmd(client dynamic.Interface) (cmd *cobra.Command) { + cmd = &cobra.Command{ + Use: "option", + Short: "Config KubeSphere as you need", + Aliases: []string{"opt"}, + } + + cmd.AddCommand(newClusterCmd(client)) + return +} diff --git a/kubectl-plugin/config/types.go b/kubectl-plugin/config/types.go new file mode 100644 index 0000000..fbd11ff --- /dev/null +++ b/kubectl-plugin/config/types.go @@ -0,0 +1,19 @@ +package config + +import "k8s.io/client-go/dynamic" + +type clusterOption struct { + role string + jwtSecret string + + // inner fields + client dynamic.Interface +} + +type kubeSphereConfig struct { + Authentication authentication +} + +type authentication struct { + JwtSecret string +} diff --git a/kubectl-plugin/entrypoint/cmd.go b/kubectl-plugin/entrypoint/cmd.go index c7ae5bb..bb7fcf6 100644 --- a/kubectl-plugin/entrypoint/cmd.go +++ b/kubectl-plugin/entrypoint/cmd.go @@ -7,6 +7,7 @@ import ( "github.com/linuxsuren/ks/kubectl-plugin/auth" "github.com/linuxsuren/ks/kubectl-plugin/common" "github.com/linuxsuren/ks/kubectl-plugin/component" + "github.com/linuxsuren/ks/kubectl-plugin/config" "github.com/linuxsuren/ks/kubectl-plugin/install" "github.com/linuxsuren/ks/kubectl-plugin/pipeline" "github.com/linuxsuren/ks/kubectl-plugin/registry" @@ -46,6 +47,7 @@ See also https://github.com/kubesphere/kubesphere`, registry.NewRegistryCmd(client), auth.NewAuthCmd(client), tool.NewToolCmd(), - install.NewInstallCmd()) + install.NewInstallCmd(), + config.NewConfigRootCmd(client)) return } diff --git a/kubectl-plugin/install/installer.go b/kubectl-plugin/install/installer.go index 1aedc1a..9275cf6 100644 --- a/kubectl-plugin/install/installer.go +++ b/kubectl-plugin/install/installer.go @@ -41,7 +41,7 @@ type installerOption struct { // inner fields client dynamic.Interface - ksInstaller ksInstaller + ksInstaller common.KSInstallerSpec } func (o *installerOption) preRunE(_ *cobra.Command, args []string) (err error) { @@ -54,7 +54,7 @@ func (o *installerOption) preRunE(_ *cobra.Command, args []string) (err error) { _, o.nightly = common.GetNightlyTag(o.nightly) // parse the ks-installer - o.ksInstaller = ksInstaller{ + o.ksInstaller = common.KSInstallerSpec{ Version: o.version, ImageNamespace: "kubesphere", } @@ -142,26 +142,6 @@ func (o *installerOption) runE(_ *cobra.Command, args []string) (err error) { return } -type ksInstaller struct { - Version string - ImageNamespace string - - Servicemesh componentStatus - Openpitrix componentStatus - Notification componentStatus - NetworkPolicy componentStatus - MetricsServer componentStatus - Logging componentStatus - Events componentStatus - DevOps componentStatus - Auditing componentStatus - Alerting componentStatus -} - -type componentStatus struct { - Enabled bool -} - var localStorageClass = ` apiVersion: storage.k8s.io/v1 kind: StorageClass