Add interactive switching to kubens

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
This commit is contained in:
Ahmet Alp Balkan 2020-04-29 11:55:28 -07:00
parent ebfd724d08
commit 64e5a0ed13
No known key found for this signature in database
GPG Key ID: 441833503E604E2C
3 changed files with 91 additions and 21 deletions

View File

@ -3,7 +3,10 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"strings" "strings"
"github.com/ahmetb/kubectx/internal/cmdutil"
) )
// UnsupportedOp indicates an unsupported flag. // UnsupportedOp indicates an unsupported flag.
@ -17,9 +20,9 @@ func (op UnsupportedOp) Run(_, _ io.Writer) error {
// and decides which operation should be taken. // and decides which operation should be taken.
func parseArgs(argv []string) Op { func parseArgs(argv []string) Op {
if len(argv) == 0 { if len(argv) == 0 {
//if env.IsInteractiveMode(os.Stdout) { if cmdutil.IsInteractiveMode(os.Stdout) {
// return InteractiveSwitchOp{SelfCmd: os.Args[0]} return InteractiveSwitchOp{SelfCmd: os.Args[0]}
//} }
return ListOp{} return ListOp{}
} }

60
cmd/kubens/fzf.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"strings"
"github.com/pkg/errors"
"github.com/ahmetb/kubectx/internal/cmdutil"
"github.com/ahmetb/kubectx/internal/env"
"github.com/ahmetb/kubectx/internal/kubeconfig"
"github.com/ahmetb/kubectx/internal/printer"
)
type InteractiveSwitchOp struct {
SelfCmd string
}
// TODO(ahmetb) This method is heavily repetitive vs kubectx/fzf.go.
func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
// parse kubeconfig just to see if it can be loaded
kc := new(kubeconfig.Kubeconfig).WithLoader(cmdutil.DefaultLoader)
if err := kc.Parse(); err != nil {
if cmdutil.IsNotFoundErr(err) {
printer.Warning(stderr, "kubeconfig file not found")
return nil
}
return errors.Wrap(err, "kubeconfig error")
}
defer kc.Close()
cmd := exec.Command("fzf", "--ansi", "--no-preview")
var out bytes.Buffer
cmd.Stdin = os.Stdin
cmd.Stderr = stderr
cmd.Stdout = &out
cmd.Env = append(os.Environ(),
fmt.Sprintf("FZF_DEFAULT_COMMAND=%s", op.SelfCmd),
fmt.Sprintf("%s=1", env.EnvForceColor))
if err := cmd.Run(); err != nil {
if _, ok := err.(*exec.ExitError); !ok {
return err
}
}
choice := strings.TrimSpace(out.String())
if choice == "" {
return errors.New("you did not choose any of the options")
}
name, err := switchNamespace(kc, choice)
if err != nil {
return errors.Wrap(err, "failed to switch context")
}
printer.Success(stderr, "Switched to context %s.", printer.SuccessColor.Sprint(name))
return nil
}

View File

@ -21,53 +21,60 @@ func (s SwitchOp) Run(_, stderr io.Writer) error {
return errors.Wrap(err, "kubeconfig error") return errors.Wrap(err, "kubeconfig error")
} }
toNS, err := switchNamespace(kc, s.Target)
if err != nil {
return err
}
err = printer.Success(stderr, "Active namespace is %q", toNS)
return err
}
func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) {
ctx := kc.GetCurrentContext() ctx := kc.GetCurrentContext()
if ctx == "" { if ctx == "" {
return errors.New("current-context is not set") return "", errors.New("current-context is not set")
} }
curNS, err := kc.NamespaceOfContext(ctx) curNS, err := kc.NamespaceOfContext(ctx)
if ctx == "" { if ctx == "" {
return errors.New("failed to get current namespace") return "", errors.New("failed to get current namespace")
} }
f := NewNSFile(ctx) f := NewNSFile(ctx)
prev, err := f.Load() prev, err := f.Load()
if err != nil { if err != nil {
return errors.Wrap(err, "failed to load previous namespace from file") return "", errors.Wrap(err, "failed to load previous namespace from file")
} }
toNS := s.Target if ns == "-" {
if s.Target == "-" {
if prev == "" { if prev == "" {
return errors.Errorf("No previous namespace found for current context (%s)", ctx) return "", errors.Errorf("No previous namespace found for current context (%s)", ctx)
} }
toNS = prev ns = prev
} }
ok, err := namespaceExists(toNS) ok, err := namespaceExists(ns)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)") return "", errors.Wrap(err, "failed to query if namespace exists (is cluster accessible?)")
} }
if !ok { if !ok {
return errors.Errorf("no namespace exists with name %q", toNS) return "", errors.Errorf("no namespace exists with name %q", ns)
} }
if err := kc.SetNamespace(ctx, toNS); err != nil { if err := kc.SetNamespace(ctx, ns); err != nil {
return errors.Wrapf(err, "failed to change to namespace %q", toNS) return "", errors.Wrapf(err, "failed to change to namespace %q", ns)
} }
if err := kc.Save(); err != nil { if err := kc.Save(); err != nil {
return errors.Wrap(err, "failed to save kubeconfig file") return "", errors.Wrap(err, "failed to save kubeconfig file")
} }
if curNS != toNS { if curNS != ns {
if err := f.Save(curNS); err != nil { if err := f.Save(curNS); err != nil {
return errors.Wrap(err, "failed to save the previous namespace to file") return "", errors.Wrap(err, "failed to save the previous namespace to file")
} }
} }
return ns, nil
err = printer.Success(stderr, "Active namespace is %q", toNS)
return err
} }
func namespaceExists(ns string) (bool, error) { func namespaceExists(ns string) (bool, error) {
nses, err := queryNamespaces() nses, err := queryNamespaces()
if err != nil { if err != nil {