diff --git a/internal/kubeconfig/contextmodify.go b/internal/kubeconfig/contextmodify.go index b04e6d4..e469fb6 100644 --- a/internal/kubeconfig/contextmodify.go +++ b/internal/kubeconfig/contextmodify.go @@ -15,7 +15,8 @@ package kubeconfig import ( - "github.com/pkg/errors" + "errors" + "sigs.k8s.io/kustomize/kyaml/yaml" ) @@ -24,60 +25,34 @@ func (k *Kubeconfig) DeleteContextEntry(deleteName string) error { if err != nil { return err } - - 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] + if err := contexts.PipeE( + yaml.ElementSetter{ + Keys: []string{"name"}, + Values: []string{deleteName}, + }, + ); err != nil { + return err } return nil } func (k *Kubeconfig) ModifyCurrentContext(name string) error { - currentCtxNode := valueOf(k.rootNode, "current-context") - if currentCtxNode != nil { - currentCtxNode.Value = name - return nil + if err := k.config.PipeE(yaml.SetField("current-context", yaml.NewScalarRNode(name))); err != nil { + return err } - - // 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, err := k.contextsNode() + context, err := k.config.Pipe(yaml.Lookup("contexts", "[name="+old+"]")) if err != nil { return err } - - 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 context == nil { + return errors.New("\"contexts\" entry is nil") } - if !changed { - return errors.New("no changes were made") + if err := context.PipeE(yaml.SetField("name", yaml.NewScalarRNode(new))); err != nil { + return err } return nil } diff --git a/internal/kubeconfig/contexts.go b/internal/kubeconfig/contexts.go index 8262494..cfda375 100644 --- a/internal/kubeconfig/contexts.go +++ b/internal/kubeconfig/contexts.go @@ -19,48 +19,40 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) -func (k *Kubeconfig) contextsNode() (*yaml.Node, error) { - contexts := valueOf(k.rootNode, "contexts") +func (k *Kubeconfig) contextsNode() (*yaml.RNode, error) { + contexts, err := k.config.Pipe(yaml.Get("contexts")) + if err != nil { + return nil, err + } if contexts == nil { return nil, errors.New("\"contexts\" entry is nil") - } else if contexts.Kind != yaml.SequenceNode { + } else if contexts.YNode().Kind != yaml.SequenceNode { return nil, errors.New("\"contexts\" is not a sequence node") } return contexts, nil } -func (k *Kubeconfig) contextNode(name string) (*yaml.Node, error) { - contexts, err := k.contextsNode() +func (k *Kubeconfig) contextNode(name string) (*yaml.RNode, error) { + context, err := k.config.Pipe(yaml.Lookup("contexts", "[name="+name+"]")) if err != nil { return nil, err } - - for _, contextNode := range contexts.Content { - nameNode := valueOf(contextNode, "name") - if nameNode.Kind == yaml.ScalarNode && nameNode.Value == name { - return contextNode, nil - } + if context == nil { + return nil, errors.Errorf("context with name \"%s\" not found", name) } - return nil, errors.Errorf("context with name \"%s\" not found", name) + return context, nil } func (k *Kubeconfig) ContextNames() []string { - contexts := valueOf(k.rootNode, "contexts") - if contexts == nil { + contexts, err := k.config.Pipe(yaml.Get("contexts")) + if err != nil { return nil } - if contexts.Kind != yaml.SequenceNode { + names, err := contexts.ElementValues("name") + if err != nil { return nil } - - var ctxNames []string - for _, ctx := range contexts.Content { - nameVal := valueOf(ctx, "name") - if nameVal != nil { - ctxNames = append(ctxNames, nameVal.Value) - } - } - return ctxNames + return names } func (k *Kubeconfig) ContextExists(name string) bool { @@ -72,15 +64,3 @@ func (k *Kubeconfig) ContextExists(name string) bool { } return false } - -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 -} diff --git a/internal/kubeconfig/currentcontext.go b/internal/kubeconfig/currentcontext.go index b13bba3..aa89b3b 100644 --- a/internal/kubeconfig/currentcontext.go +++ b/internal/kubeconfig/currentcontext.go @@ -14,18 +14,26 @@ package kubeconfig +import ( + "strings" + + "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 { - v := valueOf(k.rootNode, "current-context") - if v == nil { + v, err := k.config.Pipe(yaml.Get("current-context")) + if err != nil { return "" } - return v.Value + str, err := v.String() + if err != nil { + return "" + } + return strings.TrimSpace(str) } func (k *Kubeconfig) UnsetCurrentContext() error { - curCtxValNode := valueOf(k.rootNode, "current-context") - curCtxValNode.Value = "" - return nil + return k.config.PipeE(yaml.SetField("current-context", yaml.NewStringRNode(""))) } diff --git a/internal/kubeconfig/kubeconfig.go b/internal/kubeconfig/kubeconfig.go index 4e1b8f5..6f8d85c 100644 --- a/internal/kubeconfig/kubeconfig.go +++ b/internal/kubeconfig/kubeconfig.go @@ -35,8 +35,8 @@ type Loader interface { type Kubeconfig struct { loader Loader - f ReadWriteResetCloser - rootNode *yaml.Node + f ReadWriteResetCloser + config *yaml.RNode } func (k *Kubeconfig) WithLoader(l Loader) *Kubeconfig { @@ -65,15 +65,19 @@ func (k *Kubeconfig) Parse() error { if err := yaml.NewDecoder(f).Decode(&v); err != nil { return errors.Wrap(err, "failed to decode") } - k.rootNode = v.Content[0] - if k.rootNode.Kind != yaml.MappingNode { + k.config = yaml.NewRNode(&v) + if k.config.YNode().Kind != yaml.MappingNode { return errors.New("kubeconfig file is not a map document") } return nil } func (k *Kubeconfig) Bytes() ([]byte, error) { - return yaml.Marshal(k.rootNode) + str, err := k.config.String() + if err != nil { + return nil, err + } + return []byte(str), nil } func (k *Kubeconfig) Save() error { @@ -82,5 +86,5 @@ func (k *Kubeconfig) Save() error { } enc := yaml.NewEncoder(k.f) enc.SetIndent(0) - return enc.Encode(k.rootNode) + return enc.Encode(k.config.YNode()) } diff --git a/internal/kubeconfig/namespace.go b/internal/kubeconfig/namespace.go index 6d4bbb4..9ce2de9 100644 --- a/internal/kubeconfig/namespace.go +++ b/internal/kubeconfig/namespace.go @@ -14,7 +14,11 @@ package kubeconfig -import "sigs.k8s.io/kustomize/kyaml/yaml" +import ( + "strings" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) const ( defaultNamespace = "default" @@ -25,53 +29,27 @@ func (k *Kubeconfig) NamespaceOfContext(contextName string) (string, error) { if err != nil { return "", err } - ctxBody := valueOf(ctx, "context") - if ctxBody == nil { - return defaultNamespace, nil + namespace, err := ctx.Pipe(yaml.Lookup("context", "namespace")) + if namespace == nil || err != nil { + return defaultNamespace, err } - ns := valueOf(ctxBody, "namespace") - if ns == nil || ns.Value == "" { - return defaultNamespace, nil + nsStr, err := namespace.String() + if nsStr == "" || err != nil { + return defaultNamespace, err } - return ns.Value, nil + return strings.TrimSpace(nsStr), nil } func (k *Kubeconfig) SetNamespace(ctxName string, ns string) error { - ctxNode, err := k.contextNode(ctxName) + ctx, err := k.contextNode(ctxName) if err != nil { return err } - - var ctxBodyNodeWasEmpty bool // actual namespace value is in contexts[index].context.namespace, but .context might not exist - ctxBodyNode := valueOf(ctxNode, "context") - if ctxBodyNode == nil { - ctxBodyNodeWasEmpty = true - ctxBodyNode = &yaml.Node{ - Kind: yaml.MappingNode, - } - } - - nsNode := valueOf(ctxBodyNode, "namespace") - if nsNode != nil { - nsNode.Value = ns - return nil - } - - keyNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: "namespace", - Tag: "!!str"} - valueNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: ns, - Tag: "!!str"} - ctxBodyNode.Content = append(ctxBodyNode.Content, keyNode, valueNode) - if ctxBodyNodeWasEmpty { - ctxNode.Content = append(ctxNode.Content, &yaml.Node{ - Kind: yaml.ScalarNode, - Value: "context", - Tag: "!!str", - }, ctxBodyNode) + if err := ctx.PipeE( + yaml.LookupCreate(yaml.MappingNode, "context"), + yaml.SetField("namespace", yaml.NewStringRNode(ns)), + ); err != nil { + return err } return nil }