Add support for -u/--unset

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
This commit is contained in:
Ahmet Alp Balkan 2020-04-10 15:04:31 -07:00
parent 94e8d3b4c7
commit c5f17b83e7
No known key found for this signature in database
GPG Key ID: 441833503E604E2C
6 changed files with 107 additions and 34 deletions

View File

@ -18,6 +18,9 @@ type SwitchOp struct {
Target string // '-' for back and forth, or NAME
}
// UnsetOp indicates intention to remove current-context preference.
type UnsetOp struct{}
// UnknownOp indicates an unsupported flag.
type UnknownOp struct{ Args []string }
@ -36,6 +39,9 @@ func parseArgs(argv []string) Op {
if v == "--current" || v == "-c" {
return CurrentOp{}
}
if v == "--unset" || v == "-u" {
return UnsetOp{}
}
if strings.HasPrefix(v, "-") && v != "-" {
return UnknownOp{argv}

View File

@ -30,6 +30,12 @@ func Test_parseArgs_new(t *testing.T) {
{name: "current long form",
args: []string{"--current"},
want: CurrentOp{}},
{name: "unset shorthand",
args: []string{"-u"},
want: UnsetOp{}},
{name: "unset long form",
args: []string{"--unset"},
want: UnsetOp{}},
{name: "switch by name",
args: []string{"foo"},
want: SwitchOp{Target: "foo"}},

View File

@ -0,0 +1,52 @@
package main
import (
"io"
"os"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
func parseKubeconfigRaw(r io.Reader) (*yaml.Node, error) {
var v yaml.Node
if err := yaml.NewDecoder(r).Decode(&v); err != nil {
return nil, err
}
return v.Content[0], nil
}
func saveKubeconfigRaw(w io.Writer, rootNode *yaml.Node) error {
enc := yaml.NewEncoder(w)
enc.SetIndent(2)
return enc.Encode(rootNode)
}
func openKubeconfig() (f *os.File, rootNode *yaml.Node, err error) {
cfgPath, err := kubeconfigPath()
if err != nil {
return nil, nil, errors.Wrap(err, "cannot determine kubeconfig path")
}
f, err = os.OpenFile(cfgPath, os.O_RDWR, 0)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to open file")
}
kc, err := parseKubeconfigRaw(f)
if err != nil {
f.Close()
return nil, nil, errors.Wrap(err, "yaml parse error")
}
return f, kc, nil
}
// resetFile deletes contents of a file and sets the seek
// position to 0.
func resetFile(f *os.File) error {
if err := f.Truncate(0); err != nil {
return errors.Wrap(err, "failed to truncate")
}
_, err := f.Seek(0, 0)
return errors.Wrap(err, "failed to seek")
}

View File

@ -22,6 +22,11 @@ func main() {
printError(err.Error())
os.Exit(1)
}
case UnsetOp:
if err := unsetContext(); err != nil {
printError(err.Error())
os.Exit(1)
}
case ListOp:
if err := printListContexts(os.Stdout); err != nil {
printError(err.Error())

View File

@ -1,9 +1,6 @@
package main
import (
"io"
"os"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
@ -15,21 +12,12 @@ func switchContext(name string) (string, error) {
return "", errors.Wrap(err, "failed to determine state file")
}
cfgPath, err := kubeconfigPath()
f, kc, err := openKubeconfig()
if err != nil {
return "", errors.Wrap(err, "cannot determine kubeconfig path")
}
f, err := os.OpenFile(cfgPath, os.O_RDWR, 0)
if err != nil {
return "", errors.Wrap(err, "failed to open file")
return "", err
}
defer f.Close()
kc, err := parseKubeconfigRaw(f)
if err != nil {
return "", errors.Wrap(err, "yaml parse error")
}
prev := getCurrentContext(kc)
// TODO: add a check to ensure user can't switch to non-existing context.
@ -41,12 +29,8 @@ func switchContext(name string) (string, error) {
return "", err
}
if err := f.Truncate(0); err != nil {
return "", errors.Wrap(err, "failed to truncate")
}
if _, err := f.Seek(0, 0); err != nil {
return "", errors.Wrap(err, "failed to seek")
if err := resetFile(f); err != nil {
return "", err
}
if err := saveKubeconfigRaw(f, kc); err != nil {
@ -153,17 +137,3 @@ func modifyCurrentContext(rootNode *yaml.Node, name string) error {
rootNode.Content = append(rootNode.Content, keyNode, valueNode)
return nil
}
func parseKubeconfigRaw(r io.Reader) (*yaml.Node, error) {
var v yaml.Node
if err := yaml.NewDecoder(r).Decode(&v); err != nil {
return nil, err
}
return v.Content[0], nil
}
func saveKubeconfigRaw(w io.Writer, rootNode *yaml.Node) error {
enc := yaml.NewEncoder(w)
enc.SetIndent(2)
return enc.Encode(rootNode)
}

34
cmd/kubectx/unset.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
func unsetContext() error {
f, rootNode, err := openKubeconfig()
if err != nil {
return err
}
defer f.Close()
if err := modifyDocToUnsetContext(rootNode); err != nil {
return errors.Wrap(err, "error while modifying current-context")
}
if err := resetFile(f); err != nil {
return err
}
if err := saveKubeconfigRaw(f, rootNode); err != nil {
return errors.Wrap(err, "failed to save kubeconfig file after modification")
}
return nil
}
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
}