From fe53c618e8bb2d365f9c023a41d1174981d12870 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Thu, 12 Jul 2018 17:19:01 -0400 Subject: [PATCH] Allow modifying current context with kubectl set-context --- pkg/kubectl/cmd/config/create_context.go | 46 ++++++++++++------- pkg/kubectl/cmd/config/create_context_test.go | 33 ++++++++++++- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/pkg/kubectl/cmd/config/create_context.go b/pkg/kubectl/cmd/config/create_context.go index d5231729165..46e30df99d9 100644 --- a/pkg/kubectl/cmd/config/create_context.go +++ b/pkg/kubectl/cmd/config/create_context.go @@ -34,6 +34,7 @@ import ( type createContextOptions struct { configAccess clientcmd.ConfigAccess name string + currContext bool cluster flag.StringFlag authInfo flag.StringFlag namespace flag.StringFlag @@ -54,23 +55,24 @@ func NewCmdConfigSetContext(out io.Writer, configAccess clientcmd.ConfigAccess) options := &createContextOptions{configAccess: configAccess} cmd := &cobra.Command{ - Use: fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace), + Use: fmt.Sprintf("set-context [NAME | --current] [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace), DisableFlagsInUseLine: true, Short: i18n.T("Sets a context entry in kubeconfig"), Long: create_context_long, Example: create_context_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(options.complete(cmd)) - exists, err := options.run() + name, exists, err := options.run() cmdutil.CheckErr(err) if exists { - fmt.Fprintf(out, "Context %q modified.\n", options.name) + fmt.Fprintf(out, "Context %q modified.\n", name) } else { - fmt.Fprintf(out, "Context %q created.\n", options.name) + fmt.Fprintf(out, "Context %q created.\n", name) } }, } + cmd.Flags().BoolVar(&options.currContext, "current", options.currContext, "Modify the current context") cmd.Flags().Var(&options.cluster, clientcmd.FlagClusterName, clientcmd.FlagClusterName+" for the context entry in kubeconfig") cmd.Flags().Var(&options.authInfo, clientcmd.FlagAuthInfoName, clientcmd.FlagAuthInfoName+" for the context entry in kubeconfig") cmd.Flags().Var(&options.namespace, clientcmd.FlagNamespace, clientcmd.FlagNamespace+" for the context entry in kubeconfig") @@ -78,29 +80,37 @@ func NewCmdConfigSetContext(out io.Writer, configAccess clientcmd.ConfigAccess) return cmd } -func (o createContextOptions) run() (bool, error) { +func (o createContextOptions) run() (string, bool, error) { err := o.validate() if err != nil { - return false, err + return "", false, err } config, err := o.configAccess.GetStartingConfig() if err != nil { - return false, err + return "", false, err } - startingStanza, exists := config.Contexts[o.name] + name := o.name + if o.currContext { + if len(config.CurrentContext) == 0 { + return "", false, errors.New("no current context is set") + } + name = config.CurrentContext + } + + startingStanza, exists := config.Contexts[name] if !exists { startingStanza = clientcmdapi.NewContext() } context := o.modifyContext(*startingStanza) - config.Contexts[o.name] = &context + config.Contexts[name] = &context if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil { - return exists, err + return name, exists, err } - return exists, nil + return name, exists, nil } func (o *createContextOptions) modifyContext(existingContext clientcmdapi.Context) clientcmdapi.Context { @@ -121,17 +131,21 @@ func (o *createContextOptions) modifyContext(existingContext clientcmdapi.Contex func (o *createContextOptions) complete(cmd *cobra.Command) error { args := cmd.Flags().Args() - if len(args) != 1 { + if len(args) > 1 { return helpErrorf(cmd, "Unexpected args: %v", args) } - - o.name = args[0] + if len(args) == 1 { + o.name = args[0] + } return nil } func (o createContextOptions) validate() error { - if len(o.name) == 0 { - return errors.New("you must specify a non-empty context name") + if len(o.name) == 0 && !o.currContext { + return errors.New("you must specify a non-empty context name or --current-context") + } + if len(o.name) > 0 && o.currContext { + return errors.New("you cannot specify a context name and --current-context") } return nil diff --git a/pkg/kubectl/cmd/config/create_context_test.go b/pkg/kubectl/cmd/config/create_context_test.go index d1187742b1b..f75a6dd7d11 100644 --- a/pkg/kubectl/cmd/config/create_context_test.go +++ b/pkg/kubectl/cmd/config/create_context_test.go @@ -28,6 +28,7 @@ import ( type createContextTest struct { description string + testContext string // name of the context being modified config clientcmdapi.Config //initiate kubectl config args []string //kubectl set-context args flags []string //kubectl set-context flags @@ -38,6 +39,7 @@ type createContextTest struct { func TestCreateContext(t *testing.T) { conf := clientcmdapi.Config{} test := createContextTest{ + testContext: "shaker-context", description: "Testing for create a new context", config: conf, args: []string{"shaker-context"}, @@ -60,6 +62,7 @@ func TestModifyContext(t *testing.T) { "shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}, "not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} test := createContextTest{ + testContext: "shaker-context", description: "Testing for modify a already exist context", config: conf, args: []string{"shaker-context"}, @@ -77,6 +80,32 @@ func TestModifyContext(t *testing.T) { test.run(t) } +func TestModifyCurrentContext(t *testing.T) { + conf := clientcmdapi.Config{ + CurrentContext: "shaker-context", + Contexts: map[string]*clientcmdapi.Context{ + "shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}, + "not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} + test := createContextTest{ + testContext: "shaker-context", + description: "Testing for modify a current context", + config: conf, + args: []string{}, + flags: []string{ + "--current", + "--cluster=cluster_nickname", + "--user=user_nickname", + "--namespace=namespace", + }, + expected: `Context "shaker-context" modified.` + "\n", + expectedConfig: clientcmdapi.Config{ + Contexts: map[string]*clientcmdapi.Context{ + "shaker-context": {AuthInfo: "user_nickname", Cluster: "cluster_nickname", Namespace: "namespace"}, + "not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}, + } + test.run(t) +} + func (test createContextTest) run(t *testing.T) { fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "") if err != nil { @@ -108,8 +137,8 @@ func (test createContextTest) run(t *testing.T) { } } if test.expectedConfig.Contexts != nil { - expectContext := test.expectedConfig.Contexts[test.args[0]] - actualContext := config.Contexts[test.args[0]] + expectContext := test.expectedConfig.Contexts[test.testContext] + actualContext := config.Contexts[test.testContext] if expectContext.AuthInfo != actualContext.AuthInfo || expectContext.Cluster != actualContext.Cluster || expectContext.Namespace != actualContext.Namespace { t.Errorf("Fail in %q:\n expected Context %v\n but found %v in kubeconfig\n", test.description, expectContext, actualContext)