1
1
package main
2
2
3
3
import (
4
+ "bytes"
4
5
"fmt"
5
6
"os"
7
+ "regexp"
6
8
"sort"
7
9
"strings"
8
10
@@ -13,6 +15,7 @@ import (
13
15
"k8s.io/cli-runtime/pkg/genericclioptions"
14
16
"k8s.io/client-go/discovery"
15
17
_ "k8s.io/client-go/plugin/pkg/client/auth"
18
+ "k8s.io/kube-openapi/pkg/util/proto"
16
19
cmdutil "k8s.io/kubectl/pkg/cmd/util"
17
20
"k8s.io/kubectl/pkg/explain"
18
21
"k8s.io/kubectl/pkg/util/openapi"
@@ -27,9 +30,9 @@ type Options struct {
27
30
Discovery discovery.CachedDiscoveryInterface
28
31
Schema openapi.Resources
29
32
30
- inputFieldPath string
33
+ inputFieldPath * regexp. Regexp
31
34
resource string
32
- gvk schema.GroupVersionKind
35
+ gvks [] schema.GroupVersionKind
33
36
}
34
37
35
38
func NewCmd () * cobra.Command {
@@ -48,17 +51,22 @@ Fields are identified via a simple JSONPath identifier:
48
51
` ,
49
52
Short : "Fuzzy-find the explanation for a resource or its field." ,
50
53
Example : `
51
- # Fuzzy-find the field explanation from supported API resources.
54
+ # Fuzzy-find the field to explain from all API resources.
52
55
kubectl explore
53
56
54
- # Fuzzy-find the field explanation from "pod"
57
+ # Fuzzy-find the field to explain from fields matching the regex.
58
+ kubectl explore pod.*node
59
+ kubectl explore spec.*containers
60
+ kubectl explore lifecycle
61
+
62
+ # Fuzzy-find the field to explain from all API resources in the selected cluster.
63
+ kubectl explore --context=onecontext
64
+
65
+ # Fuzzy-find the field to explain from fields under "pod".
55
66
kubectl explore pod
56
67
57
- # Fuzzy-find the field explanation from "pod.spec.containers"
68
+ # Fuzzy-find the field to explain from fields under "pod.spec.containers".
58
69
kubectl explore pod.spec.containers
59
-
60
- # Fuzzy-find the field explanation from supported API resources in the selected cluster.
61
- kubectl explore --context=onecontext
62
70
` ,
63
71
}
64
72
cmd .Flags ().StringVar (& o .APIVersion , "api-version" , o .APIVersion , "Get different explanations for particular API version (API group/version)" )
@@ -83,13 +91,16 @@ func NewOptions(streams genericclioptions.IOStreams) *Options {
83
91
}
84
92
85
93
func (o * Options ) Complete (f cmdutil.Factory , args []string ) error {
86
- if 0 < len (args ) {
87
- o .inputFieldPath = args [0 ]
88
- }
89
- if len (args ) == 1 {
94
+ var err error
95
+ if len (args ) == 0 {
96
+ o .inputFieldPath = regexp .MustCompile (".*" )
97
+ } else {
98
+ o .inputFieldPath , err = regexp .Compile (args [0 ])
99
+ if err != nil {
100
+ return err
101
+ }
90
102
o .resource = args [0 ]
91
103
}
92
- var err error
93
104
o .Discovery , err = f .ToDiscoveryClient ()
94
105
if err != nil {
95
106
return err
@@ -103,22 +114,79 @@ func (o *Options) Complete(f cmdutil.Factory, args []string) error {
103
114
return err
104
115
}
105
116
if o .resource == "" {
106
- o .gvk , err = o .findGVK ()
117
+ gvk , err := o .findGVK ()
118
+ if err != nil {
119
+ return err
120
+ }
121
+ o .gvks = []schema.GroupVersionKind {gvk }
107
122
} else {
108
- o .gvk , err = o .getGVK (strings .Split (o .resource , "." )[0 ])
109
- }
110
- if err != nil {
111
- return err
123
+ gvk , err := o .getGVK (strings .Split (o .resource , "." )[0 ])
124
+ if err == nil {
125
+ o .gvks = []schema.GroupVersionKind {gvk }
126
+ } else {
127
+ o .gvks , err = o .listGVKs ()
128
+ if err != nil {
129
+ return err
130
+ }
131
+ }
112
132
}
113
133
return nil
114
134
}
115
135
116
136
func (o * Options ) Run () error {
117
- e , err := newExplorer (o )
137
+ pathExplainers := make (map [string ]explainer )
138
+ var paths []string
139
+ for _ , gvk := range o .gvks {
140
+ visitor := & schemaVisitor {
141
+ pathSchema : make (map [string ]proto.Schema ),
142
+ prevPath : strings .ToLower (gvk .Kind ),
143
+ err : nil ,
144
+ }
145
+ s := o .Schema .LookupResource (gvk )
146
+ if s == nil {
147
+ return fmt .Errorf ("no schema found for %s" , gvk )
148
+ }
149
+ s .Accept (visitor )
150
+ if visitor .err != nil {
151
+ return visitor .err
152
+ }
153
+ filteredPaths := visitor .listPaths (func (s string ) bool {
154
+ return o .inputFieldPath .MatchString (s )
155
+ })
156
+ for _ , p := range filteredPaths {
157
+ pathExplainers [p ] = explainer {
158
+ schemaByGvk : s ,
159
+ gvk : gvk ,
160
+ pathSchema : visitor .pathSchema ,
161
+ }
162
+ paths = append (paths , p )
163
+ }
164
+ }
165
+ if len (paths ) == 0 {
166
+ return fmt .Errorf ("no paths found for %q" , o .inputFieldPath )
167
+ }
168
+ if len (paths ) == 1 {
169
+ return pathExplainers [paths [0 ]].explain (o .Out , paths [0 ])
170
+ }
171
+ sort .Strings (paths )
172
+ idx , err := fuzzyfinder .Find (
173
+ paths ,
174
+ func (i int ) string { return paths [i ] },
175
+ fuzzyfinder .WithPreviewWindow (func (i , _ , _ int ) string {
176
+ if i < 0 {
177
+ return ""
178
+ }
179
+ var w bytes.Buffer
180
+ if err := pathExplainers [paths [i ]].explain (& w , paths [i ]); err != nil {
181
+ return fmt .Sprintf ("preview is broken: %s" , err )
182
+ }
183
+ return w .String ()
184
+ },
185
+ ))
118
186
if err != nil {
119
187
return err
120
188
}
121
- return e . explore (o .Out )
189
+ return pathExplainers [ paths [ idx ]]. explain (o .Out , paths [ idx ] )
122
190
}
123
191
124
192
func (o * Options ) findGVK () (schema.GroupVersionKind , error ) {
0 commit comments