Merge pull request #54449 from smarterclayton/get_with_options

Automatic merge from submit-queue (batch tested with PRs 54895, 54449). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Update the get command to follow more conventions of commands

Pure code movement, builds on top of #54446 and only the last commit is new. Will make refactoring get easier.
This commit is contained in:
Kubernetes Submit Queue 2017-11-01 21:25:12 -07:00 committed by GitHub
commit d595003e0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 352 additions and 303 deletions

View File

@ -3752,13 +3752,13 @@ run_client_config_tests() {
# Pre-condition: context "missing-context" does not exist # Pre-condition: context "missing-context" does not exist
# Command # Command
output_message=$(! kubectl get pod --context="missing-context" 2>&1) output_message=$(! kubectl get pod --context="missing-context" 2>&1)
kube::test::if_has_string "${output_message}" 'context "missing-context" does not exist' kube::test::if_has_string "${output_message}" 'context was not found for specified context: missing-context'
# Post-condition: invalid or missing context returns error # Post-condition: invalid or missing context returns error
# Pre-condition: cluster "missing-cluster" does not exist # Pre-condition: cluster "missing-cluster" does not exist
# Command # Command
output_message=$(! kubectl get pod --cluster="missing-cluster" 2>&1) output_message=$(! kubectl get pod --cluster="missing-cluster" 2>&1)
kube::test::if_has_string "${output_message}" 'cluster "missing-cluster" does not exist' kube::test::if_has_string "${output_message}" 'no server found for cluster "missing-cluster"'
# Post-condition: invalid or missing cluster returns error # Post-condition: invalid or missing cluster returns error
# Pre-condition: user "missing-user" does not exist # Pre-condition: user "missing-user" does not exist

View File

@ -44,14 +44,26 @@ import (
"k8s.io/kubernetes/pkg/util/interrupt" "k8s.io/kubernetes/pkg/util/interrupt"
) )
// GetOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of // GetOptions contains the input to the get command.
// referencing the cmd.Flags()
type GetOptions struct { type GetOptions struct {
Out, ErrOut io.Writer
resource.FilenameOptions resource.FilenameOptions
IgnoreNotFound bool
Raw string Raw string
Watch bool
WatchOnly bool
ChunkSize int64 ChunkSize int64
LabelSelector string
AllNamespaces bool
Namespace string
ExplicitNamespace bool
IgnoreNotFound bool
ShowKind bool
LabelColumns []string
Export bool
} }
var ( var (
@ -107,8 +119,13 @@ const (
// NewCmdGet creates a command object for the generic "get" action, which // NewCmdGet creates a command object for the generic "get" action, which
// retrieves one or more resources from a server. // retrieves one or more resources from a server.
func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
options := &GetOptions{} options := &GetOptions{
Out: out,
ErrOut: errOut,
}
// TODO: this needs to be abstracted behind the factory like ValidResourceTypeList
// and use discovery
// retrieve a list of handled resources from printer as valid args // retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{} validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{ p, err := f.Printer(nil, printers.PrintOptions{
@ -126,36 +143,278 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
Long: getLong + "\n\n" + cmdutil.ValidResourceTypeList(f), Long: getLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: getExample, Example: getExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunGet(f, out, errOut, cmd, args, options) cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(err) cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run(f, cmd, args))
}, },
SuggestFor: []string{"list", "ps"}, SuggestFor: []string{"list", "ps"},
ValidArgs: validArgs, ValidArgs: validArgs,
ArgAliases: argAliases, ArgAliases: argAliases,
} }
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmdutil.AddIncludeUninitializedFlag(cmd)
cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).")
cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful retrieval.")
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().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
addOpenAPIPrintColumnFlags(cmd)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddInclude3rdPartyFlags(cmd)
cmd.Flags().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.") cmd.Flags().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
cmd.Flags().BoolVarP(&options.Watch, "watch", "w", options.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
cmd.Flags().BoolVar(&options.WatchOnly, "watch-only", options.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
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().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)
addOpenAPIPrintColumnFlags(cmd)
cmd.Flags().BoolVar(&options.ShowKind, "show-kind", options.ShowKind, "If present, list the resource type for the requested object(s).")
cmd.Flags().StringSliceVarP(&options.LabelColumns, "label-columns", "L", options.LabelColumns, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
cmd.Flags().BoolVar(&options.Export, "export", options.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to get from a server.")
cmdutil.AddInclude3rdPartyFlags(cmd)
return cmd return cmd
} }
// RunGet implements the generic Get command // Complete takes the command arguments and factory and infers any remaining options.
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error {
if len(options.Raw) > 0 { if len(options.Raw) > 0 {
if len(args) > 0 {
return fmt.Errorf("arguments may not be passed when --raw is specified")
}
return nil
}
var err error
options.Namespace, options.ExplicitNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
if options.AllNamespaces {
options.ExplicitNamespace = false
}
switch {
case options.Watch || options.WatchOnly:
default:
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
fmt.Fprint(options.ErrOut, "You must specify the type of resource to get. ", cmdutil.ValidResourceTypeList(f))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
}
return nil
}
// Validate checks the set of flags provided by the user.
func (options *GetOptions) Validate() error {
if len(options.Raw) > 0 && (options.Watch || options.WatchOnly || len(options.LabelSelector) > 0 || options.Export) {
return fmt.Errorf("--raw may not be specified with other flags that filter the server request or alter the output")
}
return nil
}
// Run performs the get operation.
// TODO: remove the need to pass these arguments, like other commands.
func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(options.Raw) > 0 {
return options.raw(f)
}
if options.Watch || options.WatchOnly {
return options.watch(f, cmd, args)
}
mapper, typer, err := f.UnstructuredObject()
if err != nil {
return err
}
r := f.NewBuilder().
Unstructured(f.UnstructuredClientForMapping, mapper, typer).
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
SelectorParam(options.LabelSelector).
ExportParam(options.Export).
RequestChunksOf(options.ChunkSize).
IncludeUninitialized(cmdutil.ShouldIncludeUninitialized(cmd, false)). // TODO: this needs to be better factored
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest().
Flatten().
Do()
if options.IgnoreNotFound {
r.IgnoreErrors(kapierrors.IsNotFound)
}
if err := r.Err(); err != nil {
return err
}
printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{})
if err != nil {
return err
}
filterOpts := f.DefaultResourceFilterOptions(cmd, options.AllNamespaces)
filterFuncs := f.DefaultResourceFilterFunc()
if r.TargetsSingleItems() {
filterFuncs = nil
}
if printer.IsGeneric() {
return options.printGeneric(printer, r, filterFuncs, filterOpts)
}
allErrs := []error{}
errs := sets.NewString()
infos, err := r.Infos()
if err != nil {
allErrs = append(allErrs, err)
}
objs := make([]runtime.Object, len(infos))
for ix := range infos {
objs[ix] = infos[ix].Object
}
sorting, err := cmd.Flags().GetString("sort-by")
if err != nil {
return err
}
var sorter *kubectl.RuntimeSort
if len(sorting) > 0 && len(objs) > 1 {
// TODO: questionable
if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil {
return err
}
}
// use the default printer for each object
printer = nil
var lastMapping *meta.RESTMapping
w := printers.GetNewTabWriter(options.Out)
useOpenAPIPrintColumns := cmdutil.GetFlagBool(cmd, useOpenAPIPrintColumnFlagLabel)
showKind := options.ShowKind
// TODO: abstract more cleanly
if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) {
showKind = true
}
filteredResourceCount := 0
for ix := range objs {
var mapping *meta.RESTMapping
var original runtime.Object
if sorter != nil {
mapping = infos[sorter.OriginalPosition(ix)].Mapping
original = infos[sorter.OriginalPosition(ix)].Object
} else {
mapping = infos[ix].Mapping
original = infos[ix].Object
}
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
if printer != nil {
w.Flush()
}
printWithNamespace := options.AllNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
var outputOpts *printers.OutputOptions
// if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true,
// then get the default output options for this mapping from OpenAPI schema.
if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns {
outputOpts, _ = outputOptsForMappingFromOpenAPI(f, mapping)
}
printer, err = f.PrinterForMapping(cmd, false, outputOpts, mapping, printWithNamespace)
if err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
// TODO: this doesn't belong here
// add linebreak between resource groups (if there is more than one)
// skip linebreak above first resource group
noHeaders := cmdutil.GetFlagBool(cmd, "no-headers")
if lastMapping != nil && !noHeaders {
fmt.Fprintf(options.ErrOut, "%s\n", "")
}
lastMapping = mapping
}
// try to convert before apply filter func
decodedObj, _ := kubectl.DecodeUnknownObject(original)
// filter objects if filter has been defined for current object
if isFiltered, err := filterFuncs.Filter(decodedObj, filterOpts); isFiltered {
if err == nil {
filteredResourceCount++
continue
}
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
}
if resourcePrinter, found := printer.(*printers.HumanReadablePrinter); found {
resourceName := resourcePrinter.GetResourceKind()
if mapping != nil {
if resourceName == "" {
resourceName = mapping.Resource
}
if alias, ok := kubectl.ResourceShortFormFor(mapping.Resource); ok {
resourceName = alias
} else if resourceName == "" {
resourceName = "none"
}
} else {
resourceName = "none"
}
if showKind {
resourcePrinter.EnsurePrintWithKind(resourceName)
}
if err := printer.PrintObj(decodedObj, w); err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
}
continue
}
objToPrint := decodedObj
if printer.IsGeneric() {
// use raw object as recieved from the builder when using generic
// printer instead of decodedObj
objToPrint = original
}
if err := printer.PrintObj(objToPrint, w); err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
}
w.Flush()
cmdutil.PrintFilterCount(options.ErrOut, len(objs), filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
return utilerrors.NewAggregate(allErrs)
}
// raw makes a simple HTTP request to the provided path on the server using the default
// credentials.
func (options *GetOptions) raw(f cmdutil.Factory) error {
restClient, err := f.RESTClient() restClient, err := f.RESTClient()
if err != nil { if err != nil {
return err return err
@ -167,67 +426,32 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
} }
defer stream.Close() defer stream.Close()
_, err = io.Copy(out, stream) _, err = io.Copy(options.Out, stream)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return err return err
} }
return nil return nil
} }
// watch starts a client-side watch of one or more resources.
// TODO: remove the need for arguments here.
func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
mapper, typer, err := f.UnstructuredObject() mapper, typer, err := f.UnstructuredObject()
if err != nil { if err != nil {
return err return err
} }
selector := cmdutil.GetFlagString(cmd, "selector") // TODO: this could be better factored
allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces") // include uninitialized objects when watching on a single object
showKind := cmdutil.GetFlagBool(cmd, "show-kind")
builder := f.NewBuilder().Unstructured(f.UnstructuredClientForMapping, mapper, typer)
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
if allNamespaces {
enforceNamespace = false
}
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
fmt.Fprint(errOut, "You must specify the type of resource to get. ", cmdutil.ValidResourceTypeList(f))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
export := cmdutil.GetFlagBool(cmd, "export")
filterFuncs := f.DefaultResourceFilterFunc()
filterOpts := f.DefaultResourceFilterOptions(cmd, allNamespaces)
// handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only")
var includeUninitialized bool
if isWatch && len(args) == 2 {
// include the uninitialized one for watching on a single object
// unless explicitly set --include-uninitialized=false // unless explicitly set --include-uninitialized=false
includeUninitialized = true includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2)
}
includeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, includeUninitialized)
if isWatch || isWatchOnly {
r := f.NewBuilder(). r := f.NewBuilder().
Unstructured(f.UnstructuredClientForMapping, mapper, typer). Unstructured(f.UnstructuredClientForMapping, mapper, typer).
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(enforceNamespace, &options.FilenameOptions). FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
SelectorParam(selector). SelectorParam(options.LabelSelector).
ExportParam(export). ExportParam(options.Export).
RequestChunksOf(options.ChunkSize). RequestChunksOf(options.ChunkSize).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
@ -245,19 +469,16 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
if len(infos) != 1 { if len(infos) != 1 {
return i18n.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(infos)) return i18n.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(infos))
} }
filterOpts := f.DefaultResourceFilterOptions(cmd, options.AllNamespaces)
filterFuncs := f.DefaultResourceFilterFunc()
if r.TargetsSingleItems() { if r.TargetsSingleItems() {
filterFuncs = nil filterFuncs = nil
} }
info := infos[0] info := infos[0]
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
printer, err := f.PrinterForMapping(cmd, false, nil, mapping, options.AllNamespaces)
// no need to print namespace for root-scoped resources
printWithNamespace := allNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
printer, err := f.PrinterForMapping(cmd, false, nil, mapping, printWithNamespace)
if err != nil { if err != nil {
return err return err
} }
@ -282,9 +503,9 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
} }
// print the current object // print the current object
if !isWatchOnly { if !options.WatchOnly {
var objsToPrint []runtime.Object var objsToPrint []runtime.Object
writer := printers.GetNewTabWriter(out) writer := printers.GetNewTabWriter(options.Out)
if isList { if isList {
objsToPrint, _ = meta.ExtractList(obj) objsToPrint, _ = meta.ExtractList(obj)
@ -322,7 +543,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
if isFiltered, err := filterFuncs.Filter(e.Object, filterOpts); !isFiltered { if isFiltered, err := filterFuncs.Filter(e.Object, filterOpts); !isFiltered {
if err != nil { if err != nil {
glog.V(2).Infof("Unable to filter resource: %v", err) glog.V(2).Infof("Unable to filter resource: %v", err)
} else if err := printer.PrintObj(e.Object, out); err != nil { } else if err := printer.PrintObj(e.Object, options.Out); err != nil {
return false, err return false, err
} }
} }
@ -331,41 +552,13 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
return err return err
}) })
return nil return nil
} }
r := builder. func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *resource.Result, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) error {
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, &options.FilenameOptions).
SelectorParam(selector).
ExportParam(export).
RequestChunksOf(options.ChunkSize).
IncludeUninitialized(includeUninitialized).
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest().
Flatten().
Do()
err = r.Err()
if err != nil {
return err
}
printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{})
if err != nil {
return err
}
if r.TargetsSingleItems() {
filterFuncs = nil
}
if options.IgnoreNotFound {
r.IgnoreErrors(kapierrors.IsNotFound)
}
if printer.IsGeneric() {
// we flattened the data from the builder, so we have individual items, but now we'd like to either: // we flattened the data from the builder, so we have individual items, but now we'd like to either:
// 1. if there is more than one item, combine them all into a single list // 1. if there is more than one item, combine them all into a single list
// 2. if there is a single item and that item is a list, leave it as its specific list // 2. if there is a single item and that item is a list, leave it as its specific list
// 3. if there is a single item and it is not a a list, leave it as a single item // 3. if there is a single item and it is not a list, leave it as a single item
var errs []error var errs []error
singleItemImplied := false singleItemImplied := false
infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos() infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
@ -438,7 +631,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
for _, item := range items { for _, item := range items {
list.Items = append(list.Items, *item.(*unstructured.Unstructured)) list.Items = append(list.Items, *item.(*unstructured.Unstructured))
} }
if err := printer.PrintObj(list, out); err != nil { if err := printer.PrintObj(list, options.Out); err != nil {
errs = append(errs, err) errs = append(errs, err)
} }
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs))) return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
@ -447,162 +640,18 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
if isFiltered, err := filterFuncs.Filter(obj, filterOpts); !isFiltered { if isFiltered, err := filterFuncs.Filter(obj, filterOpts); !isFiltered {
if err != nil { if err != nil {
glog.V(2).Infof("Unable to filter resource: %v", err) glog.V(2).Infof("Unable to filter resource: %v", err)
} else if err := printer.PrintObj(obj, out); err != nil { } else if err := printer.PrintObj(obj, options.Out); err != nil {
errs = append(errs, err) errs = append(errs, err)
} }
} }
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs))) return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
}
allErrs := []error{}
errs := sets.NewString()
infos, err := r.Infos()
if err != nil {
allErrs = append(allErrs, err)
}
objs := make([]runtime.Object, len(infos))
for ix := range infos {
objs[ix] = infos[ix].Object
}
sorting, err := cmd.Flags().GetString("sort-by")
if err != nil {
return err
}
var sorter *kubectl.RuntimeSort
if len(sorting) > 0 && len(objs) > 1 {
// TODO: questionable
if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil {
return err
}
}
// use the default printer for each object
printer = nil
var lastMapping *meta.RESTMapping
w := printers.GetNewTabWriter(out)
useOpenAPIPrintColumns := cmdutil.GetFlagBool(cmd, useOpenAPIPrintColumnFlagLabel)
if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) {
showKind = true
}
filteredResourceCount := 0
for ix := range objs {
var mapping *meta.RESTMapping
var original runtime.Object
if sorter != nil {
mapping = infos[sorter.OriginalPosition(ix)].Mapping
original = infos[sorter.OriginalPosition(ix)].Object
} else {
mapping = infos[ix].Mapping
original = infos[ix].Object
}
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
if printer != nil {
w.Flush()
}
printWithNamespace := allNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
var outputOpts *printers.OutputOptions
// if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true,
// then get the default output options for this mapping from OpenAPI schema.
if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns {
outputOpts, _ = outputOptsForMappingFromOpenAPI(f, mapping)
}
printer, err = f.PrinterForMapping(cmd, false, outputOpts, mapping, printWithNamespace)
if err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
// add linebreak between resource groups (if there is more than one)
// skip linebreak above first resource group
noHeaders := cmdutil.GetFlagBool(cmd, "no-headers")
if lastMapping != nil && !noHeaders {
fmt.Fprintf(errOut, "%s\n", "")
}
lastMapping = mapping
}
// try to convert before apply filter func
decodedObj, _ := kubectl.DecodeUnknownObject(original)
// filter objects if filter has been defined for current object
if isFiltered, err := filterFuncs.Filter(decodedObj, filterOpts); isFiltered {
if err == nil {
filteredResourceCount++
continue
}
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
}
if resourcePrinter, found := printer.(*printers.HumanReadablePrinter); found {
resourceName := resourcePrinter.GetResourceKind()
if mapping != nil {
if resourceName == "" {
resourceName = mapping.Resource
}
if alias, ok := kubectl.ResourceShortFormFor(mapping.Resource); ok {
resourceName = alias
} else if resourceName == "" {
resourceName = "none"
}
} else {
resourceName = "none"
}
if showKind {
resourcePrinter.EnsurePrintWithKind(resourceName)
}
if err := printer.PrintObj(decodedObj, w); err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
}
continue
}
objToPrint := decodedObj
if printer.IsGeneric() {
// use raw object as recieved from the builder when using generic
// printer instead of decodedObj
objToPrint = original
}
if err := printer.PrintObj(objToPrint, w); err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
}
w.Flush()
cmdutil.PrintFilterCount(errOut, len(objs), filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
return utilerrors.NewAggregate(allErrs)
} }
func addOpenAPIPrintColumnFlags(cmd *cobra.Command) { func addOpenAPIPrintColumnFlags(cmd *cobra.Command) {
cmd.Flags().Bool(useOpenAPIPrintColumnFlagLabel, false, "If true, use x-kubernetes-print-column metadata (if present) from openapi schema for displaying a resource.") cmd.Flags().Bool(useOpenAPIPrintColumnFlagLabel, false, "If true, use x-kubernetes-print-column metadata (if present) from the OpenAPI schema for displaying a resource.")
// marking it deprecated so that it is hidden from usage/help text. // marking it deprecated so that it is hidden from usage/help text.
cmd.Flags().MarkDeprecated(useOpenAPIPrintColumnFlagLabel, "It's an experimental feature.") cmd.Flags().MarkDeprecated(useOpenAPIPrintColumnFlagLabel, "This flag is experimental and may be removed in the future.")
} }
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool { func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {