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  }