Move ctx-related YAML parse methods to pkg

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
This commit is contained in:
Ahmet Alp Balkan 2020-04-12 13:46:14 -07:00
parent 7013899503
commit 492e3e7053
No known key found for this signature in database
GPG Key ID: 441833503E604E2C
11 changed files with 181 additions and 46 deletions

View File

@ -15,12 +15,12 @@ type CurrentOp struct{}
func (_op CurrentOp) Run(stdout, _ io.Writer) error {
kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader)
defer kc.Close()
rootNode, err := kc.ParseRaw()
_, err := kc.ParseRaw()
if err != nil {
return err
}
v := kubeconfig.GetCurrentContext(rootNode)
v := kc.GetCurrentContext()
if v == "" {
return errors.New("current-context is not set")
}

View File

@ -42,7 +42,7 @@ func deleteContext(name string) (deleteName string, wasActiveContext bool, err e
return "", false, err
}
cur := kubeconfig.GetCurrentContext(rootNode)
cur := kc.GetCurrentContext()
// resolve "." to a real name
if name == "." {
@ -50,7 +50,7 @@ func deleteContext(name string) (deleteName string, wasActiveContext bool, err e
name = cur
}
if !checkContextExists(rootNode, name) {
if !kc.ContextExists(name) {
return "", false, errors.New("context does not exist")
}

View File

@ -1,9 +1,11 @@
package kubeconfig
import "gopkg.in/yaml.v3"
import (
"gopkg.in/yaml.v3"
)
func ContextNames(rootNode *yaml.Node) []string {
contexts := valueOf(rootNode, "contexts")
func (k *Kubeconfig) ContextNames() []string {
contexts := valueOf(k.rootNode, "contexts")
if contexts == nil {
return nil
}
@ -21,17 +23,14 @@ func ContextNames(rootNode *yaml.Node) []string {
return ctxNames
}
// GetCurrentContext returns "current-context" value in given
// kubeconfig object Node, or returns "" if not found.
func GetCurrentContext(rootNode *yaml.Node) string {
if rootNode.Kind != yaml.MappingNode {
return ""
func (k *Kubeconfig) ContextExists(name string) bool {
ctxNames := k.ContextNames()
for _, v := range ctxNames {
if v == name {
return true
}
}
v := valueOf(rootNode, "current-context")
if v == nil {
return ""
}
return v.Value
return false
}
func valueOf(mapNode *yaml.Node, key string) *yaml.Node {

View File

@ -0,0 +1,52 @@
package kubeconfig
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestKubeconfig_ContextNames(t *testing.T) {
tl := &testLoader{in: strings.NewReader(`
contexts:
- name: abc
- name: def
field1: value1
- name: ghi
foo:
bar: zoo`)}
kc := new(Kubeconfig).WithLoader(tl)
_, err := kc.ParseRaw()
if err != nil {
t.Fatal(err)
}
ctx := kc.ContextNames()
expected := []string{"abc", "def", "ghi"}
if diff := cmp.Diff(expected, ctx); diff != "" {
t.Fatalf("%s", diff)
}
}
func TestKubeconfig_CheckContextExists(t *testing.T) {
tl := &testLoader{in: strings.NewReader(`contexts:
- name: c1
- name: c2`)}
kc := new(Kubeconfig).WithLoader(tl)
_, err := kc.ParseRaw()
if err != nil {
t.Fatal(err)
}
if !kc.ContextExists("c1") {
t.Fatal("c1 actually exists; reported false")
}
if !kc.ContextExists("c2") {
t.Fatal("c2 actually exists; reported false")
}
if kc.ContextExists("c3") {
t.Fatal("c3 does not exist; but reported true")
}
}

View File

@ -0,0 +1,28 @@
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 ""
}
return v.Value
}
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
}

View File

@ -0,0 +1,58 @@
package kubeconfig
import (
"strings"
"testing"
)
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 {
t.Fatal(err)
}
v := kc.GetCurrentContext()
expected := "foo"
if v != expected {
t.Fatalf("expected=%q; got=%q", expected, v)
}
}
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 {
t.Fatal(err)
}
v := kc.GetCurrentContext()
expected := ""
if v != expected {
t.Fatalf("expected=%q; got=%q", expected, v)
}
}
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 {
t.Fatal(err)
}
if err := kc.UnsetCurrentContext(); err != nil {
t.Fatal(err)
}
if err := kc.Save(); err != nil {
t.Fatal(err)
}
out := tl.out.String()
expected := `current-context: ""
`
if out != expected {
t.Fatalf("expected=%q; got=%q", expected, out)
}
}

View File

@ -0,0 +1,17 @@
package kubeconfig
import (
"bytes"
"strings"
)
type testLoader struct {
in *strings.Reader
out bytes.Buffer
}
func (t *testLoader) Read(p []byte) (n int, err error) { return t.in.Read(p) }
func (t *testLoader) Write(p []byte) (n int, err error) { return t.out.Write(p) }
func (t *testLoader) Close() error { return nil }
func (t *testLoader) Reset() error { return nil }
func (t *testLoader) Load() (ReadWriteResetCloser, error) { return t, nil }

View File

@ -26,17 +26,17 @@ type ListOp struct{}
func (_ ListOp) Run(stdout, _ io.Writer) error {
kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader)
defer kc.Close()
rootNode, err := kc.ParseRaw()
_, err := kc.ParseRaw()
if err != nil {
return err
}
ctxs := kubeconfig.ContextNames(rootNode)
ctxs := kc.ContextNames()
natsort.Sort(ctxs)
// TODO support KUBECTX_CURRENT_FGCOLOR
// TODO support KUBECTX_CURRENT_BGCOLOR
cur := kubeconfig.GetCurrentContext(rootNode)
cur := kc.GetCurrentContext()
for _, c := range ctxs {
s := c
if c == cur {

View File

@ -42,16 +42,16 @@ func (op RenameOp) Run(_, stderr io.Writer) error {
return err
}
cur := kubeconfig.GetCurrentContext(rootNode)
cur := kc.GetCurrentContext()
if op.Old == "." {
op.Old = cur
}
if !checkContextExists(rootNode, op.Old) {
if !kc.ContextExists(op.Old) {
return errors.Errorf("context %q not found, can't rename it", op.Old)
}
if checkContextExists(rootNode, op.New) {
if kc.ContextExists( op.New) {
printWarning(stderr, "context %q exists, overwriting it.", op.New)
if err := modifyDocToDeleteContext(rootNode, op.New); err != nil {
return errors.Wrap(err, "failed to delete new context to overwrite it")

View File

@ -44,8 +44,8 @@ func switchContext(name string) (string, error) {
return "", err
}
prev := kubeconfig.GetCurrentContext(rootNode)
if !checkContextExists(rootNode, name) {
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 {
@ -81,16 +81,6 @@ func swapContext() (string, error) {
}
func checkContextExists(rootNode *yaml.Node, name string) bool {
ctxNames := kubeconfig.ContextNames(rootNode)
for _, v := range ctxNames {
if v == name {
return true
}
}
return false
}
// TODO delete
func valueOf(mapNode *yaml.Node, key string) *yaml.Node {
if mapNode.Kind != yaml.MappingNode {

View File

@ -5,7 +5,6 @@ import (
"io"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
"github.com/ahmetb/kubectx/cmd/kubectx/kubeconfig"
)
@ -17,12 +16,12 @@ func (_ UnsetOp) Run(_, stderr io.Writer) error {
kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader)
defer kc.Close()
rootNode, err := kc.ParseRaw()
_, err := kc.ParseRaw()
if err != nil {
return err
}
if err := modifyDocToUnsetContext(rootNode); err != nil {
if err := kc.UnsetCurrentContext(); err != nil {
return errors.Wrap(err, "error while modifying current-context")
}
if err := kc.Save(); err != nil {
@ -33,11 +32,3 @@ func (_ UnsetOp) Run(_, stderr io.Writer) error {
return err
}
func modifyDocToUnsetContext(rootNode *yaml.Node) error {
if rootNode.Kind != yaml.MappingNode {
return errors.New("kubeconfig file is not a map document")
}
curCtxValNode := valueOf(rootNode, "current-context")
curCtxValNode.Value = ""
return nil
}