Skip to content

Commit 4a3131d

Browse files
committed
add fieldSelector for kubectl get
1 parent 057b7bf commit 4a3131d

File tree

5 files changed

+53
-21
lines changed

5 files changed

+53
-21
lines changed

pkg/kubectl/cmd/apply.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
367367
clientFunc: f.UnstructuredClientForMapping,
368368
clientsetFunc: f.ClientSet,
369369

370-
selector: options.Selector,
371-
visitedUids: visitedUids,
370+
labelSelector: options.Selector,
371+
visitedUids: visitedUids,
372372

373373
cascade: options.Cascade,
374374
dryRun: dryRun,
@@ -453,8 +453,9 @@ type pruner struct {
453453
clientFunc resource.ClientMapperFunc
454454
clientsetFunc func() (internalclientset.Interface, error)
455455

456-
visitedUids sets.String
457-
selector string
456+
visitedUids sets.String
457+
labelSelector string
458+
fieldSelector string
458459

459460
cascade bool
460461
dryRun bool
@@ -474,7 +475,8 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput,
474475
mapping.GroupVersionKind.Version,
475476
false,
476477
&metav1.ListOptions{
477-
LabelSelector: p.selector,
478+
LabelSelector: p.labelSelector,
479+
FieldSelector: p.fieldSelector,
478480
IncludeUninitialized: includeUninitialized,
479481
},
480482
)

pkg/kubectl/cmd/resource/get.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type GetOptions struct {
5656
ChunkSize int64
5757

5858
LabelSelector string
59+
FieldSelector string
5960
AllNamespaces bool
6061
Namespace string
6162
ExplicitNamespace bool
@@ -158,6 +159,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
158159
cmd.Flags().Int64Var(&options.ChunkSize, "chunk-size", 500, "Return large lists in chunks rather than all at once. Pass 0 to disable. This flag is beta and may change in the future.")
159160
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", options.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
160161
cmd.Flags().StringVarP(&options.LabelSelector, "selector", "l", options.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
162+
cmd.Flags().StringVar(&options.FieldSelector, "field-selector", options.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
161163
cmd.Flags().BoolVar(&options.AllNamespaces, "all-namespaces", options.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
162164
cmdutil.AddIncludeUninitializedFlag(cmd)
163165
cmdutil.AddPrinterFlags(cmd)
@@ -234,6 +236,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
234236
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
235237
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
236238
LabelSelectorParam(options.LabelSelector).
239+
FieldSelectorParam(options.FieldSelector).
237240
ExportParam(options.Export).
238241
RequestChunksOf(options.ChunkSize).
239242
IncludeUninitialized(cmdutil.ShouldIncludeUninitialized(cmd, false)). // TODO: this needs to be better factored
@@ -451,6 +454,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
451454
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
452455
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
453456
LabelSelectorParam(options.LabelSelector).
457+
FieldSelectorParam(options.FieldSelector).
454458
ExportParam(options.Export).
455459
RequestChunksOf(options.ChunkSize).
456460
IncludeUninitialized(includeUninitialized).

pkg/kubectl/resource/builder.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type Builder struct {
5454
dir bool
5555

5656
labelSelector *string
57+
fieldSelector *string
5758
selectAll bool
5859
includeUninitialized bool
5960
limitChunks int64
@@ -296,6 +297,22 @@ func (b *Builder) LabelSelector(selector string) *Builder {
296297
return b
297298
}
298299

300+
// FieldSelectorParam defines a selector that should be applied to the object types to load.
301+
// This will not affect files loaded from disk or URL. If the parameter is empty it is
302+
// a no-op - to select all resources.
303+
func (b *Builder) FieldSelectorParam(s string) *Builder {
304+
s = strings.TrimSpace(s)
305+
if len(s) == 0 {
306+
return b
307+
}
308+
if b.selectAll {
309+
b.errs = append(b.errs, fmt.Errorf("found non-empty field selector %q with previously set 'all' parameter. ", s))
310+
return b
311+
}
312+
b.fieldSelector = &s
313+
return b
314+
}
315+
299316
// ExportParam accepts the export boolean for these resources
300317
func (b *Builder) ExportParam(export bool) *Builder {
301318
b.export = export
@@ -350,7 +367,7 @@ func (b *Builder) RequestChunksOf(chunkSize int64) *Builder {
350367

351368
// SelectEverythingParam
352369
func (b *Builder) SelectAllParam(selectAll bool) *Builder {
353-
if selectAll && b.labelSelector != nil {
370+
if selectAll && (b.labelSelector != nil || b.fieldSelector != nil) {
354371
b.errs = append(b.errs, fmt.Errorf("setting 'all' parameter but found a non empty selector. "))
355372
return b
356373
}
@@ -595,7 +612,7 @@ func (b *Builder) visitorResult() *Result {
595612
}
596613

597614
// visit selectors
598-
if b.labelSelector != nil {
615+
if b.labelSelector != nil || b.fieldSelector != nil {
599616
return b.visitBySelector()
600617
}
601618

@@ -635,6 +652,14 @@ func (b *Builder) visitBySelector() *Result {
635652
return result
636653
}
637654

655+
var labelSelector, fieldSelector string
656+
if b.labelSelector != nil {
657+
labelSelector = *b.labelSelector
658+
}
659+
if b.fieldSelector != nil {
660+
fieldSelector = *b.fieldSelector
661+
}
662+
638663
visitors := []Visitor{}
639664
for _, mapping := range mappings {
640665
client, err := b.mapper.ClientForMapping(mapping)
@@ -646,7 +671,7 @@ func (b *Builder) visitBySelector() *Result {
646671
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
647672
selectorNamespace = ""
648673
}
649-
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, *b.labelSelector, b.export, b.includeUninitialized, b.limitChunks))
674+
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, labelSelector, fieldSelector, b.export, b.includeUninitialized, b.limitChunks))
650675
}
651676
if b.continueOnError {
652677
result.visitor = EagerVisitorList(visitors)

pkg/kubectl/resource/helper.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,12 @@ func (m *Helper) List(namespace, apiVersion string, export bool, options *metav1
7575
return req.Do().Get()
7676
}
7777

78-
func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector string) (watch.Interface, error) {
78+
func (m *Helper) Watch(namespace, apiVersion string, options *metav1.ListOptions) (watch.Interface, error) {
79+
options.Watch = true
7980
return m.RESTClient.Get().
8081
NamespaceIfScoped(namespace, m.NamespaceScoped).
8182
Resource(m.Resource).
82-
VersionedParams(&metav1.ListOptions{
83-
ResourceVersion: resourceVersion,
84-
Watch: true,
85-
LabelSelector: labelSelector,
86-
}, metav1.ParameterCodec).
83+
VersionedParams(options, metav1.ParameterCodec).
8784
Watch()
8885
}
8986

pkg/kubectl/resource/selector.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,20 @@ type Selector struct {
3131
Mapping *meta.RESTMapping
3232
Namespace string
3333
LabelSelector string
34+
FieldSelector string
3435
Export bool
3536
IncludeUninitialized bool
3637
LimitChunks int64
3738
}
3839

3940
// NewSelector creates a resource selector which hides details of getting items by their label selector.
40-
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector string, export, includeUninitialized bool, limitChunks int64) *Selector {
41+
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace, labelSelector, fieldSelector string, export, includeUninitialized bool, limitChunks int64) *Selector {
4142
return &Selector{
4243
Client: client,
4344
Mapping: mapping,
4445
Namespace: namespace,
45-
LabelSelector: selector,
46+
LabelSelector: labelSelector,
47+
FieldSelector: fieldSelector,
4648
Export: export,
4749
IncludeUninitialized: includeUninitialized,
4850
LimitChunks: limitChunks,
@@ -59,6 +61,7 @@ func (r *Selector) Visit(fn VisitorFunc) error {
5961
r.Export,
6062
&metav1.ListOptions{
6163
LabelSelector: r.LabelSelector,
64+
FieldSelector: r.FieldSelector,
6265
IncludeUninitialized: r.IncludeUninitialized,
6366
Limit: r.LimitChunks,
6467
Continue: continueToken,
@@ -71,17 +74,17 @@ func (r *Selector) Visit(fn VisitorFunc) error {
7174
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
7275
if se, ok := err.(*errors.StatusError); ok {
7376
// modify the message without hiding this is an API error
74-
if len(r.LabelSelector) == 0 {
77+
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
7578
se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", r.Mapping.Resource, se.ErrStatus.Message)
7679
} else {
77-
se.ErrStatus.Message = fmt.Sprintf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.LabelSelector, se.ErrStatus.Message)
80+
se.ErrStatus.Message = fmt.Sprintf("Unable to find %q that match label selector %q, field selector %q: %v", r.Mapping.Resource, r.LabelSelector, r.FieldSelector, se.ErrStatus.Message)
7881
}
7982
return se
8083
}
81-
if len(r.LabelSelector) == 0 {
84+
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
8285
return fmt.Errorf("Unable to list %q: %v", r.Mapping.Resource, err)
8386
}
84-
return fmt.Errorf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.LabelSelector, err)
87+
return fmt.Errorf("Unable to find %q that match label selector %q, field selector %q: %v", r.Mapping.Resource, r.LabelSelector, r.FieldSelector, err)
8588
}
8689
return err
8790
}
@@ -107,7 +110,8 @@ func (r *Selector) Visit(fn VisitorFunc) error {
107110
}
108111

109112
func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) {
110-
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.LabelSelector)
113+
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(),
114+
&metav1.ListOptions{ResourceVersion: resourceVersion, LabelSelector: r.LabelSelector, FieldSelector: r.FieldSelector})
111115
}
112116

113117
// ResourceMapping returns the mapping for this resource and implements ResourceMapping

0 commit comments

Comments
 (0)