Skip to content

Commit

Permalink
feat(kubens): added force flag to switch namespaces even if it doesn'… (
Browse files Browse the repository at this point in the history
#416)

* 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
  • Loading branch information
idsulik authored Jul 10, 2024
1 parent 4997a26 commit b5daf2c
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 20 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 34 additions & 10 deletions cmd/kubens/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"io"
"os"
"slices"

Check failure on line 21 in cmd/kubens/flags.go

View workflow job for this annotation

GitHub Actions / ci

package slices is not in GOROOT (/opt/hostedtoolcache/go/1.20.14/x64/src/slices)
"strings"

"github.com/ahmetb/kubectx/internal/cmdutil"
Expand All @@ -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}
}
20 changes: 19 additions & 1 deletion cmd/kubens/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")}},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubens/fzf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
1 change: 1 addition & 0 deletions cmd/kubens/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func printUsage(out io.Writer) error {
help := `USAGE:
%PROG% : list the namespaces in the current context
%PROG% <NAME> : change the active namespace of current context
%PROG% <NAME> --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
Expand Down
19 changes: 11 additions & 8 deletions cmd/kubens/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -38,15 +39,15 @@ 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
}
err = printer.Success(stderr, "Active namespace is \"%s\"", printer.SuccessColor.Sprint(toNS))
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")
Expand All @@ -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 {
Expand Down

0 comments on commit b5daf2c

Please sign in to comment.