Skip to content

Commit

Permalink
allow kubectl -f to filter by selector
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedanese committed Sep 13, 2016
1 parent cd1ab76 commit 9e180fe
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 40 deletions.
3 changes: 3 additions & 0 deletions pkg/kubectl/cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
type ApplyOptions struct {
Filenames []string
Recursive bool
Selector string
}

const (
Expand Down Expand Up @@ -86,6 +87,7 @@ func NewCmdApply(f *cmdutil.Factory, out io.Writer) *cobra.Command {
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmd.MarkFlagRequired("filename")
cmd.Flags().Bool("overwrite", true, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on")
cmdutil.AddValidateFlags(cmd)
cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
cmdutil.AddOutputFlagsForMutation(cmd)
Expand Down Expand Up @@ -120,6 +122,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
SelectorParam(options.Selector).
Flatten().
Do()
err = r.Err()
Expand Down
75 changes: 35 additions & 40 deletions pkg/kubectl/resource/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,41 @@ func (b *Builder) visitorResult() *Result {
b.selector = labels.Everything()
}

// visit items specified by paths
if len(b.paths) != 0 {
singular := !b.dir && !b.stream && len(b.paths) == 1
if len(b.resources) != 0 {
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
}
if len(b.resourceTuples) != 0 {
return &Result{err: fmt.Errorf("paths cannot be used when passing resource/name arguments")}
}

var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(b.paths)
} else {
visitors = VisitorList(b.paths)
}

// only items from disk can be refetched
if b.latest {
// must flatten lists prior to fetching
if b.flatten {
visitors = NewFlattenListVisitor(visitors, b.mapper)
}
// must set namespace prior to fetching
if b.defaultNamespace {
visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
}
visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
}
if b.selector != nil {
visitors = NewFilteredVisitor(visitors, FilterBySelector(b.selector))
}
return &Result{singular: singular, visitor: visitors, sources: b.paths}
}

// visit selectors
if b.selector != nil {
if len(b.names) != 0 {
Expand All @@ -542,14 +577,6 @@ func (b *Builder) visitorResult() *Result {
if len(b.resources) == 0 {
return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
}
// empty selector has different error message for paths being provided
if len(b.paths) != 0 {
if b.selector.Empty() {
return &Result{err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
} else {
return &Result{err: fmt.Errorf("a selector may not be specified when path, URL, or stdin is provided as input")}
}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{err: err}
Expand Down Expand Up @@ -582,9 +609,6 @@ func (b *Builder) visitorResult() *Result {
isSingular = len(b.resourceTuples) == 1
}

if len(b.paths) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
}
Expand Down Expand Up @@ -683,35 +707,6 @@ func (b *Builder) visitorResult() *Result {
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
}

// visit items specified by paths
if len(b.paths) != 0 {
singular := !b.dir && !b.stream && len(b.paths) == 1
if len(b.resources) != 0 {
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
}

var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(b.paths)
} else {
visitors = VisitorList(b.paths)
}

// only items from disk can be refetched
if b.latest {
// must flatten lists prior to fetching
if b.flatten {
visitors = NewFlattenListVisitor(visitors, b.mapper)
}
// must set namespace prior to fetching
if b.defaultNamespace {
visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
}
visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
}
return &Result{singular: singular, visitor: visitors, sources: b.paths}
}

if len(b.resources) != 0 {
return &Result{err: fmt.Errorf("resource(s) were provided, but no name, label selector, or --all flag specified")}
}
Expand Down
49 changes: 49 additions & 0 deletions pkg/kubectl/resource/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/yaml"
Expand Down Expand Up @@ -631,3 +632,51 @@ func RetrieveLazy(info *Info, err error) error {
}
return nil
}

type FilterFunc func(info *Info, err error) (bool, error)

type FilteredVisitor struct {
visitor Visitor
filters []FilterFunc
}

func NewFilteredVisitor(v Visitor, fn ...FilterFunc) Visitor {
if len(fn) == 0 {
return v
}
return FilteredVisitor{v, fn}
}

func (v FilteredVisitor) Visit(fn VisitorFunc) error {
return v.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
for _, filter := range v.filters {
ok, err := filter(info, nil)
if err != nil {
return err
}
if !ok {
return nil
}
}
return fn(info, nil)
})
}

func FilterBySelector(s labels.Selector) FilterFunc {
return func(info *Info, err error) (bool, error) {
if err != nil {
return false, err
}
a, err := meta.Accessor(info.Object)
if err != nil {
return false, err
}
if !s.Matches(labels.Set(a.GetLabels())) {
return false, nil
}
return true, nil
}
}

0 comments on commit 9e180fe

Please sign in to comment.