Add filtering contexts

This commit is contained in:
Maciej Malarz 2024-11-29 15:40:56 +01:00 committed by Maciej Malarz
parent 561793c356
commit c44c9306fd
6 changed files with 85 additions and 9 deletions

View File

@ -40,6 +40,16 @@ func parseArgs(argv []string) Op {
return ListOp{} return ListOp{}
} }
if argv[0] == "--list" || argv[0] == "-l" {
if len(argv) == 1 {
return ListOp{}
}
if filters, ok := parseFilterSyntax(argv[1:]); ok {
return ListOp{Filters: filters}
}
return UnsupportedOp{Err: fmt.Errorf("'-l' filters must use A=B format")}
}
if argv[0] == "-d" { if argv[0] == "-d" {
if len(argv) == 1 { if len(argv) == 1 {
if cmdutil.IsInteractiveMode(os.Stdout) { if cmdutil.IsInteractiveMode(os.Stdout) {

View File

@ -39,6 +39,21 @@ func Test_parseArgs_new(t *testing.T) {
{name: "help long form", {name: "help long form",
args: []string{"--help"}, args: []string{"--help"},
want: HelpOp{}}, want: HelpOp{}},
{name: "list shorthand",
args: []string{"-l"},
want: ListOp{}},
{name: "list long form",
args: []string{"--list"},
want: ListOp{}},
{name: "list long form filters",
args: []string{"--list", "cluster=cl"},
want: ListOp{Filters: map[string]string{"cluster": "cl"}}},
{name: "list long form filters - wrong syntax",
args: []string{"--list", "cluster-cl"},
want: UnsupportedOp{fmt.Errorf("'-l' filters must use A=B format")}},
{name: "current long form",
args: []string{"--current"},
want: CurrentOp{}},
{name: "current shorthand", {name: "current shorthand",
args: []string{"-c"}, args: []string{"-c"},
want: CurrentOp{}}, want: CurrentOp{}},

View File

@ -88,7 +88,7 @@ func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
} }
kc.Close() kc.Close()
if len(kc.ContextNames()) == 0 { if len(kc.ContextNames(nil)) == 0 {
return errors.New("no contexts found in config") return errors.New("no contexts found in config")
} }

View File

@ -17,6 +17,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"strings"
"facette.io/natsort" "facette.io/natsort"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -27,9 +28,29 @@ import (
) )
// ListOp describes listing contexts. // ListOp describes listing contexts.
type ListOp struct{} type ListOp struct {
Filters map[string]string
}
func (_ ListOp) Run(stdout, stderr io.Writer) error { // parseFilterSyntax parses multiple A=B form into a map[A]=B and returns
// whether it is parsed correctly.
func parseFilterSyntax(v []string) (map[string]string, bool) {
m := make(map[string]string)
for _, vv := range v {
s := strings.Split(vv, "=")
if len(s) != 2 {
return nil, false
}
key, value := s[0], s[1]
if key == "" || value == "" {
return nil, false
}
m[key] = value
}
return m, true
}
func (op ListOp) Run(stdout, stderr io.Writer) error {
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader) kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
defer kc.Close() defer kc.Close()
if err := kc.Parse(); err != nil { if err := kc.Parse(); err != nil {
@ -40,7 +61,7 @@ func (_ ListOp) Run(stdout, stderr io.Writer) error {
return errors.Wrap(err, "kubeconfig error") return errors.Wrap(err, "kubeconfig error")
} }
ctxs := kc.ContextNames() ctxs := kc.ContextNames(op.Filters)
natsort.Sort(ctxs) natsort.Sort(ctxs)
cur := kc.GetCurrentContext() cur := kc.GetCurrentContext()

View File

@ -44,7 +44,7 @@ func (k *Kubeconfig) contextNode(name string) (*yaml.Node, error) {
return nil, errors.Errorf("context with name \"%s\" not found", name) return nil, errors.Errorf("context with name \"%s\" not found", name)
} }
func (k *Kubeconfig) ContextNames() []string { func (k *Kubeconfig) ContextNames(filters map[string]string) []string {
contexts := valueOf(k.rootNode, "contexts") contexts := valueOf(k.rootNode, "contexts")
if contexts == nil { if contexts == nil {
return nil return nil
@ -54,8 +54,19 @@ func (k *Kubeconfig) ContextNames() []string {
} }
var ctxNames []string var ctxNames []string
ctxLoop:
for _, ctx := range contexts.Content { for _, ctx := range contexts.Content {
nameVal := valueOf(ctx, "name") nameVal := valueOf(ctx, "name")
for k, v := range filters {
ctxVal := valueOf(ctx, "context")
if ctxVal == nil {
continue ctxLoop
}
vVal := valueOf(ctxVal, k)
if vVal == nil || vVal.Value != v {
continue ctxLoop
}
}
if nameVal != nil { if nameVal != nil {
ctxNames = append(ctxNames, nameVal.Value) ctxNames = append(ctxNames, nameVal.Value)
} }
@ -64,7 +75,7 @@ func (k *Kubeconfig) ContextNames() []string {
} }
func (k *Kubeconfig) ContextExists(name string) bool { func (k *Kubeconfig) ContextExists(name string) bool {
ctxNames := k.ContextNames() ctxNames := k.ContextNames(nil)
for _, v := range ctxNames { for _, v := range ctxNames {
if v == name { if v == name {
return true return true

View File

@ -33,20 +33,39 @@ func TestKubeconfig_ContextNames(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ctx := kc.ContextNames() ctx := kc.ContextNames(nil)
expected := []string{"abc", "def", "ghi"} expected := []string{"abc", "def", "ghi"}
if diff := cmp.Diff(expected, ctx); diff != "" { if diff := cmp.Diff(expected, ctx); diff != "" {
t.Fatalf("%s", diff) t.Fatalf("%s", diff)
} }
} }
func TestKubeconfig_ContextNames_Filtered(t *testing.T) {
tl := WithMockKubeconfigLoader(
testutil.KC().WithCtxs(
testutil.Ctx("abc").Ns("ns1"),
testutil.Ctx("def"),
testutil.Ctx("ghi").Ns("ns2"),
).Set("field1", map[string]string{"bar": "zoo"}).ToYAML(t))
kc := new(Kubeconfig).WithLoader(tl)
if err := kc.Parse(); err != nil {
t.Fatal(err)
}
ctx := kc.ContextNames(map[string]string{"namespace": "ns2"})
expected := []string{"ghi"}
if diff := cmp.Diff(expected, ctx); diff != "" {
t.Fatalf("%s", diff)
}
}
func TestKubeconfig_ContextNames_noContextsEntry(t *testing.T) { func TestKubeconfig_ContextNames_noContextsEntry(t *testing.T) {
tl := WithMockKubeconfigLoader(`a: b`) tl := WithMockKubeconfigLoader(`a: b`)
kc := new(Kubeconfig).WithLoader(tl) kc := new(Kubeconfig).WithLoader(tl)
if err := kc.Parse(); err != nil { if err := kc.Parse(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
ctx := kc.ContextNames() ctx := kc.ContextNames(nil)
var expected []string = nil var expected []string = nil
if diff := cmp.Diff(expected, ctx); diff != "" { if diff := cmp.Diff(expected, ctx); diff != "" {
t.Fatalf("%s", diff) t.Fatalf("%s", diff)
@ -59,7 +78,7 @@ func TestKubeconfig_ContextNames_nonArrayContextsEntry(t *testing.T) {
if err := kc.Parse(); err != nil { if err := kc.Parse(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
ctx := kc.ContextNames() ctx := kc.ContextNames(nil)
var expected []string = nil var expected []string = nil
if diff := cmp.Diff(expected, ctx); diff != "" { if diff := cmp.Diff(expected, ctx); diff != "" {
t.Fatalf("%s", diff) t.Fatalf("%s", diff)