Support for -d (deleting contexts)

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
This commit is contained in:
Ahmet Alp Balkan 2020-04-10 15:38:50 -07:00
parent 32d65fc527
commit 5ec2f4f032
No known key found for this signature in database
GPG Key ID: 441833503E604E2C
4 changed files with 113 additions and 0 deletions

85
cmd/kubectx/delete.go Normal file
View File

@ -0,0 +1,85 @@
package main
import (
"fmt"
"io"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
// deleteContexts deletes context entries one by one.
func deleteContexts(w io.Writer, ctxs []string) error {
for _, ctx := range ctxs {
// TODO inefficency here. we open/write/close the same file many times.
deletedName, wasActiveContext, err := deleteContext(ctx)
if err != nil {
return errors.Wrapf(err, "error deleting context %q", ctx)
}
if wasActiveContext {
// TODO we don't always run as kubectx (sometimes "kubectl ctx")
printWarning("You deleted the current context. use \"kubectx\" to select a different one.")
}
fmt.Fprintf(w, "deleted context %q\n", deletedName) // TODO write with printSuccess (i.e. green)
}
return nil
}
// deleteContext deletes a context entry by NAME or current-context
// indicated by ".".
func deleteContext(name string) (deleteName string, wasActiveContext bool, err error) {
f, rootNode, err := openKubeconfig()
if err != nil {
return "", false, err
}
defer f.Close()
cur := getCurrentContext(rootNode)
// resolve "." to a real name
if name == "." {
wasActiveContext = true
name = cur
}
if !checkContextExists(rootNode, name) {
return "", false, errors.New("context does not exist")
}
if err := modifyDocToDeleteContext(rootNode, name); err != nil {
return "", false, errors.Wrap(err, "failed to modify yaml doc")
}
if err := resetFile(f); err != nil {
return "", false, err
}
return name, wasActiveContext, errors.Wrap(saveKubeconfigRaw(f, rootNode), "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
}

View File

@ -21,6 +21,11 @@ type SwitchOp struct {
// UnsetOp indicates intention to remove current-context preference.
type UnsetOp struct{}
// DeleteOp indicates intention to delete contexts.
type DeleteOp struct {
Contexts []string // NAME or '.' to indicate current-context.
}
// UnknownOp indicates an unsupported flag.
type UnknownOp struct{ Args []string }
@ -31,6 +36,11 @@ func parseArgs(argv []string) Op {
return ListOp{}
}
if argv[0] == "-d" {
ctxs := argv[1:]
return DeleteOp{ctxs}
}
if len(argv) == 1 {
v := argv[0]
if v == "--help" || v == "-h" {

View File

@ -42,6 +42,15 @@ func Test_parseArgs_new(t *testing.T) {
{name: "switch by swap",
args: []string{"-"},
want: SwitchOp{Target: "-"}},
{name: "delete - without contexts",
args: []string{"-d"},
want: DeleteOp{[]string{}}},
{name: "delete - current context",
args: []string{"-d", "."},
want: DeleteOp{[]string{"."}}},
{name: "delete - multiple contexts",
args: []string{"-d", ".", "a", "b"},
want: DeleteOp{[]string{".", "a", "b"}}},
{name: "unrecognized flag",
args: []string{"-x"},
want: UnknownOp{Args: []string{"-x"}}},

View File

@ -32,6 +32,11 @@ func main() {
printError(err.Error())
os.Exit(1)
}
case DeleteOp:
if err := deleteContexts(os.Stderr, v.Contexts); err != nil {
printError(err.Error())
os.Exit(1)
}
case SwitchOp:
var newCtx string
var err error
@ -57,3 +62,7 @@ func main() {
func printError(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, color.RedString("error: ")+format+"\n", args...)
}
func printWarning(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, color.YellowString("warning: ")+format+"\n", args...)
}