2019-01-12 04:58:27 +00:00
/ *
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 (
2021-07-02 08:43:15 +00:00
"bytes"
2019-01-12 04:58:27 +00:00
"context"
"encoding/json"
"fmt"
"io"
2021-07-02 08:43:15 +00:00
"io/ioutil"
2019-01-12 04:58:27 +00:00
"net/url"
2021-07-02 08:43:15 +00:00
"os"
2020-03-26 21:07:15 +00:00
"strings"
2019-01-12 04:58:27 +00:00
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
2020-03-26 21:07:15 +00:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/api/meta"
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"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions"
2019-04-07 17:07:55 +00:00
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
2019-09-27 21:51:53 +00:00
kubernetesscheme "k8s.io/client-go/kubernetes/scheme"
2019-01-12 04:58:27 +00:00
"k8s.io/client-go/rest"
watchtools "k8s.io/client-go/tools/watch"
2021-07-02 08:43:15 +00:00
"k8s.io/kubectl/pkg/cmd/apiresources"
2019-09-27 21:51:53 +00:00
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/rawhttp"
2019-12-12 01:27:03 +00:00
"k8s.io/kubectl/pkg/scheme"
2019-09-27 21:51:53 +00:00
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/interrupt"
"k8s.io/kubectl/pkg/util/templates"
utilpointer "k8s.io/utils/pointer"
2019-01-12 04:58:27 +00:00
)
// GetOptions contains the input to the get command.
type GetOptions struct {
PrintFlags * PrintFlags
2019-09-27 21:51:53 +00:00
ToPrinter func ( * meta . RESTMapping , * bool , bool , bool ) ( printers . ResourcePrinterFunc , error )
2019-01-12 04:58:27 +00:00
IsHumanReadablePrinter bool
2019-08-30 18:33:25 +00:00
PrintWithOpenAPICols bool
2019-01-12 04:58:27 +00:00
CmdParent string
resource . FilenameOptions
Raw string
Watch bool
WatchOnly bool
ChunkSize int64
2019-09-27 21:51:53 +00:00
OutputWatchEvents bool
2019-01-12 04:58:27 +00:00
LabelSelector string
FieldSelector string
AllNamespaces bool
Namespace string
ExplicitNamespace bool
ServerPrint bool
NoHeaders bool
Sort bool
IgnoreNotFound bool
genericclioptions . IOStreams
}
var (
2020-12-01 01:06:26 +00:00
getLong = templates . LongDesc ( i18n . T ( `
2021-07-02 08:43:15 +00:00
Display one or many resources .
2019-01-12 04:58:27 +00:00
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
2020-12-01 01:06:26 +00:00
of the -- template flag , you can filter the attributes of the fetched resources . ` ) )
2019-01-12 04:58:27 +00:00
getExample = templates . Examples ( i18n . T ( `
2021-07-02 08:43:15 +00:00
# List all pods in ps output format
2019-01-12 04:58:27 +00:00
kubectl get pods
2021-07-02 08:43:15 +00:00
# List all pods in ps output format with more information ( such as node name )
2019-01-12 04:58:27 +00:00
kubectl get pods - o wide
2021-07-02 08:43:15 +00:00
# List a single replication controller with specified NAME in ps output format
2019-01-12 04:58:27 +00:00
kubectl get replicationcontroller web
2021-07-02 08:43:15 +00:00
# List deployments in JSON output format , in the "v1" version of the "apps" API group
2019-01-12 04:58:27 +00:00
kubectl get deployments . v1 . apps - o json
2021-07-02 08:43:15 +00:00
# List a single pod in JSON output format
2019-01-12 04:58:27 +00:00
kubectl get - o json pod web - pod - 13 je7
2021-07-02 08:43:15 +00:00
# List a pod identified by type and name specified in "pod.yaml" in JSON output format
2019-01-12 04:58:27 +00:00
kubectl get - f pod . yaml - o json
2021-07-02 08:43:15 +00:00
# List resources from a directory with kustomization . yaml - e . g . dir / kustomization . yaml
2019-04-07 17:07:55 +00:00
kubectl get - k dir /
2021-07-02 08:43:15 +00:00
# Return only the phase value of the specified pod
2019-01-12 04:58:27 +00:00
kubectl get - o template pod / web - pod - 13 je7 -- template = { { . status . phase } }
2021-07-02 08:43:15 +00:00
# List resource information in custom columns
2019-04-07 17:07:55 +00:00
kubectl get pod test - pod - o custom - columns = CONTAINER : . spec . containers [ 0 ] . name , IMAGE : . spec . containers [ 0 ] . image
2021-07-02 08:43:15 +00:00
# List all replication controllers and services together in ps output format
2019-01-12 04:58:27 +00:00
kubectl get rc , services
2021-07-02 08:43:15 +00:00
# List one or more resources by their type and names
2019-01-12 04:58:27 +00:00
kubectl get rc / web service / frontend pods / web - pod - 13 je7 ` ) )
)
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 ,
2021-07-02 08:43:15 +00:00
ChunkSize : cmdutil . DefaultChunkSize ,
2019-01-12 04:58:27 +00:00
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 {
2021-07-02 08:43:15 +00:00
Use : fmt . Sprintf ( "get [(-o|--output=)%s] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]" , strings . Join ( o . PrintFlags . AllowedFormats ( ) , "|" ) ) ,
2019-01-12 04:58:27 +00:00
DisableFlagsInUseLine : true ,
Short : i18n . T ( "Display one or many resources" ) ,
2019-04-07 17:07:55 +00:00
Long : getLong + "\n\n" + cmdutil . SuggestAPIResources ( parent ) ,
2019-01-12 04:58:27 +00:00
Example : getExample ,
2021-07-02 08:43:15 +00:00
ValidArgsFunction : func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
var comps [ ] string
if len ( args ) == 0 {
comps = apiresources . CompGetResourceList ( f , cmd , toComplete )
} else if len ( args ) == 1 {
comps = CompGetResource ( f , cmd , args [ 0 ] , toComplete )
}
return comps , cobra . ShellCompDirectiveNoFileComp
} ,
2019-01-12 04:58:27 +00:00
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." )
2019-09-27 21:51:53 +00:00
cmd . Flags ( ) . BoolVar ( & o . OutputWatchEvents , "output-watch-events" , o . OutputWatchEvents , "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events." )
2019-01-12 04:58:27 +00:00
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." )
2019-04-07 17:07:55 +00:00
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." )
2019-01-12 04:58:27 +00:00
addOpenAPIPrintColumnFlags ( cmd , o )
addServerPrintColumnFlags ( cmd , o )
cmdutil . AddFilenameOptionFlags ( cmd , & o . FilenameOptions , "identifying the resource to get from a server." )
2021-07-02 08:43:15 +00:00
cmdutil . AddChunkSizeFlag ( cmd , & o . ChunkSize )
2019-01-12 04:58:27 +00:00
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
}
2019-08-30 18:33:25 +00:00
sortBy , err := cmd . Flags ( ) . GetString ( "sort-by" )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2019-08-30 18:33:25 +00:00
o . Sort = len ( sortBy ) > 0
2019-01-12 04:58:27 +00:00
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 ( )
2020-12-01 01:06:26 +00:00
if strings . Contains ( outputOption , "custom-columns" ) || outputOption == "yaml" || strings . Contains ( outputOption , "json" ) {
2019-01-12 04:58:27 +00:00
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
}
2019-09-27 21:51:53 +00:00
o . ToPrinter = func ( mapping * meta . RESTMapping , outputObjects * bool , withNamespace bool , withKind bool ) ( printers . ResourcePrinterFunc , error ) {
2019-01-12 04:58:27 +00:00
// make a new copy of current flags / opts before mutating
printFlags := o . PrintFlags . Copy ( )
if mapping != nil {
2019-08-30 18:33:25 +00:00
if ! cmdSpecifiesOutputFmt ( cmd ) && o . PrintWithOpenAPICols {
if apiSchema , err := f . OpenAPISchema ( ) ; err == nil {
printFlags . UseOpenAPIColumns ( apiSchema , mapping )
}
}
2019-01-12 04:58:27 +00:00
printFlags . SetKind ( mapping . GroupVersionKind . GroupKind ( ) )
}
if withNamespace {
printFlags . EnsureWithNamespace ( )
}
if withKind {
printFlags . EnsureWithKind ( )
}
printer , err := printFlags . ToPrinter ( )
if err != nil {
return nil , err
}
2019-12-12 01:27:03 +00:00
printer , err = printers . NewTypeSetter ( scheme . Scheme ) . WrapToPrinter ( printer , nil )
if err != nil {
return nil , err
}
2019-01-12 04:58:27 +00:00
2019-08-30 18:33:25 +00:00
if o . Sort {
printer = & SortingPrinter { Delegate : printer , SortField : sortBy }
}
2019-09-27 21:51:53 +00:00
if outputObjects != nil {
printer = & skipPrinter { delegate : printer , output : outputObjects }
}
2019-08-30 18:33:25 +00:00
if o . ServerPrint {
printer = & TablePrinter { Delegate : printer }
}
2019-01-12 04:58:27 +00:00
return printer . PrintObj , nil
}
switch {
case o . Watch || o . WatchOnly :
2019-08-30 18:33:25 +00:00
if o . Sort {
fmt . Fprintf ( o . IOStreams . ErrOut , "warning: --watch or --watch-only requested, --sort-by will be ignored\n" )
}
2019-01-12 04:58:27 +00:00
default :
2019-08-30 18:33:25 +00:00
if len ( args ) == 0 && cmdutil . IsFilenameSliceEmpty ( o . Filenames , o . Kustomize ) {
2019-04-07 17:07:55 +00:00
fmt . Fprintf ( o . ErrOut , "You must specify the type of resource to get. %s\n\n" , cmdutil . SuggestAPIResources ( o . CmdParent ) )
2019-01-12 04:58:27 +00:00
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 )
}
}
2019-08-30 18:33:25 +00:00
// 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 )
}
2019-01-12 04:58:27 +00:00
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 {
2020-08-10 17:43:49 +00:00
if o . Watch || o . WatchOnly || len ( o . LabelSelector ) > 0 {
2019-01-12 04:58:27 +00:00
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 )
}
}
2019-09-27 21:51:53 +00:00
if o . OutputWatchEvents && ! ( o . Watch || o . WatchOnly ) {
return cmdutil . UsageErrorf ( cmd , "--output-watch-events option can only be used with --watch or --watch-only" )
}
2019-01-12 04:58:27 +00:00
return nil
}
2019-04-07 17:07:55 +00:00
// OriginalPositioner and NopPositioner is required for swap/sort operations of data in table format
2019-01-12 04:58:27 +00:00
type OriginalPositioner interface {
OriginalPosition ( int ) int
}
2019-04-07 17:07:55 +00:00
// NopPositioner and OriginalPositioner is required for swap/sort operations of data in table format
2019-01-12 04:58:27 +00:00
type NopPositioner struct { }
2019-04-07 17:07:55 +00:00
// OriginalPosition returns the original position from NopPositioner object
2019-01-12 04:58:27 +00:00
func ( t * NopPositioner ) OriginalPosition ( ix int ) int {
return ix
}
2019-04-07 17:07:55 +00:00
// RuntimeSorter holds the required objects to perform sorting of runtime objects
2019-01-12 04:58:27 +00:00
type RuntimeSorter struct {
field string
decoder runtime . Decoder
objects [ ] runtime . Object
positioner OriginalPositioner
}
2019-04-07 17:07:55 +00:00
// Sort performs the sorting of runtime objects
2019-01-12 04:58:27 +00:00
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 {
2020-03-26 21:07:15 +00:00
_ , isTable := r . objects [ 0 ] . ( * metav1 . Table )
2019-01-12 04:58:27 +00:00
if ! isTable {
return nil
}
}
includesTable := false
includesRuntimeObjs := false
for _ , obj := range r . objects {
switch t := obj . ( type ) {
2020-03-26 21:07:15 +00:00
case * metav1 . Table :
2019-01-12 04:58:27 +00:00
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
}
2019-04-07 17:07:55 +00:00
// OriginalPosition returns the original position of a runtime object
2019-01-12 04:58:27 +00:00
func ( r * RuntimeSorter ) OriginalPosition ( ix int ) int {
if r . positioner == nil {
return 0
}
return r . positioner . OriginalPosition ( ix )
}
2019-04-07 17:07:55 +00:00
// WithDecoder allows custom decoder to be set for testing
2019-01-12 04:58:27 +00:00
func ( r * RuntimeSorter ) WithDecoder ( decoder runtime . Decoder ) * RuntimeSorter {
r . decoder = decoder
return r
}
2019-04-07 17:07:55 +00:00
// NewRuntimeSorter returns a new instance of RuntimeSorter
2019-01-12 04:58:27 +00:00
func NewRuntimeSorter ( objects [ ] runtime . Object , sortBy string ) * RuntimeSorter {
parsedField , err := RelaxedJSONPathExpression ( sortBy )
if err != nil {
parsedField = sortBy
}
return & RuntimeSorter {
field : parsedField ,
2019-09-27 21:51:53 +00:00
decoder : kubernetesscheme . Codecs . UniversalDecoder ( ) ,
2019-01-12 04:58:27 +00:00
objects : objects ,
}
}
2019-08-30 18:33:25 +00:00
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
}
2020-03-26 21:07:15 +00:00
req . SetHeader ( "Accept" , strings . Join ( [ ] string {
fmt . Sprintf ( "application/json;as=Table;v=%s;g=%s" , metav1 . SchemeGroupVersion . Version , metav1 . GroupName ) ,
fmt . Sprintf ( "application/json;as=Table;v=%s;g=%s" , metav1beta1 . SchemeGroupVersion . Version , metav1beta1 . GroupName ) ,
"application/json" ,
} , "," ) )
2019-08-30 18:33:25 +00:00
// if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
if o . Sort {
req . Param ( "includeObject" , "Object" )
}
}
2019-01-12 04:58:27 +00:00
// 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 {
2019-09-27 21:51:53 +00:00
restClient , err := f . RESTClient ( )
if err != nil {
return err
}
return rawhttp . RawGet ( restClient , o . IOStreams , o . Raw )
2019-01-12 04:58:27 +00:00
}
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 ) .
RequestChunksOf ( chunkSize ) .
ResourceTypeOrNameArgs ( true , args ... ) .
ContinueOnError ( ) .
Latest ( ) .
Flatten ( ) .
2019-08-30 18:33:25 +00:00
TransformRequests ( o . transformRequests ) .
2019-01-12 04:58:27 +00:00
Do ( )
if o . IgnoreNotFound {
2020-03-26 21:07:15 +00:00
r . IgnoreErrors ( apierrors . IsNotFound )
2019-01-12 04:58:27 +00:00
}
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
2019-08-30 18:33:25 +00:00
// track if we write any output
trackingWriter := & trackingWriterWrapper { Delegate : o . Out }
2019-09-27 21:51:53 +00:00
// output an empty line separating output
separatorWriter := & separatorWriterWrapper { Delegate : trackingWriter }
2019-08-30 18:33:25 +00:00
2019-12-12 01:27:03 +00:00
w := printers . GetNewTabWriter ( separatorWriter )
2020-08-10 17:43:49 +00:00
allResourcesNamespaced := ! o . AllNamespaces
2019-01-12 04:58:27 +00:00
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
}
2020-08-10 17:43:49 +00:00
allResourcesNamespaced = allResourcesNamespaced && info . Namespaced ( )
2019-01-12 04:58:27 +00:00
printWithNamespace := o . AllNamespaces
if mapping != nil && mapping . Scope . Name ( ) == meta . RESTScopeNameRoot {
printWithNamespace = false
}
if shouldGetNewPrinterForMapping ( printer , lastMapping , mapping ) {
w . Flush ( )
2019-04-07 17:07:55 +00:00
w . SetRememberedWidths ( nil )
2019-01-12 04:58:27 +00:00
2019-09-27 21:51:53 +00:00
// add linebreaks between resource groups (if there is more than one)
// when it satisfies all following 3 conditions:
// 1) it's not the first resource group
// 2) it has row header
// 3) we've written output since the last time we started a new set of headers
if lastMapping != nil && ! o . NoHeaders && trackingWriter . Written > 0 {
separatorWriter . SetReady ( true )
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
printer , err = o . ToPrinter ( mapping , nil , printWithNamespace , printWithKind )
2019-01-12 04:58:27 +00:00
if err != nil {
if ! errs . Has ( err . Error ( ) ) {
errs . Insert ( err . Error ( ) )
allErrs = append ( allErrs , err )
}
continue
}
lastMapping = mapping
}
2019-08-30 18:33:25 +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
}
2019-09-27 21:51:53 +00:00
printer . PrintObj ( info . Object , w )
2019-01-12 04:58:27 +00:00
}
w . Flush ( )
2019-08-30 18:33:25 +00:00
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
2020-08-10 17:43:49 +00:00
if allResourcesNamespaced {
2020-12-01 01:06:26 +00:00
fmt . Fprintf ( o . ErrOut , "No resources found in %s namespace.\n" , o . Namespace )
2019-09-27 21:51:53 +00:00
} else {
2020-12-01 01:06:26 +00:00
fmt . Fprintln ( o . ErrOut , "No resources found" )
2019-09-27 21:51:53 +00:00
}
2019-01-12 04:58:27 +00:00
}
return utilerrors . NewAggregate ( allErrs )
}
2019-08-30 18:33:25 +00:00
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 )
}
2019-09-27 21:51:53 +00:00
type separatorWriterWrapper struct {
Delegate io . Writer
Ready bool
}
2019-01-12 04:58:27 +00:00
2019-09-27 21:51:53 +00:00
func ( s * separatorWriterWrapper ) Write ( p [ ] byte ) ( n int , err error ) {
// If we're about to write non-empty bytes and `s` is ready,
// we prepend an empty line to `p` and reset `s.Read`.
if len ( p ) != 0 && s . Ready {
fmt . Fprintln ( s . Delegate )
s . Ready = false
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
return s . Delegate . Write ( p )
}
2019-01-12 04:58:27 +00:00
2019-09-27 21:51:53 +00:00
func ( s * separatorWriterWrapper ) SetReady ( state bool ) {
s . Ready = state
2019-01-12 04:58:27 +00:00
}
// 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 ) .
RequestChunksOf ( o . ChunkSize ) .
ResourceTypeOrNameArgs ( true , args ... ) .
SingleResourceType ( ) .
Latest ( ) .
2019-08-30 18:33:25 +00:00
TransformRequests ( o . transformRequests ) .
2019-01-12 04:58:27 +00:00
Do ( )
if err := r . Err ( ) ; err != nil {
return err
}
infos , err := r . Infos ( )
if err != nil {
return err
}
if multipleGVKsRequested ( infos ) {
2019-08-30 18:33:25 +00:00
return i18n . Errorf ( "watch is only supported on individual resources and resource collections - more than 1 resource was found" )
2019-01-12 04:58:27 +00:00
}
info := infos [ 0 ]
mapping := info . ResourceMapping ( )
2019-09-27 21:51:53 +00:00
outputObjects := utilpointer . BoolPtr ( ! o . WatchOnly )
printer , err := o . ToPrinter ( mapping , outputObjects , o . AllNamespaces , false )
2019-01-12 04:58:27 +00:00
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
}
}
2019-12-12 01:27:03 +00:00
writer := printers . GetNewTabWriter ( o . Out )
2019-04-07 17:07:55 +00:00
2019-01-12 04:58:27 +00:00
// print the current object
2019-09-27 21:51:53 +00:00
var objsToPrint [ ] runtime . Object
if isList {
objsToPrint , _ = meta . ExtractList ( obj )
} else {
objsToPrint = append ( objsToPrint , obj )
}
for _ , objToPrint := range objsToPrint {
if o . OutputWatchEvents {
objToPrint = & metav1 . WatchEvent { Type : string ( watch . Added ) , Object : runtime . RawExtension { Object : objToPrint } }
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
if err := printer . PrintObj ( objToPrint , writer ) ; err != nil {
return fmt . Errorf ( "unable to output the provided object: %v" , err )
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
}
writer . Flush ( )
if isList {
// we can start outputting objects now, watches started from lists don't emit synthetic added events
* outputObjects = true
} else {
// suppress output, since watches started for individual items emit a synthetic ADDED event first
* outputObjects = false
2019-01-12 04:58:27 +00:00
}
// print watched changes
w , err := r . Watch ( rv )
if err != nil {
return err
}
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 ) {
objToPrint := e . Object
2019-09-27 21:51:53 +00:00
if o . OutputWatchEvents {
objToPrint = & metav1 . WatchEvent { Type : string ( e . Type ) , Object : runtime . RawExtension { Object : objToPrint } }
2019-01-12 04:58:27 +00:00
}
2019-04-07 17:07:55 +00:00
if err := printer . PrintObj ( objToPrint , writer ) ; err != nil {
2019-01-12 04:58:27 +00:00
return false , err
}
2019-04-07 17:07:55 +00:00
writer . Flush ( )
2019-09-27 21:51:53 +00:00
// after processing at least one event, start outputting objects
* outputObjects = true
2019-01-12 04:58:27 +00:00
return false , nil
} )
return err
} )
return nil
}
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 ) ) )
}
2019-09-27 21:51:53 +00:00
printer , err := o . ToPrinter ( nil , nil , false , false )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
var obj runtime . Object
2019-09-27 21:51:53 +00:00
if ! singleItemImplied || len ( infos ) != 1 {
// we have zero or multple items, so coerce all items into a list.
2019-01-12 04:58:27 +00:00
// 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 ) {
2019-08-30 18:33:25 +00:00
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." )
2019-01-12 04:58:27 +00:00
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
}
2019-08-30 18:33:25 +00:00
func cmdSpecifiesOutputFmt ( cmd * cobra . Command ) bool {
return cmdutil . GetFlagString ( cmd , "output" ) != ""
2019-01-12 04:58:27 +00:00
}
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
}
2021-07-02 08:43:15 +00:00
// CompGetResource gets the list of the resource specified which begin with `toComplete`.
func CompGetResource ( f cmdutil . Factory , cmd * cobra . Command , resourceName string , toComplete string ) [ ] string {
template := "{{ range .items }}{{ .metadata.name }} {{ end }}"
return CompGetFromTemplate ( & template , f , "" , cmd , [ ] string { resourceName } , toComplete )
}
// CompGetContainers gets the list of containers of the specified pod which begin with `toComplete`.
func CompGetContainers ( f cmdutil . Factory , cmd * cobra . Command , podName string , toComplete string ) [ ] string {
template := "{{ range .spec.initContainers }}{{ .name }} {{end}}{{ range .spec.containers }}{{ .name }} {{ end }}"
return CompGetFromTemplate ( & template , f , "" , cmd , [ ] string { "pod" , podName } , toComplete )
}
// CompGetFromTemplate executes a Get operation using the specified template and args and returns the results
// which begin with `toComplete`.
func CompGetFromTemplate ( template * string , f cmdutil . Factory , namespace string , cmd * cobra . Command , args [ ] string , toComplete string ) [ ] string {
buf := new ( bytes . Buffer )
streams := genericclioptions . IOStreams { In : os . Stdin , Out : buf , ErrOut : ioutil . Discard }
o := NewGetOptions ( "kubectl" , streams )
// Get the list of names of the specified resource
o . PrintFlags . TemplateFlags . GoTemplatePrintFlags . TemplateArgument = template
format := "go-template"
o . PrintFlags . OutputFormat = & format
// Do the steps Complete() would have done.
// We cannot actually call Complete() or Validate() as these function check for
// the presence of flags, which, in our case won't be there
if namespace != "" {
o . Namespace = namespace
o . ExplicitNamespace = true
} else {
var err error
o . Namespace , o . ExplicitNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
if err != nil {
return nil
}
}
o . ToPrinter = func ( mapping * meta . RESTMapping , outputObjects * bool , withNamespace bool , withKind bool ) ( printers . ResourcePrinterFunc , error ) {
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return nil , err
}
return printer . PrintObj , nil
}
o . Run ( f , cmd , args )
var comps [ ] string
resources := strings . Split ( buf . String ( ) , " " )
for _ , res := range resources {
if res != "" && strings . HasPrefix ( res , toComplete ) {
comps = append ( comps , res )
}
}
return comps
}