github.com/oam-dev/kubevela@v1.9.11/references/cli/uischema.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cli 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "os" 25 "path" 26 "path/filepath" 27 "strings" 28 29 "github.com/spf13/cobra" 30 v1 "k8s.io/api/core/v1" 31 apierrors "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 k8stypes "k8s.io/apimachinery/pkg/types" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/yaml" 36 37 "github.com/oam-dev/kubevela/apis/types" 38 "github.com/oam-dev/kubevela/pkg/utils/common" 39 "github.com/oam-dev/kubevela/pkg/utils/schema" 40 "github.com/oam-dev/kubevela/pkg/utils/util" 41 ) 42 43 // NewUISchemaCommand creates `uischema` command 44 func NewUISchemaCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command { 45 cmd := &cobra.Command{ 46 Use: "uischema", 47 Aliases: []string{"ui"}, 48 Short: "Manage UI schema for addons.", 49 Long: "Manage UI schema for addons.", 50 Annotations: map[string]string{ 51 types.TagCommandOrder: order, 52 types.TagCommandType: types.TypeExtension, 53 }, 54 } 55 cmd.AddCommand(&cobra.Command{ 56 Use: "apply", 57 Short: "apply <ui schema file/dir path>", 58 Args: cobra.ExactArgs(1), 59 Long: "apply UI schema from a file or dir", 60 Annotations: map[string]string{ 61 types.TagCommandType: types.TypeExtension, 62 }, 63 RunE: func(cmd *cobra.Command, args []string) error { 64 if len(args) == 0 { 65 return errors.New("please provider the ui schema file or dir path") 66 } 67 allUISchemaFiles, err := loadUISchemaFiles(args[0]) 68 if err != nil { 69 return err 70 } 71 client, err := c.GetClient() 72 if err != nil { 73 return err 74 } 75 for _, file := range allUISchemaFiles { 76 if err := applyUISchemaFile(client, file); err != nil { 77 ioStreams.Errorf("apply %s failure %s \n", file, err.Error()) 78 continue 79 } 80 ioStreams.Infof("apply %s success \n", file) 81 } 82 return nil 83 }, 84 }) 85 86 return cmd 87 } 88 89 func loadUISchemaFiles(setpath string) ([]string, error) { 90 schema, err := os.Stat(setpath) 91 if err != nil { 92 return nil, err 93 } 94 var readDir func(string) ([]string, error) 95 readDir = func(filepath string) ([]string, error) { 96 files, err := os.ReadDir(filepath) 97 if err != nil { 98 return nil, err 99 } 100 var subFiles []string 101 for _, f := range files { 102 if f.IsDir() { 103 files, err := readDir(path.Join(filepath, f.Name())) 104 if err != nil { 105 return nil, err 106 } 107 subFiles = append(subFiles, files...) 108 } else if strings.HasSuffix(f.Name(), ".yaml") { 109 subFiles = append(subFiles, path.Join(filepath, f.Name())) 110 } 111 } 112 return subFiles, nil 113 } 114 var allUISchemaFiles = []string{} 115 if schema.IsDir() { 116 allUISchemaFiles, err = readDir(setpath) 117 if err != nil { 118 return nil, err 119 } 120 } else { 121 allUISchemaFiles = append(allUISchemaFiles, setpath) 122 } 123 return allUISchemaFiles, nil 124 } 125 126 func applyUISchemaFile(client client.Client, uischemaFile string) error { 127 cdata, err := os.ReadFile(filepath.Clean(uischemaFile)) 128 if err != nil { 129 return err 130 } 131 fileBaseName := path.Base(uischemaFile) 132 fileBaseNameWithoutExt := fileBaseName[:strings.Index(fileBaseName, ".")] // nolint 133 134 infos := strings.SplitN(fileBaseNameWithoutExt, "-", func() int { 135 if strings.Contains(fileBaseNameWithoutExt, "uischema") { 136 return 3 137 } 138 return 2 139 }()) 140 if len(infos) == 2 || len(infos) == 3 && infos[1] == "uischema" { 141 name := infos[1] 142 if len(infos) == 3 { 143 name = infos[2] 144 } 145 err = addDefinitionUISchema(context.Background(), client, name, infos[0], string(cdata)) 146 if err != nil { 147 return err 148 } 149 return nil 150 } 151 return fmt.Errorf("file name is invalid") 152 } 153 154 func addDefinitionUISchema(ctx context.Context, client client.Client, name, defType, configRaw string) error { 155 var uiParameters []*schema.UIParameter 156 err := yaml.Unmarshal([]byte(configRaw), &uiParameters) 157 if err != nil { 158 return err 159 } 160 dataBate, err := json.Marshal(uiParameters) 161 if err != nil { 162 return err 163 } 164 165 var cm v1.ConfigMap 166 if err := client.Get(ctx, k8stypes.NamespacedName{ 167 Namespace: types.DefaultKubeVelaNS, 168 Name: fmt.Sprintf("%s-uischema-%s", defType, name), 169 }, &cm); err != nil { 170 if apierrors.IsNotFound(err) { 171 err = client.Create(ctx, &v1.ConfigMap{ 172 ObjectMeta: metav1.ObjectMeta{ 173 Namespace: types.DefaultKubeVelaNS, 174 Name: fmt.Sprintf("%s-uischema-%s", defType, name), 175 }, 176 Data: map[string]string{ 177 types.UISchema: string(dataBate), 178 }, 179 }) 180 } 181 if err != nil { 182 return err 183 } 184 } else { 185 cm.Data[types.UISchema] = string(dataBate) 186 err := client.Update(ctx, &cm) 187 if err != nil { 188 return err 189 } 190 } 191 return nil 192 }