From b5daf2cef7908862b58ebd2a1db244276fcf57b0 Mon Sep 17 00:00:00 2001 From: Suleiman Dibirov <3595194+idsulik@users.noreply.github.com> Date: Wed, 10 Jul 2024 07:38:40 +0300 Subject: [PATCH] =?UTF-8?q?feat(kubens):=20added=20force=20flag=20to=20swi?= =?UTF-8?q?tch=20namespaces=20even=20if=20it=20doesn'=E2=80=A6=20(#416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(kubens): added force flag to switch namespaces even if it doesn't exist * Merged lines * fixed README.md and flags.go * updated flags.go, flags_test.go --- README.md | 9 ++++++++ cmd/kubens/flags.go | 44 +++++++++++++++++++++++++++++++--------- cmd/kubens/flags_test.go | 20 +++++++++++++++++- cmd/kubens/fzf.go | 2 +- cmd/kubens/help.go | 1 + cmd/kubens/switch.go | 19 +++++++++-------- 6 files changed, 75 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 87e8f47..051012b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,15 @@ Active namespace is "kube-system". $ kubens - Context "test" set. Active namespace is "default". + +# change the active namespace even if it doesn't exist +$ kubens not-found-namespace --force +Context "test" set. +Active namespace is "not-found-namespace". +--- +$ kubens not-found-namespace -f +Context "test" set. +Active namespace is "not-found-namespace". ``` If you have [`fzf`](https://github.com/junegunn/fzf) installed, you can also diff --git a/cmd/kubens/flags.go b/cmd/kubens/flags.go index fc3c64d..030889c 100644 --- a/cmd/kubens/flags.go +++ b/cmd/kubens/flags.go @@ -18,6 +18,7 @@ import ( "fmt" "io" "os" + "slices" "strings" "github.com/ahmetb/kubectx/internal/cmdutil" @@ -33,28 +34,51 @@ func (op UnsupportedOp) Run(_, _ io.Writer) error { // parseArgs looks at flags (excl. executable name, i.e. argv[0]) // and decides which operation should be taken. func parseArgs(argv []string) Op { - if len(argv) == 0 { + n := len(argv) + + if n == 0 { if cmdutil.IsInteractiveMode(os.Stdout) { return InteractiveSwitchOp{SelfCmd: os.Args[0]} } return ListOp{} } - if len(argv) == 1 { + if n == 1 { v := argv[0] - if v == "--help" || v == "-h" { + switch v { + case "--help", "-h": return HelpOp{} - } - if v == "--version" || v == "-V" { + case "--version", "-V": return VersionOp{} - } - if v == "--current" || v == "-c" { + case "--current", "-c": return CurrentOp{} + default: + return getSwitchOp(v, false) } - if strings.HasPrefix(v, "-") && v != "-" { - return UnsupportedOp{Err: fmt.Errorf("unsupported option '%s'", v)} + } else if n == 2 { + // {namespace} -f|--force + name := argv[0] + force := slices.Contains([]string{"-f", "--force"}, argv[1]) + + if !force { + if !slices.Contains([]string{"-f", "--force"}, argv[0]) { + return UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", argv)} + } + + // -f|--force {namespace} + force = true + name = argv[1] } - return SwitchOp{Target: argv[0]} + + return getSwitchOp(name, force) } + return UnsupportedOp{Err: fmt.Errorf("too many arguments")} } + +func getSwitchOp(v string, force bool) Op { + if strings.HasPrefix(v, "-") && v != "-" { + return UnsupportedOp{Err: fmt.Errorf("unsupported option %q", v)} + } + return SwitchOp{Target: v, Force: force} +} diff --git a/cmd/kubens/flags_test.go b/cmd/kubens/flags_test.go index e3e7551..19102a1 100644 --- a/cmd/kubens/flags_test.go +++ b/cmd/kubens/flags_test.go @@ -48,12 +48,30 @@ func Test_parseArgs_new(t *testing.T) { {name: "switch by name", args: []string{"foo"}, want: SwitchOp{Target: "foo"}}, + {name: "switch by name force short flag", + args: []string{"foo", "-f"}, + want: SwitchOp{Target: "foo", Force: true}}, + {name: "switch by name force long flag", + args: []string{"foo", "--force"}, + want: SwitchOp{Target: "foo", Force: true}}, + {name: "switch by name force short flag before name", + args: []string{"-f", "foo"}, + want: SwitchOp{Target: "foo", Force: true}}, + {name: "switch by name force long flag before name", + args: []string{"--force", "foo"}, + want: SwitchOp{Target: "foo", Force: true}}, + {name: "switch by name unknown arguments", + args: []string{"foo", "-x"}, + want: UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", []string{"foo", "-x"})}}, + {name: "switch by name unknown arguments", + args: []string{"-x", "foo"}, + want: UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", []string{"-x", "foo"})}}, {name: "switch by swap", args: []string{"-"}, want: SwitchOp{Target: "-"}}, {name: "unrecognized flag", args: []string{"-x"}, - want: UnsupportedOp{Err: fmt.Errorf("unsupported option '-x'")}}, + want: UnsupportedOp{Err: fmt.Errorf("unsupported option %q", "-x")}}, {name: "too many args", args: []string{"a", "b", "c"}, want: UnsupportedOp{Err: fmt.Errorf("too many arguments")}}, diff --git a/cmd/kubens/fzf.go b/cmd/kubens/fzf.go index 8a49770..f093398 100644 --- a/cmd/kubens/fzf.go +++ b/cmd/kubens/fzf.go @@ -65,7 +65,7 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error { if choice == "" { return errors.New("you did not choose any of the options") } - name, err := switchNamespace(kc, choice) + name, err := switchNamespace(kc, choice, false) if err != nil { return errors.Wrap(err, "failed to switch namespace") } diff --git a/cmd/kubens/help.go b/cmd/kubens/help.go index 1337b0b..027d333 100644 --- a/cmd/kubens/help.go +++ b/cmd/kubens/help.go @@ -35,6 +35,7 @@ func printUsage(out io.Writer) error { help := `USAGE: %PROG% : list the namespaces in the current context %PROG% : change the active namespace of current context + %PROG% --force/-f : force change the active namespace of current context (even if it doesn't exist) %PROG% - : switch to the previous namespace in this context %PROG% -c, --current : show the current namespace %PROG% -h,--help : show this message diff --git a/cmd/kubens/switch.go b/cmd/kubens/switch.go index 7568683..bcf2a70 100644 --- a/cmd/kubens/switch.go +++ b/cmd/kubens/switch.go @@ -29,6 +29,7 @@ import ( type SwitchOp struct { Target string // '-' for back and forth, or NAME + Force bool // force switch even if the namespace doesn't exist } func (s SwitchOp) Run(_, stderr io.Writer) error { @@ -38,7 +39,7 @@ func (s SwitchOp) Run(_, stderr io.Writer) error { return errors.Wrap(err, "kubeconfig error") } - toNS, err := switchNamespace(kc, s.Target) + toNS, err := switchNamespace(kc, s.Target, s.Force) if err != nil { return err } @@ -46,7 +47,7 @@ func (s SwitchOp) Run(_, stderr io.Writer) error { return err } -func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) { +func switchNamespace(kc *kubeconfig.Kubeconfig, ns string, force bool) (string, error) { ctx := kc.GetCurrentContext() if ctx == "" { return "", errors.New("current-context is not set") @@ -69,12 +70,14 @@ func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) { ns = prev } - ok, err := namespaceExists(kc, ns) - if err != nil { - return "", errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)") - } - if !ok { - return "", errors.Errorf("no namespace exists with name \"%s\"", ns) + if !force { + ok, err := namespaceExists(kc, ns) + if err != nil { + return "", errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)") + } + if !ok { + return "", errors.Errorf("no namespace exists with name \"%s\"", ns) + } } if err := kc.SetNamespace(ctx, ns); err != nil {