123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package get
- import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/url"
- "github.com/spf13/cobra"
- "k8s.io/klog"
- corev1 "k8s.io/api/core/v1"
- kapierrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/api/meta"
- metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- utilerrors "k8s.io/apimachinery/pkg/util/errors"
- "k8s.io/apimachinery/pkg/util/sets"
- "k8s.io/apimachinery/pkg/watch"
- "k8s.io/cli-runtime/pkg/genericclioptions"
- "k8s.io/cli-runtime/pkg/printers"
- "k8s.io/cli-runtime/pkg/resource"
- "k8s.io/client-go/rest"
- watchtools "k8s.io/client-go/tools/watch"
- "k8s.io/kubernetes/pkg/api/legacyscheme"
- cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
- "k8s.io/kubernetes/pkg/kubectl/util/i18n"
- "k8s.io/kubernetes/pkg/kubectl/util/interrupt"
- utilprinters "k8s.io/kubernetes/pkg/kubectl/util/printers"
- "k8s.io/kubernetes/pkg/kubectl/util/templates"
- )
- // GetOptions contains the input to the get command.
- type GetOptions struct {
- PrintFlags *PrintFlags
- ToPrinter func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
- IsHumanReadablePrinter bool
- PrintWithOpenAPICols bool
- CmdParent string
- resource.FilenameOptions
- Raw string
- Watch bool
- WatchOnly bool
- ChunkSize int64
- LabelSelector string
- FieldSelector string
- AllNamespaces bool
- Namespace string
- ExplicitNamespace bool
- ServerPrint bool
- NoHeaders bool
- Sort bool
- IgnoreNotFound bool
- Export bool
- genericclioptions.IOStreams
- }
- var (
- getLong = templates.LongDesc(`
- Display one or many resources
- Prints a table of the most important information about the specified resources.
- You can filter the list using a label selector and the --selector flag. If the
- desired resource type is namespaced you will only see results in your current
- namespace unless you pass --all-namespaces.
- Uninitialized objects are not shown unless --include-uninitialized is passed.
- By specifying the output as 'template' and providing a Go template as the value
- of the --template flag, you can filter the attributes of the fetched resources.`)
- getExample = templates.Examples(i18n.T(`
- # List all pods in ps output format.
- kubectl get pods
- # List all pods in ps output format with more information (such as node name).
- kubectl get pods -o wide
- # List a single replication controller with specified NAME in ps output format.
- kubectl get replicationcontroller web
- # List deployments in JSON output format, in the "v1" version of the "apps" API group:
- kubectl get deployments.v1.apps -o json
- # List a single pod in JSON output format.
- kubectl get -o json pod web-pod-13je7
- # List a pod identified by type and name specified in "pod.yaml" in JSON output format.
- kubectl get -f pod.yaml -o json
- # List resources from a directory with kustomization.yaml - e.g. dir/kustomization.yaml.
- kubectl get -k dir/
- # Return only the phase value of the specified pod.
- kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}
- # List resource information in custom columns.
- kubectl get pod test-pod -o custom-columns=CONTAINER:.spec.containers[0].name,IMAGE:.spec.containers[0].image
- # List all replication controllers and services together in ps output format.
- kubectl get rc,services
- # List one or more resources by their type and names.
- kubectl get rc/web service/frontend pods/web-pod-13je7`))
- )
- const (
- useOpenAPIPrintColumnFlagLabel = "use-openapi-print-columns"
- useServerPrintColumns = "server-print"
- )
- // NewGetOptions returns a GetOptions with default chunk size 500.
- func NewGetOptions(parent string, streams genericclioptions.IOStreams) *GetOptions {
- return &GetOptions{
- PrintFlags: NewGetPrintFlags(),
- CmdParent: parent,
- IOStreams: streams,
- ChunkSize: 500,
- ServerPrint: true,
- }
- }
- // NewCmdGet creates a command object for the generic "get" action, which
- // retrieves one or more resources from a server.
- func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
- o := NewGetOptions(parent, streams)
- cmd := &cobra.Command{
- Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]",
- DisableFlagsInUseLine: true,
- Short: i18n.T("Display one or many resources"),
- Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
- Example: getExample,
- Run: func(cmd *cobra.Command, args []string) {
- cmdutil.CheckErr(o.Complete(f, cmd, args))
- cmdutil.CheckErr(o.Validate(cmd))
- cmdutil.CheckErr(o.Run(f, cmd, args))
- },
- SuggestFor: []string{"list", "ps"},
- }
- o.PrintFlags.AddFlags(cmd)
- cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
- cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
- cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
- cmd.Flags().Int64Var(&o.ChunkSize, "chunk-size", o.ChunkSize, "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(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
- cmd.Flags().StringVarP(&o.LabelSelector, "selector", "l", o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
- cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.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().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.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)
- addOpenAPIPrintColumnFlags(cmd, o)
- addServerPrintColumnFlags(cmd, o)
- cmd.Flags().BoolVar(&o.Export, "export", o.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
- cmd.Flags().MarkDeprecated("export", "This flag is deprecated and will be removed in future.")
- cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to get from a server.")
- return cmd
- }
- // Complete takes the command arguments and factory and infers any remaining options.
- func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
- if len(o.Raw) > 0 {
- if len(args) > 0 {
- return fmt.Errorf("arguments may not be passed when --raw is specified")
- }
- return nil
- }
- var err error
- o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace()
- if err != nil {
- return err
- }
- if o.AllNamespaces {
- o.ExplicitNamespace = false
- }
- sortBy, err := cmd.Flags().GetString("sort-by")
- if err != nil {
- return err
- }
- o.Sort = len(sortBy) > 0
- o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
- // TODO (soltysh): currently we don't support custom columns
- // with server side print. So in these cases force the old behavior.
- outputOption := cmd.Flags().Lookup("output").Value.String()
- if outputOption == "custom-columns" {
- o.ServerPrint = false
- }
- templateArg := ""
- if o.PrintFlags.TemplateFlags != nil && o.PrintFlags.TemplateFlags.TemplateArgument != nil {
- templateArg = *o.PrintFlags.TemplateFlags.TemplateArgument
- }
- // human readable printers have special conversion rules, so we determine if we're using one.
- if (len(*o.PrintFlags.OutputFormat) == 0 && len(templateArg) == 0) || *o.PrintFlags.OutputFormat == "wide" {
- o.IsHumanReadablePrinter = true
- }
- o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
- // make a new copy of current flags / opts before mutating
- printFlags := o.PrintFlags.Copy()
- if mapping != nil {
- if !cmdSpecifiesOutputFmt(cmd) && o.PrintWithOpenAPICols {
- if apiSchema, err := f.OpenAPISchema(); err == nil {
- printFlags.UseOpenAPIColumns(apiSchema, mapping)
- }
- }
- printFlags.SetKind(mapping.GroupVersionKind.GroupKind())
- }
- if withNamespace {
- printFlags.EnsureWithNamespace()
- }
- if withKind {
- printFlags.EnsureWithKind()
- }
- printer, err := printFlags.ToPrinter()
- if err != nil {
- return nil, err
- }
- if o.Sort {
- printer = &SortingPrinter{Delegate: printer, SortField: sortBy}
- }
- if o.ServerPrint {
- printer = &TablePrinter{Delegate: printer}
- }
- return printer.PrintObj, nil
- }
- switch {
- case o.Watch || o.WatchOnly:
- if o.Sort {
- fmt.Fprintf(o.IOStreams.ErrOut, "warning: --watch or --watch-only requested, --sort-by will be ignored\n")
- }
- default:
- if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
- fmt.Fprintf(o.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.SuggestAPIResources(o.CmdParent))
- 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)
- }
- }
- // openapi printing is mutually exclusive with server side printing
- if o.PrintWithOpenAPICols && o.ServerPrint {
- fmt.Fprintf(o.IOStreams.ErrOut, "warning: --%s requested, --%s will be ignored\n", useOpenAPIPrintColumnFlagLabel, useServerPrintColumns)
- }
- return nil
- }
- // Validate checks the set of flags provided by the user.
- func (o *GetOptions) Validate(cmd *cobra.Command) error {
- if len(o.Raw) > 0 {
- if o.Watch || o.WatchOnly || len(o.LabelSelector) > 0 || o.Export {
- return fmt.Errorf("--raw may not be specified with other flags that filter the server request or alter the output")
- }
- if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
- return cmdutil.UsageErrorf(cmd, "--raw and --output are mutually exclusive")
- }
- if _, err := url.ParseRequestURI(o.Raw); err != nil {
- return cmdutil.UsageErrorf(cmd, "--raw must be a valid URL path: %v", err)
- }
- }
- if cmdutil.GetFlagBool(cmd, "show-labels") {
- outputOption := cmd.Flags().Lookup("output").Value.String()
- if outputOption != "" && outputOption != "wide" {
- return fmt.Errorf("--show-labels option cannot be used with %s printer", outputOption)
- }
- }
- return nil
- }
- // OriginalPositioner and NopPositioner is required for swap/sort operations of data in table format
- type OriginalPositioner interface {
- OriginalPosition(int) int
- }
- // NopPositioner and OriginalPositioner is required for swap/sort operations of data in table format
- type NopPositioner struct{}
- // OriginalPosition returns the original position from NopPositioner object
- func (t *NopPositioner) OriginalPosition(ix int) int {
- return ix
- }
- // RuntimeSorter holds the required objects to perform sorting of runtime objects
- type RuntimeSorter struct {
- field string
- decoder runtime.Decoder
- objects []runtime.Object
- positioner OriginalPositioner
- }
- // Sort performs the sorting of runtime objects
- func (r *RuntimeSorter) Sort() error {
- // a list is only considered "sorted" if there are 0 or 1 items in it
- // AND (if 1 item) the item is not a Table object
- if len(r.objects) == 0 {
- return nil
- }
- if len(r.objects) == 1 {
- _, isTable := r.objects[0].(*metav1beta1.Table)
- if !isTable {
- return nil
- }
- }
- includesTable := false
- includesRuntimeObjs := false
- for _, obj := range r.objects {
- switch t := obj.(type) {
- case *metav1beta1.Table:
- includesTable = true
- if sorter, err := NewTableSorter(t, r.field); err != nil {
- return err
- } else if err := sorter.Sort(); err != nil {
- return err
- }
- default:
- includesRuntimeObjs = true
- }
- }
- // we use a NopPositioner when dealing with Table objects
- // because the objects themselves are not swapped, but rather
- // the rows in each object are swapped / sorted.
- r.positioner = &NopPositioner{}
- if includesRuntimeObjs && includesTable {
- return fmt.Errorf("sorting is not supported on mixed Table and non-Table object lists")
- }
- if includesTable {
- return nil
- }
- // if not dealing with a Table response from the server, assume
- // all objects are runtime.Object as usual, and sort using old method.
- var err error
- if r.positioner, err = SortObjects(r.decoder, r.objects, r.field); err != nil {
- return err
- }
- return nil
- }
- // OriginalPosition returns the original position of a runtime object
- func (r *RuntimeSorter) OriginalPosition(ix int) int {
- if r.positioner == nil {
- return 0
- }
- return r.positioner.OriginalPosition(ix)
- }
- // WithDecoder allows custom decoder to be set for testing
- func (r *RuntimeSorter) WithDecoder(decoder runtime.Decoder) *RuntimeSorter {
- r.decoder = decoder
- return r
- }
- // NewRuntimeSorter returns a new instance of RuntimeSorter
- func NewRuntimeSorter(objects []runtime.Object, sortBy string) *RuntimeSorter {
- parsedField, err := RelaxedJSONPathExpression(sortBy)
- if err != nil {
- parsedField = sortBy
- }
- return &RuntimeSorter{
- field: parsedField,
- decoder: legacyscheme.Codecs.UniversalDecoder(),
- objects: objects,
- }
- }
- func (o *GetOptions) transformRequests(req *rest.Request) {
- // We need full objects if printing with openapi columns
- if o.PrintWithOpenAPICols {
- return
- }
- if !o.ServerPrint || !o.IsHumanReadablePrinter {
- return
- }
- group := metav1beta1.GroupName
- version := metav1beta1.SchemeGroupVersion.Version
- tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
- req.SetHeader("Accept", tableParam)
- // if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
- if o.Sort {
- req.Param("includeObject", "Object")
- }
- }
- // Run performs the get operation.
- // TODO: remove the need to pass these arguments, like other commands.
- func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
- if len(o.Raw) > 0 {
- return o.raw(f)
- }
- if o.Watch || o.WatchOnly {
- return o.watch(f, cmd, args)
- }
- chunkSize := o.ChunkSize
- if o.Sort {
- // TODO(juanvallejo): in the future, we could have the client use chunking
- // to gather all results, then sort them all at the end to reduce server load.
- chunkSize = 0
- }
- r := f.NewBuilder().
- Unstructured().
- NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
- FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
- LabelSelectorParam(o.LabelSelector).
- FieldSelectorParam(o.FieldSelector).
- ExportParam(o.Export).
- RequestChunksOf(chunkSize).
- ResourceTypeOrNameArgs(true, args...).
- ContinueOnError().
- Latest().
- Flatten().
- TransformRequests(o.transformRequests).
- Do()
- if o.IgnoreNotFound {
- r.IgnoreErrors(kapierrors.IsNotFound)
- }
- if err := r.Err(); err != nil {
- return err
- }
- if !o.IsHumanReadablePrinter {
- return o.printGeneric(r)
- }
- allErrs := []error{}
- errs := sets.NewString()
- infos, err := r.Infos()
- if err != nil {
- allErrs = append(allErrs, err)
- }
- printWithKind := multipleGVKsRequested(infos)
- 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 positioner OriginalPositioner
- if o.Sort {
- sorter := NewRuntimeSorter(objs, sorting)
- if err := sorter.Sort(); err != nil {
- return err
- }
- positioner = sorter
- }
- var printer printers.ResourcePrinter
- var lastMapping *meta.RESTMapping
- // track if we write any output
- trackingWriter := &trackingWriterWrapper{Delegate: o.Out}
- w := utilprinters.GetNewTabWriter(trackingWriter)
- for ix := range objs {
- var mapping *meta.RESTMapping
- var info *resource.Info
- if positioner != nil {
- info = infos[positioner.OriginalPosition(ix)]
- mapping = info.Mapping
- } else {
- info = infos[ix]
- mapping = info.Mapping
- }
- printWithNamespace := o.AllNamespaces
- if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
- printWithNamespace = false
- }
- if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
- w.Flush()
- w.SetRememberedWidths(nil)
- // TODO: this doesn't belong here
- // add linebreak between resource groups (if there is more than one)
- // skip linebreak above first resource group
- if lastMapping != nil && !o.NoHeaders {
- fmt.Fprintln(o.ErrOut)
- }
- printer, err = o.ToPrinter(mapping, printWithNamespace, printWithKind)
- if err != nil {
- if !errs.Has(err.Error()) {
- errs.Insert(err.Error())
- allErrs = append(allErrs, err)
- }
- continue
- }
- lastMapping = mapping
- }
- // ensure a versioned object is passed to the custom-columns printer
- // if we are using OpenAPI columns to print
- if o.PrintWithOpenAPICols {
- printer.PrintObj(info.Object, w)
- continue
- }
- internalObj, err := legacyscheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion())
- if err != nil {
- // if there's an error, try to print what you have (mirrors old behavior).
- klog.V(1).Info(err)
- printer.PrintObj(info.Object, w)
- } else {
- printer.PrintObj(internalObj, w)
- }
- }
- w.Flush()
- if trackingWriter.Written == 0 && !o.IgnoreNotFound && len(allErrs) == 0 {
- // if we wrote no output, and had no errors, and are not ignoring NotFound, be sure we output something
- fmt.Fprintln(o.ErrOut, "No resources found.")
- }
- return utilerrors.NewAggregate(allErrs)
- }
- type trackingWriterWrapper struct {
- Delegate io.Writer
- Written int
- }
- func (t *trackingWriterWrapper) Write(p []byte) (n int, err error) {
- t.Written += len(p)
- return t.Delegate.Write(p)
- }
- // raw makes a simple HTTP request to the provided path on the server using the default
- // credentials.
- func (o *GetOptions) raw(f cmdutil.Factory) error {
- restClient, err := f.RESTClient()
- if err != nil {
- return err
- }
- stream, err := restClient.Get().RequestURI(o.Raw).Stream()
- if err != nil {
- return err
- }
- defer stream.Close()
- _, err = io.Copy(o.Out, stream)
- if err != nil && err != io.EOF {
- return err
- }
- return nil
- }
- // watch starts a client-side watch of one or more resources.
- // TODO: remove the need for arguments here.
- func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
- r := f.NewBuilder().
- Unstructured().
- NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
- FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
- LabelSelectorParam(o.LabelSelector).
- FieldSelectorParam(o.FieldSelector).
- ExportParam(o.Export).
- RequestChunksOf(o.ChunkSize).
- ResourceTypeOrNameArgs(true, args...).
- SingleResourceType().
- Latest().
- TransformRequests(o.transformRequests).
- Do()
- if err := r.Err(); err != nil {
- return err
- }
- infos, err := r.Infos()
- if err != nil {
- return err
- }
- if multipleGVKsRequested(infos) {
- return i18n.Errorf("watch is only supported on individual resources and resource collections - more than 1 resource was found")
- }
- info := infos[0]
- mapping := info.ResourceMapping()
- printer, err := o.ToPrinter(mapping, o.AllNamespaces, false)
- if err != nil {
- return err
- }
- obj, err := r.Object()
- if err != nil {
- return err
- }
- // watching from resourceVersion 0, starts the watch at ~now and
- // will return an initial watch event. Starting form ~now, rather
- // the rv of the object will insure that we start the watch from
- // inside the watch window, which the rv of the object might not be.
- rv := "0"
- isList := meta.IsListType(obj)
- if isList {
- // the resourceVersion of list objects is ~now but won't return
- // an initial watch event
- rv, err = meta.NewAccessor().ResourceVersion(obj)
- if err != nil {
- return err
- }
- }
- writer := utilprinters.GetNewTabWriter(o.Out)
- tableGK := metainternal.SchemeGroupVersion.WithKind("Table").GroupKind()
- // print the current object
- if !o.WatchOnly {
- var objsToPrint []runtime.Object
- if isList {
- objsToPrint, _ = meta.ExtractList(obj)
- } else {
- objsToPrint = append(objsToPrint, obj)
- }
- for _, objToPrint := range objsToPrint {
- if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
- // printing anything other than tables always takes the internal version, but the watch event uses externals
- internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
- objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
- }
- if err := printer.PrintObj(objToPrint, writer); err != nil {
- return fmt.Errorf("unable to output the provided object: %v", err)
- }
- }
- writer.Flush()
- }
- // print watched changes
- w, err := r.Watch(rv)
- if err != nil {
- return err
- }
- first := true
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- intr := interrupt.New(nil, cancel)
- intr.Run(func() error {
- _, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
- if !isList && first {
- // drop the initial watch event in the single resource case
- first = false
- return false, nil
- }
- // printing always takes the internal version, but the watch event uses externals
- // TODO fix printing to use server-side or be version agnostic
- objToPrint := e.Object
- if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
- internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
- objToPrint = attemptToConvertToInternal(e.Object, legacyscheme.Scheme, internalGV)
- }
- if err := printer.PrintObj(objToPrint, writer); err != nil {
- return false, err
- }
- writer.Flush()
- return false, nil
- })
- return err
- })
- return nil
- }
- // attemptToConvertToInternal tries to convert to an internal type, but returns the original if it can't
- func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConvertor, targetVersion schema.GroupVersion) runtime.Object {
- internalObject, err := converter.ConvertToVersion(obj, targetVersion)
- if err != nil {
- klog.V(1).Infof("Unable to convert %T to %v: %v", obj, targetVersion, err)
- return obj
- }
- return internalObject
- }
- func (o *GetOptions) printGeneric(r *resource.Result) error {
- // 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
- // 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 list, leave it as a single item
- var errs []error
- singleItemImplied := false
- infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
- if err != nil {
- if singleItemImplied {
- return err
- }
- errs = append(errs, err)
- }
- if len(infos) == 0 && o.IgnoreNotFound {
- return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
- }
- printer, err := o.ToPrinter(nil, false, false)
- if err != nil {
- return err
- }
- var obj runtime.Object
- if !singleItemImplied || len(infos) > 1 {
- // we have more than one item, so coerce all items into a list.
- // we don't want an *unstructured.Unstructured list yet, as we
- // may be dealing with non-unstructured objects. Compose all items
- // into an corev1.List, and then decode using an unstructured scheme.
- list := corev1.List{
- TypeMeta: metav1.TypeMeta{
- Kind: "List",
- APIVersion: "v1",
- },
- ListMeta: metav1.ListMeta{},
- }
- for _, info := range infos {
- list.Items = append(list.Items, runtime.RawExtension{Object: info.Object})
- }
- listData, err := json.Marshal(list)
- if err != nil {
- return err
- }
- converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, listData)
- if err != nil {
- return err
- }
- obj = converted
- } else {
- obj = infos[0].Object
- }
- isList := meta.IsListType(obj)
- if isList {
- items, err := meta.ExtractList(obj)
- if err != nil {
- return err
- }
- // take the items and create a new list for display
- list := &unstructured.UnstructuredList{
- Object: map[string]interface{}{
- "kind": "List",
- "apiVersion": "v1",
- "metadata": map[string]interface{}{},
- },
- }
- if listMeta, err := meta.ListAccessor(obj); err == nil {
- list.Object["metadata"] = map[string]interface{}{
- "selfLink": listMeta.GetSelfLink(),
- "resourceVersion": listMeta.GetResourceVersion(),
- }
- }
- for _, item := range items {
- list.Items = append(list.Items, *item.(*unstructured.Unstructured))
- }
- if err := printer.PrintObj(list, o.Out); err != nil {
- errs = append(errs, err)
- }
- return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
- }
- if printErr := printer.PrintObj(obj, o.Out); printErr != nil {
- errs = append(errs, printErr)
- }
- return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
- }
- func addOpenAPIPrintColumnFlags(cmd *cobra.Command, opt *GetOptions) {
- cmd.Flags().BoolVar(&opt.PrintWithOpenAPICols, useOpenAPIPrintColumnFlagLabel, opt.PrintWithOpenAPICols, "If true, use x-kubernetes-print-column metadata (if present) from the OpenAPI schema for displaying a resource.")
- cmd.Flags().MarkDeprecated(useOpenAPIPrintColumnFlagLabel, "deprecated in favor of server-side printing")
- }
- func addServerPrintColumnFlags(cmd *cobra.Command, opt *GetOptions) {
- cmd.Flags().BoolVar(&opt.ServerPrint, useServerPrintColumns, opt.ServerPrint, "If true, have the server return the appropriate table output. Supports extension APIs and CRDs.")
- }
- func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
- return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
- }
- func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool {
- return cmdutil.GetFlagString(cmd, "output") != ""
- }
- func multipleGVKsRequested(infos []*resource.Info) bool {
- if len(infos) < 2 {
- return false
- }
- gvk := infos[0].Mapping.GroupVersionKind
- for _, info := range infos {
- if info.Mapping.GroupVersionKind != gvk {
- return true
- }
- }
- return false
- }
|