From b3782f1201c486057cbc5482729074d3db753de7 Mon Sep 17 00:00:00 2001 From: zhzhuang-zju Date: Wed, 22 Nov 2023 16:10:33 +0800 Subject: [PATCH] support karmadactl config use-context to set the current-context in a kubeconfig file Signed-off-by: zhzhuang-zju --- pkg/karmadactl/config/config.go | 58 ++++++++++++++ pkg/karmadactl/config/use-context.go | 110 +++++++++++++++++++++++++++ pkg/karmadactl/karmadactl.go | 3 + 3 files changed, 171 insertions(+) create mode 100644 pkg/karmadactl/config/config.go create mode 100644 pkg/karmadactl/config/use-context.go diff --git a/pkg/karmadactl/config/config.go b/pkg/karmadactl/config/config.go new file mode 100644 index 000000000000..191f47b802c5 --- /dev/null +++ b/pkg/karmadactl/config/config.go @@ -0,0 +1,58 @@ +/* +Copyright 2023 The Karmada Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "fmt" + "path" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/tools/clientcmd" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +// NewCmdConfig creates a command object for the "config" action, and adds all child commands to it. +func NewCmdConfig(parentCommand string, pathOptions *clientcmd.PathOptions, streams genericclioptions.IOStreams) *cobra.Command { + if len(pathOptions.ExplicitFileFlag) == 0 { + pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag + } + + cmd := &cobra.Command{ + Use: "config SUBCOMMAND", + DisableFlagsInUseLine: true, + Short: i18n.T("Modify kubeconfig files"), + Long: fmt.Sprintf(templates.LongDesc(i18n.T(` + Modify kubeconfig files using subcommands like "%[1]s config set current-context my-context". + + The loading order follows these rules: + + 1. If the --`)+pathOptions.ExplicitFileFlag+i18n.T(` flag is set, then only that file is loaded. The flag may only be set once and no merging takes place. + 2. If $`)+pathOptions.EnvVar+i18n.T(` environment variable is set, then it is used as a list of paths (normal path delimiting rules for your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the last file in the list. + + 3. Otherwise, `)+path.Join("${HOME}", pathOptions.GlobalFileSubpath)+i18n.T(` is used and no merging takes place.`)), parentCommand), + Run: cmdutil.DefaultSubCommandRun(streams.ErrOut), + } + + // file paths are common to all sub commands + cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file") + + cmd.AddCommand(NewCmdConfigUseContext(parentCommand, streams, pathOptions)) + return cmd +} diff --git a/pkg/karmadactl/config/use-context.go b/pkg/karmadactl/config/use-context.go new file mode 100644 index 000000000000..fa4024f38d89 --- /dev/null +++ b/pkg/karmadactl/config/use-context.go @@ -0,0 +1,110 @@ +/* +Copyright 2023 The Karmada Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/completion" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + useContextExample = templates.Examples(` + # Use the context for the minikube cluster + %[1]s config use-context minikube`) +) + +type useContextOptions struct { + configAccess clientcmd.ConfigAccess + streams genericclioptions.IOStreams + config clientcmdapi.Config + contextName string +} + +// NewCmdConfigUseContext returns a Command instance for 'config use-context' sub command +func NewCmdConfigUseContext(parentCommand string, streams genericclioptions.IOStreams, configAccess clientcmd.ConfigAccess) *cobra.Command { + options := &useContextOptions{ + configAccess: configAccess, + streams: streams, + } + + cmd := &cobra.Command{ + Use: "use-context CONTEXT_NAME", + DisableFlagsInUseLine: true, + Short: i18n.T("Set the current-context in a kubeconfig file"), + Aliases: []string{"use"}, + Long: `Set the current-context in a kubeconfig file.`, + Example: fmt.Sprintf(useContextExample, parentCommand), + ValidArgsFunction: completion.ContextCompletionFunc, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(options.complete(cmd)) + cmdutil.CheckErr(options.validate()) + cmdutil.CheckErr(options.run()) + fmt.Fprintf(streams.Out, "Switched to context %q.\n", options.contextName) + }, + } + + return cmd +} + +func (o *useContextOptions) complete(cmd *cobra.Command) error { + endingArgs := cmd.Flags().Args() + if len(endingArgs) != 1 { + err := cmd.Help() + if err != nil { + fmt.Fprintf(o.streams.Out, "puts out the help for the command karmadactl config use-context failed: %s", err.Error()) + } + return fmt.Errorf("Unexpected args: %v", endingArgs) + } + + o.contextName = endingArgs[0] + return nil +} + +func (o *useContextOptions) validate() error { + if len(o.contextName) == 0 { + return errors.New("empty context names are not allowed") + } + + config, err := o.configAccess.GetStartingConfig() + if err != nil { + return err + } + o.config = *config + + for name := range config.Contexts { + if name == o.contextName { + return nil + } + } + + return fmt.Errorf("no context exists with the name: %q", o.contextName) +} + +func (o *useContextOptions) run() error { + o.config.CurrentContext = o.contextName + + return clientcmd.ModifyConfig(o.configAccess, o.config, true) +} diff --git a/pkg/karmadactl/karmadactl.go b/pkg/karmadactl/karmadactl.go index 653ad010f0b0..e1a57efd04e7 100644 --- a/pkg/karmadactl/karmadactl.go +++ b/pkg/karmadactl/karmadactl.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/tools/clientcmd" apiserverflag "k8s.io/component-base/cli/flag" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/util/templates" @@ -31,6 +32,7 @@ import ( "github.com/karmada-io/karmada/pkg/karmadactl/addons" "github.com/karmada-io/karmada/pkg/karmadactl/apply" "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit" + "github.com/karmada-io/karmada/pkg/karmadactl/config" "github.com/karmada-io/karmada/pkg/karmadactl/cordon" "github.com/karmada-io/karmada/pkg/karmadactl/deinit" "github.com/karmada-io/karmada/pkg/karmadactl/describe" @@ -133,6 +135,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command { rootCmd.AddCommand(sharedcommand.NewCmdVersion(parentCommand)) rootCmd.AddCommand(options.NewCmdOptions(parentCommand, ioStreams.Out)) + rootCmd.AddCommand(config.NewCmdConfig(parentCommand, clientcmd.NewDefaultPathOptions(), ioStreams)) templates.ActsAsRootCommand(rootCmd, filters, groups...)