diff --git a/cmd/rename.go b/cmd/rename.go new file mode 100644 index 0000000..931e2ab --- /dev/null +++ b/cmd/rename.go @@ -0,0 +1,81 @@ +package cmd + +import ( + "fmt" + + "github.com/fatih/color" + + "github.com/gjbae1212/kubectl-cred/internal" + "github.com/spf13/cobra" +) + +var ( + renameCmd = &cobra.Command{ + Use: "rename", + Short: "Rename context name in k8s config, using an interactive CLI.", + Long: "Rename context name in k8s config, using an interactive CLI.", + PreRun: renamePreRun(), + Run: renameRun(), + } +) + +func renamePreRun() commandRun { + return func(cmd *cobra.Command, args []string) {} +} + +func renameRun() commandRun { + return func(cmd *cobra.Command, args []string) { + contexts, err := kubeConfig.GetContexts() + if err != nil { + internal.PanicWithRed(fmt.Errorf("[err] failed getting current kubernetes context. %w", err)) + } + + currentContext, err := kubeConfig.GetCurrentContext() + if err != nil { + internal.PanicWithRed(fmt.Errorf("[err] failed getting current kubernetes context. %w", err)) + } + + outdatedContextName, err := askContext(contexts) + if err != nil { + internal.PanicWithRed(fmt.Errorf("[err] failed context selecting")) + } + + var needCurrentContext bool + if currentContext.Name == outdatedContextName { + needCurrentContext = true + } + + wantedContextName := askWantedContextName() + if wantedContextName == "" { + internal.PanicWithRed(fmt.Errorf("[err] failed invalid context name")) + } else { + if err := kubeConfig.ChangeContextName(outdatedContextName, wantedContextName); err != nil { + internal.PanicWithRed(fmt.Errorf("[err] failed changing context name")) + } + } + + if needCurrentContext { + if err := kubeConfig.SetCurrentContext(wantedContextName); err != nil { + internal.PanicWithRed(fmt.Errorf("[err] failed changing context name")) + } + } + if ok := askConfirmChangingContextName(outdatedContextName, wantedContextName); !ok { + return + } + + if err := kubeConfig.Sync(); err != nil { + internal.PanicWithRed(fmt.Errorf("[err] config sync error")) + } + fmt.Printf("%s %s %s, %s %s\n", + color.GreenString("[success]"), + color.YellowString("change context:"), + color.CyanString(outdatedContextName), + color.YellowString("to context:"), + color.CyanString(wantedContextName), + ) + } +} + +func init() { + rootCmd.AddCommand(renameCmd) +} diff --git a/cmd/rename_test.go b/cmd/rename_test.go new file mode 100644 index 0000000..1d619dd --- /dev/null +++ b/cmd/rename_test.go @@ -0,0 +1 @@ +package cmd diff --git a/cmd/root.go b/cmd/root.go index 8565589..544e894 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "sort" + "strings" "github.com/AlecAivazis/survey/v2" "github.com/fatih/color" @@ -18,12 +19,18 @@ var ( Example: ` # Switch context using an interactive CLI. kubectl cred ctx + # Switch namespace using an interactive CLI. kubectl cred ns + # Show your all of contexts information formatted tree. kubectl cred ls -# Show your current context information + +# Show your current context information. kubectl cred current + +# Rename context name in k8s config, using an interactive CLI. +kubectl cred rename `, } ) @@ -105,3 +112,22 @@ func askNamespace(contextName string, namespaces []string) (string, error) { return selectKey, nil } + +func askWantedContextName() string { + prompt := &survey.Input{ + Message: "Type to your wanted to change context name:", + } + var contextName string + survey.AskOne(prompt, &contextName) + contextName = strings.TrimSpace(contextName) + return contextName +} + +func askConfirmChangingContextName(currentContextName, changeContextName string) bool { + ok := false + prompt := &survey.Confirm{ + Message: fmt.Sprintf("Do you really want to change %s to %s", currentContextName, changeContextName), + } + survey.AskOne(prompt, &ok) + return ok +} diff --git a/internal/kubeconfig.go b/internal/kubeconfig.go index f620fc2..2ae7bd8 100644 --- a/internal/kubeconfig.go +++ b/internal/kubeconfig.go @@ -85,6 +85,18 @@ func (k *KubeConfig) SetNamespace(ctxName, namespace string) error { return nil } +// ChangeContextName changes context name in context section. +func (k *KubeConfig) ChangeContextName(ctxName, changedCtxName string) error { + node, err := k.rootNode.Query(fmt.Sprintf(".contexts.(.name=%s)", ctxName)) + if err != nil { + return err + } + if err := node.Put(".name", changedCtxName); err != nil { + return err + } + return nil +} + // GetContexts returns kubernetes context list. func (k *KubeConfig) GetContexts() ([]*KubeContext, error) { // get cluster nodes. diff --git a/internal/kubeconfig_test.go b/internal/kubeconfig_test.go index c7a5e97..ec8904e 100644 --- a/internal/kubeconfig_test.go +++ b/internal/kubeconfig_test.go @@ -135,6 +135,34 @@ func TestKubeConfig_SetNamespace(t *testing.T) { } } +func TestKubeConfig_ChangeContextName(t *testing.T) { + assert := assert.New(t) + + cfgPath, err := KubeConfigPath() + if err != nil { + return + } + + kubeConfig, err := NewKubeConfig(cfgPath) + assert.NoError(err) + + tests := map[string]struct { + contextName string + changedContextName string + isErr bool + }{ + "success-1": { + contextName: "minikube", + changedContextName: "minikube-test", + }, + } + + for _, t := range tests { + err := kubeConfig.ChangeContextName(t.contextName, t.changedContextName) + assert.Equal(t.isErr, err != nil) + } +} + func TestKubeConfig_GetContexts(t *testing.T) { assert := assert.New(t)