add fieldSelector for kubectl get

This commit is contained in:
Di Xu 2017-08-04 17:56:44 +08:00
parent 057b7bf767
commit 4a3131ddaa
5 changed files with 53 additions and 21 deletions

View File

@ -367,8 +367,8 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
clientFunc: f.UnstructuredClientForMapping,
clientsetFunc: f.ClientSet,
selector: options.Selector,
visitedUids: visitedUids,
labelSelector: options.Selector,
visitedUids: visitedUids,
cascade: options.Cascade,
dryRun: dryRun,
@ -453,8 +453,9 @@ type pruner struct {
clientFunc resource.ClientMapperFunc
clientsetFunc func() (internalclientset.Interface, error)
visitedUids sets.String
selector string
visitedUids sets.String
labelSelector string
fieldSelector string
cascade bool
dryRun bool
@ -474,7 +475,8 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput,
mapping.GroupVersionKind.Version,
false,
&metav1.ListOptions{
LabelSelector: p.selector,
LabelSelector: p.labelSelector,
FieldSelector: p.fieldSelector,
IncludeUninitialized: includeUninitialized,
},
)

View File

@ -56,6 +56,7 @@ type GetOptions struct {
ChunkSize int64
LabelSelector string
FieldSelector string
AllNamespaces bool
Namespace string
ExplicitNamespace bool
@ -158,6 +159,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
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.")
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", options.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
cmd.Flags().StringVarP(&options.LabelSelector, "selector", "l", options.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
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.")
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.")
cmdutil.AddIncludeUninitializedFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
@ -234,6 +236,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector).
FieldSelectorParam(options.FieldSelector).
ExportParam(options.Export).
RequestChunksOf(options.ChunkSize).
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
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector).
FieldSelectorParam(options.FieldSelector).
ExportParam(options.Export).
RequestChunksOf(options.ChunkSize).
IncludeUninitialized(includeUninitialized).

View File

@ -54,6 +54,7 @@ type Builder struct {
dir bool
labelSelector *string
fieldSelector *string
selectAll bool
includeUninitialized bool
limitChunks int64
@ -296,6 +297,22 @@ func (b *Builder) LabelSelector(selector string) *Builder {
return b
}
// FieldSelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources.
func (b *Builder) FieldSelectorParam(s string) *Builder {
s = strings.TrimSpace(s)
if len(s) == 0 {
return b
}
if b.selectAll {
b.errs = append(b.errs, fmt.Errorf("found non-empty field selector %q with previously set 'all' parameter. ", s))
return b
}
b.fieldSelector = &s
return b
}
// ExportParam accepts the export boolean for these resources
func (b *Builder) ExportParam(export bool) *Builder {
b.export = export
@ -350,7 +367,7 @@ func (b *Builder) RequestChunksOf(chunkSize int64) *Builder {
// SelectEverythingParam
func (b *Builder) SelectAllParam(selectAll bool) *Builder {
if selectAll && b.labelSelector != nil {
if selectAll && (b.labelSelector != nil || b.fieldSelector != nil) {
b.errs = append(b.errs, fmt.Errorf("setting 'all' parameter but found a non empty selector. "))
return b
}
@ -595,7 +612,7 @@ func (b *Builder) visitorResult() *Result {
}
// visit selectors
if b.labelSelector != nil {
if b.labelSelector != nil || b.fieldSelector != nil {
return b.visitBySelector()
}
@ -635,6 +652,14 @@ func (b *Builder) visitBySelector() *Result {
return result
}
var labelSelector, fieldSelector string
if b.labelSelector != nil {
labelSelector = *b.labelSelector
}
if b.fieldSelector != nil {
fieldSelector = *b.fieldSelector
}
visitors := []Visitor{}
for _, mapping := range mappings {
client, err := b.mapper.ClientForMapping(mapping)
@ -646,7 +671,7 @@ func (b *Builder) visitBySelector() *Result {
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
}
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, *b.labelSelector, b.export, b.includeUninitialized, b.limitChunks))
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, labelSelector, fieldSelector, b.export, b.includeUninitialized, b.limitChunks))
}
if b.continueOnError {
result.visitor = EagerVisitorList(visitors)

View File

@ -75,15 +75,12 @@ func (m *Helper) List(namespace, apiVersion string, export bool, options *metav1
return req.Do().Get()
}
func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector string) (watch.Interface, error) {
func (m *Helper) Watch(namespace, apiVersion string, options *metav1.ListOptions) (watch.Interface, error) {
options.Watch = true
return m.RESTClient.Get().
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource).
VersionedParams(&metav1.ListOptions{
ResourceVersion: resourceVersion,
Watch: true,
LabelSelector: labelSelector,
}, metav1.ParameterCodec).
VersionedParams(options, metav1.ParameterCodec).
Watch()
}

View File

@ -31,18 +31,20 @@ type Selector struct {
Mapping *meta.RESTMapping
Namespace string
LabelSelector string
FieldSelector string
Export bool
IncludeUninitialized bool
LimitChunks int64
}
// NewSelector creates a resource selector which hides details of getting items by their label selector.
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector string, export, includeUninitialized bool, limitChunks int64) *Selector {
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace, labelSelector, fieldSelector string, export, includeUninitialized bool, limitChunks int64) *Selector {
return &Selector{
Client: client,
Mapping: mapping,
Namespace: namespace,
LabelSelector: selector,
LabelSelector: labelSelector,
FieldSelector: fieldSelector,
Export: export,
IncludeUninitialized: includeUninitialized,
LimitChunks: limitChunks,
@ -59,6 +61,7 @@ func (r *Selector) Visit(fn VisitorFunc) error {
r.Export,
&metav1.ListOptions{
LabelSelector: r.LabelSelector,
FieldSelector: r.FieldSelector,
IncludeUninitialized: r.IncludeUninitialized,
Limit: r.LimitChunks,
Continue: continueToken,
@ -71,17 +74,17 @@ func (r *Selector) Visit(fn VisitorFunc) error {
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
if se, ok := err.(*errors.StatusError); ok {
// modify the message without hiding this is an API error
if len(r.LabelSelector) == 0 {
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", r.Mapping.Resource, se.ErrStatus.Message)
} else {
se.ErrStatus.Message = fmt.Sprintf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.LabelSelector, se.ErrStatus.Message)
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)
}
return se
}
if len(r.LabelSelector) == 0 {
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
return fmt.Errorf("Unable to list %q: %v", r.Mapping.Resource, err)
}
return fmt.Errorf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.LabelSelector, err)
return fmt.Errorf("Unable to find %q that match label selector %q, field selector %q: %v", r.Mapping.Resource, r.LabelSelector, r.FieldSelector, err)
}
return err
}
@ -107,7 +110,8 @@ func (r *Selector) Visit(fn VisitorFunc) error {
}
func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) {
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.LabelSelector)
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(),
&metav1.ListOptions{ResourceVersion: resourceVersion, LabelSelector: r.LabelSelector, FieldSelector: r.FieldSelector})
}
// ResourceMapping returns the mapping for this resource and implements ResourceMapping