Skip to content

Commit b5daf2c

Browse files
authored
feat(kubens): added force flag to switch namespaces even if it doesn'… (#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
1 parent 4997a26 commit b5daf2c

File tree

6 files changed

+75
-20
lines changed

6 files changed

+75
-20
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ Active namespace is "kube-system".
4646
$ kubens -
4747
Context "test" set.
4848
Active namespace is "default".
49+
50+
# change the active namespace even if it doesn't exist
51+
$ kubens not-found-namespace --force
52+
Context "test" set.
53+
Active namespace is "not-found-namespace".
54+
---
55+
$ kubens not-found-namespace -f
56+
Context "test" set.
57+
Active namespace is "not-found-namespace".
4958
```
5059

5160
If you have [`fzf`](https://github.com/junegunn/fzf) installed, you can also

cmd/kubens/flags.go

+34-10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"io"
2020
"os"
21+
"slices"
2122
"strings"
2223

2324
"github.com/ahmetb/kubectx/internal/cmdutil"
@@ -33,28 +34,51 @@ func (op UnsupportedOp) Run(_, _ io.Writer) error {
3334
// parseArgs looks at flags (excl. executable name, i.e. argv[0])
3435
// and decides which operation should be taken.
3536
func parseArgs(argv []string) Op {
36-
if len(argv) == 0 {
37+
n := len(argv)
38+
39+
if n == 0 {
3740
if cmdutil.IsInteractiveMode(os.Stdout) {
3841
return InteractiveSwitchOp{SelfCmd: os.Args[0]}
3942
}
4043
return ListOp{}
4144
}
4245

43-
if len(argv) == 1 {
46+
if n == 1 {
4447
v := argv[0]
45-
if v == "--help" || v == "-h" {
48+
switch v {
49+
case "--help", "-h":
4650
return HelpOp{}
47-
}
48-
if v == "--version" || v == "-V" {
51+
case "--version", "-V":
4952
return VersionOp{}
50-
}
51-
if v == "--current" || v == "-c" {
53+
case "--current", "-c":
5254
return CurrentOp{}
55+
default:
56+
return getSwitchOp(v, false)
5357
}
54-
if strings.HasPrefix(v, "-") && v != "-" {
55-
return UnsupportedOp{Err: fmt.Errorf("unsupported option '%s'", v)}
58+
} else if n == 2 {
59+
// {namespace} -f|--force
60+
name := argv[0]
61+
force := slices.Contains([]string{"-f", "--force"}, argv[1])
62+
63+
if !force {
64+
if !slices.Contains([]string{"-f", "--force"}, argv[0]) {
65+
return UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", argv)}
66+
}
67+
68+
// -f|--force {namespace}
69+
force = true
70+
name = argv[1]
5671
}
57-
return SwitchOp{Target: argv[0]}
72+
73+
return getSwitchOp(name, force)
5874
}
75+
5976
return UnsupportedOp{Err: fmt.Errorf("too many arguments")}
6077
}
78+
79+
func getSwitchOp(v string, force bool) Op {
80+
if strings.HasPrefix(v, "-") && v != "-" {
81+
return UnsupportedOp{Err: fmt.Errorf("unsupported option %q", v)}
82+
}
83+
return SwitchOp{Target: v, Force: force}
84+
}

cmd/kubens/flags_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,30 @@ func Test_parseArgs_new(t *testing.T) {
4848
{name: "switch by name",
4949
args: []string{"foo"},
5050
want: SwitchOp{Target: "foo"}},
51+
{name: "switch by name force short flag",
52+
args: []string{"foo", "-f"},
53+
want: SwitchOp{Target: "foo", Force: true}},
54+
{name: "switch by name force long flag",
55+
args: []string{"foo", "--force"},
56+
want: SwitchOp{Target: "foo", Force: true}},
57+
{name: "switch by name force short flag before name",
58+
args: []string{"-f", "foo"},
59+
want: SwitchOp{Target: "foo", Force: true}},
60+
{name: "switch by name force long flag before name",
61+
args: []string{"--force", "foo"},
62+
want: SwitchOp{Target: "foo", Force: true}},
63+
{name: "switch by name unknown arguments",
64+
args: []string{"foo", "-x"},
65+
want: UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", []string{"foo", "-x"})}},
66+
{name: "switch by name unknown arguments",
67+
args: []string{"-x", "foo"},
68+
want: UnsupportedOp{Err: fmt.Errorf("unsupported arguments %q", []string{"-x", "foo"})}},
5169
{name: "switch by swap",
5270
args: []string{"-"},
5371
want: SwitchOp{Target: "-"}},
5472
{name: "unrecognized flag",
5573
args: []string{"-x"},
56-
want: UnsupportedOp{Err: fmt.Errorf("unsupported option '-x'")}},
74+
want: UnsupportedOp{Err: fmt.Errorf("unsupported option %q", "-x")}},
5775
{name: "too many args",
5876
args: []string{"a", "b", "c"},
5977
want: UnsupportedOp{Err: fmt.Errorf("too many arguments")}},

cmd/kubens/fzf.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
6565
if choice == "" {
6666
return errors.New("you did not choose any of the options")
6767
}
68-
name, err := switchNamespace(kc, choice)
68+
name, err := switchNamespace(kc, choice, false)
6969
if err != nil {
7070
return errors.Wrap(err, "failed to switch namespace")
7171
}

cmd/kubens/help.go

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func printUsage(out io.Writer) error {
3535
help := `USAGE:
3636
%PROG% : list the namespaces in the current context
3737
%PROG% <NAME> : change the active namespace of current context
38+
%PROG% <NAME> --force/-f : force change the active namespace of current context (even if it doesn't exist)
3839
%PROG% - : switch to the previous namespace in this context
3940
%PROG% -c, --current : show the current namespace
4041
%PROG% -h,--help : show this message

cmd/kubens/switch.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
type SwitchOp struct {
3131
Target string // '-' for back and forth, or NAME
32+
Force bool // force switch even if the namespace doesn't exist
3233
}
3334

3435
func (s SwitchOp) Run(_, stderr io.Writer) error {
@@ -38,15 +39,15 @@ func (s SwitchOp) Run(_, stderr io.Writer) error {
3839
return errors.Wrap(err, "kubeconfig error")
3940
}
4041

41-
toNS, err := switchNamespace(kc, s.Target)
42+
toNS, err := switchNamespace(kc, s.Target, s.Force)
4243
if err != nil {
4344
return err
4445
}
4546
err = printer.Success(stderr, "Active namespace is \"%s\"", printer.SuccessColor.Sprint(toNS))
4647
return err
4748
}
4849

49-
func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) {
50+
func switchNamespace(kc *kubeconfig.Kubeconfig, ns string, force bool) (string, error) {
5051
ctx := kc.GetCurrentContext()
5152
if ctx == "" {
5253
return "", errors.New("current-context is not set")
@@ -69,12 +70,14 @@ func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) {
6970
ns = prev
7071
}
7172

72-
ok, err := namespaceExists(kc, ns)
73-
if err != nil {
74-
return "", errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)")
75-
}
76-
if !ok {
77-
return "", errors.Errorf("no namespace exists with name \"%s\"", ns)
73+
if !force {
74+
ok, err := namespaceExists(kc, ns)
75+
if err != nil {
76+
return "", errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)")
77+
}
78+
if !ok {
79+
return "", errors.Errorf("no namespace exists with name \"%s\"", ns)
80+
}
7881
}
7982

8083
if err := kc.SetNamespace(ctx, ns); err != nil {

0 commit comments

Comments
 (0)