mirror of
https://github.com/ahmetb/kubectx.git
synced 2026-03-10 16:02:14 +00:00
Compare commits
1 Commits
master
...
abalkan/go
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a444364ed3 |
@@ -35,12 +35,14 @@ func (_op CurrentOp) Run(stdout, _ io.Writer) error {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
v := kc.GetCurrentContext()
|
||||
v, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
if v == "" {
|
||||
return errors.New("current-context is not set")
|
||||
}
|
||||
_, err := fmt.Fprintln(stdout, v)
|
||||
if err != nil {
|
||||
if _, err := fmt.Fprintln(stdout, v); err != nil {
|
||||
return fmt.Errorf("write error: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -44,7 +44,7 @@ func (op DeleteOp) Run(_, stderr io.Writer) error {
|
||||
selfName())
|
||||
}
|
||||
|
||||
printer.Success(stderr, `Deleted context %s.`, printer.SuccessColor.Sprint(deletedName))
|
||||
_ = printer.Success(stderr, `Deleted context %s.`, printer.SuccessColor.Sprint(deletedName))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -58,7 +58,10 @@ func deleteContext(name string) (deleteName string, wasActiveContext bool, err e
|
||||
return deleteName, false, fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
cur := kc.GetCurrentContext()
|
||||
cur, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return deleteName, false, fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
// resolve "." to a real name
|
||||
if name == "." {
|
||||
if cur == "" {
|
||||
@@ -68,7 +71,11 @@ func deleteContext(name string) (deleteName string, wasActiveContext bool, err e
|
||||
name = cur
|
||||
}
|
||||
|
||||
if !kc.ContextExists(name) {
|
||||
exists, err := kc.ContextExists(name)
|
||||
if err != nil {
|
||||
return name, false, fmt.Errorf("failed to check context: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return name, false, errors.New("context does not exist")
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
// parse kubeconfig just to see if it can be loaded
|
||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||
defer kc.Close()
|
||||
if err := kc.Parse(); err != nil {
|
||||
if cmdutil.IsNotFoundErr(err) {
|
||||
printer.Warning(stderr, "kubeconfig file not found")
|
||||
@@ -50,7 +51,6 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
kc.Close()
|
||||
|
||||
cmd := exec.Command("fzf", "--ansi", "--no-preview")
|
||||
var out bytes.Buffer
|
||||
@@ -75,7 +75,7 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to switch context: %w", err)
|
||||
}
|
||||
printer.Success(stderr, "Switched to context \"%s\".", printer.SuccessColor.Sprint(name))
|
||||
_ = printer.Success(stderr, "Switched to context \"%s\".", printer.SuccessColor.Sprint(name))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
// parse kubeconfig just to see if it can be loaded
|
||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||
defer kc.Close()
|
||||
if err := kc.Parse(); err != nil {
|
||||
if cmdutil.IsNotFoundErr(err) {
|
||||
printer.Warning(stderr, "kubeconfig file not found")
|
||||
@@ -92,9 +93,12 @@ func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
kc.Close()
|
||||
|
||||
if len(kc.ContextNames()) == 0 {
|
||||
ctxNames, err := kc.ContextNames()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get context names: %w", err)
|
||||
}
|
||||
if len(ctxNames) == 0 {
|
||||
return errors.New("no contexts found in config")
|
||||
}
|
||||
|
||||
@@ -129,7 +133,7 @@ func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
|
||||
selfName())
|
||||
}
|
||||
|
||||
printer.Success(stderr, `Deleted context %s.`, printer.SuccessColor.Sprint(name))
|
||||
_ = printer.Success(stderr, `Deleted context %s.`, printer.SuccessColor.Sprint(name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ func checkIsolatedMode() error {
|
||||
return fmt.Errorf("you are in a locked single-context shell, use 'exit' to leave")
|
||||
}
|
||||
|
||||
cur := kc.GetCurrentContext()
|
||||
cur, _ := kc.GetCurrentContext()
|
||||
return fmt.Errorf("you are in a locked single-context shell (\"%s\"), use 'exit' to leave", cur)
|
||||
}
|
||||
|
||||
@@ -42,10 +42,16 @@ func (_ ListOp) Run(stdout, stderr io.Writer) error {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
ctxs := kc.ContextNames()
|
||||
ctxs, err := kc.ContextNames()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get context names: %w", err)
|
||||
}
|
||||
natsort.Sort(ctxs)
|
||||
|
||||
cur := kc.GetCurrentContext()
|
||||
cur, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
for _, c := range ctxs {
|
||||
s := c
|
||||
if c == cur {
|
||||
|
||||
@@ -52,16 +52,27 @@ func (op RenameOp) Run(_, stderr io.Writer) error {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
cur := kc.GetCurrentContext()
|
||||
cur, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
if op.Old == "." {
|
||||
op.Old = cur
|
||||
}
|
||||
|
||||
if !kc.ContextExists(op.Old) {
|
||||
oldExists, err := kc.ContextExists(op.Old)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check context: %w", err)
|
||||
}
|
||||
if !oldExists {
|
||||
return fmt.Errorf("context \"%s\" not found, can't rename it", op.Old)
|
||||
}
|
||||
|
||||
if kc.ContextExists(op.New) {
|
||||
newExists, err := kc.ContextExists(op.New)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check context: %w", err)
|
||||
}
|
||||
if newExists {
|
||||
printer.Warning(stderr, "context \"%s\" exists, overwriting it.", op.New)
|
||||
if err := kc.DeleteContextEntry(op.New); err != nil {
|
||||
return fmt.Errorf("failed to delete new context to overwrite it: %w", err)
|
||||
@@ -79,7 +90,7 @@ func (op RenameOp) Run(_, stderr io.Writer) error {
|
||||
if err := kc.Save(); err != nil {
|
||||
return fmt.Errorf("failed to save modified kubeconfig: %w", err)
|
||||
}
|
||||
printer.Success(stderr, "Context %s renamed to %s.",
|
||||
_ = printer.Success(stderr, "Context %s renamed to %s.",
|
||||
printer.SuccessColor.Sprint(op.Old),
|
||||
printer.SuccessColor.Sprint(op.New))
|
||||
return nil
|
||||
|
||||
@@ -35,10 +35,17 @@ func (op ShellOp) Run(_, stderr io.Writer) error {
|
||||
if err := kc.Parse(); err != nil {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
if !kc.ContextExists(op.Target) {
|
||||
exists, err := kc.ContextExists(op.Target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check context: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("no context exists with the name: \"%s\"", op.Target)
|
||||
}
|
||||
previousCtx := kc.GetCurrentContext()
|
||||
previousCtx, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
|
||||
// Extract minimal kubeconfig using kubectl
|
||||
data, err := extractMinimalKubeconfig(kubectlPath, op.Target)
|
||||
|
||||
@@ -61,8 +61,15 @@ func switchContext(name string) (string, error) {
|
||||
return "", fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
prev := kc.GetCurrentContext()
|
||||
if !kc.ContextExists(name) {
|
||||
prev, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
exists, err := kc.ContextExists(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check context: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return "", fmt.Errorf("no context exists with the name: \"%s\"", name)
|
||||
}
|
||||
if err := kc.ModifyCurrentContext(name); err != nil {
|
||||
|
||||
@@ -31,7 +31,10 @@ func (c CurrentOp) Run(stdout, _ io.Writer) error {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
ctx := kc.GetCurrentContext()
|
||||
ctx, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
if ctx == "" {
|
||||
return errors.New("current-context is not set")
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type InteractiveSwitchOp struct {
|
||||
func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
// parse kubeconfig just to see if it can be loaded
|
||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||
defer kc.Close()
|
||||
if err := kc.Parse(); err != nil {
|
||||
if cmdutil.IsNotFoundErr(err) {
|
||||
printer.Warning(stderr, "kubeconfig file not found")
|
||||
@@ -44,7 +45,6 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
defer kc.Close()
|
||||
|
||||
cmd := exec.Command("fzf", "--ansi", "--no-preview")
|
||||
var out bytes.Buffer
|
||||
@@ -69,6 +69,6 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to switch namespace: %w", err)
|
||||
}
|
||||
printer.Success(stderr, "Active namespace is \"%s\".", printer.SuccessColor.Sprint(name))
|
||||
_ = printer.Success(stderr, "Active namespace is \"%s\".", printer.SuccessColor.Sprint(name))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -39,7 +40,10 @@ func (op ListOp) Run(stdout, stderr io.Writer) error {
|
||||
return fmt.Errorf("kubeconfig error: %w", err)
|
||||
}
|
||||
|
||||
ctx := kc.GetCurrentContext()
|
||||
ctx, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
if ctx == "" {
|
||||
return errors.New("current-context is not set")
|
||||
}
|
||||
@@ -86,6 +90,7 @@ func queryNamespaces(kc *kubeconfig.Kubeconfig) ([]string, error) {
|
||||
return nil, fmt.Errorf("failed to list namespaces from k8s API: %w", err)
|
||||
}
|
||||
next = list.Continue
|
||||
out = slices.Grow(out, len(list.Items))
|
||||
for _, it := range list.Items {
|
||||
out = append(out, it.Name)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,10 @@ func (s SwitchOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
|
||||
func switchNamespace(kc *kubeconfig.Kubeconfig, ns string, force bool) (string, error) {
|
||||
ctx := kc.GetCurrentContext()
|
||||
ctx, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
if ctx == "" {
|
||||
return "", errors.New("current-context is not set")
|
||||
}
|
||||
|
||||
@@ -42,7 +42,10 @@ func (_ UnsetOp) Run(_, stderr io.Writer) error {
|
||||
}
|
||||
|
||||
func clearNamespace(kc *kubeconfig.Kubeconfig) (string, error) {
|
||||
ctx := kc.GetCurrentContext()
|
||||
ctx, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current context: %w", err)
|
||||
}
|
||||
ns := "default"
|
||||
if ctx == "" {
|
||||
return "", errors.New("current-context is not set")
|
||||
|
||||
@@ -50,18 +50,25 @@ func (k *Kubeconfig) contextNode(name string) (*yaml.RNode, error) {
|
||||
return context, nil
|
||||
}
|
||||
|
||||
func (k *Kubeconfig) ContextNames() []string {
|
||||
func (k *Kubeconfig) ContextNames() ([]string, error) {
|
||||
contexts, err := k.config.Pipe(yaml.Get("contexts"))
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, fmt.Errorf("failed to get contexts: %w", err)
|
||||
}
|
||||
if contexts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
names, err := contexts.ElementValues("name")
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, fmt.Errorf("failed to get context names: %w", err)
|
||||
}
|
||||
return names
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (k *Kubeconfig) ContextExists(name string) bool {
|
||||
return slices.Contains(k.ContextNames(), name)
|
||||
func (k *Kubeconfig) ContextExists(name string) (bool, error) {
|
||||
names, err := k.ContextNames()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return slices.Contains(names, name), nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,10 @@ func TestKubeconfig_ContextNames(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx := kc.ContextNames()
|
||||
ctx, err := kc.ContextNames()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"abc", "def", "ghi"}
|
||||
if diff := cmp.Diff(expected, ctx); diff != "" {
|
||||
t.Fatalf("%s", diff)
|
||||
@@ -46,7 +49,10 @@ func TestKubeconfig_ContextNames_noContextsEntry(t *testing.T) {
|
||||
if err := kc.Parse(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := kc.ContextNames()
|
||||
ctx, err := kc.ContextNames()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var expected []string = nil
|
||||
if diff := cmp.Diff(expected, ctx); diff != "" {
|
||||
t.Fatalf("%s", diff)
|
||||
@@ -59,10 +65,9 @@ func TestKubeconfig_ContextNames_nonArrayContextsEntry(t *testing.T) {
|
||||
if err := kc.Parse(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := kc.ContextNames()
|
||||
var expected []string = nil
|
||||
if diff := cmp.Diff(expected, ctx); diff != "" {
|
||||
t.Fatalf("%s", diff)
|
||||
_, err := kc.ContextNames()
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-array contexts entry")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +82,15 @@ func TestKubeconfig_CheckContextExists(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !kc.ContextExists("c1") {
|
||||
if exists, err := kc.ContextExists("c1"); err != nil || !exists {
|
||||
t.Fatal("c1 actually exists; reported false")
|
||||
}
|
||||
if !kc.ContextExists("c2") {
|
||||
if exists, err := kc.ContextExists("c2"); err != nil || !exists {
|
||||
t.Fatal("c2 actually exists; reported false")
|
||||
}
|
||||
if kc.ContextExists("c3") {
|
||||
if exists, err := kc.ContextExists("c3"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if exists {
|
||||
t.Fatal("c3 does not exist; but reported true")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,19 @@
|
||||
package kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// GetCurrentContext returns "current-context" value in given
|
||||
// kubeconfig object Node, or returns "" if not found.
|
||||
func (k *Kubeconfig) GetCurrentContext() string {
|
||||
// kubeconfig object Node, or returns ("", nil) if not found.
|
||||
func (k *Kubeconfig) GetCurrentContext() (string, error) {
|
||||
v, err := k.config.Pipe(yaml.Get("current-context"))
|
||||
if err != nil {
|
||||
return ""
|
||||
return "", fmt.Errorf("failed to read current-context: %w", err)
|
||||
}
|
||||
return yaml.GetValue(v)
|
||||
return yaml.GetValue(v), nil
|
||||
}
|
||||
|
||||
func (k *Kubeconfig) UnsetCurrentContext() error {
|
||||
|
||||
@@ -26,7 +26,10 @@ func TestKubeconfig_GetCurrentContext(t *testing.T) {
|
||||
if err := kc.Parse(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v := kc.GetCurrentContext()
|
||||
v, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := "foo"
|
||||
if v != expected {
|
||||
@@ -40,7 +43,10 @@ func TestKubeconfig_GetCurrentContext_missingField(t *testing.T) {
|
||||
if err := kc.Parse(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v := kc.GetCurrentContext()
|
||||
v, err := kc.GetCurrentContext()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := ""
|
||||
if v != expected {
|
||||
|
||||
@@ -87,5 +87,8 @@ func (k *Kubeconfig) Save() error {
|
||||
}
|
||||
enc := yaml.NewEncoder(k.f)
|
||||
enc.SetIndent(0)
|
||||
return enc.Encode(k.config.YNode())
|
||||
if err := enc.Encode(k.config.YNode()); err != nil {
|
||||
return err
|
||||
}
|
||||
return enc.Close()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user