From 1f4eed962a275942be9c47b2b084cd173d53920f Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Sun, 12 Apr 2020 14:22:52 -0700 Subject: [PATCH] Move all yaml logic to pkg/kubeconfig Signed-off-by: Ahmet Alp Balkan --- cmd/kubectx/current.go | 7 +- cmd/kubectx/delete.go | 35 +---- cmd/kubectx/kubeconfig.go | 17 --- cmd/kubectx/kubeconfig/contextmodify.go | 74 ++++++++++ cmd/kubectx/kubeconfig/contextmodify_test.go | 129 ++++++++++++++++++ cmd/kubectx/kubeconfig/contexts_test.go | 7 +- cmd/kubectx/kubeconfig/currentcontext.go | 11 -- cmd/kubectx/kubeconfig/currentcontext_test.go | 9 +- cmd/kubectx/kubeconfig/kubeconfig.go | 11 +- cmd/kubectx/kubeconfig/kubeconfig_test.go | 23 ++++ cmd/kubectx/kubeconfig_test.go | 59 -------- cmd/kubectx/list.go | 6 +- cmd/kubectx/rename.go | 38 +----- cmd/kubectx/switch.go | 53 +------ cmd/kubectx/unset.go | 8 +- 15 files changed, 260 insertions(+), 227 deletions(-) create mode 100644 cmd/kubectx/kubeconfig/contextmodify.go create mode 100644 cmd/kubectx/kubeconfig/contextmodify_test.go create mode 100644 cmd/kubectx/kubeconfig/kubeconfig_test.go diff --git a/cmd/kubectx/current.go b/cmd/kubectx/current.go index c164ef5..738822e 100644 --- a/cmd/kubectx/current.go +++ b/cmd/kubectx/current.go @@ -15,15 +15,14 @@ type CurrentOp struct{} func (_op CurrentOp) Run(stdout, _ io.Writer) error { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - _, err := kc.ParseRaw() - if err != nil { - return err + if err := kc.Parse(); err != nil { + return errors.Wrap(err, "failed to parse kubeconfig") } v := kc.GetCurrentContext() if v == "" { return errors.New("current-context is not set") } - _, err = fmt.Fprintln(stdout, v) + _, err := fmt.Fprintln(stdout, v) return err } diff --git a/cmd/kubectx/delete.go b/cmd/kubectx/delete.go index ece176b..fe0277d 100644 --- a/cmd/kubectx/delete.go +++ b/cmd/kubectx/delete.go @@ -4,7 +4,6 @@ import ( "io" "github.com/pkg/errors" - "gopkg.in/yaml.v3" "github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig" ) @@ -37,9 +36,8 @@ func (op DeleteOp) Run(_, stderr io.Writer) error { func deleteContext(name string) (deleteName string, wasActiveContext bool, err error) { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - rootNode, err := kc.ParseRaw() - if err != nil { - return "", false, err + if err := kc.Parse(); err != nil { + return "", false, errors.Wrap(err, "failed to parse kubeconfig") } cur := kc.GetCurrentContext() @@ -54,36 +52,9 @@ func deleteContext(name string) (deleteName string, wasActiveContext bool, err e return "", false, errors.New("context does not exist") } - if err := modifyDocToDeleteContext(rootNode, name); err != nil { + if err := kc.DeleteContextEntry(name); err != nil { return "", false, errors.Wrap(err, "failed to modify yaml doc") } return name, wasActiveContext, errors.Wrap(kc.Save(), "failed to save kubeconfig file") } -func modifyDocToDeleteContext(rootNode *yaml.Node, deleteName string) error { - if rootNode.Kind != yaml.MappingNode { - return errors.New("root node was not a mapping node") - } - contexts := valueOf(rootNode, "contexts") - if contexts == nil { - return errors.New("there are no contexts in kubeconfig") - } - if contexts.Kind != yaml.SequenceNode { - return errors.New("'contexts' key is not a sequence") - } - - i := -1 - for j, ctxNode := range contexts.Content { - nameNode := valueOf(ctxNode, "name") - if nameNode != nil && nameNode.Kind == yaml.ScalarNode && nameNode.Value == deleteName { - i = j - break - } - } - if i >= 0 { - copy(contexts.Content[i:], contexts.Content[i+1:]) - contexts.Content[len(contexts.Content)-1] = nil - contexts.Content = contexts.Content[:len(contexts.Content)-1] - } - return nil -} diff --git a/cmd/kubectx/kubeconfig.go b/cmd/kubectx/kubeconfig.go index e78ad8f..91ce5e4 100644 --- a/cmd/kubectx/kubeconfig.go +++ b/cmd/kubectx/kubeconfig.go @@ -5,7 +5,6 @@ import ( "path/filepath" "github.com/pkg/errors" - "gopkg.in/yaml.v3" "github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig" ) @@ -69,19 +68,3 @@ func homeDir() string { } return home } - -// TODO parseKubeconfig doesn't seem necessary when there's a raw version that returns what's needed -func parseKubeconfig(path string) (kubeconfigContents, error) { - // TODO refactor to accept io.Reader instead of file - var v kubeconfigContents - - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return v, nil - } - return v, errors.Wrap(err, "file open error") - } - err = yaml.NewDecoder(f).Decode(&v) - return v, errors.Wrap(err, "yaml parse error") -} diff --git a/cmd/kubectx/kubeconfig/contextmodify.go b/cmd/kubectx/kubeconfig/contextmodify.go new file mode 100644 index 0000000..b3e31c2 --- /dev/null +++ b/cmd/kubectx/kubeconfig/contextmodify.go @@ -0,0 +1,74 @@ +package kubeconfig + +import ( + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +func (k *Kubeconfig) DeleteContextEntry(deleteName string) error { + contexts := valueOf(k.rootNode, "contexts") + if contexts == nil { + return errors.New("there are no contexts in kubeconfig") + } + if contexts.Kind != yaml.SequenceNode { + return errors.New("'contexts' key is not a sequence") + } + + i := -1 + for j, ctxNode := range contexts.Content { + nameNode := valueOf(ctxNode, "name") + if nameNode != nil && nameNode.Kind == yaml.ScalarNode && nameNode.Value == deleteName { + i = j + break + } + } + if i >= 0 { + copy(contexts.Content[i:], contexts.Content[i+1:]) + contexts.Content[len(contexts.Content)-1] = nil + contexts.Content = contexts.Content[:len(contexts.Content)-1] + } + return nil +} + +func (k *Kubeconfig) ModifyCurrentContext(name string) error { + currentCtxNode := valueOf(k.rootNode, "current-context") + if currentCtxNode != nil { + currentCtxNode.Value = name + return nil + } + + // if current-context field doesn't exist, create new field + keyNode := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: "current-context", + Tag: "!!str"} + valueNode := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: name, + Tag: "!!str"} + k.rootNode.Content = append(k.rootNode.Content, keyNode, valueNode) + return nil +} + +func (k *Kubeconfig) ModifyContextName(old, new string) error { + contexts := valueOf(k.rootNode, "contexts") + if contexts == nil { + return errors.New("\"contexts\" entry is nil") + } else if contexts.Kind != yaml.SequenceNode { + return errors.New("\"contexts\" is not a sequence node") + } + + var changed bool + for _, contextNode := range contexts.Content { + nameNode := valueOf(contextNode, "name") + if nameNode.Kind == yaml.ScalarNode && nameNode.Value == old { + nameNode.Value = new + changed = true + break + } + } + if !changed { + return errors.New("no changes were made") + } + return nil +} diff --git a/cmd/kubectx/kubeconfig/contextmodify_test.go b/cmd/kubectx/kubeconfig/contextmodify_test.go new file mode 100644 index 0000000..1449df4 --- /dev/null +++ b/cmd/kubectx/kubeconfig/contextmodify_test.go @@ -0,0 +1,129 @@ +package kubeconfig + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestKubeconfig_DeleteContextEntry_errors(t *testing.T) { + kc := new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`[1, 2, 3]`)}) + _ = kc.Parse() + err := kc.DeleteContextEntry("foo") + if err == nil { + t.Fatal("supposed to fail on non-mapping nodes") + } + + kc = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`a: b`)}) + _ = kc.Parse() + err = kc.DeleteContextEntry("foo") + if err == nil { + t.Fatal("supposed to fail if contexts key does not exist") + } + + kc = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`contexts: "some string"`)}) + _ = kc.Parse() + err = kc.DeleteContextEntry("foo") + if err == nil { + t.Fatal("supposed to fail if contexts key is not an array") + } +} + +func TestKubeconfig_DeleteContextEntry(t *testing.T) { + test := &testLoader{in: strings.NewReader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + kc := new(Kubeconfig).WithLoader(test) + if err := kc.Parse(); err != nil { + t.Fatal(err) + } + if err := kc.DeleteContextEntry("c1"); err != nil { + t.Fatal(err) + } + if err := kc.Save(); err != nil { + t.Fatal(err) + } + + expected := "contexts: [{name: c2}, {name: c3}]\n" + out := test.out.String() + if diff := cmp.Diff(expected,out); diff != "" { + t.Fatalf("diff: %s", diff) + } +} + +func TestKubeconfig_ModifyCurrentContext_fieldExists(t *testing.T) { + test := &testLoader{in: strings.NewReader( + `current-context: abc +field1: value1`)} + kc := new(Kubeconfig).WithLoader(test) + if err := kc.Parse(); err != nil { + t.Fatal(err) + } + if err := kc.ModifyCurrentContext("foo"); err != nil { + t.Fatal(err) + } + if err := kc.Save(); err != nil { + t.Fatal(err) + } + + expected := `current-context: foo +field1: value1`+"\n" + out := test.out.String() + if diff := cmp.Diff(expected,out); diff != "" { + t.Fatalf("diff: %s", diff) + } +} + +func TestKubeconfig_ModifyCurrentContext_fieldMissing(t *testing.T) { + test := &testLoader{in: strings.NewReader( + `field1: value1`)} + kc := new(Kubeconfig).WithLoader(test) + if err := kc.Parse(); err != nil { + t.Fatal(err) + } + if err := kc.ModifyCurrentContext("foo"); err != nil { + t.Fatal(err) + } + if err := kc.Save(); err != nil { + t.Fatal(err) + } + + expected := `field1: value1` + "\n" + "current-context: foo\n" + out := test.out.String() + if diff := cmp.Diff(expected,out); diff != "" { + t.Fatalf("diff: %s", diff) + } +} + +func TestKubeconfig_ModifyContextName_noChange(t *testing.T) { + test := &testLoader{in: strings.NewReader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + kc := new(Kubeconfig).WithLoader(test) + if err := kc.Parse(); err != nil { + t.Fatal(err) + } + if err := kc.ModifyContextName("c5", "c6"); err == nil { + t.Fatal("was expecting error for 'no changes made'") + } +} + +func TestKubeconfig_ModifyContextName(t *testing.T) { + test := &testLoader{in: strings.NewReader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + kc := new(Kubeconfig).WithLoader(test) + if err := kc.Parse(); err != nil { + t.Fatal(err) + } + if err := kc.ModifyContextName("c1", "ccc"); err != nil { + t.Fatal(err) + } + if err := kc.Save(); err != nil { + t.Fatal(err) + } + + expected := "contexts: [{name: ccc}, {name: c2}, {name: c3}]\n" + out := test.out.String() + if diff := cmp.Diff(expected,out); diff != "" { + t.Fatalf("diff: %s", diff) + } +} diff --git a/cmd/kubectx/kubeconfig/contexts_test.go b/cmd/kubectx/kubeconfig/contexts_test.go index 3708405..d314417 100644 --- a/cmd/kubectx/kubeconfig/contexts_test.go +++ b/cmd/kubectx/kubeconfig/contexts_test.go @@ -18,10 +18,10 @@ contexts: bar: zoo`)} kc := new(Kubeconfig).WithLoader(tl) - _, err := kc.ParseRaw() - if err != nil { + if err := kc.Parse(); err != nil { t.Fatal(err) } + ctx := kc.ContextNames() expected := []string{"abc", "def", "ghi"} if diff := cmp.Diff(expected, ctx); diff != "" { @@ -35,8 +35,7 @@ func TestKubeconfig_CheckContextExists(t *testing.T) { - name: c2`)} kc := new(Kubeconfig).WithLoader(tl) - _, err := kc.ParseRaw() - if err != nil { + if err := kc.Parse(); err != nil { t.Fatal(err) } diff --git a/cmd/kubectx/kubeconfig/currentcontext.go b/cmd/kubectx/kubeconfig/currentcontext.go index 6662985..7225591 100644 --- a/cmd/kubectx/kubeconfig/currentcontext.go +++ b/cmd/kubectx/kubeconfig/currentcontext.go @@ -1,16 +1,8 @@ package kubeconfig -import ( - "github.com/pkg/errors" - "gopkg.in/yaml.v3" -) - // GetCurrentContext returns "current-context" value in given // kubeconfig object Node, or returns "" if not found. func (k *Kubeconfig) GetCurrentContext() string { - if k.rootNode.Kind != yaml.MappingNode { - return "" - } v := valueOf(k.rootNode, "current-context") if v == nil { return "" @@ -19,9 +11,6 @@ func (k *Kubeconfig) GetCurrentContext() string { } func (k *Kubeconfig) UnsetCurrentContext() error { - if k.rootNode.Kind != yaml.MappingNode { - return errors.New("kubeconfig file is not a map document") - } curCtxValNode := valueOf(k.rootNode, "current-context") curCtxValNode.Value = "" return nil diff --git a/cmd/kubectx/kubeconfig/currentcontext_test.go b/cmd/kubectx/kubeconfig/currentcontext_test.go index 8626615..b0fde35 100644 --- a/cmd/kubectx/kubeconfig/currentcontext_test.go +++ b/cmd/kubectx/kubeconfig/currentcontext_test.go @@ -8,8 +8,7 @@ import ( func TestKubeconfig_GetCurrentContext(t *testing.T) { tl := &testLoader{in: strings.NewReader(`current-context: foo`)} kc := new(Kubeconfig).WithLoader(tl) - _, err := kc.ParseRaw() - if err != nil { + if err := kc.Parse(); err != nil { t.Fatal(err) } v := kc.GetCurrentContext() @@ -23,8 +22,7 @@ func TestKubeconfig_GetCurrentContext(t *testing.T) { func TestKubeconfig_GetCurrentContext_missingField(t *testing.T) { tl := &testLoader{in: strings.NewReader(`abc: def`)} kc := new(Kubeconfig).WithLoader(tl) - _, err := kc.ParseRaw() - if err != nil { + if err := kc.Parse(); err != nil { t.Fatal(err) } v := kc.GetCurrentContext() @@ -38,8 +36,7 @@ func TestKubeconfig_GetCurrentContext_missingField(t *testing.T) { func TestKubeconfig_UnsetCurrentContext(t *testing.T) { tl := &testLoader{in: strings.NewReader(`current-context: foo`)} kc := new(Kubeconfig).WithLoader(tl) - _, err := kc.ParseRaw() - if err != nil { + if err := kc.Parse(); err != nil { t.Fatal(err) } if err := kc.UnsetCurrentContext(); err != nil { diff --git a/cmd/kubectx/kubeconfig/kubeconfig.go b/cmd/kubectx/kubeconfig/kubeconfig.go index c371609..0f5ff06 100644 --- a/cmd/kubectx/kubeconfig/kubeconfig.go +++ b/cmd/kubectx/kubeconfig/kubeconfig.go @@ -37,20 +37,23 @@ func (k *Kubeconfig) Close() error { return k.f.Close() } -func (k *Kubeconfig) ParseRaw() (*yaml.Node, error) { +func (k *Kubeconfig) Parse() error { f, err := k.loader.Load() if err != nil { - return nil, errors.Wrap(err, "failed to load kubeconfig") + return errors.Wrap(err, "failed to load") } k.f = f var v yaml.Node if err := yaml.NewDecoder(f).Decode(&v); err != nil { - return nil, errors.Wrap(err, "failed to decode kubeconfig") + return errors.Wrap(err, "failed to decode") } k.rootNode = v.Content[0] - return k. rootNode, nil + if k.rootNode.Kind != yaml.MappingNode { + return errors.New("kubeconfig file is not a map document") + } + return nil } func (k *Kubeconfig) Save() error { diff --git a/cmd/kubectx/kubeconfig/kubeconfig_test.go b/cmd/kubectx/kubeconfig/kubeconfig_test.go new file mode 100644 index 0000000..898d5e9 --- /dev/null +++ b/cmd/kubectx/kubeconfig/kubeconfig_test.go @@ -0,0 +1,23 @@ +package kubeconfig + +import ( + "strings" + "testing" +) + +func TestParse(t *testing.T) { + err := new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`a:b`)}).Parse() + if err == nil { + t.Fatal("expected error from bad yaml") + } + + err = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`[1, 2, 3]`)}).Parse() + if err == nil { + t.Fatal("expected error from not-mapping root node") + } + + err = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`current-context: foo`)}).Parse() + if err != nil { + t.Fatal(err) + } +} diff --git a/cmd/kubectx/kubeconfig_test.go b/cmd/kubectx/kubeconfig_test.go index d2179d5..a1ff3fa 100644 --- a/cmd/kubectx/kubeconfig_test.go +++ b/cmd/kubectx/kubeconfig_test.go @@ -6,8 +6,6 @@ import ( "path/filepath" "strings" "testing" - - "github.com/google/go-cmp/cmp" ) func Test_kubeconfigPath_homePath(t *testing.T) { @@ -77,63 +75,6 @@ func Test_kubeconfigPath_envOvverideDoesNotSupportPathSeparator(t *testing.T) { if err == nil { t.Fatal("expected error")} } - -func Test_parseKubeconfig_openError(t *testing.T) { - _, err := parseKubeconfig("/non/existing/path") - if err == nil { - t.Fatalf("expected error") - } - msg := err.Error() - expected := `file open error` - if !strings.Contains(msg, expected) { - t.Fatalf("expected=%q, got=%q", expected, msg) - } -} - -func Test_parseKubeconfig_yamlFormatError(t *testing.T) { - file, cleanup := testfile(t, `a: [1,2 `) // supposed to be invalid yaml - defer cleanup() - - _, err := parseKubeconfig(file) - if err == nil { - t.Fatal("expected error") - } - expected := "yaml parse error" - if got := err.Error(); !strings.Contains(got, expected) { - t.Fatalf("expected=%q; got=%q", expected, got) - } -} - -func Test_parseKubeconfig(t *testing.T) { - file, cleanup := testfile(t, - ` -apiVersion: v1 -current-context: foo -contexts: -- name: c3 -- name: c2 -- name: c1`) - defer cleanup() - - got, err := parseKubeconfig(file) - if err != nil { - t.Fatal(err) - } - - expected := kubeconfig{ - APIVersion: "v1", - CurrentContext: "foo", - Contexts: []context{ - {Name:"c3"},{Name:"c2"},{Name:"c1"}, - }, - } - - diff := cmp.Diff(expected, got) - if diff != "" { - t.Fatalf("got wrong object:\n%s", diff) - } -} - func testfile(t *testing.T, contents string) (path string, cleanup func()) { t.Helper() diff --git a/cmd/kubectx/list.go b/cmd/kubectx/list.go index 3d51aea..a06c341 100644 --- a/cmd/kubectx/list.go +++ b/cmd/kubectx/list.go @@ -6,6 +6,7 @@ import ( "facette.io/natsort" "github.com/fatih/color" + "github.com/pkg/errors" "github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig" ) @@ -26,9 +27,8 @@ type ListOp struct{} func (_ ListOp) Run(stdout, _ io.Writer) error { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - _, err := kc.ParseRaw() - if err != nil { - return err + if err := kc.Parse(); err != nil { + return errors.Wrap(err, "failed to parse kubeconfig") } ctxs := kc.ContextNames() diff --git a/cmd/kubectx/rename.go b/cmd/kubectx/rename.go index 97860f2..3f9ca82 100644 --- a/cmd/kubectx/rename.go +++ b/cmd/kubectx/rename.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/pkg/errors" - "gopkg.in/yaml.v3" "github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig" ) @@ -36,10 +35,8 @@ func parseRenameSyntax(v string) (string, string, bool) { func (op RenameOp) Run(_, stderr io.Writer) error { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - - rootNode, err := kc.ParseRaw() - if err != nil { - return err + if err := kc.Parse(); err != nil { + return errors.Wrap(err, "failed to parse kubeconfig") } cur := kc.GetCurrentContext() @@ -53,16 +50,16 @@ func (op RenameOp) Run(_, stderr io.Writer) error { if kc.ContextExists( op.New) { printWarning(stderr, "context %q exists, overwriting it.", op.New) - if err := modifyDocToDeleteContext(rootNode, op.New); err != nil { + if err := kc.DeleteContextEntry(op.New); err != nil { return errors.Wrap(err, "failed to delete new context to overwrite it") } } - if err := modifyContextName(rootNode, op.Old, op.New); err != nil { + if err := kc.ModifyContextName(op.Old, op.New); err != nil { return errors.Wrap(err, "failed to change context name") } if op.New == cur { - if err := modifyCurrentContext(rootNode, op.New); err != nil { + if err := kc.ModifyCurrentContext( op.New); err != nil { return errors.Wrap(err, "failed to set current-context to new name") } } @@ -73,28 +70,3 @@ func (op RenameOp) Run(_, stderr io.Writer) error { return nil } -func modifyContextName(rootNode *yaml.Node, old, new string) error { - if rootNode.Kind != yaml.MappingNode { - return errors.New("root doc is not a mapping node") - } - contexts := valueOf(rootNode, "contexts") - if contexts == nil { - return errors.New("\"contexts\" entry is nil") - } else if contexts.Kind != yaml.SequenceNode { - return errors.New("\"contexts\" is not a sequence node") - } - - var changed bool - for _, contextNode := range contexts.Content { - nameNode := valueOf(contextNode, "name") - if nameNode.Kind == yaml.ScalarNode && nameNode.Value == old { - nameNode.Value = new - changed = true - break - } - } - if !changed { - return errors.New("no changes were made") - } - return nil -} diff --git a/cmd/kubectx/switch.go b/cmd/kubectx/switch.go index fffcba3..ef78ceb 100644 --- a/cmd/kubectx/switch.go +++ b/cmd/kubectx/switch.go @@ -4,7 +4,6 @@ import ( "io" "github.com/pkg/errors" - "gopkg.in/yaml.v3" "github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig" ) @@ -14,7 +13,7 @@ type SwitchOp struct { Target string // '-' for back and forth, or NAME } -func (op SwitchOp) Run(stdout, stderr io.Writer) error { +func (op SwitchOp) Run(_, stderr io.Writer) error { var newCtx string var err error if op.Target == "-" { @@ -38,17 +37,15 @@ func switchContext(name string) (string, error) { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - - rootNode, err := kc.ParseRaw() - if err != nil { - return "", err + if err := kc.Parse(); err != nil { + return "", errors.Wrap(err, "failed to parse kubeconfig") } prev := kc.GetCurrentContext() if !kc.ContextExists(name) { return "", errors.Errorf("no context exists with the name: %q", name) } - if err := modifyCurrentContext(rootNode, name); err != nil { + if err := kc.ModifyCurrentContext(name); err != nil { return "", err } if err := kc.Save(); err != nil { @@ -79,45 +76,3 @@ func swapContext() (string, error) { } return switchContext(prev) } - - -// TODO delete -func valueOf(mapNode *yaml.Node, key string) *yaml.Node { - if mapNode.Kind != yaml.MappingNode { - return nil - } - for i, ch := range mapNode.Content { - if i%2 == 0 && ch.Kind == yaml.ScalarNode && ch.Value == key { - return mapNode.Content[i+1] - } - } - return nil -} - - - -func modifyCurrentContext(rootNode *yaml.Node, name string) error { - if rootNode.Kind != yaml.MappingNode { - return errors.New("document is not a map") - } - - // find current-context field => modify value (next children) - for i, ch := range rootNode.Content { - if i%2 == 0 && ch.Value == "current-context" { - rootNode.Content[i+1].Value = name - return nil - } - } - - // if current-context ==> create New field - keyNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: "current-context", - Tag: "!!str"} - valueNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: name, - Tag: "!!str"} - rootNode.Content = append(rootNode.Content, keyNode, valueNode) - return nil -} diff --git a/cmd/kubectx/unset.go b/cmd/kubectx/unset.go index a36075b..816bb40 100644 --- a/cmd/kubectx/unset.go +++ b/cmd/kubectx/unset.go @@ -15,10 +15,8 @@ type UnsetOp struct{} func (_ UnsetOp) Run(_, stderr io.Writer) error { kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) defer kc.Close() - - _, err := kc.ParseRaw() - if err != nil { - return err + if err := kc.Parse(); err != nil { + return errors.Wrap(err, "failed to parse kubeconfig") } if err := kc.UnsetCurrentContext(); err != nil { @@ -28,7 +26,7 @@ func (_ UnsetOp) Run(_, stderr io.Writer) error { return errors.Wrap(err, "failed to save kubeconfig file after modification") } - _, err = fmt.Fprintln(stderr, "Successfully unset the current context") + _, err := fmt.Fprintln(stderr, "Successfully unset the current context") return err }