From e9d59ac8db75c4e015508cc62c9e595312e56f89 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Fri, 19 Aug 2016 17:37:14 -0400 Subject: [PATCH] UPSTREAM: 0000: add resource filter handling Fixes: https://github.com/openshift/origin/issues/9901 Resources are currently filtered (in order to prevent printing) at print time in their HumanReadablePrinter handlers. This design makes it not possible to filter objects when they are printed using any other printer, such as YAML, JSON, or the NamePrinter. This patch removes any filters previously added at the printer level for pods and adds a way to define resource-specific filters before they are sent to a printer handler. A woking filter handler for pods has also been implemented. Filters affect resources being printed through the HumanReadablePrinter, YAML, JSON, and `--template` printers. --- .../k8s.io/kubernetes/pkg/kubectl/cmd/get.go | 52 +++++---- .../pkg/kubectl/cmd/util/factory.go | 2 +- .../pkg/kubectl/cmd/util/helpers.go | 2 +- .../kubernetes/pkg/kubectl/resource_filter.go | 100 ++++++++++++++++++ .../pkg/kubectl/resource_printer.go | 4 - 5 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get.go index 64689f1a7f62..06d36afaaa91 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get.go @@ -239,11 +239,6 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string } if generic { - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - allErrs := []error{} singular := false infos, err := r.IntoSingular(&singular).Infos() @@ -254,21 +249,15 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string allErrs = append(allErrs, err) } - // the outermost object will be converted to the output-version, but inner - // objects can use their mappings - version, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) - if err != nil { - return err - } - - obj, err := resource.AsVersionedObject(infos, !singular, version, f.JSONEncoder()) - if err != nil { - return err + for ix := range infos { + filter := kubectl.NewResourceFilter(filterOptionsForCommand(cmd, allNamespaces)) + if isFiltered, _ := filter.Filter(infos[ix].Object); !isFiltered { + if err := printer.PrintObj(infos[ix].Object, out); err != nil { + allErrs = append(allErrs, err) + } + } } - if err := printer.PrintObj(obj, out); err != nil { - allErrs = append(allErrs, err) - } return utilerrors.NewAggregate(allErrs) } @@ -338,6 +327,13 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string } lastMapping = mapping } + + // filter objects if filter has been defined for current object + filter := kubectl.NewResourceFilter(filterOptionsForCommand(cmd, allNamespaces)) + if isFiltered, _ := filter.Filter(original); isFiltered { + continue + } + if resourcePrinter, found := printer.(*kubectl.HumanReadablePrinter); found { resourceName := resourcePrinter.GetResourceName() if mapping != nil { @@ -394,3 +390,23 @@ func mustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *k return false } + +// filterOptionsForCommand instantiates FilterOptions which makes use of PrintOptions in order +// implement specific filter handlers for resources before they are printed +func filterOptionsForCommand(cmd *cobra.Command, withNamespace bool) *kubectl.FilterOptions { + columnLabel, err := cmd.Flags().GetStringSlice("label-columns") + if err != nil { + columnLabel = []string{} + } + return &kubectl.FilterOptions{ + kubectl.PrintOptions{ + NoHeaders: cmdutil.GetFlagBool(cmd, "no-headers"), + WithNamespace: withNamespace, + Wide: cmdutil.GetWideFlag(cmd), + ShowAll: cmdutil.GetFlagBool(cmd, "show-all"), + ShowLabels: cmdutil.GetFlagBool(cmd, "show-labels"), + AbsoluteTimestamps: cmdutil.IsWatch(cmd), + ColumnLabels: columnLabel, + }, + } +} diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go index 8e28d338b51b..b6020ae33d46 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go @@ -1230,7 +1230,7 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin Wide: GetWideFlag(cmd), ShowAll: GetFlagBool(cmd, "show-all"), ShowLabels: GetFlagBool(cmd, "show-labels"), - AbsoluteTimestamps: isWatch(cmd), + AbsoluteTimestamps: IsWatch(cmd), ColumnLabels: columnLabel, }) if err != nil { diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go index d7cb1bb9a6b7..d8bf3402fb1b 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go @@ -245,7 +245,7 @@ func UsageError(cmd *cobra.Command, format string, args ...interface{}) error { } // Whether this cmd need watching objects. -func isWatch(cmd *cobra.Command) bool { +func IsWatch(cmd *cobra.Command) bool { if w, err := cmd.Flags().GetBool("watch"); w && err == nil { return true } diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go new file mode 100644 index 000000000000..f101b861b939 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go @@ -0,0 +1,100 @@ +package kubectl + +import ( + "fmt" + "reflect" + + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/runtime" +) + +type filterEntry struct { + filterFunc reflect.Value +} + +// FilterOptions implements PrintOptions +type FilterOptions struct { + PrintOptions +} + +type ResourceFilter struct { + filterMap map[reflect.Type]*filterEntry + options FilterOptions +} + +func NewResourceFilter(opts *FilterOptions) *ResourceFilter { + filter := &ResourceFilter{ + filterMap: make(map[reflect.Type]*filterEntry), + options: *opts, + } + + filter.addDefaultHandlers() + return filter +} + +func (f *ResourceFilter) addDefaultHandlers() { + f.Handler(filterPod) +} + +// Handler adds a filter handler to a ResourceFilter instance. +// See validateFilterFunc for required method signature. +func (f *ResourceFilter) Handler(filterFunc interface{}) error { + filterFuncValue := reflect.ValueOf(filterFunc) + if err := f.validateFilterFunc(filterFuncValue); err != nil { + glog.Errorf("Unable to add print handler: %v", err) + return err + } + objType := filterFuncValue.Type().In(0) + f.filterMap[objType] = &filterEntry{ + filterFunc: filterFuncValue, + } + return nil +} + +// validateFilterFunc validates the filter handler signature. +// filterFunc is the function that will be called to filter an object. +// It must be of the following type: +// func filterFunc(object ObjectType, options FilterOptions) error +// where ObjectType is the type of the object that will be printed. +func (f *ResourceFilter) validateFilterFunc(filterFunc reflect.Value) error { + if filterFunc.Kind() != reflect.Func { + return fmt.Errorf("invalid filter handler. %#v is not a function", filterFunc) + } + funcType := filterFunc.Type() + if funcType.NumIn() != 2 || funcType.NumOut() != 1 { + return fmt.Errorf("invalid filter handler." + + "Must accept 2 parameters and return 1 value.") + } + if funcType.In(1) != reflect.TypeOf((*FilterOptions)(nil)).Elem() || + funcType.Out(0) != reflect.TypeOf((*bool)(nil)).Elem() { + return fmt.Errorf("invalid filter handler. The expected signature is: "+ + "func handler(obj %v, options FilterOptions) error", funcType.In(0)) + } + return nil +} + +// filterPod returns true if a pod should be skipped. +// defaults to true for terminated pods +func filterPod(pod *api.Pod, options FilterOptions) bool { + reason := string(pod.Status.Phase) + if pod.Status.Reason != "" { + reason = pod.Status.Reason + } + if !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) { + return true + } + return false +} + +// Filter extracts the filter handler, if one exists, for the given resource +func (f *ResourceFilter) Filter(obj runtime.Object) (bool, error) { + t := reflect.TypeOf(obj) + if filter := f.filterMap[t]; filter != nil { + args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(f.options)} + resultValue := filter.filterFunc.Call(args)[0] + return resultValue.Interface().(bool), nil + } + + return false, fmt.Errorf("error: no filter for type %#v", obj) +} diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go index e2ae42d8049c..7ae2c38ed9ba 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go @@ -619,10 +619,6 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error { readyContainers := 0 reason := string(pod.Status.Phase) - // if not printing all pods, skip terminated pods (default) - if !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) { - return nil - } if pod.Status.Reason != "" { reason = pod.Status.Reason }