k3s/pkg/kubectl/cmd/get/get.go

761 lines
24 KiB
Go
Raw Normal View History

/*
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.
*/
2018-04-19 21:57:09 +00:00
package get
import (
"encoding/json"
"fmt"
"io"
2018-05-02 15:27:45 +00:00
"net/url"
"github.com/golang/glog"
"github.com/spf13/cobra"
2017-02-14 19:48:07 +00:00
kapierrors "k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime"
2018-02-19 20:16:45 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2017-01-11 14:09:48 +00:00
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
2015-08-05 22:03:47 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-04-25 12:32:08 +00:00
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
2016-08-08 17:56:19 +00:00
"k8s.io/kubernetes/pkg/util/interrupt"
)
// GetOptions contains the input to the get command.
type GetOptions struct {
2018-05-04 12:39:47 +00:00
PrintFlags *PrintFlags
ToPrinter func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
2018-05-04 12:39:47 +00:00
IsHumanReadablePrinter bool
PrintWithOpenAPICols bool
2018-04-19 21:57:09 +00:00
2018-04-25 23:55:26 +00:00
CmdParent string
resource.FilenameOptions
2016-08-11 12:52:12 +00:00
Raw string
Watch bool
WatchOnly bool
ChunkSize int64
LabelSelector string
2017-08-04 09:56:44 +00:00
FieldSelector string
AllNamespaces bool
Namespace string
ExplicitNamespace bool
ServerPrint bool
2018-04-19 21:57:09 +00:00
NoHeaders bool
Sort bool
2017-02-14 19:48:07 +00:00
IgnoreNotFound bool
Export bool
IncludeUninitialized bool
2018-04-25 12:32:08 +00:00
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.
2018-02-28 09:13:45 +00:00
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
2015-06-29 18:36:06 +00:00
# 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
# Return only the phase value of the specified pod.
kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}
# 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.
2018-04-25 23:55:26 +00:00
func NewGetOptions(parent string, streams genericclioptions.IOStreams) *GetOptions {
return &GetOptions{
2018-05-02 19:15:47 +00:00
PrintFlags: NewGetPrintFlags(),
2018-04-25 23:55:26 +00:00
CmdParent: parent,
2018-04-25 12:32:08 +00:00
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.
2018-04-25 23:55:26 +00:00
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,
2017-01-25 01:00:32 +00:00
Short: i18n.T("Display one or many resources"),
2018-04-25 23:55:26 +00:00
Long: getLong + "\n\n" + cmdutil.SuggestApiResources(parent),
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
2018-04-19 21:57:09 +00:00
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run(f, cmd, args))
},
SuggestFor: []string{"list", "ps"},
}
2018-04-19 21:57:09 +00:00
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().BoolVar(&o.AllNamespaces, "all-namespaces", 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)
2018-04-19 21:57:09 +00:00
cmd.Flags().BoolVar(&o.Export, "export", o.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
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.
2018-04-19 21:57:09 +00:00
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")
2016-08-11 12:52:12 +00:00
}
2016-10-06 15:42:19 +00:00
return nil
2016-08-11 12:52:12 +00:00
}
var err error
o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
2018-04-19 21:57:09 +00:00
if o.AllNamespaces {
o.ExplicitNamespace = false
}
isSorting, err := cmd.Flags().GetString("sort-by")
if err != nil {
return err
}
2018-04-19 21:57:09 +00:00
o.Sort = len(isSorting) > 0
o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
// TODO (soltysh): currently we don't support sorting and custom columns
// with server side print. So in these cases force the old behavior.
outputOption := cmd.Flags().Lookup("output").Value.String()
2018-04-19 21:57:09 +00:00
if o.Sort && outputOption == "custom-columns" {
o.ServerPrint = false
}
templateArg := ""
if o.PrintFlags.TemplateFlags != nil && o.PrintFlags.TemplateFlags.TemplateArgument != nil {
templateArg = *o.PrintFlags.TemplateFlags.TemplateArgument
}
2018-05-04 12:39:47 +00:00
// 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" {
2018-05-04 12:39:47 +00:00
o.IsHumanReadablePrinter = true
}
2018-04-19 21:57:09 +00:00
o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
2018-04-19 21:57:09 +00:00
// 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)
}
}
2018-05-20 05:26:53 +00:00
printFlags.SetKind(mapping.GroupVersionKind.GroupKind())
2018-04-19 21:57:09 +00:00
}
if withNamespace {
printFlags.EnsureWithNamespace()
}
if withKind {
printFlags.EnsureWithKind()
}
2018-04-19 21:57:09 +00:00
printer, err := printFlags.ToPrinter()
if err != nil {
return nil, err
}
2018-05-17 21:42:34 +00:00
printer = maybeWrapSortingPrinter(printer, isSorting)
2018-04-19 21:57:09 +00:00
return printer.PrintObj, nil
}
switch {
2018-04-19 21:57:09 +00:00
case o.Watch || o.WatchOnly:
// include uninitialized objects when watching on a single object
// unless explicitly set --include-uninitialized=false
2018-04-19 21:57:09 +00:00
o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2)
default:
2018-04-19 21:57:09 +00:00
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
2018-04-25 23:55:26 +00:00
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)
}
}
return nil
}
// Validate checks the set of flags provided by the user.
2018-04-19 21:57:09 +00:00
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")
}
2018-04-19 21:57:09 +00:00
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
}
// Run performs the get operation.
// TODO: remove the need to pass these arguments, like other commands.
2018-04-19 21:57:09 +00:00
func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(o.Raw) > 0 {
return o.raw(f)
}
2018-04-19 21:57:09 +00:00
if o.Watch || o.WatchOnly {
return o.watch(f, cmd, args)
}
// 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)
}
r := f.NewBuilder().
Unstructured().
2018-04-19 21:57:09 +00:00
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
ExportParam(o.Export).
RequestChunksOf(o.ChunkSize).
IncludeUninitialized(o.IncludeUninitialized).
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest().
Flatten().
TransformRequests(func(req *rest.Request) {
2018-06-28 23:33:22 +00:00
// We need full objects if printing with openapi columns
if o.PrintWithOpenAPICols {
return
}
2018-05-04 12:39:47 +00:00
if o.ServerPrint && o.IsHumanReadablePrinter && !o.Sort {
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)
}
}).
Do()
2018-04-19 21:57:09 +00:00
if o.IgnoreNotFound {
r.IgnoreErrors(kapierrors.IsNotFound)
}
if err := r.Err(); err != nil {
return err
}
2018-05-04 12:39:47 +00:00
if !o.IsHumanReadablePrinter {
2018-04-19 21:57:09 +00:00
return o.printGeneric(r)
}
allErrs := []error{}
2016-11-22 06:14:15 +00:00
errs := sets.NewString()
infos, err := r.Infos()
if err != nil {
allErrs = append(allErrs, err)
}
printWithKind := multipleGVKsRequested(infos)
2014-11-12 01:31:13 +00:00
2015-10-20 05:44:48 +00:00
objs := make([]runtime.Object, len(infos))
for ix := range infos {
2018-04-19 21:57:09 +00:00
if o.ServerPrint {
table, err := o.decodeIntoTable(infos[ix].Object)
if err == nil {
infos[ix].Object = table
} else {
// if we are unable to decode server response into a v1beta1.Table,
// fallback to client-side printing with whatever info the server returned.
glog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
}
}
2015-10-20 05:44:48 +00:00
objs[ix] = infos[ix].Object
}
sorting, err := cmd.Flags().GetString("sort-by")
2016-07-19 03:09:44 +00:00
if err != nil {
return err
}
2015-10-20 05:44:48 +00:00
var sorter *kubectl.RuntimeSort
2018-04-19 21:57:09 +00:00
if o.Sort && len(objs) > 1 {
// TODO: questionable
2018-02-21 17:10:38 +00:00
if sorter, err = kubectl.SortObjects(cmdutil.InternalVersionDecoder(), objs, sorting); err != nil {
2015-10-20 05:44:48 +00:00
return err
}
}
2018-04-19 21:57:09 +00:00
var printer printers.ResourcePrinter
var lastMapping *meta.RESTMapping
2018-04-19 21:57:09 +00:00
nonEmptyObjCount := 0
w := printers.GetNewTabWriter(o.Out)
2015-10-20 05:44:48 +00:00
for ix := range objs {
var mapping *meta.RESTMapping
var info *resource.Info
2015-10-20 05:44:48 +00:00
if sorter != nil {
info = infos[sorter.OriginalPosition(ix)]
mapping = info.Mapping
2015-10-20 05:44:48 +00:00
} else {
info = infos[ix]
mapping = info.Mapping
}
// if dealing with a table that has no rows, skip remaining steps
// and avoid printing an unnecessary newline
if table, isTable := info.Object.(*metav1beta1.Table); isTable {
if len(table.Rows) == 0 {
continue
}
}
2018-04-19 21:57:09 +00:00
nonEmptyObjCount++
2018-04-19 21:57:09 +00:00
printWithNamespace := o.AllNamespaces
2018-04-19 21:57:09 +00:00
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
2018-04-19 21:57:09 +00:00
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
w.Flush()
2018-04-19 21:57:09 +00:00
// 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 {
2016-11-22 06:14:15 +00:00
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
add linebreak between resource groups Printing multiple groups via `kubectl get all` can produce output that is hard to read in cases where there are a lot of resource types to display / some resource types contain varying column amounts. This patch adds a linebreak above each group of resources only when there is more than one group to display, and always omitting the linebreak above the first group. This makes for slightly improved output. Linebreaks are printed to stderr, and honor the `--no-headers` option. **Before** ``` $ kubectl get all NAME READY STATUS RESTARTS AGE po/database-1-u9m9l 1/1 Running 3 5d po/idling-echo-1-9fmz6 2/2 Running 8 5d po/idling-echo-1-gzb0v 2/2 Running 4 5d NAME DESIRED CURRENT READY AGE rc/database-1 1 1 1 6d rc/idling-echo-1 2 2 2 6d NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/database 172.30.11.104 <none> 5434/TCP 6d svc/frontend 172.30.196.217 <none> 5432/TCP 6d svc/idling-echo 172.30.115.67 <none> 8675/TCP,3090/UDP 6d svc/kubernetes 172.30.0.1 <none> 443/TCP,53/UDP,53/TCP 6d svc/mynodeport 172.30.81.254 <nodes> 8080/TCP 5d svc/mynodeport1 172.30.198.193 <nodes> 8080/TCP 5d svc/mynodeport2 172.30.149.48 <nodes> 8080/TCP 5d svc/mynodeport3 172.30.195.235 <nodes> 8080/TCP 5d ``` **After** ``` $ kubectl get all NAME READY STATUS RESTARTS AGE po/database-1-u9m9l 1/1 Running 3 5d po/idling-echo-1-9fmz6 2/2 Running 8 5d po/idling-echo-1-gzb0v 2/2 Running 4 5d NAME DESIRED CURRENT READY AGE rc/database-1 1 1 1 6d rc/idling-echo-1 2 2 2 6d NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE svc/database 172.30.11.104 <none> 5434/TCP 6d svc/frontend 172.30.196.217 <none> 5432/TCP 6d svc/idling-echo 172.30.115.67 <none> 8675/TCP,3090/UDP 6d svc/kubernetes 172.30.0.1 <none> 443/TCP,53/UDP,53/TCP 6d svc/mynodeport 172.30.81.254 <nodes> 8080/TCP 5d svc/mynodeport1 172.30.198.193 <nodes> 8080/TCP 5d svc/mynodeport2 172.30.149.48 <nodes> 8080/TCP 5d svc/mynodeport3 172.30.195.235 <nodes> 8080/TCP 5d ```
2016-09-28 18:21:46 +00:00
2015-10-20 05:44:48 +00:00
lastMapping = mapping
}
2018-05-14 16:36:12 +00:00
// 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).
glog.V(1).Info(err)
printer.PrintObj(info.Object, w)
} else {
printer.PrintObj(internalObj, w)
}
2015-10-20 05:44:48 +00:00
}
w.Flush()
2018-04-19 21:57:09 +00:00
if nonEmptyObjCount == 0 && !o.IgnoreNotFound {
fmt.Fprintln(o.ErrOut, "No resources found.")
2018-03-13 07:24:24 +00:00
}
return utilerrors.NewAggregate(allErrs)
}
// raw makes a simple HTTP request to the provided path on the server using the default
// credentials.
2018-04-19 21:57:09 +00:00
func (o *GetOptions) raw(f cmdutil.Factory) error {
restClient, err := f.RESTClient()
if err != nil {
return err
}
2018-04-19 21:57:09 +00:00
stream, err := restClient.Get().RequestURI(o.Raw).Stream()
if err != nil {
return err
}
defer stream.Close()
2018-04-19 21:57:09 +00:00
_, 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.
2018-04-19 21:57:09 +00:00
func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
r := f.NewBuilder().
Unstructured().
2018-04-19 21:57:09 +00:00
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
ExportParam(o.Export).
RequestChunksOf(o.ChunkSize).
IncludeUninitialized(o.IncludeUninitialized).
ResourceTypeOrNameArgs(true, args...).
SingleResourceType().
Latest().
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 resources were 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
}
}
// print the current object
2018-04-19 21:57:09 +00:00
if !o.WatchOnly {
var objsToPrint []runtime.Object
2018-04-19 21:57:09 +00:00
writer := printers.GetNewTabWriter(o.Out)
if isList {
objsToPrint, _ = meta.ExtractList(obj)
} else {
objsToPrint = append(objsToPrint, obj)
}
for _, objToPrint := range objsToPrint {
2018-05-04 12:39:47 +00:00
if o.IsHumanReadablePrinter {
2018-04-19 21:57:09 +00:00
// printing 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)
2018-04-19 21:57:09 +00:00
}
if err := printer.PrintObj(objToPrint, writer); err != nil {
2018-03-13 07:24:24 +00:00
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
intr := interrupt.New(nil, w.Stop)
intr.Run(func() error {
_, err := watch.Until(0, 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
}
2018-03-13 07:24:24 +00:00
// printing always takes the internal version, but the watch event uses externals
// TODO fix printing to use server-side or be version agnostic
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
if err := printer.PrintObj(attemptToConvertToInternal(e.Object, legacyscheme.Scheme, internalGV), o.Out); err != nil {
2018-03-13 07:24:24 +00:00
return false, err
}
return false, nil
})
return err
})
return nil
}
2018-02-19 20:16:45 +00:00
// 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 {
2018-06-21 07:31:19 +00:00
glog.V(1).Infof("Unable to convert %T to %v: %v", obj, targetVersion, err)
2018-02-19 20:16:45 +00:00
return obj
}
return internalObject
}
func (o *GetOptions) decodeIntoTable(obj runtime.Object) (runtime.Object, error) {
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
}
unstr, ok := obj.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("attempt to decode non-Unstructured object")
}
table := &metav1beta1.Table{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil {
return nil, err
}
for i := range table.Rows {
row := &table.Rows[i]
if row.Object.Raw == nil || row.Object.Object != nil {
continue
}
converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw)
if err != nil {
return nil, err
}
row.Object.Object = converted
}
return table, nil
}
2018-04-19 21:57:09 +00:00
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)
}
2018-04-19 21:57:09 +00:00
if len(infos) == 0 && o.IgnoreNotFound {
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
}
printer, err := o.ToPrinter(nil, false, false)
2018-04-19 21:57:09 +00:00
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 api.List, and then decode using an unstructured scheme.
list := api.List{
TypeMeta: metav1.TypeMeta{
Kind: "List",
APIVersion: "v1",
},
ListMeta: metav1.ListMeta{},
}
for _, info := range infos {
list.Items = append(list.Items, 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 {
2018-03-13 07:24:24 +00:00
items, err := meta.ExtractList(obj)
if err != nil {
return err
}
2018-03-13 07:24:24 +00:00
// 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))
}
2018-04-19 21:57:09 +00:00
if err := printer.PrintObj(list, o.Out); err != nil {
errs = append(errs, err)
}
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
}
2018-04-19 21:57:09 +00:00
if printErr := printer.PrintObj(obj, o.Out); printErr != nil {
2018-03-13 07:24:24 +00:00
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") != ""
}
2018-05-17 21:42:34 +00:00
func maybeWrapSortingPrinter(printer printers.ResourcePrinter, sortBy string) printers.ResourcePrinter {
if len(sortBy) != 0 {
return &kubectl.SortingPrinter{
Delegate: printer,
SortField: fmt.Sprintf("%s", sortBy),
}
}
return printer
}
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
}