diff --git a/kubectl-plugin/config/config_root.go b/kubectl-plugin/config/config_root.go index 8d8048d..082e02f 100644 --- a/kubectl-plugin/config/config_root.go +++ b/kubectl-plugin/config/config_root.go @@ -13,6 +13,6 @@ func NewConfigRootCmd(client dynamic.Interface) (cmd *cobra.Command) { Aliases: []string{"opt"}, } - cmd.AddCommand(newClusterCmd(client)) + cmd.AddCommand(newClusterCmd(client), newMigrateCmd(client)) return } diff --git a/kubectl-plugin/config/migrate.go b/kubectl-plugin/config/migrate.go new file mode 100644 index 0000000..54d6553 --- /dev/null +++ b/kubectl-plugin/config/migrate.go @@ -0,0 +1,130 @@ +package config + +import ( + "context" + "errors" + "fmt" + "github.com/linuxsuren/ks/kubectl-plugin/types" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" + "strings" +) + +func newMigrateCmd(client dynamic.Interface) (cmd *cobra.Command) { + opt := migrateOption{ + client: client, + } + + cmd = &cobra.Command{ + Use: "migrate", + Short: "Migrate DevOps into a separate one", + PreRunE: opt.preRunE, + RunE: opt.runE, + } + + flags := cmd.Flags() + flags.BoolVarP(&opt.devops, "devops", "", true, + "Migrate DevOps") + flags.StringVarP(&opt.service, "service", "", "", + "The service address of DevOps") + flags.StringVarP(&opt.namespace, "namespace", "", "kubesphere-devops-system", + "The namespace of DevOps") + return +} + +func (o *migrateOption) preRunE(cmd *cobra.Command, args []string) (err error) { + if o.service == "" { + err = errors.New("the flag --service cannot be empty") + return + } + if o.namespace == "" { + err = errors.New("the flag --namespace cannot be empty") + } + return +} + +func (o *migrateOption) runE(cmd *cobra.Command, args []string) (err error) { + if err = o.updateKubeSphereConfig("kubesphere-config", "kubesphere-system", map[string]interface{}{ + "enable": false, + "devopsServiceAddress": o.service, + }); err != nil { + return + } + + var password string + if password, err = o.getDevOpsPassword(); err == nil { + err = o.updateKubeSphereConfig("devops-config", o.namespace, map[string]interface{}{ + "password": password, + }) + } + return +} + +func (o *migrateOption) updateKubeSphereConfig(name, namespace string, ksdataMap map[string]interface{}) (err error) { + var rawConfigMap *unstructured.Unstructured + if rawConfigMap, err = o.client.Resource(types.GetConfigMapSchema()).Namespace(namespace). + Get(context.TODO(), name, metav1.GetOptions{}); err == nil { + data := rawConfigMap.Object["data"] + dataMap := data.(map[string]interface{}) + + result := updateAuthWithObj(dataMap["kubesphere.yaml"].(string), ksdataMap) + if strings.TrimSpace(result) == "" { + err = fmt.Errorf("error happend when parse kubesphere-config") + return + } + + rawConfigMap.Object["data"] = map[string]interface{}{ + "kubesphere.yaml": result, + } + _, err = o.client.Resource(types.GetConfigMapSchema()).Namespace(namespace).Update(context.TODO(), + rawConfigMap, metav1.UpdateOptions{}) + } else { + err = fmt.Errorf("cannot found configmap kubesphere-config, %v", err) + } + return +} + +func (o *migrateOption) getDevOpsPassword() (password string, err error) { + var rawConfigMap *unstructured.Unstructured + if rawConfigMap, err = o.client.Resource(types.GetConfigMapSchema()).Namespace("kubesphere-system"). + Get(context.TODO(), "kubesphere-config", metav1.GetOptions{}); err == nil { + data := rawConfigMap.Object["data"] + dataMap := data.(map[string]interface{}) + mapData := make(map[string]interface{}) + if err := yaml.Unmarshal([]byte(dataMap["kubesphere.yaml"].(string)), mapData); err == nil { + var obj interface{} + var ok bool + var mapObj map[string]interface{} + if obj, ok = mapData["devops"]; ok { + mapObj = obj.(map[string]interface{}) + password = mapObj["password"].(string) + } + } + } else { + err = fmt.Errorf("cannot found configmap kubesphere-config, %v", err) + } + return +} + +func updateAuthWithObj(yamlf string, dataMap map[string]interface{}) string { + mapData := make(map[string]interface{}) + if err := yaml.Unmarshal([]byte(yamlf), mapData); err == nil { + var obj interface{} + var ok bool + var mapObj map[string]interface{} + if obj, ok = mapData["devops"]; ok { + mapObj = obj.(map[string]interface{}) + } else { + return "" + } + + for key, val := range dataMap { + mapObj[key] = val + } + } + resultData, _ := yaml.Marshal(mapData) + return string(resultData) +} diff --git a/kubectl-plugin/config/types.go b/kubectl-plugin/config/types.go index fbd11ff..d8dc248 100644 --- a/kubectl-plugin/config/types.go +++ b/kubectl-plugin/config/types.go @@ -10,6 +10,15 @@ type clusterOption struct { client dynamic.Interface } +type migrateOption struct { + service string + devops bool + namespace string + + // inner fields + client dynamic.Interface +} + type kubeSphereConfig struct { Authentication authentication }