github.com/oam-dev/kubevela@v1.9.11/references/cli/config.go (about)

     1  /*
     2  Copyright 2022 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  	"bufio"
    21  	"bytes"
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  
    27  	"github.com/spf13/cobra"
    28  	"helm.sh/helm/v3/pkg/strvals"
    29  	"k8s.io/kubectl/pkg/util/i18n"
    30  	"k8s.io/kubectl/pkg/util/templates"
    31  	"sigs.k8s.io/yaml"
    32  
    33  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    34  
    35  	"github.com/oam-dev/kubevela/apis/types"
    36  	velacmd "github.com/oam-dev/kubevela/pkg/cmd"
    37  	"github.com/oam-dev/kubevela/pkg/config"
    38  	pkgUtils "github.com/oam-dev/kubevela/pkg/utils"
    39  	"github.com/oam-dev/kubevela/pkg/utils/util"
    40  	"github.com/oam-dev/kubevela/references/docgen"
    41  )
    42  
    43  // ConfigCommandGroup commands for the config
    44  func ConfigCommandGroup(f velacmd.Factory, order string, streams util.IOStreams) *cobra.Command {
    45  	cmd := &cobra.Command{
    46  		Use:   "config",
    47  		Short: i18n.T("Manage the configs."),
    48  		Long:  i18n.T("Manage the configs, such as the terraform provider, image registry, helm repository, etc."),
    49  		Annotations: map[string]string{
    50  			types.TagCommandType:  types.TypePlatform,
    51  			types.TagCommandOrder: order,
    52  		},
    53  	}
    54  	cmd.AddCommand(NewListConfigCommand(f, streams))
    55  	cmd.AddCommand(NewCreateConfigCommand(f, streams))
    56  	cmd.AddCommand(NewDistributeConfigCommand(f, streams))
    57  	cmd.AddCommand(NewDeleteConfigCommand(f, streams))
    58  	return cmd
    59  }
    60  
    61  // TemplateCommandGroup commands for the template of the config
    62  func TemplateCommandGroup(f velacmd.Factory, order string, streams util.IOStreams) *cobra.Command {
    63  	cmd := &cobra.Command{
    64  		Use:     "config-template",
    65  		Aliases: []string{"ct"},
    66  		Short:   i18n.T("Manage the template of config."),
    67  		Annotations: map[string]string{
    68  			types.TagCommandType:  types.TypePlatform,
    69  			types.TagCommandOrder: order,
    70  		},
    71  	}
    72  	cmd.AddCommand(NewTemplateApplyCommand(f, streams))
    73  	cmd.AddCommand(NewTemplateListCommand(f, streams))
    74  	cmd.AddCommand(NewTemplateDeleteCommand(f, streams))
    75  	cmd.AddCommand(NewTemplateShowCommand(f, streams))
    76  	return cmd
    77  }
    78  
    79  // TemplateApplyCommandOptions the options of the command that apply the config template.
    80  type TemplateApplyCommandOptions struct {
    81  	File      string
    82  	Namespace string
    83  	Name      string
    84  }
    85  
    86  // TemplateCommandOptions the options of the command that delete or show the config template.
    87  type TemplateCommandOptions struct {
    88  	Namespace string
    89  	Name      string
    90  }
    91  
    92  // TemplateListCommandOptions the options of the command that list the config templates.
    93  type TemplateListCommandOptions struct {
    94  	Namespace    string
    95  	AllNamespace bool
    96  }
    97  
    98  // NewTemplateApplyCommand command for creating and updating the config template
    99  func NewTemplateApplyCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   100  	var options TemplateApplyCommandOptions
   101  	cmd := &cobra.Command{
   102  		Use:   "apply",
   103  		Short: i18n.T("Apply a config template."),
   104  		Annotations: map[string]string{
   105  			types.TagCommandType: types.TypeExtension,
   106  		},
   107  		Args: cobra.ExactArgs(0),
   108  		RunE: func(cmd *cobra.Command, args []string) error {
   109  			body, err := pkgUtils.ReadRemoteOrLocalPath(options.File, false)
   110  			if err != nil {
   111  				return err
   112  			}
   113  			inf := config.NewConfigFactory(f.Client())
   114  			template, err := inf.ParseTemplate(options.Name, body)
   115  			if err != nil {
   116  				return err
   117  			}
   118  			if err := inf.CreateOrUpdateConfigTemplate(context.Background(), options.Namespace, template); err != nil {
   119  				return err
   120  			}
   121  			streams.Infof("the config template %s applied successfully\n", template.Name)
   122  			return nil
   123  		},
   124  	}
   125  	cmd.Flags().StringVarP(&options.File, "file", "f", "", "specify the template file name")
   126  	cmd.Flags().StringVarP(&options.Name, "name", "", "", "specify the config template name")
   127  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the template")
   128  	return cmd
   129  }
   130  
   131  // NewTemplateListCommand command for listing the config templates
   132  func NewTemplateListCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   133  	var options TemplateListCommandOptions
   134  	cmd := &cobra.Command{
   135  		Use:     "list",
   136  		Short:   i18n.T("List the config templates."),
   137  		Example: "vela config template list [-A]",
   138  		Annotations: map[string]string{
   139  			types.TagCommandType: types.TypeExtension,
   140  		},
   141  		Args: cobra.ExactArgs(0),
   142  		RunE: func(cmd *cobra.Command, args []string) error {
   143  			inf := config.NewConfigFactory(f.Client())
   144  			if options.AllNamespace {
   145  				options.Namespace = ""
   146  			}
   147  			templateList, err := inf.ListTemplates(context.Background(), options.Namespace, "")
   148  			if err != nil {
   149  				return err
   150  			}
   151  			table := newUITable()
   152  			header := []interface{}{"NAME", "ALIAS", "SCOPE", "SENSITIVE", "CREATED-TIME"}
   153  			if options.AllNamespace {
   154  				header = append([]interface{}{"NAMESPACE"}, header...)
   155  			}
   156  			table.AddRow(header...)
   157  			for _, t := range templateList {
   158  				row := []interface{}{t.Name, t.Alias, t.Scope, t.Sensitive, t.CreateTime}
   159  				if options.AllNamespace {
   160  					row = append([]interface{}{t.Namespace}, row...)
   161  				}
   162  				table.AddRow(row...)
   163  			}
   164  			if _, err := streams.Out.Write(table.Bytes()); err != nil {
   165  				return err
   166  			}
   167  			if _, err := streams.Out.Write([]byte("\n")); err != nil {
   168  				return err
   169  			}
   170  			return nil
   171  		},
   172  	}
   173  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the template")
   174  	cmd.Flags().BoolVarP(&options.AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
   175  	return cmd
   176  }
   177  
   178  // NewTemplateShowCommand command for show the properties document
   179  func NewTemplateShowCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   180  	var options TemplateCommandOptions
   181  	cmd := &cobra.Command{
   182  		Use:     "show",
   183  		Short:   i18n.T("Show the documents of the template properties"),
   184  		Example: "vela config-template show <name> [-n]",
   185  		Annotations: map[string]string{
   186  			types.TagCommandType: types.TypeExtension,
   187  		},
   188  		Args: cobra.ExactArgs(1),
   189  		RunE: func(cmd *cobra.Command, args []string) error {
   190  			options.Name = args[0]
   191  			if options.Name == "" {
   192  				return fmt.Errorf("can not show the properties without the template")
   193  			}
   194  			inf := config.NewConfigFactory(f.Client())
   195  			template, err := inf.LoadTemplate(context.Background(), options.Name, options.Namespace)
   196  			if err != nil {
   197  				return err
   198  			}
   199  			doc, err := docgen.GenerateConsoleDocument(template.Schema.Title, template.Schema)
   200  			if err != nil {
   201  				return err
   202  			}
   203  			if _, err := streams.Out.Write([]byte(doc)); err != nil {
   204  				return err
   205  			}
   206  			return nil
   207  		},
   208  	}
   209  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the template")
   210  	return cmd
   211  }
   212  
   213  // NewTemplateDeleteCommand command for deleting the config template
   214  func NewTemplateDeleteCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   215  	var options TemplateCommandOptions
   216  	cmd := &cobra.Command{
   217  		Use:     "delete",
   218  		Short:   i18n.T("Delete a config template."),
   219  		Example: fmt.Sprintf("vela config template delete <name> [-n %s]", types.DefaultKubeVelaNS),
   220  		Annotations: map[string]string{
   221  			types.TagCommandType: types.TypeCD,
   222  		},
   223  		Args: cobra.ExactArgs(1),
   224  		RunE: func(cmd *cobra.Command, args []string) error {
   225  			if len(args) == 0 {
   226  				return fmt.Errorf("please must provides the template name")
   227  			}
   228  			options.Name = args[0]
   229  			userInput := &UserInput{
   230  				Writer: streams.Out,
   231  				Reader: bufio.NewReader(streams.In),
   232  			}
   233  			if !assumeYes {
   234  				userConfirmation := userInput.AskBool("Do you want to delete this template", &UserInputOptions{assumeYes})
   235  				if !userConfirmation {
   236  					return fmt.Errorf("stopping deleting")
   237  				}
   238  			}
   239  			inf := config.NewConfigFactory(f.Client())
   240  			if err := inf.DeleteTemplate(context.Background(), options.Namespace, options.Name); err != nil {
   241  				return err
   242  			}
   243  			streams.Infof("the config template %s deleted successfully\n", options.Name)
   244  			return nil
   245  		},
   246  	}
   247  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the template")
   248  	return cmd
   249  }
   250  
   251  // DistributeConfigCommandOptions the options of the command that distribute the config.
   252  type DistributeConfigCommandOptions struct {
   253  	Targets   []string
   254  	Config    string
   255  	Namespace string
   256  	Recalled  bool
   257  }
   258  
   259  // CreateConfigCommandOptions the options of the command that create the config.
   260  type CreateConfigCommandOptions struct {
   261  	Template    string
   262  	Namespace   string
   263  	Name        string
   264  	File        string
   265  	Properties  map[string]interface{}
   266  	DryRun      bool
   267  	Targets     []string
   268  	Description string
   269  	Alias       string
   270  }
   271  
   272  // Validate validate the options
   273  func (i CreateConfigCommandOptions) Validate() error {
   274  	if i.Name == "" {
   275  		return fmt.Errorf("the config name must be specified")
   276  	}
   277  	if len(i.Targets) > 0 && i.DryRun {
   278  		return fmt.Errorf("can not set the distribution in dry-run mode")
   279  	}
   280  	return nil
   281  }
   282  
   283  func (i *CreateConfigCommandOptions) parseProperties(args []string) error {
   284  	if i.File != "" {
   285  		body, err := pkgUtils.ReadRemoteOrLocalPath(i.File, false)
   286  		if err != nil {
   287  			return err
   288  		}
   289  		var properties = map[string]interface{}{}
   290  		if err := yaml.Unmarshal(body, &properties); err != nil {
   291  			return err
   292  		}
   293  		i.Properties = properties
   294  		return nil
   295  	}
   296  	res := map[string]interface{}{}
   297  	for _, arg := range args {
   298  		if err := strvals.ParseInto(arg, res); err != nil {
   299  			return err
   300  		}
   301  	}
   302  	i.Properties = res
   303  	return nil
   304  }
   305  
   306  // ConfigListCommandOptions the options of the command that list configs.
   307  type ConfigListCommandOptions struct {
   308  	Namespace    string
   309  	Template     string
   310  	AllNamespace bool
   311  }
   312  
   313  // NewListConfigCommand command for listing the config secrets
   314  func NewListConfigCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   315  	var options ConfigListCommandOptions
   316  	cmd := &cobra.Command{
   317  		Use:   "list",
   318  		Short: i18n.T("List the configs."),
   319  		Annotations: map[string]string{
   320  			types.TagCommandType: types.TypeCD,
   321  		},
   322  		Args: cobra.ExactArgs(0),
   323  		RunE: func(cmd *cobra.Command, args []string) error {
   324  			name := options.Template
   325  			if strings.Contains(options.Template, "/") {
   326  				namespacedName := strings.SplitN(options.Template, "/", 2)
   327  				name = namespacedName[1]
   328  			}
   329  			if options.AllNamespace {
   330  				options.Namespace = ""
   331  			}
   332  			inf := config.NewConfigFactory(f.Client())
   333  			configs, err := inf.ListConfigs(context.Background(), options.Namespace, name, "", true)
   334  			if err != nil {
   335  				return err
   336  			}
   337  			table := newUITable()
   338  			header := []interface{}{"NAME", "ALIAS", "DISTRIBUTION", "TEMPLATE", "CREATED-TIME", "DESCRIPTION"}
   339  			if options.AllNamespace {
   340  				header = append([]interface{}{"NAMESPACE"}, header...)
   341  			}
   342  			table.AddRow(header...)
   343  			for _, t := range configs {
   344  				var targetShow = ""
   345  				for _, target := range t.Targets {
   346  					if targetShow != "" {
   347  						targetShow += " "
   348  					}
   349  					switch target.Status {
   350  					case string(workflowv1alpha1.WorkflowStepPhaseSucceeded):
   351  						targetShow += green.Sprintf("%s/%s", target.ClusterName, target.Namespace)
   352  					case string(workflowv1alpha1.WorkflowStepPhaseFailed):
   353  						targetShow += red.Sprintf("%s/%s", target.ClusterName, target.Namespace)
   354  					default:
   355  						targetShow += yellow.Sprintf("%s/%s", target.ClusterName, target.Namespace)
   356  					}
   357  				}
   358  				row := []interface{}{t.Name, t.Alias, targetShow, fmt.Sprintf("%s/%s", t.Template.Namespace, t.Template.Name), t.CreateTime, t.Description}
   359  				if options.AllNamespace {
   360  					row = append([]interface{}{t.Namespace}, row...)
   361  				}
   362  				table.AddRow(row...)
   363  			}
   364  			if _, err := streams.Out.Write(table.Bytes()); err != nil {
   365  				return err
   366  			}
   367  			if _, err := streams.Out.Write([]byte("\n")); err != nil {
   368  				return err
   369  			}
   370  			return nil
   371  		},
   372  	}
   373  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the config")
   374  	cmd.Flags().StringVarP(&options.Template, "template", "t", "", "specify the template of the config")
   375  	cmd.Flags().BoolVarP(&options.AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
   376  	return cmd
   377  }
   378  
   379  // NewCreateConfigCommand command for creating the config
   380  func NewCreateConfigCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   381  	var options CreateConfigCommandOptions
   382  	createConfigExample := templates.Examples(i18n.T(`
   383  		# Generate a config with the args
   384  		vela config create test-registry --template=image-registry registry=index.docker.io auth.username=test auth.password=test
   385  		
   386  		# Generate a config with the file
   387  		vela config create test-config --template=image-registry  -f config.yaml
   388  
   389  		# Generate a config without the template
   390  		vela config create test-vela -f config.yaml
   391  		`))
   392  
   393  	cmd := &cobra.Command{
   394  		Use:     "create",
   395  		Aliases: []string{"c"},
   396  		Short:   i18n.T("Create a config."),
   397  		Example: createConfigExample,
   398  		Annotations: map[string]string{
   399  			types.TagCommandType: types.TypeCD,
   400  		},
   401  		RunE: func(cmd *cobra.Command, args []string) error {
   402  			inf := config.NewConfigFactory(f.Client())
   403  			options.Name = args[0]
   404  			if err := options.Validate(); err != nil {
   405  				return err
   406  			}
   407  			name := options.Template
   408  			namespace := types.DefaultKubeVelaNS
   409  			if strings.Contains(options.Template, "/") {
   410  				namespacedName := strings.SplitN(options.Template, "/", 2)
   411  				namespace = namespacedName[0]
   412  				name = namespacedName[1]
   413  			}
   414  			if err := options.parseProperties(args[1:]); err != nil {
   415  				return err
   416  			}
   417  			configItem, err := inf.ParseConfig(context.Background(), config.NamespacedName{
   418  				Name:      name,
   419  				Namespace: namespace,
   420  			}, config.Metadata{
   421  				NamespacedName: config.NamespacedName{
   422  					Name:      options.Name,
   423  					Namespace: options.Namespace,
   424  				},
   425  				Properties:  options.Properties,
   426  				Alias:       options.Alias,
   427  				Description: options.Description,
   428  			})
   429  			if err != nil {
   430  				return err
   431  			}
   432  			if options.DryRun {
   433  				var outBuilder = bytes.NewBuffer(nil)
   434  				out, err := yaml.Marshal(configItem.Secret)
   435  				if err != nil {
   436  					return err
   437  				}
   438  				_, err = outBuilder.Write(out)
   439  				if err != nil {
   440  					return err
   441  				}
   442  				if configItem.OutputObjects != nil {
   443  					for k, object := range configItem.OutputObjects {
   444  						_, err = outBuilder.WriteString("# Object: \n ---" + k)
   445  						if err != nil {
   446  							return err
   447  						}
   448  						out, err := yaml.Marshal(object)
   449  						if err != nil {
   450  							return err
   451  						}
   452  						if _, err := outBuilder.Write(out); err != nil {
   453  							return err
   454  						}
   455  					}
   456  				}
   457  				_, err = streams.Out.Write(outBuilder.Bytes())
   458  				return err
   459  			}
   460  			if err := inf.CreateOrUpdateConfig(context.Background(), configItem, options.Namespace); err != nil {
   461  				return err
   462  			}
   463  			if len(options.Targets) > 0 {
   464  				ads := &config.CreateDistributionSpec{
   465  					Targets: []*config.ClusterTarget{},
   466  					Configs: []*config.NamespacedName{
   467  						&configItem.NamespacedName,
   468  					},
   469  				}
   470  				for _, t := range options.Targets {
   471  					ti := strings.Split(t, "/")
   472  					if len(ti) == 2 {
   473  						ads.Targets = append(ads.Targets, &config.ClusterTarget{
   474  							ClusterName: ti[0],
   475  							Namespace:   ti[1],
   476  						})
   477  					} else {
   478  						ads.Targets = append(ads.Targets, &config.ClusterTarget{
   479  							ClusterName: types.ClusterLocalName,
   480  							Namespace:   ti[0],
   481  						})
   482  					}
   483  				}
   484  				name := config.DefaultDistributionName(options.Name)
   485  				if err := inf.CreateOrUpdateDistribution(context.Background(), options.Namespace, name, ads); err != nil {
   486  					return err
   487  				}
   488  			}
   489  			streams.Infof("the config %s applied successfully\n", options.Name)
   490  			return nil
   491  		},
   492  	}
   493  	cmd.Flags().StringVarP(&options.Template, "template", "t", "", "specify the config template name and namespace")
   494  	cmd.Flags().StringVarP(&options.File, "file", "f", "", "specify the file name of the config properties")
   495  	cmd.Flags().StringArrayVarP(&options.Targets, "target", "", []string{}, "this config will be distributed if this flag is set")
   496  	cmd.Flags().BoolVarP(&options.DryRun, "dry-run", "", false, "Dry run to apply the config")
   497  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the config")
   498  	cmd.Flags().StringVarP(&options.Description, "description", "", "", "specify the description of the config")
   499  	cmd.Flags().StringVarP(&options.Alias, "alias", "", "", "specify the alias of the config")
   500  	return cmd
   501  }
   502  
   503  // NewDistributeConfigCommand command for distributing the config
   504  func NewDistributeConfigCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   505  	var options DistributeConfigCommandOptions
   506  
   507  	distributionExample := templates.Examples(i18n.T(`
   508  		# distribute the config(test-registry) from the vela-system namespace to the default namespace in the local cluster.
   509  		vela config d test-registry -t default
   510  
   511  		# distribute the config(test-registry) from the vela-system namespace to the other clusters.
   512  		vela config d test-registry -t cluster1/default -t cluster2/default
   513  
   514  		# recall the config
   515  		vela config d test-registry --recall
   516  		`))
   517  
   518  	cmd := &cobra.Command{
   519  		Use:     "distribute",
   520  		Aliases: []string{"d"},
   521  		Short:   i18n.T("Distribute a config"),
   522  		Example: distributionExample,
   523  		Annotations: map[string]string{
   524  			types.TagCommandType: types.TypeCD,
   525  		},
   526  		Args: cobra.ExactArgs(1),
   527  		RunE: func(cmd *cobra.Command, args []string) error {
   528  			inf := config.NewConfigFactory(f.Client())
   529  			options.Config = args[0]
   530  			name := config.DefaultDistributionName(options.Config)
   531  			if options.Recalled {
   532  				userInput := &UserInput{
   533  					Writer: streams.Out,
   534  					Reader: bufio.NewReader(streams.In),
   535  				}
   536  				if !assumeYes {
   537  					userConfirmation := userInput.AskBool("Do you want to recall this config", &UserInputOptions{assumeYes})
   538  					if !userConfirmation {
   539  						return fmt.Errorf("recalling stopped")
   540  					}
   541  				}
   542  				if err := inf.DeleteDistribution(context.Background(), options.Namespace, name); err != nil {
   543  					return err
   544  				}
   545  				streams.Infof("the distribution %s deleted successfully\n", name)
   546  				return nil
   547  			}
   548  
   549  			ads := &config.CreateDistributionSpec{
   550  				Targets: []*config.ClusterTarget{},
   551  				Configs: []*config.NamespacedName{
   552  					{
   553  						Name:      options.Config,
   554  						Namespace: options.Namespace,
   555  					},
   556  				},
   557  			}
   558  			for _, t := range options.Targets {
   559  				ti := strings.Split(t, "/")
   560  				if len(ti) == 2 {
   561  					ads.Targets = append(ads.Targets, &config.ClusterTarget{
   562  						ClusterName: ti[0],
   563  						Namespace:   ti[1],
   564  					})
   565  				} else {
   566  					ads.Targets = append(ads.Targets, &config.ClusterTarget{
   567  						ClusterName: types.ClusterLocalName,
   568  						Namespace:   ti[0],
   569  					})
   570  				}
   571  			}
   572  			if err := inf.CreateOrUpdateDistribution(context.Background(), options.Namespace, name, ads); err != nil {
   573  				return err
   574  			}
   575  			streams.Infof("the distribution %s applied successfully\n", name)
   576  			return nil
   577  		},
   578  	}
   579  	cmd.Flags().StringArrayVarP(&options.Targets, "target", "t", []string{}, "specify the targets that want to distribute,the format is: <clusterName>/<namespace>")
   580  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the distribution")
   581  	cmd.Flags().BoolVarP(&options.Recalled, "recall", "r", false, "this field means recalling the configs from all targets.")
   582  	return cmd
   583  }
   584  
   585  // ConfigDeleteCommandOptions the options of the command that delete the config.
   586  type ConfigDeleteCommandOptions struct {
   587  	Namespace string
   588  	Name      string
   589  	NotRecall bool
   590  }
   591  
   592  // NewDeleteConfigCommand command for deleting the config secret
   593  func NewDeleteConfigCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
   594  	var options ConfigDeleteCommandOptions
   595  	cmd := &cobra.Command{
   596  		Use:   "delete",
   597  		Short: i18n.T("Delete a config."),
   598  		Annotations: map[string]string{
   599  			types.TagCommandType: types.TypeCD,
   600  		},
   601  		Args: cobra.ExactArgs(1),
   602  		RunE: func(cmd *cobra.Command, args []string) error {
   603  			options.Name = args[0]
   604  			inf := config.NewConfigFactory(f.Client())
   605  			userInput := &UserInput{
   606  				Writer: streams.Out,
   607  				Reader: bufio.NewReader(streams.In),
   608  			}
   609  			if !assumeYes {
   610  				userConfirmation := userInput.AskBool("Do you want to delete this config", &UserInputOptions{assumeYes})
   611  				if !userConfirmation {
   612  					return fmt.Errorf("deleting stopped")
   613  				}
   614  			}
   615  
   616  			if !options.NotRecall {
   617  				if err := inf.DeleteDistribution(context.Background(), options.Namespace, config.DefaultDistributionName(options.Name)); err != nil && !errors.Is(err, config.ErrNotFoundDistribution) {
   618  					return err
   619  				}
   620  			}
   621  
   622  			if err := inf.DeleteConfig(context.Background(), options.Namespace, options.Name); err != nil {
   623  				return err
   624  			}
   625  
   626  			streams.Infof("the config %s deleted successfully\n", options.Name)
   627  			return nil
   628  		},
   629  	}
   630  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", types.DefaultKubeVelaNS, "specify the namespace of the config")
   631  	cmd.Flags().BoolVarP(&options.NotRecall, "not-recall", "", false, "means only deleting the config from the local and do not recall from targets.")
   632  	return cmd
   633  }