From 1982becb155ccbb6840dd44624ec0b23becb1ffc Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Sat, 18 Apr 2020 13:09:59 -0700 Subject: [PATCH] kubens: Start implementing stubs Signed-off-by: Ahmet Alp Balkan --- cmd/kubectx/env.go | 1 - cmd/kubectx/flags.go | 6 ++- cmd/kubectx/fzf.go | 22 ---------- cmd/kubectx/kubeconfig_test.go | 1 - cmd/kubens/current.go | 9 ++++ cmd/kubens/flags.go | 40 +++++++++++++++++ cmd/kubens/flags_test.go | 63 +++++++++++++++++++++++++++ cmd/kubens/help.go | 44 +++++++++++++++++++ cmd/kubens/list.go | 11 +++++ cmd/kubens/main.go | 27 ++++++++++++ cmd/kubens/switch.go | 11 +++++ internal/cmdutil/interactive.go | 30 +++++++++++++ internal/env/constants.go | 1 - internal/kubeconfig/helpers_test.go | 1 - internal/testutil/kubeconfigloader.go | 1 - internal/testutil/testutil.go | 1 - 16 files changed, 239 insertions(+), 30 deletions(-) create mode 100644 cmd/kubens/current.go create mode 100644 cmd/kubens/flags.go create mode 100644 cmd/kubens/flags_test.go create mode 100644 cmd/kubens/help.go create mode 100644 cmd/kubens/list.go create mode 100644 cmd/kubens/main.go create mode 100644 cmd/kubens/switch.go create mode 100644 internal/cmdutil/interactive.go diff --git a/cmd/kubectx/env.go b/cmd/kubectx/env.go index c9ecbf5..06ab7d0 100644 --- a/cmd/kubectx/env.go +++ b/cmd/kubectx/env.go @@ -1,2 +1 @@ package main - diff --git a/cmd/kubectx/flags.go b/cmd/kubectx/flags.go index 9b4d954..0b6982b 100644 --- a/cmd/kubectx/flags.go +++ b/cmd/kubectx/flags.go @@ -5,6 +5,8 @@ import ( "io" "os" "strings" + + "github.com/ahmetb/kubectx/internal/cmdutil" ) // UnsupportedOp indicates an unsupported flag. @@ -18,7 +20,7 @@ func (op UnsupportedOp) Run(_, _ io.Writer) error { // and decides which operation should be taken. func parseArgs(argv []string) Op { if len(argv) == 0 { - if isInteractiveMode(os.Stdout) { + if env.IsInteractiveMode(os.Stdout) { return InteractiveSwitchOp{SelfCmd: os.Args[0]} } return ListOp{} @@ -26,7 +28,7 @@ func parseArgs(argv []string) Op { if argv[0] == "-d" { if len(argv) == 1 { - return UnsupportedOp{Err:fmt.Errorf("'-d' needs arguments")} + return UnsupportedOp{Err: fmt.Errorf("'-d' needs arguments")} } return DeleteOp{Contexts: argv[1:]} } diff --git a/cmd/kubectx/fzf.go b/cmd/kubectx/fzf.go index 6943a02..851cf42 100644 --- a/cmd/kubectx/fzf.go +++ b/cmd/kubectx/fzf.go @@ -10,8 +10,6 @@ import ( "github.com/pkg/errors" - "github.com/mattn/go-isatty" - "github.com/ahmetb/kubectx/internal/env" "github.com/ahmetb/kubectx/internal/kubeconfig" "github.com/ahmetb/kubectx/internal/printer" @@ -58,23 +56,3 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error { printer.Success(stderr, "Switched to context %s.", printer.SuccessColor.Sprint(name)) return nil } - -// isTerminal determines if given fd is a TTY. -func isTerminal(fd *os.File) bool { - return isatty.IsTerminal(fd.Fd()) -} - -// fzfInstalled determines if fzf(1) is in PATH. -func fzfInstalled() bool { - v, _ := exec.LookPath("fzf") - if v != "" { - return true - } - return false -} - -// isInteractiveMode determines if we can do choosing with fzf. -func isInteractiveMode(stdout *os.File) bool { - v := os.Getenv(env.EnvFZFIgnore) - return v == "" && isTerminal(stdout) && fzfInstalled() -} diff --git a/cmd/kubectx/kubeconfig_test.go b/cmd/kubectx/kubeconfig_test.go index 4350ca4..f344104 100644 --- a/cmd/kubectx/kubeconfig_test.go +++ b/cmd/kubectx/kubeconfig_test.go @@ -11,7 +11,6 @@ import ( "github.com/ahmetb/kubectx/internal/testutil" ) - func Test_homeDir(t *testing.T) { type env struct{ k, v string } cases := []struct { diff --git a/cmd/kubens/current.go b/cmd/kubens/current.go new file mode 100644 index 0000000..5139e97 --- /dev/null +++ b/cmd/kubens/current.go @@ -0,0 +1,9 @@ +package main + +import "io" + +type CurrentOp struct{} + +func (c CurrentOp) Run(stdout, stderr io.Writer) error { + panic("implement me") +} diff --git a/cmd/kubens/flags.go b/cmd/kubens/flags.go new file mode 100644 index 0000000..f86d4c3 --- /dev/null +++ b/cmd/kubens/flags.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "io" + "strings" +) + +// UnsupportedOp indicates an unsupported flag. +type UnsupportedOp struct{ Err error } + +func (op UnsupportedOp) Run(_, _ io.Writer) error { + return op.Err +} + +// 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 { + //if env.IsInteractiveMode(os.Stdout) { + // return InteractiveSwitchOp{SelfCmd: os.Args[0]} + //} + return ListOp{} + } + + if len(argv) == 1 { + v := argv[0] + if v == "--help" || v == "-h" { + return HelpOp{} + } + if v == "--current" || v == "-c" { + return CurrentOp{} + } + if strings.HasPrefix(v, "-") && v != "-" { + return UnsupportedOp{Err: fmt.Errorf("unsupported option '%s'", v)} + } + return SwitchOp{Target: argv[0]} + } + return UnsupportedOp{Err: fmt.Errorf("too many arguments")} +} diff --git a/cmd/kubens/flags_test.go b/cmd/kubens/flags_test.go new file mode 100644 index 0000000..5b75d55 --- /dev/null +++ b/cmd/kubens/flags_test.go @@ -0,0 +1,63 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func Test_parseArgs_new(t *testing.T) { + tests := []struct { + name string + args []string + want Op + }{ + {name: "nil Args", + args: nil, + want: ListOp{}}, + {name: "empty Args", + args: []string{}, + want: ListOp{}}, + {name: "help shorthand", + args: []string{"-h"}, + want: HelpOp{}}, + {name: "help long form", + args: []string{"--help"}, + want: HelpOp{}}, + {name: "current shorthand", + args: []string{"-c"}, + want: CurrentOp{}}, + {name: "current long form", + args: []string{"--current"}, + want: CurrentOp{}}, + {name: "switch by name", + args: []string{"foo"}, + want: SwitchOp{Target: "foo"}}, + {name: "switch by swap", + args: []string{"-"}, + want: SwitchOp{Target: "-"}}, + {name: "unrecognized flag", + args: []string{"-x"}, + want: UnsupportedOp{Err: fmt.Errorf("unsupported option '-x'")}}, + {name: "too many args", + args: []string{"a", "b", "c"}, + want: UnsupportedOp{Err: fmt.Errorf("too many arguments")}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := parseArgs(tt.args) + + var opts cmp.Options + if _, ok := tt.want.(UnsupportedOp); ok { + opts = append(opts, cmp.Comparer(func(x, y UnsupportedOp) bool { + return (x.Err == nil && y.Err == nil) || (x.Err.Error() == y.Err.Error()) + })) + } + + if diff := cmp.Diff(got, tt.want, opts...); diff != "" { + t.Errorf("parseArgs(%#v) diff: %s", tt.args, diff) + } + }) + } +} diff --git a/cmd/kubens/help.go b/cmd/kubens/help.go new file mode 100644 index 0000000..9056197 --- /dev/null +++ b/cmd/kubens/help.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +// HelpOp describes printing help. +type HelpOp struct{} + +func (_ HelpOp) Run(stdout, _ io.Writer) error { + return printUsage(stdout) +} + +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% - : switch to the previous namespace in this context + %PROG% -c, --current : show the current namespace + %PROG% -h,--help : show this message +` + // TODO this replace logic is duplicated between this and kubectx + help = strings.ReplaceAll(help, "%PROG%", selfName()) + + _, err := fmt.Fprintf(out, "%s\n", help) + return errors.Wrap(err, "write error") +} + +// selfName guesses how the user invoked the program. +func selfName() string { + // TODO this method is duplicated between this and kubectx + me := filepath.Base(os.Args[0]) + pluginPrefix := "kubectl-" + if strings.HasPrefix(me, pluginPrefix) { + return "kubectl " + strings.TrimPrefix(me, pluginPrefix) + } + return "kubectx" +} diff --git a/cmd/kubens/list.go b/cmd/kubens/list.go new file mode 100644 index 0000000..51ccba9 --- /dev/null +++ b/cmd/kubens/list.go @@ -0,0 +1,11 @@ +package main + +import ( + "io" +) + +type ListOp struct{} + +func (op ListOp) Run(stdout, stderr io.Writer) error { + panic("implement me") +} diff --git a/cmd/kubens/main.go b/cmd/kubens/main.go new file mode 100644 index 0000000..fcfc75d --- /dev/null +++ b/cmd/kubens/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "io" + "os" + + "github.com/ahmetb/kubectx/internal/env" + "github.com/ahmetb/kubectx/internal/printer" +) + +type Op interface { + Run(stdout, stderr io.Writer) error +} + +func main() { + op := parseArgs(os.Args[1:]) + if err := op.Run(os.Stdout, os.Stderr); err != nil { + printer.Error(os.Stderr, err.Error()) + + if _, ok := os.LookupEnv(env.EnvDebug); ok { + // print stack trace in verbose mode + fmt.Fprintf(os.Stderr, "[DEBUG] error: %+v\n", err) + } + defer os.Exit(1) + } +} diff --git a/cmd/kubens/switch.go b/cmd/kubens/switch.go new file mode 100644 index 0000000..81f2401 --- /dev/null +++ b/cmd/kubens/switch.go @@ -0,0 +1,11 @@ +package main + +import ( + "io" +) + +type SwitchOp struct{ Target string } + +func (s SwitchOp) Run(stdout, stderr io.Writer) error { + panic("implement me") +} diff --git a/internal/cmdutil/interactive.go b/internal/cmdutil/interactive.go new file mode 100644 index 0000000..5a081e9 --- /dev/null +++ b/internal/cmdutil/interactive.go @@ -0,0 +1,30 @@ +package env + +import ( + "os" + "os/exec" + + "github.com/mattn/go-isatty" + + "github.com/ahmetb/kubectx/internal/env" +) + +// isTerminal determines if given fd is a TTY. +func isTerminal(fd *os.File) bool { + return isatty.IsTerminal(fd.Fd()) +} + +// fzfInstalled determines if fzf(1) is in PATH. +func fzfInstalled() bool { + v, _ := exec.LookPath("fzf") + if v != "" { + return true + } + return false +} + +// IsInteractiveMode determines if we can do choosing with fzf. +func IsInteractiveMode(stdout *os.File) bool { + v := os.Getenv(env.EnvFZFIgnore) + return v == "" && isTerminal(stdout) && fzfInstalled() +} diff --git a/internal/env/constants.go b/internal/env/constants.go index 77fa8ba..c5bced3 100644 --- a/internal/env/constants.go +++ b/internal/env/constants.go @@ -16,4 +16,3 @@ const ( // EnvDebug describes the internal environment variable for more verbose logging. EnvDebug = `DEBUG` ) - diff --git a/internal/kubeconfig/helpers_test.go b/internal/kubeconfig/helpers_test.go index 659d0cc..f263a6d 100644 --- a/internal/kubeconfig/helpers_test.go +++ b/internal/kubeconfig/helpers_test.go @@ -1,2 +1 @@ package kubeconfig - diff --git a/internal/testutil/kubeconfigloader.go b/internal/testutil/kubeconfigloader.go index bb1b162..110b2e6 100644 --- a/internal/testutil/kubeconfigloader.go +++ b/internal/testutil/kubeconfigloader.go @@ -1,2 +1 @@ package testutil - diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index 12470b8..65858b4 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -15,4 +15,3 @@ func WithEnvVar(key, value string) func() { } } } -