Skip to content

Commit

Permalink
Merge pull request #45 from keisku/issue44
Browse files Browse the repository at this point in the history
Add `--show-brackets`
  • Loading branch information
keisku authored Dec 7, 2024
2 parents 4405da3 + 74c6fc3 commit 913fa6b
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 52 deletions.
22 changes: 13 additions & 9 deletions explore/explainer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package explore

import (
"errors"
"fmt"
"io"
"strings"
Expand All @@ -12,22 +11,27 @@ import (
)

type explainer struct {
gvr schema.GroupVersionResource
openAPIV3Client openapiclient.Client
enablePrintPath bool
gvr schema.GroupVersionResource
openAPIV3Client openapiclient.Client
enablePrintPath bool
enablePrintBrackets bool
}

func (e explainer) explain(w io.Writer, path string) error {
if path == "" {
return errors.New("path must be provided")
func (e explainer) explain(w io.Writer, path path) error {
if path.isEmpty() {
return fmt.Errorf("path must not be empty: %#v", path)
}
fields := strings.Split(path, ".")
fields := strings.Split(path.original, ".")
if len(fields) > 0 {
// Remove resource name
fields = fields[1:]
}
if e.enablePrintPath {
w.Write([]byte(fmt.Sprintf("PATH: %s\n", path)))
if e.enablePrintBrackets {
w.Write([]byte(fmt.Sprintf("PATH: %s\n", path.withBrackets)))
} else {
w.Write([]byte(fmt.Sprintf("PATH: %s\n", path.original)))
}
}
return explainv2.PrintModelDescription(
fields,
Expand Down
9 changes: 9 additions & 0 deletions explore/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package explore

func SetDisablePrintPath(o *Options, b bool) {
o.disablePrintPath = b
}

func SetShowBrackets(o *Options, b bool) {
o.showBrackets = b
}
32 changes: 20 additions & 12 deletions explore/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Options struct {
apiVersion string
inputFieldPath string
disablePrintPath bool
showBrackets bool

// After completion
inputFieldPathRegex *regexp.Regexp
Expand Down Expand Up @@ -69,6 +70,7 @@ kubectl explore --context=onecontext
}
cmd.Flags().StringVar(&o.apiVersion, "api-version", o.apiVersion, "Get different explanations for particular API version (API group/version)")
cmd.Flags().BoolVar(&o.disablePrintPath, "disable-print-path", o.disablePrintPath, "Disable printing the path to explain")
cmd.Flags().BoolVar(&o.showBrackets, "show-brackets", o.showBrackets, "Enable showing brackets for fields that are arrays")
kubeConfigFlags := defaultConfigFlags().WithWarningPrinter(o.IOStreams)
flags := cmd.PersistentFlags()
kubeConfigFlags.AddFlags(flags)
Expand Down Expand Up @@ -194,13 +196,16 @@ func (o *Options) Complete(f cmdutil.Factory, args []string) error {
}

func (o *Options) Run() error {
pathExplainers := make(map[string]explainer)
var paths []string
pathExplainers := make(map[path]explainer)
var paths []path
for _, gvr := range o.gvrs {
visitor := &schemaVisitor{
pathSchema: make(map[string]proto.Schema),
prevPath: strings.ToLower(gvr.Resource),
err: nil,
pathSchema: make(map[path]proto.Schema),
prevPath: path{
original: strings.ToLower(gvr.Resource),
withBrackets: strings.ToLower(gvr.Resource),
},
err: nil,
}
gvk, err := o.mapper.KindFor(gvr)
if err != nil {
Expand All @@ -214,14 +219,15 @@ func (o *Options) Run() error {
if visitor.err != nil {
return visitor.err
}
filteredPaths := visitor.listPaths(func(s string) bool {
return o.inputFieldPathRegex.MatchString(s)
filteredPaths := visitor.listPaths(func(s path) bool {
return o.inputFieldPathRegex.MatchString(s.original)
})
for _, p := range filteredPaths {
pathExplainers[p] = explainer{
gvr: gvr,
openAPIV3Client: o.cachedOpenAPIV3Client,
enablePrintPath: !o.disablePrintPath,
gvr: gvr,
openAPIV3Client: o.cachedOpenAPIV3Client,
enablePrintPath: !o.disablePrintPath,
enablePrintBrackets: o.showBrackets,
}
paths = append(paths, p)
}
Expand All @@ -232,10 +238,12 @@ func (o *Options) Run() error {
if len(paths) == 1 {
return pathExplainers[paths[0]].explain(o.Out, paths[0])
}
sort.Strings(paths)
sort.SliceStable(paths, func(i, j int) bool {
return paths[i].original < paths[j].original
})
idx, err := fuzzyfinder.Find(
paths,
func(i int) string { return paths[i] },
func(i int) string { return paths[i].original },
fuzzyfinder.WithPreviewWindow(func(i, _, _ int) string {
if i < 0 {
return ""
Expand Down
107 changes: 83 additions & 24 deletions explore/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,49 +213,62 @@ func Test_Run(t *testing.T) {
},
}
tests := []struct {
inputFieldPath string
expectRunError bool
expectKeywords []string
inputFieldPath string
disablePrintPath bool
showBrackets bool
expectRunError bool
expectKeywords []string
unexpectKeywords []string
}{
{
inputFieldPath: "no.*pro",
expectRunError: false,
inputFieldPath: "no.*pro",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"Node",
"providerID",
"PATH: nodes.spec.providerID",
},
},
{
inputFieldPath: "node.*pro",
expectRunError: false,
inputFieldPath: "node.*pro",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"Node",
"providerID",
"PATH: nodes.spec.providerID",
},
},
{
inputFieldPath: "nodes.*pro",
expectRunError: false,
inputFieldPath: "nodes.*pro",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"Node",
"providerID",
"PATH: nodes.spec.providerID",
},
},
{
inputFieldPath: "providerID",
expectRunError: false,
inputFieldPath: "providerID",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"Node",
"providerID",
"PATH: nodes.spec.providerID",
},
},
{
inputFieldPath: "hpa.*own.*id",
expectRunError: false,
inputFieldPath: "hpa.*own.*id",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"autoscaling",
"HorizontalPodAutoscaler",
Expand All @@ -264,8 +277,10 @@ func Test_Run(t *testing.T) {
},
},
{
inputFieldPath: "horizontalpodautoscalers.*own.*id",
expectRunError: false,
inputFieldPath: "horizontalpodautoscalers.*own.*id",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"autoscaling",
"HorizontalPodAutoscaler",
Expand All @@ -274,8 +289,10 @@ func Test_Run(t *testing.T) {
},
},
{
inputFieldPath: "horizontalpodautoscaler.*own.*id",
expectRunError: false,
inputFieldPath: "horizontalpodautoscaler.*own.*id",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"autoscaling",
"HorizontalPodAutoscaler",
Expand All @@ -284,8 +301,10 @@ func Test_Run(t *testing.T) {
},
},
{
inputFieldPath: "csistoragecapacity.maximumVolumeSize",
expectRunError: false,
inputFieldPath: "csistoragecapacity.maximumVolumeSize",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"CSIStorageCapacity",
"storage.k8s.io",
Expand All @@ -294,8 +313,10 @@ func Test_Run(t *testing.T) {
},
},
{
inputFieldPath: "csistoragecapacities.maximumVolumeSize",
expectRunError: false,
inputFieldPath: "csistoragecapacities.maximumVolumeSize",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"CSIStorageCapacity",
"storage.k8s.io",
Expand All @@ -304,19 +325,52 @@ func Test_Run(t *testing.T) {
},
},
{
inputFieldPath: "CSIStorageCapacity.*VolumeSize",
expectRunError: false,
inputFieldPath: "CSIStorageCapacity.*VolumeSize",
disablePrintPath: false,
showBrackets: false,
expectRunError: false,
expectKeywords: []string{
"CSIStorageCapacity",
"storage.k8s.io",
"v1",
"PATH: csistoragecapacities.maximumVolumeSize",
},
},
{
inputFieldPath: "nodes.status.conditions.type",
disablePrintPath: false,
showBrackets: true,
expectRunError: false,
expectKeywords: []string{
"Node",
"type",
"PATH: nodes.status.conditions[].type",
},
},
{
inputFieldPath: "nodes.status.conditions.type",
disablePrintPath: true,
showBrackets: true,
expectRunError: false,
expectKeywords: []string{
"Node",
"type",
},
unexpectKeywords: []string{
"PATH: nodes.status.conditions[].type",
"PATH: nodes.status.conditions.type",
},
},
}
for _, tt := range tests {
for _, version := range k8sVersions {
t.Run(fmt.Sprintf("version: %s inputFieldPath: %s", version, tt.inputFieldPath), func(t *testing.T) {
t.Run(fmt.Sprintf(
"version: %s inputFieldPath: %s, disablePrintPath: %v, showBrackets: %v",
version,
tt.inputFieldPath,
tt.disablePrintPath,
tt.showBrackets,
), func(t *testing.T) {
fakeServer := fakeServers[version]
fakeDiscoveryClient := discovery.NewDiscoveryClientForConfigOrDie(&rest.Config{Host: fakeServer.HttpServer.URL})
tf := cmdtesting.NewTestFactory()
Expand All @@ -338,6 +392,8 @@ func Test_Run(t *testing.T) {
Out: &stdout,
ErrOut: &errout,
})
explore.SetDisablePrintPath(opts, tt.disablePrintPath)
explore.SetShowBrackets(opts, tt.showBrackets)
require.NoError(t, opts.Complete(tf, []string{tt.inputFieldPath}))
err := opts.Run()
if tt.expectRunError {
Expand All @@ -348,6 +404,9 @@ func Test_Run(t *testing.T) {
for _, keyword := range tt.expectKeywords {
require.Contains(t, stdout.String(), keyword)
}
for _, keyword := range tt.unexpectKeywords {
require.NotContains(t, stdout.String(), keyword)
}
})
}
}
Expand Down
31 changes: 24 additions & 7 deletions explore/schema_visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,41 @@ import (
"k8s.io/kubectl/pkg/explain"
)

type path struct {
original string
withBrackets string
}

func (p path) isEmpty() bool {
return p.original == "" && p.withBrackets == ""
}

type schemaVisitor struct {
prevPath string
pathSchema map[string]proto.Schema
prevPath path
pathSchema map[path]proto.Schema
err error
}

var _ proto.SchemaVisitor = (*schemaVisitor)(nil)

func (v *schemaVisitor) VisitKind(k *proto.Kind) {
keys := k.Keys()
paths := make([]string, len(keys))
paths := make([]path, len(keys))
for i, key := range keys {
paths[i] = strings.Join([]string{v.prevPath, key}, ".")
paths[i] = path{
original: strings.Join([]string{v.prevPath.original, key}, "."),
withBrackets: strings.Join([]string{v.prevPath.withBrackets, key}, "."),
}
}
for i, key := range keys {
schema, err := explain.LookupSchemaForField(k, []string{key})
if err != nil {
v.err = err
return
}
if _, ok := schema.(*proto.Array); ok {
paths[i].withBrackets += "[]"
}
v.pathSchema[paths[i]] = schema
v.prevPath = paths[i]
schema.Accept(v)
Expand Down Expand Up @@ -57,13 +72,15 @@ func (v *schemaVisitor) VisitMap(m *proto.Map) {
m.SubType.Accept(v)
}

func (v *schemaVisitor) listPaths(filter func(string) bool) []string {
paths := make([]string, 0, len(v.pathSchema))
func (v *schemaVisitor) listPaths(filter func(path) bool) []path {
paths := make([]path, 0, len(v.pathSchema))
for path := range v.pathSchema {
if filter(path) {
paths = append(paths, path)
}
}
sort.Strings(paths)
sort.SliceStable(paths, func(i, j int) bool {
return paths[i].original < paths[j].original
})
return paths
}

0 comments on commit 913fa6b

Please sign in to comment.