...

Source file src/k8s.io/kubectl/pkg/describe/describe.go

Documentation: k8s.io/kubectl/pkg/describe

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package describe
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/x509"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  	"net/url"
    27  	"reflect"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  	"text/tabwriter"
    32  	"time"
    33  	"unicode"
    34  
    35  	"github.com/fatih/camelcase"
    36  	appsv1 "k8s.io/api/apps/v1"
    37  	autoscalingv1 "k8s.io/api/autoscaling/v1"
    38  	autoscalingv2 "k8s.io/api/autoscaling/v2"
    39  	batchv1 "k8s.io/api/batch/v1"
    40  	batchv1beta1 "k8s.io/api/batch/v1beta1"
    41  	certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
    42  	coordinationv1 "k8s.io/api/coordination/v1"
    43  	corev1 "k8s.io/api/core/v1"
    44  	discoveryv1 "k8s.io/api/discovery/v1"
    45  	discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
    46  	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
    47  	networkingv1 "k8s.io/api/networking/v1"
    48  	networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
    49  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    50  	policyv1 "k8s.io/api/policy/v1"
    51  	policyv1beta1 "k8s.io/api/policy/v1beta1"
    52  	rbacv1 "k8s.io/api/rbac/v1"
    53  	schedulingv1 "k8s.io/api/scheduling/v1"
    54  	storagev1 "k8s.io/api/storage/v1"
    55  	storagev1alpha1 "k8s.io/api/storage/v1alpha1"
    56  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    57  	"k8s.io/apimachinery/pkg/api/meta"
    58  	"k8s.io/apimachinery/pkg/api/resource"
    59  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    60  	"k8s.io/apimachinery/pkg/fields"
    61  	"k8s.io/apimachinery/pkg/labels"
    62  	"k8s.io/apimachinery/pkg/runtime"
    63  	"k8s.io/apimachinery/pkg/runtime/schema"
    64  	"k8s.io/apimachinery/pkg/types"
    65  	"k8s.io/apimachinery/pkg/util/duration"
    66  	"k8s.io/apimachinery/pkg/util/intstr"
    67  	"k8s.io/apimachinery/pkg/util/sets"
    68  	"k8s.io/cli-runtime/pkg/genericclioptions"
    69  	"k8s.io/cli-runtime/pkg/printers"
    70  	runtimeresource "k8s.io/cli-runtime/pkg/resource"
    71  	"k8s.io/client-go/dynamic"
    72  	clientset "k8s.io/client-go/kubernetes"
    73  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    74  	"k8s.io/client-go/rest"
    75  	"k8s.io/client-go/tools/reference"
    76  	utilcsr "k8s.io/client-go/util/certificate/csr"
    77  	"k8s.io/klog/v2"
    78  	"k8s.io/kubectl/pkg/scheme"
    79  	"k8s.io/kubectl/pkg/util/certificate"
    80  	deploymentutil "k8s.io/kubectl/pkg/util/deployment"
    81  	"k8s.io/kubectl/pkg/util/event"
    82  	"k8s.io/kubectl/pkg/util/fieldpath"
    83  	"k8s.io/kubectl/pkg/util/qos"
    84  	"k8s.io/kubectl/pkg/util/rbac"
    85  	resourcehelper "k8s.io/kubectl/pkg/util/resource"
    86  	"k8s.io/kubectl/pkg/util/slice"
    87  	storageutil "k8s.io/kubectl/pkg/util/storage"
    88  )
    89  
    90  // Each level has 2 spaces for PrefixWriter
    91  const (
    92  	LEVEL_0 = iota
    93  	LEVEL_1
    94  	LEVEL_2
    95  	LEVEL_3
    96  	LEVEL_4
    97  )
    98  
    99  var (
   100  	// globally skipped annotations
   101  	skipAnnotations = sets.NewString(corev1.LastAppliedConfigAnnotation)
   102  
   103  	// DescriberFn gives a way to easily override the function for unit testing if needed
   104  	DescriberFn DescriberFunc = Describer
   105  )
   106  
   107  // Describer returns a Describer for displaying the specified RESTMapping type or an error.
   108  func Describer(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping) (ResourceDescriber, error) {
   109  	clientConfig, err := restClientGetter.ToRESTConfig()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	// try to get a describer
   114  	if describer, ok := DescriberFor(mapping.GroupVersionKind.GroupKind(), clientConfig); ok {
   115  		return describer, nil
   116  	}
   117  	// if this is a kind we don't have a describer for yet, go generic if possible
   118  	if genericDescriber, ok := GenericDescriberFor(mapping, clientConfig); ok {
   119  		return genericDescriber, nil
   120  	}
   121  	// otherwise return an unregistered error
   122  	return nil, fmt.Errorf("no description has been implemented for %s", mapping.GroupVersionKind.String())
   123  }
   124  
   125  // PrefixWriter can write text at various indentation levels.
   126  type PrefixWriter interface {
   127  	// Write writes text with the specified indentation level.
   128  	Write(level int, format string, a ...interface{})
   129  	// WriteLine writes an entire line with no indentation level.
   130  	WriteLine(a ...interface{})
   131  	// Flush forces indentation to be reset.
   132  	Flush()
   133  }
   134  
   135  // prefixWriter implements PrefixWriter
   136  type prefixWriter struct {
   137  	out io.Writer
   138  }
   139  
   140  var _ PrefixWriter = &prefixWriter{}
   141  
   142  // NewPrefixWriter creates a new PrefixWriter.
   143  func NewPrefixWriter(out io.Writer) PrefixWriter {
   144  	return &prefixWriter{out: out}
   145  }
   146  
   147  func (pw *prefixWriter) Write(level int, format string, a ...interface{}) {
   148  	levelSpace := "  "
   149  	prefix := ""
   150  	for i := 0; i < level; i++ {
   151  		prefix += levelSpace
   152  	}
   153  	output := fmt.Sprintf(prefix+format, a...)
   154  	printers.WriteEscaped(pw.out, output)
   155  }
   156  
   157  func (pw *prefixWriter) WriteLine(a ...interface{}) {
   158  	output := fmt.Sprintln(a...)
   159  	printers.WriteEscaped(pw.out, output)
   160  }
   161  
   162  func (pw *prefixWriter) Flush() {
   163  	if f, ok := pw.out.(flusher); ok {
   164  		f.Flush()
   165  	}
   166  }
   167  
   168  // nestedPrefixWriter implements PrefixWriter by increasing the level
   169  // before passing text on to some other writer.
   170  type nestedPrefixWriter struct {
   171  	PrefixWriter
   172  	indent int
   173  }
   174  
   175  var _ PrefixWriter = &prefixWriter{}
   176  
   177  // NewPrefixWriter creates a new PrefixWriter.
   178  func NewNestedPrefixWriter(out PrefixWriter, indent int) PrefixWriter {
   179  	return &nestedPrefixWriter{PrefixWriter: out, indent: indent}
   180  }
   181  
   182  func (npw *nestedPrefixWriter) Write(level int, format string, a ...interface{}) {
   183  	npw.PrefixWriter.Write(level+npw.indent, format, a...)
   184  }
   185  
   186  func (npw *nestedPrefixWriter) WriteLine(a ...interface{}) {
   187  	npw.PrefixWriter.Write(npw.indent, "%s", fmt.Sprintln(a...))
   188  }
   189  
   190  func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescriber, error) {
   191  	c, err := clientset.NewForConfig(clientConfig)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	m := map[schema.GroupKind]ResourceDescriber{
   197  		{Group: corev1.GroupName, Kind: "Pod"}:                                    &PodDescriber{c},
   198  		{Group: corev1.GroupName, Kind: "ReplicationController"}:                  &ReplicationControllerDescriber{c},
   199  		{Group: corev1.GroupName, Kind: "Secret"}:                                 &SecretDescriber{c},
   200  		{Group: corev1.GroupName, Kind: "Service"}:                                &ServiceDescriber{c},
   201  		{Group: corev1.GroupName, Kind: "ServiceAccount"}:                         &ServiceAccountDescriber{c},
   202  		{Group: corev1.GroupName, Kind: "Node"}:                                   &NodeDescriber{c},
   203  		{Group: corev1.GroupName, Kind: "LimitRange"}:                             &LimitRangeDescriber{c},
   204  		{Group: corev1.GroupName, Kind: "ResourceQuota"}:                          &ResourceQuotaDescriber{c},
   205  		{Group: corev1.GroupName, Kind: "PersistentVolume"}:                       &PersistentVolumeDescriber{c},
   206  		{Group: corev1.GroupName, Kind: "PersistentVolumeClaim"}:                  &PersistentVolumeClaimDescriber{c},
   207  		{Group: corev1.GroupName, Kind: "Namespace"}:                              &NamespaceDescriber{c},
   208  		{Group: corev1.GroupName, Kind: "Endpoints"}:                              &EndpointsDescriber{c},
   209  		{Group: corev1.GroupName, Kind: "ConfigMap"}:                              &ConfigMapDescriber{c},
   210  		{Group: corev1.GroupName, Kind: "PriorityClass"}:                          &PriorityClassDescriber{c},
   211  		{Group: discoveryv1beta1.GroupName, Kind: "EndpointSlice"}:                &EndpointSliceDescriber{c},
   212  		{Group: discoveryv1.GroupName, Kind: "EndpointSlice"}:                     &EndpointSliceDescriber{c},
   213  		{Group: autoscalingv2.GroupName, Kind: "HorizontalPodAutoscaler"}:         &HorizontalPodAutoscalerDescriber{c},
   214  		{Group: extensionsv1beta1.GroupName, Kind: "Ingress"}:                     &IngressDescriber{c},
   215  		{Group: networkingv1beta1.GroupName, Kind: "Ingress"}:                     &IngressDescriber{c},
   216  		{Group: networkingv1beta1.GroupName, Kind: "IngressClass"}:                &IngressClassDescriber{c},
   217  		{Group: networkingv1.GroupName, Kind: "Ingress"}:                          &IngressDescriber{c},
   218  		{Group: networkingv1.GroupName, Kind: "IngressClass"}:                     &IngressClassDescriber{c},
   219  		{Group: networkingv1alpha1.GroupName, Kind: "ServiceCIDR"}:                &ServiceCIDRDescriber{c},
   220  		{Group: networkingv1alpha1.GroupName, Kind: "IPAddress"}:                  &IPAddressDescriber{c},
   221  		{Group: batchv1.GroupName, Kind: "Job"}:                                   &JobDescriber{c},
   222  		{Group: batchv1.GroupName, Kind: "CronJob"}:                               &CronJobDescriber{c},
   223  		{Group: batchv1beta1.GroupName, Kind: "CronJob"}:                          &CronJobDescriber{c},
   224  		{Group: appsv1.GroupName, Kind: "StatefulSet"}:                            &StatefulSetDescriber{c},
   225  		{Group: appsv1.GroupName, Kind: "Deployment"}:                             &DeploymentDescriber{c},
   226  		{Group: appsv1.GroupName, Kind: "DaemonSet"}:                              &DaemonSetDescriber{c},
   227  		{Group: appsv1.GroupName, Kind: "ReplicaSet"}:                             &ReplicaSetDescriber{c},
   228  		{Group: certificatesv1beta1.GroupName, Kind: "CertificateSigningRequest"}: &CertificateSigningRequestDescriber{c},
   229  		{Group: storagev1.GroupName, Kind: "StorageClass"}:                        &StorageClassDescriber{c},
   230  		{Group: storagev1.GroupName, Kind: "CSINode"}:                             &CSINodeDescriber{c},
   231  		{Group: storagev1alpha1.GroupName, Kind: "VolumeAttributesClass"}:         &VolumeAttributesClassDescriber{c},
   232  		{Group: policyv1beta1.GroupName, Kind: "PodDisruptionBudget"}:             &PodDisruptionBudgetDescriber{c},
   233  		{Group: policyv1.GroupName, Kind: "PodDisruptionBudget"}:                  &PodDisruptionBudgetDescriber{c},
   234  		{Group: rbacv1.GroupName, Kind: "Role"}:                                   &RoleDescriber{c},
   235  		{Group: rbacv1.GroupName, Kind: "ClusterRole"}:                            &ClusterRoleDescriber{c},
   236  		{Group: rbacv1.GroupName, Kind: "RoleBinding"}:                            &RoleBindingDescriber{c},
   237  		{Group: rbacv1.GroupName, Kind: "ClusterRoleBinding"}:                     &ClusterRoleBindingDescriber{c},
   238  		{Group: networkingv1.GroupName, Kind: "NetworkPolicy"}:                    &NetworkPolicyDescriber{c},
   239  		{Group: schedulingv1.GroupName, Kind: "PriorityClass"}:                    &PriorityClassDescriber{c},
   240  	}
   241  
   242  	return m, nil
   243  }
   244  
   245  // DescriberFor returns the default describe functions for each of the standard
   246  // Kubernetes types.
   247  func DescriberFor(kind schema.GroupKind, clientConfig *rest.Config) (ResourceDescriber, bool) {
   248  	describers, err := describerMap(clientConfig)
   249  	if err != nil {
   250  		klog.V(1).Info(err)
   251  		return nil, false
   252  	}
   253  
   254  	f, ok := describers[kind]
   255  	return f, ok
   256  }
   257  
   258  // GenericDescriberFor returns a generic describer for the specified mapping
   259  // that uses only information available from runtime.Unstructured
   260  func GenericDescriberFor(mapping *meta.RESTMapping, clientConfig *rest.Config) (ResourceDescriber, bool) {
   261  	// used to fetch the resource
   262  	dynamicClient, err := dynamic.NewForConfig(clientConfig)
   263  	if err != nil {
   264  		return nil, false
   265  	}
   266  
   267  	// used to get events for the resource
   268  	clientSet, err := clientset.NewForConfig(clientConfig)
   269  	if err != nil {
   270  		return nil, false
   271  	}
   272  	eventsClient := clientSet.CoreV1()
   273  
   274  	return &genericDescriber{mapping, dynamicClient, eventsClient}, true
   275  }
   276  
   277  type genericDescriber struct {
   278  	mapping *meta.RESTMapping
   279  	dynamic dynamic.Interface
   280  	events  corev1client.EventsGetter
   281  }
   282  
   283  func (g *genericDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) {
   284  	obj, err := g.dynamic.Resource(g.mapping.Resource).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   285  	if err != nil {
   286  		return "", err
   287  	}
   288  
   289  	var events *corev1.EventList
   290  	if describerSettings.ShowEvents {
   291  		events, _ = searchEvents(g.events, obj, describerSettings.ChunkSize)
   292  	}
   293  
   294  	return tabbedString(func(out io.Writer) error {
   295  		w := NewPrefixWriter(out)
   296  		w.Write(LEVEL_0, "Name:\t%s\n", obj.GetName())
   297  		w.Write(LEVEL_0, "Namespace:\t%s\n", obj.GetNamespace())
   298  		printLabelsMultiline(w, "Labels", obj.GetLabels())
   299  		printAnnotationsMultiline(w, "Annotations", obj.GetAnnotations())
   300  		printUnstructuredContent(w, LEVEL_0, obj.UnstructuredContent(), "", ".metadata.managedFields", ".metadata.name",
   301  			".metadata.namespace", ".metadata.labels", ".metadata.annotations")
   302  		if events != nil {
   303  			DescribeEvents(events, w)
   304  		}
   305  		return nil
   306  	})
   307  }
   308  
   309  func printUnstructuredContent(w PrefixWriter, level int, content map[string]interface{}, skipPrefix string, skip ...string) {
   310  	fields := []string{}
   311  	for field := range content {
   312  		fields = append(fields, field)
   313  	}
   314  	sort.Strings(fields)
   315  
   316  	for _, field := range fields {
   317  		value := content[field]
   318  		switch typedValue := value.(type) {
   319  		case map[string]interface{}:
   320  			skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
   321  			if slice.ContainsString(skip, skipExpr, nil) {
   322  				continue
   323  			}
   324  			w.Write(level, "%s:\n", smartLabelFor(field))
   325  			printUnstructuredContent(w, level+1, typedValue, skipExpr, skip...)
   326  
   327  		case []interface{}:
   328  			skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
   329  			if slice.ContainsString(skip, skipExpr, nil) {
   330  				continue
   331  			}
   332  			w.Write(level, "%s:\n", smartLabelFor(field))
   333  			for _, child := range typedValue {
   334  				switch typedChild := child.(type) {
   335  				case map[string]interface{}:
   336  					printUnstructuredContent(w, level+1, typedChild, skipExpr, skip...)
   337  				default:
   338  					w.Write(level+1, "%v\n", typedChild)
   339  				}
   340  			}
   341  
   342  		default:
   343  			skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
   344  			if slice.ContainsString(skip, skipExpr, nil) {
   345  				continue
   346  			}
   347  			w.Write(level, "%s:\t%v\n", smartLabelFor(field), typedValue)
   348  		}
   349  	}
   350  }
   351  
   352  func smartLabelFor(field string) string {
   353  	// skip creating smart label if field name contains
   354  	// special characters other than '-'
   355  	if strings.IndexFunc(field, func(r rune) bool {
   356  		return !unicode.IsLetter(r) && r != '-'
   357  	}) != -1 {
   358  		return field
   359  	}
   360  
   361  	commonAcronyms := []string{"API", "URL", "UID", "OSB", "GUID"}
   362  	parts := camelcase.Split(field)
   363  	result := make([]string, 0, len(parts))
   364  	for _, part := range parts {
   365  		if part == "_" {
   366  			continue
   367  		}
   368  
   369  		if slice.ContainsString(commonAcronyms, strings.ToUpper(part), nil) {
   370  			part = strings.ToUpper(part)
   371  		} else {
   372  			part = strings.Title(part)
   373  		}
   374  		result = append(result, part)
   375  	}
   376  
   377  	return strings.Join(result, " ")
   378  }
   379  
   380  // DefaultObjectDescriber can describe the default Kubernetes objects.
   381  var DefaultObjectDescriber ObjectDescriber
   382  
   383  func init() {
   384  	d := &Describers{}
   385  	err := d.Add(
   386  		describeCertificateSigningRequest,
   387  		describeCronJob,
   388  		describeCSINode,
   389  		describeDaemonSet,
   390  		describeDeployment,
   391  		describeEndpoints,
   392  		describeEndpointSliceV1,
   393  		describeEndpointSliceV1beta1,
   394  		describeHorizontalPodAutoscalerV1,
   395  		describeHorizontalPodAutoscalerV2,
   396  		describeJob,
   397  		describeLimitRange,
   398  		describeNamespace,
   399  		describeNetworkPolicy,
   400  		describeNode,
   401  		describePersistentVolume,
   402  		describePersistentVolumeClaim,
   403  		describePod,
   404  		describePodDisruptionBudgetV1,
   405  		describePodDisruptionBudgetV1beta1,
   406  		describePriorityClass,
   407  		describeQuota,
   408  		describeReplicaSet,
   409  		describeReplicationController,
   410  		describeSecret,
   411  		describeService,
   412  		describeServiceAccount,
   413  		describeStatefulSet,
   414  		describeStorageClass,
   415  		describeVolumeAttributesClass,
   416  	)
   417  	if err != nil {
   418  		klog.Fatalf("Cannot register describers: %v", err)
   419  	}
   420  	DefaultObjectDescriber = d
   421  }
   422  
   423  // NamespaceDescriber generates information about a namespace
   424  type NamespaceDescriber struct {
   425  	clientset.Interface
   426  }
   427  
   428  func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
   429  	ns, err := d.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
   430  	if err != nil {
   431  		return "", err
   432  	}
   433  
   434  	resourceQuotaList := &corev1.ResourceQuotaList{}
   435  	err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
   436  		func(options metav1.ListOptions) (runtime.Object, error) {
   437  			newList, err := d.CoreV1().ResourceQuotas(name).List(context.TODO(), options)
   438  			if err != nil {
   439  				return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceQuotas.String())
   440  			}
   441  			resourceQuotaList.Items = append(resourceQuotaList.Items, newList.Items...)
   442  			return newList, nil
   443  		})
   444  	if err != nil {
   445  		if apierrors.IsNotFound(err) {
   446  			// Server does not support resource quotas.
   447  			// Not an error, will not show resource quotas information.
   448  			resourceQuotaList = nil
   449  		} else {
   450  			return "", err
   451  		}
   452  	}
   453  
   454  	limitRangeList := &corev1.LimitRangeList{}
   455  	err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
   456  		func(options metav1.ListOptions) (runtime.Object, error) {
   457  			newList, err := d.CoreV1().LimitRanges(name).List(context.TODO(), options)
   458  			if err != nil {
   459  				return nil, runtimeresource.EnhanceListError(err, options, "limitranges")
   460  			}
   461  			limitRangeList.Items = append(limitRangeList.Items, newList.Items...)
   462  			return newList, nil
   463  		})
   464  	if err != nil {
   465  		if apierrors.IsNotFound(err) {
   466  			// Server does not support limit ranges.
   467  			// Not an error, will not show limit ranges information.
   468  			limitRangeList = nil
   469  		} else {
   470  			return "", err
   471  		}
   472  	}
   473  	return describeNamespace(ns, resourceQuotaList, limitRangeList)
   474  }
   475  
   476  func describeNamespace(namespace *corev1.Namespace, resourceQuotaList *corev1.ResourceQuotaList, limitRangeList *corev1.LimitRangeList) (string, error) {
   477  	return tabbedString(func(out io.Writer) error {
   478  		w := NewPrefixWriter(out)
   479  		w.Write(LEVEL_0, "Name:\t%s\n", namespace.Name)
   480  		printLabelsMultiline(w, "Labels", namespace.Labels)
   481  		printAnnotationsMultiline(w, "Annotations", namespace.Annotations)
   482  		w.Write(LEVEL_0, "Status:\t%s\n", string(namespace.Status.Phase))
   483  
   484  		if len(namespace.Status.Conditions) > 0 {
   485  			w.Write(LEVEL_0, "Conditions:\n")
   486  			w.Write(LEVEL_1, "Type\tStatus\tLastTransitionTime\tReason\tMessage\n")
   487  			w.Write(LEVEL_1, "----\t------\t------------------\t------\t-------\n")
   488  			for _, c := range namespace.Status.Conditions {
   489  				w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
   490  					c.Type,
   491  					c.Status,
   492  					c.LastTransitionTime.Time.Format(time.RFC1123Z),
   493  					c.Reason,
   494  					c.Message)
   495  			}
   496  		}
   497  
   498  		if resourceQuotaList != nil {
   499  			w.Write(LEVEL_0, "\n")
   500  			DescribeResourceQuotas(resourceQuotaList, w)
   501  		}
   502  
   503  		if limitRangeList != nil {
   504  			w.Write(LEVEL_0, "\n")
   505  			DescribeLimitRanges(limitRangeList, w)
   506  		}
   507  
   508  		return nil
   509  	})
   510  }
   511  
   512  func describeLimitRangeSpec(spec corev1.LimitRangeSpec, prefix string, w PrefixWriter) {
   513  	for i := range spec.Limits {
   514  		item := spec.Limits[i]
   515  		maxResources := item.Max
   516  		minResources := item.Min
   517  		defaultLimitResources := item.Default
   518  		defaultRequestResources := item.DefaultRequest
   519  		ratio := item.MaxLimitRequestRatio
   520  
   521  		set := map[corev1.ResourceName]bool{}
   522  		for k := range maxResources {
   523  			set[k] = true
   524  		}
   525  		for k := range minResources {
   526  			set[k] = true
   527  		}
   528  		for k := range defaultLimitResources {
   529  			set[k] = true
   530  		}
   531  		for k := range defaultRequestResources {
   532  			set[k] = true
   533  		}
   534  		for k := range ratio {
   535  			set[k] = true
   536  		}
   537  
   538  		for k := range set {
   539  			// if no value is set, we output -
   540  			maxValue := "-"
   541  			minValue := "-"
   542  			defaultLimitValue := "-"
   543  			defaultRequestValue := "-"
   544  			ratioValue := "-"
   545  
   546  			maxQuantity, maxQuantityFound := maxResources[k]
   547  			if maxQuantityFound {
   548  				maxValue = maxQuantity.String()
   549  			}
   550  
   551  			minQuantity, minQuantityFound := minResources[k]
   552  			if minQuantityFound {
   553  				minValue = minQuantity.String()
   554  			}
   555  
   556  			defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
   557  			if defaultLimitQuantityFound {
   558  				defaultLimitValue = defaultLimitQuantity.String()
   559  			}
   560  
   561  			defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
   562  			if defaultRequestQuantityFound {
   563  				defaultRequestValue = defaultRequestQuantity.String()
   564  			}
   565  
   566  			ratioQuantity, ratioQuantityFound := ratio[k]
   567  			if ratioQuantityFound {
   568  				ratioValue = ratioQuantity.String()
   569  			}
   570  
   571  			msg := "%s%s\t%v\t%v\t%v\t%v\t%v\t%v\n"
   572  			w.Write(LEVEL_0, msg, prefix, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
   573  		}
   574  	}
   575  }
   576  
   577  // DescribeLimitRanges merges a set of limit range items into a single tabular description
   578  func DescribeLimitRanges(limitRanges *corev1.LimitRangeList, w PrefixWriter) {
   579  	if len(limitRanges.Items) == 0 {
   580  		w.Write(LEVEL_0, "No LimitRange resource.\n")
   581  		return
   582  	}
   583  	w.Write(LEVEL_0, "Resource Limits\n Type\tResource\tMin\tMax\tDefault Request\tDefault Limit\tMax Limit/Request Ratio\n")
   584  	w.Write(LEVEL_0, " ----\t--------\t---\t---\t---------------\t-------------\t-----------------------\n")
   585  	for _, limitRange := range limitRanges.Items {
   586  		describeLimitRangeSpec(limitRange.Spec, " ", w)
   587  	}
   588  }
   589  
   590  // DescribeResourceQuotas merges a set of quota items into a single tabular description of all quotas
   591  func DescribeResourceQuotas(quotas *corev1.ResourceQuotaList, w PrefixWriter) {
   592  	if len(quotas.Items) == 0 {
   593  		w.Write(LEVEL_0, "No resource quota.\n")
   594  		return
   595  	}
   596  	sort.Sort(SortableResourceQuotas(quotas.Items))
   597  
   598  	w.Write(LEVEL_0, "Resource Quotas\n")
   599  	for _, q := range quotas.Items {
   600  		w.Write(LEVEL_1, "Name:\t%s\n", q.Name)
   601  		if len(q.Spec.Scopes) > 0 {
   602  			scopes := make([]string, 0, len(q.Spec.Scopes))
   603  			for _, scope := range q.Spec.Scopes {
   604  				scopes = append(scopes, string(scope))
   605  			}
   606  			sort.Strings(scopes)
   607  			w.Write(LEVEL_1, "Scopes:\t%s\n", strings.Join(scopes, ", "))
   608  			for _, scope := range scopes {
   609  				helpText := helpTextForResourceQuotaScope(corev1.ResourceQuotaScope(scope))
   610  				if len(helpText) > 0 {
   611  					w.Write(LEVEL_1, "* %s\n", helpText)
   612  				}
   613  			}
   614  		}
   615  
   616  		w.Write(LEVEL_1, "Resource\tUsed\tHard\n")
   617  		w.Write(LEVEL_1, "--------\t---\t---\n")
   618  
   619  		resources := make([]corev1.ResourceName, 0, len(q.Status.Hard))
   620  		for resource := range q.Status.Hard {
   621  			resources = append(resources, resource)
   622  		}
   623  		sort.Sort(SortableResourceNames(resources))
   624  
   625  		for _, resource := range resources {
   626  			hardQuantity := q.Status.Hard[resource]
   627  			usedQuantity := q.Status.Used[resource]
   628  			w.Write(LEVEL_1, "%s\t%s\t%s\n", string(resource), usedQuantity.String(), hardQuantity.String())
   629  		}
   630  	}
   631  }
   632  
   633  // LimitRangeDescriber generates information about a limit range
   634  type LimitRangeDescriber struct {
   635  	clientset.Interface
   636  }
   637  
   638  func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
   639  	lr := d.CoreV1().LimitRanges(namespace)
   640  
   641  	limitRange, err := lr.Get(context.TODO(), name, metav1.GetOptions{})
   642  	if err != nil {
   643  		return "", err
   644  	}
   645  	return describeLimitRange(limitRange)
   646  }
   647  
   648  func describeLimitRange(limitRange *corev1.LimitRange) (string, error) {
   649  	return tabbedString(func(out io.Writer) error {
   650  		w := NewPrefixWriter(out)
   651  		w.Write(LEVEL_0, "Name:\t%s\n", limitRange.Name)
   652  		w.Write(LEVEL_0, "Namespace:\t%s\n", limitRange.Namespace)
   653  		w.Write(LEVEL_0, "Type\tResource\tMin\tMax\tDefault Request\tDefault Limit\tMax Limit/Request Ratio\n")
   654  		w.Write(LEVEL_0, "----\t--------\t---\t---\t---------------\t-------------\t-----------------------\n")
   655  		describeLimitRangeSpec(limitRange.Spec, "", w)
   656  		return nil
   657  	})
   658  }
   659  
   660  // ResourceQuotaDescriber generates information about a resource quota
   661  type ResourceQuotaDescriber struct {
   662  	clientset.Interface
   663  }
   664  
   665  func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
   666  	rq := d.CoreV1().ResourceQuotas(namespace)
   667  
   668  	resourceQuota, err := rq.Get(context.TODO(), name, metav1.GetOptions{})
   669  	if err != nil {
   670  		return "", err
   671  	}
   672  
   673  	return describeQuota(resourceQuota)
   674  }
   675  
   676  func helpTextForResourceQuotaScope(scope corev1.ResourceQuotaScope) string {
   677  	switch scope {
   678  	case corev1.ResourceQuotaScopeTerminating:
   679  		return "Matches all pods that have an active deadline. These pods have a limited lifespan on a node before being actively terminated by the system."
   680  	case corev1.ResourceQuotaScopeNotTerminating:
   681  		return "Matches all pods that do not have an active deadline. These pods usually include long running pods whose container command is not expected to terminate."
   682  	case corev1.ResourceQuotaScopeBestEffort:
   683  		return "Matches all pods that do not have resource requirements set. These pods have a best effort quality of service."
   684  	case corev1.ResourceQuotaScopeNotBestEffort:
   685  		return "Matches all pods that have at least one resource requirement set. These pods have a burstable or guaranteed quality of service."
   686  	default:
   687  		return ""
   688  	}
   689  }
   690  func describeQuota(resourceQuota *corev1.ResourceQuota) (string, error) {
   691  	return tabbedString(func(out io.Writer) error {
   692  		w := NewPrefixWriter(out)
   693  		w.Write(LEVEL_0, "Name:\t%s\n", resourceQuota.Name)
   694  		w.Write(LEVEL_0, "Namespace:\t%s\n", resourceQuota.Namespace)
   695  		if len(resourceQuota.Spec.Scopes) > 0 {
   696  			scopes := make([]string, 0, len(resourceQuota.Spec.Scopes))
   697  			for _, scope := range resourceQuota.Spec.Scopes {
   698  				scopes = append(scopes, string(scope))
   699  			}
   700  			sort.Strings(scopes)
   701  			w.Write(LEVEL_0, "Scopes:\t%s\n", strings.Join(scopes, ", "))
   702  			for _, scope := range scopes {
   703  				helpText := helpTextForResourceQuotaScope(corev1.ResourceQuotaScope(scope))
   704  				if len(helpText) > 0 {
   705  					w.Write(LEVEL_0, " * %s\n", helpText)
   706  				}
   707  			}
   708  		}
   709  		w.Write(LEVEL_0, "Resource\tUsed\tHard\n")
   710  		w.Write(LEVEL_0, "--------\t----\t----\n")
   711  
   712  		resources := make([]corev1.ResourceName, 0, len(resourceQuota.Status.Hard))
   713  		for resource := range resourceQuota.Status.Hard {
   714  			resources = append(resources, resource)
   715  		}
   716  		sort.Sort(SortableResourceNames(resources))
   717  
   718  		msg := "%v\t%v\t%v\n"
   719  		for i := range resources {
   720  			resourceName := resources[i]
   721  			hardQuantity := resourceQuota.Status.Hard[resourceName]
   722  			usedQuantity := resourceQuota.Status.Used[resourceName]
   723  			if hardQuantity.Format != usedQuantity.Format {
   724  				usedQuantity = *resource.NewQuantity(usedQuantity.Value(), hardQuantity.Format)
   725  			}
   726  			w.Write(LEVEL_0, msg, resourceName, usedQuantity.String(), hardQuantity.String())
   727  		}
   728  		return nil
   729  	})
   730  }
   731  
   732  // PodDescriber generates information about a pod and the replication controllers that
   733  // create it.
   734  type PodDescriber struct {
   735  	clientset.Interface
   736  }
   737  
   738  func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
   739  	pod, err := d.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   740  	if err != nil {
   741  		if describerSettings.ShowEvents {
   742  			eventsInterface := d.CoreV1().Events(namespace)
   743  			selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil)
   744  			initialOpts := metav1.ListOptions{
   745  				FieldSelector: selector.String(),
   746  				Limit:         describerSettings.ChunkSize,
   747  			}
   748  			events := &corev1.EventList{}
   749  			err2 := runtimeresource.FollowContinue(&initialOpts,
   750  				func(options metav1.ListOptions) (runtime.Object, error) {
   751  					newList, err := eventsInterface.List(context.TODO(), options)
   752  					if err != nil {
   753  						return nil, runtimeresource.EnhanceListError(err, options, "events")
   754  					}
   755  					events.Items = append(events.Items, newList.Items...)
   756  					return newList, nil
   757  				})
   758  
   759  			if err2 == nil && len(events.Items) > 0 {
   760  				return tabbedString(func(out io.Writer) error {
   761  					w := NewPrefixWriter(out)
   762  					w.Write(LEVEL_0, "Pod '%v': error '%v', but found events.\n", name, err)
   763  					DescribeEvents(events, w)
   764  					return nil
   765  				})
   766  			}
   767  		}
   768  		return "", err
   769  	}
   770  
   771  	var events *corev1.EventList
   772  	if describerSettings.ShowEvents {
   773  		if ref, err := reference.GetReference(scheme.Scheme, pod); err != nil {
   774  			klog.Errorf("Unable to construct reference to '%#v': %v", pod, err)
   775  		} else {
   776  			ref.Kind = ""
   777  			if _, isMirrorPod := pod.Annotations[corev1.MirrorPodAnnotationKey]; isMirrorPod {
   778  				ref.UID = types.UID(pod.Annotations[corev1.MirrorPodAnnotationKey])
   779  			}
   780  			events, _ = searchEvents(d.CoreV1(), ref, describerSettings.ChunkSize)
   781  		}
   782  	}
   783  
   784  	return describePod(pod, events)
   785  }
   786  
   787  func describePod(pod *corev1.Pod, events *corev1.EventList) (string, error) {
   788  	return tabbedString(func(out io.Writer) error {
   789  		w := NewPrefixWriter(out)
   790  		w.Write(LEVEL_0, "Name:\t%s\n", pod.Name)
   791  		w.Write(LEVEL_0, "Namespace:\t%s\n", pod.Namespace)
   792  		if pod.Spec.Priority != nil {
   793  			w.Write(LEVEL_0, "Priority:\t%d\n", *pod.Spec.Priority)
   794  		}
   795  		if len(pod.Spec.PriorityClassName) > 0 {
   796  			w.Write(LEVEL_0, "Priority Class Name:\t%s\n", pod.Spec.PriorityClassName)
   797  		}
   798  		if pod.Spec.RuntimeClassName != nil && len(*pod.Spec.RuntimeClassName) > 0 {
   799  			w.Write(LEVEL_0, "Runtime Class Name:\t%s\n", *pod.Spec.RuntimeClassName)
   800  		}
   801  		if len(pod.Spec.ServiceAccountName) > 0 {
   802  			w.Write(LEVEL_0, "Service Account:\t%s\n", pod.Spec.ServiceAccountName)
   803  		}
   804  		if pod.Spec.NodeName == "" {
   805  			w.Write(LEVEL_0, "Node:\t<none>\n")
   806  		} else {
   807  			w.Write(LEVEL_0, "Node:\t%s\n", pod.Spec.NodeName+"/"+pod.Status.HostIP)
   808  		}
   809  		if pod.Status.StartTime != nil {
   810  			w.Write(LEVEL_0, "Start Time:\t%s\n", pod.Status.StartTime.Time.Format(time.RFC1123Z))
   811  		}
   812  		printLabelsMultiline(w, "Labels", pod.Labels)
   813  		printAnnotationsMultiline(w, "Annotations", pod.Annotations)
   814  		if pod.DeletionTimestamp != nil {
   815  			w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampSince(*pod.DeletionTimestamp))
   816  			w.Write(LEVEL_0, "Termination Grace Period:\t%ds\n", *pod.DeletionGracePeriodSeconds)
   817  		} else {
   818  			w.Write(LEVEL_0, "Status:\t%s\n", string(pod.Status.Phase))
   819  		}
   820  		if len(pod.Status.Reason) > 0 {
   821  			w.Write(LEVEL_0, "Reason:\t%s\n", pod.Status.Reason)
   822  		}
   823  		if len(pod.Status.Message) > 0 {
   824  			w.Write(LEVEL_0, "Message:\t%s\n", pod.Status.Message)
   825  		}
   826  		if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SeccompProfile != nil {
   827  			w.Write(LEVEL_0, "SeccompProfile:\t%s\n", pod.Spec.SecurityContext.SeccompProfile.Type)
   828  			if pod.Spec.SecurityContext.SeccompProfile.Type == corev1.SeccompProfileTypeLocalhost {
   829  				w.Write(LEVEL_0, "LocalhostProfile:\t%s\n", *pod.Spec.SecurityContext.SeccompProfile.LocalhostProfile)
   830  			}
   831  		}
   832  		// remove when .IP field is deprecated
   833  		w.Write(LEVEL_0, "IP:\t%s\n", pod.Status.PodIP)
   834  		describePodIPs(pod, w, "")
   835  		if controlledBy := printController(pod); len(controlledBy) > 0 {
   836  			w.Write(LEVEL_0, "Controlled By:\t%s\n", controlledBy)
   837  		}
   838  		if len(pod.Status.NominatedNodeName) > 0 {
   839  			w.Write(LEVEL_0, "NominatedNodeName:\t%s\n", pod.Status.NominatedNodeName)
   840  		}
   841  
   842  		if len(pod.Spec.InitContainers) > 0 {
   843  			describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "")
   844  		}
   845  		describeContainers("Containers", pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(pod), w, "")
   846  		if len(pod.Spec.EphemeralContainers) > 0 {
   847  			var ec []corev1.Container
   848  			for i := range pod.Spec.EphemeralContainers {
   849  				ec = append(ec, corev1.Container(pod.Spec.EphemeralContainers[i].EphemeralContainerCommon))
   850  			}
   851  			describeContainers("Ephemeral Containers", ec, pod.Status.EphemeralContainerStatuses, EnvValueRetriever(pod), w, "")
   852  		}
   853  		if len(pod.Spec.ReadinessGates) > 0 {
   854  			w.Write(LEVEL_0, "Readiness Gates:\n  Type\tStatus\n")
   855  			for _, g := range pod.Spec.ReadinessGates {
   856  				status := "<none>"
   857  				for _, c := range pod.Status.Conditions {
   858  					if c.Type == g.ConditionType {
   859  						status = fmt.Sprintf("%v", c.Status)
   860  						break
   861  					}
   862  				}
   863  				w.Write(LEVEL_1, "%v \t%v \n",
   864  					g.ConditionType,
   865  					status)
   866  			}
   867  		}
   868  		if len(pod.Status.Conditions) > 0 {
   869  			w.Write(LEVEL_0, "Conditions:\n  Type\tStatus\n")
   870  			for _, c := range pod.Status.Conditions {
   871  				w.Write(LEVEL_1, "%v \t%v \n",
   872  					c.Type,
   873  					c.Status)
   874  			}
   875  		}
   876  		describeVolumes(pod.Spec.Volumes, w, "")
   877  		w.Write(LEVEL_0, "QoS Class:\t%s\n", qos.GetPodQOS(pod))
   878  		printLabelsMultiline(w, "Node-Selectors", pod.Spec.NodeSelector)
   879  		printPodTolerationsMultiline(w, "Tolerations", pod.Spec.Tolerations)
   880  		describeTopologySpreadConstraints(pod.Spec.TopologySpreadConstraints, w, "")
   881  		if events != nil {
   882  			DescribeEvents(events, w)
   883  		}
   884  		return nil
   885  	})
   886  }
   887  
   888  func printController(controllee metav1.Object) string {
   889  	if controllerRef := metav1.GetControllerOf(controllee); controllerRef != nil {
   890  		return fmt.Sprintf("%s/%s", controllerRef.Kind, controllerRef.Name)
   891  	}
   892  	return ""
   893  }
   894  
   895  func describePodIPs(pod *corev1.Pod, w PrefixWriter, space string) {
   896  	if len(pod.Status.PodIPs) == 0 {
   897  		w.Write(LEVEL_0, "%sIPs:\t<none>\n", space)
   898  		return
   899  	}
   900  	w.Write(LEVEL_0, "%sIPs:\n", space)
   901  	for _, ipInfo := range pod.Status.PodIPs {
   902  		w.Write(LEVEL_1, "IP:\t%s\n", ipInfo.IP)
   903  	}
   904  }
   905  
   906  func describeTopologySpreadConstraints(tscs []corev1.TopologySpreadConstraint, w PrefixWriter, space string) {
   907  	if len(tscs) == 0 {
   908  		return
   909  	}
   910  
   911  	sort.Slice(tscs, func(i, j int) bool {
   912  		return tscs[i].TopologyKey < tscs[j].TopologyKey
   913  	})
   914  
   915  	w.Write(LEVEL_0, "%sTopology Spread Constraints:\t", space)
   916  	for i, tsc := range tscs {
   917  		if i != 0 {
   918  			w.Write(LEVEL_0, "%s", space)
   919  			w.Write(LEVEL_0, "%s", "\t")
   920  		}
   921  
   922  		w.Write(LEVEL_0, "%s:", tsc.TopologyKey)
   923  		w.Write(LEVEL_0, "%v", tsc.WhenUnsatisfiable)
   924  		w.Write(LEVEL_0, " when max skew %d is exceeded", tsc.MaxSkew)
   925  		if tsc.LabelSelector != nil {
   926  			w.Write(LEVEL_0, " for selector %s", metav1.FormatLabelSelector(tsc.LabelSelector))
   927  		}
   928  		w.Write(LEVEL_0, "\n")
   929  	}
   930  }
   931  
   932  func describeVolumes(volumes []corev1.Volume, w PrefixWriter, space string) {
   933  	if len(volumes) == 0 {
   934  		w.Write(LEVEL_0, "%sVolumes:\t<none>\n", space)
   935  		return
   936  	}
   937  
   938  	w.Write(LEVEL_0, "%sVolumes:\n", space)
   939  	for _, volume := range volumes {
   940  		nameIndent := ""
   941  		if len(space) > 0 {
   942  			nameIndent = " "
   943  		}
   944  		w.Write(LEVEL_1, "%s%v:\n", nameIndent, volume.Name)
   945  		switch {
   946  		case volume.VolumeSource.HostPath != nil:
   947  			printHostPathVolumeSource(volume.VolumeSource.HostPath, w)
   948  		case volume.VolumeSource.EmptyDir != nil:
   949  			printEmptyDirVolumeSource(volume.VolumeSource.EmptyDir, w)
   950  		case volume.VolumeSource.GCEPersistentDisk != nil:
   951  			printGCEPersistentDiskVolumeSource(volume.VolumeSource.GCEPersistentDisk, w)
   952  		case volume.VolumeSource.AWSElasticBlockStore != nil:
   953  			printAWSElasticBlockStoreVolumeSource(volume.VolumeSource.AWSElasticBlockStore, w)
   954  		case volume.VolumeSource.GitRepo != nil:
   955  			printGitRepoVolumeSource(volume.VolumeSource.GitRepo, w)
   956  		case volume.VolumeSource.Secret != nil:
   957  			printSecretVolumeSource(volume.VolumeSource.Secret, w)
   958  		case volume.VolumeSource.ConfigMap != nil:
   959  			printConfigMapVolumeSource(volume.VolumeSource.ConfigMap, w)
   960  		case volume.VolumeSource.NFS != nil:
   961  			printNFSVolumeSource(volume.VolumeSource.NFS, w)
   962  		case volume.VolumeSource.ISCSI != nil:
   963  			printISCSIVolumeSource(volume.VolumeSource.ISCSI, w)
   964  		case volume.VolumeSource.Glusterfs != nil:
   965  			printGlusterfsVolumeSource(volume.VolumeSource.Glusterfs, w)
   966  		case volume.VolumeSource.PersistentVolumeClaim != nil:
   967  			printPersistentVolumeClaimVolumeSource(volume.VolumeSource.PersistentVolumeClaim, w)
   968  		case volume.VolumeSource.Ephemeral != nil:
   969  			printEphemeralVolumeSource(volume.VolumeSource.Ephemeral, w)
   970  		case volume.VolumeSource.RBD != nil:
   971  			printRBDVolumeSource(volume.VolumeSource.RBD, w)
   972  		case volume.VolumeSource.Quobyte != nil:
   973  			printQuobyteVolumeSource(volume.VolumeSource.Quobyte, w)
   974  		case volume.VolumeSource.DownwardAPI != nil:
   975  			printDownwardAPIVolumeSource(volume.VolumeSource.DownwardAPI, w)
   976  		case volume.VolumeSource.AzureDisk != nil:
   977  			printAzureDiskVolumeSource(volume.VolumeSource.AzureDisk, w)
   978  		case volume.VolumeSource.VsphereVolume != nil:
   979  			printVsphereVolumeSource(volume.VolumeSource.VsphereVolume, w)
   980  		case volume.VolumeSource.Cinder != nil:
   981  			printCinderVolumeSource(volume.VolumeSource.Cinder, w)
   982  		case volume.VolumeSource.PhotonPersistentDisk != nil:
   983  			printPhotonPersistentDiskVolumeSource(volume.VolumeSource.PhotonPersistentDisk, w)
   984  		case volume.VolumeSource.PortworxVolume != nil:
   985  			printPortworxVolumeSource(volume.VolumeSource.PortworxVolume, w)
   986  		case volume.VolumeSource.ScaleIO != nil:
   987  			printScaleIOVolumeSource(volume.VolumeSource.ScaleIO, w)
   988  		case volume.VolumeSource.CephFS != nil:
   989  			printCephFSVolumeSource(volume.VolumeSource.CephFS, w)
   990  		case volume.VolumeSource.StorageOS != nil:
   991  			printStorageOSVolumeSource(volume.VolumeSource.StorageOS, w)
   992  		case volume.VolumeSource.FC != nil:
   993  			printFCVolumeSource(volume.VolumeSource.FC, w)
   994  		case volume.VolumeSource.AzureFile != nil:
   995  			printAzureFileVolumeSource(volume.VolumeSource.AzureFile, w)
   996  		case volume.VolumeSource.FlexVolume != nil:
   997  			printFlexVolumeSource(volume.VolumeSource.FlexVolume, w)
   998  		case volume.VolumeSource.Flocker != nil:
   999  			printFlockerVolumeSource(volume.VolumeSource.Flocker, w)
  1000  		case volume.VolumeSource.Projected != nil:
  1001  			printProjectedVolumeSource(volume.VolumeSource.Projected, w)
  1002  		case volume.VolumeSource.CSI != nil:
  1003  			printCSIVolumeSource(volume.VolumeSource.CSI, w)
  1004  		default:
  1005  			w.Write(LEVEL_1, "<unknown>\n")
  1006  		}
  1007  	}
  1008  }
  1009  
  1010  func printHostPathVolumeSource(hostPath *corev1.HostPathVolumeSource, w PrefixWriter) {
  1011  	hostPathType := "<none>"
  1012  	if hostPath.Type != nil {
  1013  		hostPathType = string(*hostPath.Type)
  1014  	}
  1015  	w.Write(LEVEL_2, "Type:\tHostPath (bare host directory volume)\n"+
  1016  		"    Path:\t%v\n"+
  1017  		"    HostPathType:\t%v\n",
  1018  		hostPath.Path, hostPathType)
  1019  }
  1020  
  1021  func printEmptyDirVolumeSource(emptyDir *corev1.EmptyDirVolumeSource, w PrefixWriter) {
  1022  	var sizeLimit string
  1023  	if emptyDir.SizeLimit != nil && emptyDir.SizeLimit.Cmp(resource.Quantity{}) > 0 {
  1024  		sizeLimit = fmt.Sprintf("%v", emptyDir.SizeLimit)
  1025  	} else {
  1026  		sizeLimit = "<unset>"
  1027  	}
  1028  	w.Write(LEVEL_2, "Type:\tEmptyDir (a temporary directory that shares a pod's lifetime)\n"+
  1029  		"    Medium:\t%v\n"+
  1030  		"    SizeLimit:\t%v\n",
  1031  		emptyDir.Medium, sizeLimit)
  1032  }
  1033  
  1034  func printGCEPersistentDiskVolumeSource(gce *corev1.GCEPersistentDiskVolumeSource, w PrefixWriter) {
  1035  	w.Write(LEVEL_2, "Type:\tGCEPersistentDisk (a Persistent Disk resource in Google Compute Engine)\n"+
  1036  		"    PDName:\t%v\n"+
  1037  		"    FSType:\t%v\n"+
  1038  		"    Partition:\t%v\n"+
  1039  		"    ReadOnly:\t%v\n",
  1040  		gce.PDName, gce.FSType, gce.Partition, gce.ReadOnly)
  1041  }
  1042  
  1043  func printAWSElasticBlockStoreVolumeSource(aws *corev1.AWSElasticBlockStoreVolumeSource, w PrefixWriter) {
  1044  	w.Write(LEVEL_2, "Type:\tAWSElasticBlockStore (a Persistent Disk resource in AWS)\n"+
  1045  		"    VolumeID:\t%v\n"+
  1046  		"    FSType:\t%v\n"+
  1047  		"    Partition:\t%v\n"+
  1048  		"    ReadOnly:\t%v\n",
  1049  		aws.VolumeID, aws.FSType, aws.Partition, aws.ReadOnly)
  1050  }
  1051  
  1052  func printGitRepoVolumeSource(git *corev1.GitRepoVolumeSource, w PrefixWriter) {
  1053  	w.Write(LEVEL_2, "Type:\tGitRepo (a volume that is pulled from git when the pod is created)\n"+
  1054  		"    Repository:\t%v\n"+
  1055  		"    Revision:\t%v\n",
  1056  		git.Repository, git.Revision)
  1057  }
  1058  
  1059  func printSecretVolumeSource(secret *corev1.SecretVolumeSource, w PrefixWriter) {
  1060  	optional := secret.Optional != nil && *secret.Optional
  1061  	w.Write(LEVEL_2, "Type:\tSecret (a volume populated by a Secret)\n"+
  1062  		"    SecretName:\t%v\n"+
  1063  		"    Optional:\t%v\n",
  1064  		secret.SecretName, optional)
  1065  }
  1066  
  1067  func printConfigMapVolumeSource(configMap *corev1.ConfigMapVolumeSource, w PrefixWriter) {
  1068  	optional := configMap.Optional != nil && *configMap.Optional
  1069  	w.Write(LEVEL_2, "Type:\tConfigMap (a volume populated by a ConfigMap)\n"+
  1070  		"    Name:\t%v\n"+
  1071  		"    Optional:\t%v\n",
  1072  		configMap.Name, optional)
  1073  }
  1074  
  1075  func printProjectedVolumeSource(projected *corev1.ProjectedVolumeSource, w PrefixWriter) {
  1076  	w.Write(LEVEL_2, "Type:\tProjected (a volume that contains injected data from multiple sources)\n")
  1077  	for _, source := range projected.Sources {
  1078  		if source.Secret != nil {
  1079  			w.Write(LEVEL_2, "SecretName:\t%v\n"+
  1080  				"    SecretOptionalName:\t%v\n",
  1081  				source.Secret.Name, source.Secret.Optional)
  1082  		} else if source.DownwardAPI != nil {
  1083  			w.Write(LEVEL_2, "DownwardAPI:\ttrue\n")
  1084  		} else if source.ConfigMap != nil {
  1085  			w.Write(LEVEL_2, "ConfigMapName:\t%v\n"+
  1086  				"    ConfigMapOptional:\t%v\n",
  1087  				source.ConfigMap.Name, source.ConfigMap.Optional)
  1088  		} else if source.ServiceAccountToken != nil {
  1089  			w.Write(LEVEL_2, "TokenExpirationSeconds:\t%d\n",
  1090  				*source.ServiceAccountToken.ExpirationSeconds)
  1091  		}
  1092  	}
  1093  }
  1094  
  1095  func printNFSVolumeSource(nfs *corev1.NFSVolumeSource, w PrefixWriter) {
  1096  	w.Write(LEVEL_2, "Type:\tNFS (an NFS mount that lasts the lifetime of a pod)\n"+
  1097  		"    Server:\t%v\n"+
  1098  		"    Path:\t%v\n"+
  1099  		"    ReadOnly:\t%v\n",
  1100  		nfs.Server, nfs.Path, nfs.ReadOnly)
  1101  }
  1102  
  1103  func printQuobyteVolumeSource(quobyte *corev1.QuobyteVolumeSource, w PrefixWriter) {
  1104  	w.Write(LEVEL_2, "Type:\tQuobyte (a Quobyte mount on the host that shares a pod's lifetime)\n"+
  1105  		"    Registry:\t%v\n"+
  1106  		"    Volume:\t%v\n"+
  1107  		"    ReadOnly:\t%v\n",
  1108  		quobyte.Registry, quobyte.Volume, quobyte.ReadOnly)
  1109  }
  1110  
  1111  func printPortworxVolumeSource(pwxVolume *corev1.PortworxVolumeSource, w PrefixWriter) {
  1112  	w.Write(LEVEL_2, "Type:\tPortworxVolume (a Portworx Volume resource)\n"+
  1113  		"    VolumeID:\t%v\n",
  1114  		pwxVolume.VolumeID)
  1115  }
  1116  
  1117  func printISCSIVolumeSource(iscsi *corev1.ISCSIVolumeSource, w PrefixWriter) {
  1118  	initiator := "<none>"
  1119  	if iscsi.InitiatorName != nil {
  1120  		initiator = *iscsi.InitiatorName
  1121  	}
  1122  	w.Write(LEVEL_2, "Type:\tISCSI (an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod)\n"+
  1123  		"    TargetPortal:\t%v\n"+
  1124  		"    IQN:\t%v\n"+
  1125  		"    Lun:\t%v\n"+
  1126  		"    ISCSIInterface\t%v\n"+
  1127  		"    FSType:\t%v\n"+
  1128  		"    ReadOnly:\t%v\n"+
  1129  		"    Portals:\t%v\n"+
  1130  		"    DiscoveryCHAPAuth:\t%v\n"+
  1131  		"    SessionCHAPAuth:\t%v\n"+
  1132  		"    SecretRef:\t%v\n"+
  1133  		"    InitiatorName:\t%v\n",
  1134  		iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.ISCSIInterface, iscsi.FSType, iscsi.ReadOnly, iscsi.Portals, iscsi.DiscoveryCHAPAuth, iscsi.SessionCHAPAuth, iscsi.SecretRef, initiator)
  1135  }
  1136  
  1137  func printISCSIPersistentVolumeSource(iscsi *corev1.ISCSIPersistentVolumeSource, w PrefixWriter) {
  1138  	initiatorName := "<none>"
  1139  	if iscsi.InitiatorName != nil {
  1140  		initiatorName = *iscsi.InitiatorName
  1141  	}
  1142  	w.Write(LEVEL_2, "Type:\tISCSI (an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod)\n"+
  1143  		"    TargetPortal:\t%v\n"+
  1144  		"    IQN:\t%v\n"+
  1145  		"    Lun:\t%v\n"+
  1146  		"    ISCSIInterface\t%v\n"+
  1147  		"    FSType:\t%v\n"+
  1148  		"    ReadOnly:\t%v\n"+
  1149  		"    Portals:\t%v\n"+
  1150  		"    DiscoveryCHAPAuth:\t%v\n"+
  1151  		"    SessionCHAPAuth:\t%v\n"+
  1152  		"    SecretRef:\t%v\n"+
  1153  		"    InitiatorName:\t%v\n",
  1154  		iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.ISCSIInterface, iscsi.FSType, iscsi.ReadOnly, iscsi.Portals, iscsi.DiscoveryCHAPAuth, iscsi.SessionCHAPAuth, iscsi.SecretRef, initiatorName)
  1155  }
  1156  
  1157  func printGlusterfsVolumeSource(glusterfs *corev1.GlusterfsVolumeSource, w PrefixWriter) {
  1158  	w.Write(LEVEL_2, "Type:\tGlusterfs (a Glusterfs mount on the host that shares a pod's lifetime)\n"+
  1159  		"    EndpointsName:\t%v\n"+
  1160  		"    Path:\t%v\n"+
  1161  		"    ReadOnly:\t%v\n",
  1162  		glusterfs.EndpointsName, glusterfs.Path, glusterfs.ReadOnly)
  1163  }
  1164  
  1165  func printGlusterfsPersistentVolumeSource(glusterfs *corev1.GlusterfsPersistentVolumeSource, w PrefixWriter) {
  1166  	endpointsNamespace := "<unset>"
  1167  	if glusterfs.EndpointsNamespace != nil {
  1168  		endpointsNamespace = *glusterfs.EndpointsNamespace
  1169  	}
  1170  	w.Write(LEVEL_2, "Type:\tGlusterfs (a Glusterfs mount on the host that shares a pod's lifetime)\n"+
  1171  		"    EndpointsName:\t%v\n"+
  1172  		"    EndpointsNamespace:\t%v\n"+
  1173  		"    Path:\t%v\n"+
  1174  		"    ReadOnly:\t%v\n",
  1175  		glusterfs.EndpointsName, endpointsNamespace, glusterfs.Path, glusterfs.ReadOnly)
  1176  }
  1177  
  1178  func printPersistentVolumeClaimVolumeSource(claim *corev1.PersistentVolumeClaimVolumeSource, w PrefixWriter) {
  1179  	w.Write(LEVEL_2, "Type:\tPersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)\n"+
  1180  		"    ClaimName:\t%v\n"+
  1181  		"    ReadOnly:\t%v\n",
  1182  		claim.ClaimName, claim.ReadOnly)
  1183  }
  1184  
  1185  func printEphemeralVolumeSource(ephemeral *corev1.EphemeralVolumeSource, w PrefixWriter) {
  1186  	w.Write(LEVEL_2, "Type:\tEphemeralVolume (an inline specification for a volume that gets created and deleted with the pod)\n")
  1187  	if ephemeral.VolumeClaimTemplate != nil {
  1188  		printPersistentVolumeClaim(NewNestedPrefixWriter(w, LEVEL_2),
  1189  			&corev1.PersistentVolumeClaim{
  1190  				ObjectMeta: ephemeral.VolumeClaimTemplate.ObjectMeta,
  1191  				Spec:       ephemeral.VolumeClaimTemplate.Spec,
  1192  			}, false /* not a full PVC */)
  1193  	}
  1194  }
  1195  
  1196  func printRBDVolumeSource(rbd *corev1.RBDVolumeSource, w PrefixWriter) {
  1197  	w.Write(LEVEL_2, "Type:\tRBD (a Rados Block Device mount on the host that shares a pod's lifetime)\n"+
  1198  		"    CephMonitors:\t%v\n"+
  1199  		"    RBDImage:\t%v\n"+
  1200  		"    FSType:\t%v\n"+
  1201  		"    RBDPool:\t%v\n"+
  1202  		"    RadosUser:\t%v\n"+
  1203  		"    Keyring:\t%v\n"+
  1204  		"    SecretRef:\t%v\n"+
  1205  		"    ReadOnly:\t%v\n",
  1206  		rbd.CephMonitors, rbd.RBDImage, rbd.FSType, rbd.RBDPool, rbd.RadosUser, rbd.Keyring, rbd.SecretRef, rbd.ReadOnly)
  1207  }
  1208  
  1209  func printRBDPersistentVolumeSource(rbd *corev1.RBDPersistentVolumeSource, w PrefixWriter) {
  1210  	w.Write(LEVEL_2, "Type:\tRBD (a Rados Block Device mount on the host that shares a pod's lifetime)\n"+
  1211  		"    CephMonitors:\t%v\n"+
  1212  		"    RBDImage:\t%v\n"+
  1213  		"    FSType:\t%v\n"+
  1214  		"    RBDPool:\t%v\n"+
  1215  		"    RadosUser:\t%v\n"+
  1216  		"    Keyring:\t%v\n"+
  1217  		"    SecretRef:\t%v\n"+
  1218  		"    ReadOnly:\t%v\n",
  1219  		rbd.CephMonitors, rbd.RBDImage, rbd.FSType, rbd.RBDPool, rbd.RadosUser, rbd.Keyring, rbd.SecretRef, rbd.ReadOnly)
  1220  }
  1221  
  1222  func printDownwardAPIVolumeSource(d *corev1.DownwardAPIVolumeSource, w PrefixWriter) {
  1223  	w.Write(LEVEL_2, "Type:\tDownwardAPI (a volume populated by information about the pod)\n    Items:\n")
  1224  	for _, mapping := range d.Items {
  1225  		if mapping.FieldRef != nil {
  1226  			w.Write(LEVEL_3, "%v -> %v\n", mapping.FieldRef.FieldPath, mapping.Path)
  1227  		}
  1228  		if mapping.ResourceFieldRef != nil {
  1229  			w.Write(LEVEL_3, "%v -> %v\n", mapping.ResourceFieldRef.Resource, mapping.Path)
  1230  		}
  1231  	}
  1232  }
  1233  
  1234  func printAzureDiskVolumeSource(d *corev1.AzureDiskVolumeSource, w PrefixWriter) {
  1235  	w.Write(LEVEL_2, "Type:\tAzureDisk (an Azure Data Disk mount on the host and bind mount to the pod)\n"+
  1236  		"    DiskName:\t%v\n"+
  1237  		"    DiskURI:\t%v\n"+
  1238  		"    Kind: \t%v\n"+
  1239  		"    FSType:\t%v\n"+
  1240  		"    CachingMode:\t%v\n"+
  1241  		"    ReadOnly:\t%v\n",
  1242  		d.DiskName, d.DataDiskURI, *d.Kind, *d.FSType, *d.CachingMode, *d.ReadOnly)
  1243  }
  1244  
  1245  func printVsphereVolumeSource(vsphere *corev1.VsphereVirtualDiskVolumeSource, w PrefixWriter) {
  1246  	w.Write(LEVEL_2, "Type:\tvSphereVolume (a Persistent Disk resource in vSphere)\n"+
  1247  		"    VolumePath:\t%v\n"+
  1248  		"    FSType:\t%v\n"+
  1249  		"    StoragePolicyName:\t%v\n",
  1250  		vsphere.VolumePath, vsphere.FSType, vsphere.StoragePolicyName)
  1251  }
  1252  
  1253  func printPhotonPersistentDiskVolumeSource(photon *corev1.PhotonPersistentDiskVolumeSource, w PrefixWriter) {
  1254  	w.Write(LEVEL_2, "Type:\tPhotonPersistentDisk (a Persistent Disk resource in photon platform)\n"+
  1255  		"    PdID:\t%v\n"+
  1256  		"    FSType:\t%v\n",
  1257  		photon.PdID, photon.FSType)
  1258  }
  1259  
  1260  func printCinderVolumeSource(cinder *corev1.CinderVolumeSource, w PrefixWriter) {
  1261  	w.Write(LEVEL_2, "Type:\tCinder (a Persistent Disk resource in OpenStack)\n"+
  1262  		"    VolumeID:\t%v\n"+
  1263  		"    FSType:\t%v\n"+
  1264  		"    ReadOnly:\t%v\n"+
  1265  		"    SecretRef:\t%v\n",
  1266  		cinder.VolumeID, cinder.FSType, cinder.ReadOnly, cinder.SecretRef)
  1267  }
  1268  
  1269  func printCinderPersistentVolumeSource(cinder *corev1.CinderPersistentVolumeSource, w PrefixWriter) {
  1270  	w.Write(LEVEL_2, "Type:\tCinder (a Persistent Disk resource in OpenStack)\n"+
  1271  		"    VolumeID:\t%v\n"+
  1272  		"    FSType:\t%v\n"+
  1273  		"    ReadOnly:\t%v\n"+
  1274  		"    SecretRef:\t%v\n",
  1275  		cinder.VolumeID, cinder.FSType, cinder.ReadOnly, cinder.SecretRef)
  1276  }
  1277  
  1278  func printScaleIOVolumeSource(sio *corev1.ScaleIOVolumeSource, w PrefixWriter) {
  1279  	w.Write(LEVEL_2, "Type:\tScaleIO (a persistent volume backed by a block device in ScaleIO)\n"+
  1280  		"    Gateway:\t%v\n"+
  1281  		"    System:\t%v\n"+
  1282  		"    Protection Domain:\t%v\n"+
  1283  		"    Storage Pool:\t%v\n"+
  1284  		"    Storage Mode:\t%v\n"+
  1285  		"    VolumeName:\t%v\n"+
  1286  		"    FSType:\t%v\n"+
  1287  		"    ReadOnly:\t%v\n",
  1288  		sio.Gateway, sio.System, sio.ProtectionDomain, sio.StoragePool, sio.StorageMode, sio.VolumeName, sio.FSType, sio.ReadOnly)
  1289  }
  1290  
  1291  func printScaleIOPersistentVolumeSource(sio *corev1.ScaleIOPersistentVolumeSource, w PrefixWriter) {
  1292  	var secretNS, secretName string
  1293  	if sio.SecretRef != nil {
  1294  		secretName = sio.SecretRef.Name
  1295  		secretNS = sio.SecretRef.Namespace
  1296  	}
  1297  	w.Write(LEVEL_2, "Type:\tScaleIO (a persistent volume backed by a block device in ScaleIO)\n"+
  1298  		"    Gateway:\t%v\n"+
  1299  		"    System:\t%v\n"+
  1300  		"    Protection Domain:\t%v\n"+
  1301  		"    Storage Pool:\t%v\n"+
  1302  		"    Storage Mode:\t%v\n"+
  1303  		"    VolumeName:\t%v\n"+
  1304  		"    SecretName:\t%v\n"+
  1305  		"    SecretNamespace:\t%v\n"+
  1306  		"    FSType:\t%v\n"+
  1307  		"    ReadOnly:\t%v\n",
  1308  		sio.Gateway, sio.System, sio.ProtectionDomain, sio.StoragePool, sio.StorageMode, sio.VolumeName, secretName, secretNS, sio.FSType, sio.ReadOnly)
  1309  }
  1310  
  1311  func printLocalVolumeSource(ls *corev1.LocalVolumeSource, w PrefixWriter) {
  1312  	w.Write(LEVEL_2, "Type:\tLocalVolume (a persistent volume backed by local storage on a node)\n"+
  1313  		"    Path:\t%v\n",
  1314  		ls.Path)
  1315  }
  1316  
  1317  func printCephFSVolumeSource(cephfs *corev1.CephFSVolumeSource, w PrefixWriter) {
  1318  	w.Write(LEVEL_2, "Type:\tCephFS (a CephFS mount on the host that shares a pod's lifetime)\n"+
  1319  		"    Monitors:\t%v\n"+
  1320  		"    Path:\t%v\n"+
  1321  		"    User:\t%v\n"+
  1322  		"    SecretFile:\t%v\n"+
  1323  		"    SecretRef:\t%v\n"+
  1324  		"    ReadOnly:\t%v\n",
  1325  		cephfs.Monitors, cephfs.Path, cephfs.User, cephfs.SecretFile, cephfs.SecretRef, cephfs.ReadOnly)
  1326  }
  1327  
  1328  func printCephFSPersistentVolumeSource(cephfs *corev1.CephFSPersistentVolumeSource, w PrefixWriter) {
  1329  	w.Write(LEVEL_2, "Type:\tCephFS (a CephFS mount on the host that shares a pod's lifetime)\n"+
  1330  		"    Monitors:\t%v\n"+
  1331  		"    Path:\t%v\n"+
  1332  		"    User:\t%v\n"+
  1333  		"    SecretFile:\t%v\n"+
  1334  		"    SecretRef:\t%v\n"+
  1335  		"    ReadOnly:\t%v\n",
  1336  		cephfs.Monitors, cephfs.Path, cephfs.User, cephfs.SecretFile, cephfs.SecretRef, cephfs.ReadOnly)
  1337  }
  1338  
  1339  func printStorageOSVolumeSource(storageos *corev1.StorageOSVolumeSource, w PrefixWriter) {
  1340  	w.Write(LEVEL_2, "Type:\tStorageOS (a StorageOS Persistent Disk resource)\n"+
  1341  		"    VolumeName:\t%v\n"+
  1342  		"    VolumeNamespace:\t%v\n"+
  1343  		"    FSType:\t%v\n"+
  1344  		"    ReadOnly:\t%v\n",
  1345  		storageos.VolumeName, storageos.VolumeNamespace, storageos.FSType, storageos.ReadOnly)
  1346  }
  1347  
  1348  func printStorageOSPersistentVolumeSource(storageos *corev1.StorageOSPersistentVolumeSource, w PrefixWriter) {
  1349  	w.Write(LEVEL_2, "Type:\tStorageOS (a StorageOS Persistent Disk resource)\n"+
  1350  		"    VolumeName:\t%v\n"+
  1351  		"    VolumeNamespace:\t%v\n"+
  1352  		"    FSType:\t%v\n"+
  1353  		"    ReadOnly:\t%v\n",
  1354  		storageos.VolumeName, storageos.VolumeNamespace, storageos.FSType, storageos.ReadOnly)
  1355  }
  1356  
  1357  func printFCVolumeSource(fc *corev1.FCVolumeSource, w PrefixWriter) {
  1358  	lun := "<none>"
  1359  	if fc.Lun != nil {
  1360  		lun = strconv.Itoa(int(*fc.Lun))
  1361  	}
  1362  	w.Write(LEVEL_2, "Type:\tFC (a Fibre Channel disk)\n"+
  1363  		"    TargetWWNs:\t%v\n"+
  1364  		"    LUN:\t%v\n"+
  1365  		"    FSType:\t%v\n"+
  1366  		"    ReadOnly:\t%v\n",
  1367  		strings.Join(fc.TargetWWNs, ", "), lun, fc.FSType, fc.ReadOnly)
  1368  }
  1369  
  1370  func printAzureFileVolumeSource(azureFile *corev1.AzureFileVolumeSource, w PrefixWriter) {
  1371  	w.Write(LEVEL_2, "Type:\tAzureFile (an Azure File Service mount on the host and bind mount to the pod)\n"+
  1372  		"    SecretName:\t%v\n"+
  1373  		"    ShareName:\t%v\n"+
  1374  		"    ReadOnly:\t%v\n",
  1375  		azureFile.SecretName, azureFile.ShareName, azureFile.ReadOnly)
  1376  }
  1377  
  1378  func printAzureFilePersistentVolumeSource(azureFile *corev1.AzureFilePersistentVolumeSource, w PrefixWriter) {
  1379  	ns := ""
  1380  	if azureFile.SecretNamespace != nil {
  1381  		ns = *azureFile.SecretNamespace
  1382  	}
  1383  	w.Write(LEVEL_2, "Type:\tAzureFile (an Azure File Service mount on the host and bind mount to the pod)\n"+
  1384  		"    SecretName:\t%v\n"+
  1385  		"    SecretNamespace:\t%v\n"+
  1386  		"    ShareName:\t%v\n"+
  1387  		"    ReadOnly:\t%v\n",
  1388  		azureFile.SecretName, ns, azureFile.ShareName, azureFile.ReadOnly)
  1389  }
  1390  
  1391  func printFlexPersistentVolumeSource(flex *corev1.FlexPersistentVolumeSource, w PrefixWriter) {
  1392  	w.Write(LEVEL_2, "Type:\tFlexVolume (a generic volume resource that is provisioned/attached using an exec based plugin)\n"+
  1393  		"    Driver:\t%v\n"+
  1394  		"    FSType:\t%v\n"+
  1395  		"    SecretRef:\t%v\n"+
  1396  		"    ReadOnly:\t%v\n"+
  1397  		"    Options:\t%v\n",
  1398  		flex.Driver, flex.FSType, flex.SecretRef, flex.ReadOnly, flex.Options)
  1399  }
  1400  
  1401  func printFlexVolumeSource(flex *corev1.FlexVolumeSource, w PrefixWriter) {
  1402  	w.Write(LEVEL_2, "Type:\tFlexVolume (a generic volume resource that is provisioned/attached using an exec based plugin)\n"+
  1403  		"    Driver:\t%v\n"+
  1404  		"    FSType:\t%v\n"+
  1405  		"    SecretRef:\t%v\n"+
  1406  		"    ReadOnly:\t%v\n"+
  1407  		"    Options:\t%v\n",
  1408  		flex.Driver, flex.FSType, flex.SecretRef, flex.ReadOnly, flex.Options)
  1409  }
  1410  
  1411  func printFlockerVolumeSource(flocker *corev1.FlockerVolumeSource, w PrefixWriter) {
  1412  	w.Write(LEVEL_2, "Type:\tFlocker (a Flocker volume mounted by the Flocker agent)\n"+
  1413  		"    DatasetName:\t%v\n"+
  1414  		"    DatasetUUID:\t%v\n",
  1415  		flocker.DatasetName, flocker.DatasetUUID)
  1416  }
  1417  
  1418  func printCSIVolumeSource(csi *corev1.CSIVolumeSource, w PrefixWriter) {
  1419  	var readOnly bool
  1420  	var fsType string
  1421  	if csi.ReadOnly != nil && *csi.ReadOnly {
  1422  		readOnly = true
  1423  	}
  1424  	if csi.FSType != nil {
  1425  		fsType = *csi.FSType
  1426  	}
  1427  	w.Write(LEVEL_2, "Type:\tCSI (a Container Storage Interface (CSI) volume source)\n"+
  1428  		"    Driver:\t%v\n"+
  1429  		"    FSType:\t%v\n"+
  1430  		"    ReadOnly:\t%v\n",
  1431  		csi.Driver, fsType, readOnly)
  1432  	printCSIPersistentVolumeAttributesMultiline(w, "VolumeAttributes", csi.VolumeAttributes)
  1433  }
  1434  
  1435  func printCSIPersistentVolumeSource(csi *corev1.CSIPersistentVolumeSource, w PrefixWriter) {
  1436  	w.Write(LEVEL_2, "Type:\tCSI (a Container Storage Interface (CSI) volume source)\n"+
  1437  		"    Driver:\t%v\n"+
  1438  		"    FSType:\t%v\n"+
  1439  		"    VolumeHandle:\t%v\n"+
  1440  		"    ReadOnly:\t%v\n",
  1441  		csi.Driver, csi.FSType, csi.VolumeHandle, csi.ReadOnly)
  1442  	printCSIPersistentVolumeAttributesMultiline(w, "VolumeAttributes", csi.VolumeAttributes)
  1443  }
  1444  
  1445  func printCSIPersistentVolumeAttributesMultiline(w PrefixWriter, title string, annotations map[string]string) {
  1446  	printCSIPersistentVolumeAttributesMultilineIndent(w, "", title, "\t", annotations, sets.NewString())
  1447  }
  1448  
  1449  func printCSIPersistentVolumeAttributesMultilineIndent(w PrefixWriter, initialIndent, title, innerIndent string, attributes map[string]string, skip sets.String) {
  1450  	w.Write(LEVEL_2, "%s%s:%s", initialIndent, title, innerIndent)
  1451  
  1452  	if len(attributes) == 0 {
  1453  		w.WriteLine("<none>")
  1454  		return
  1455  	}
  1456  
  1457  	// to print labels in the sorted order
  1458  	keys := make([]string, 0, len(attributes))
  1459  	for key := range attributes {
  1460  		if skip.Has(key) {
  1461  			continue
  1462  		}
  1463  		keys = append(keys, key)
  1464  	}
  1465  	if len(attributes) == 0 {
  1466  		w.WriteLine("<none>")
  1467  		return
  1468  	}
  1469  	sort.Strings(keys)
  1470  
  1471  	for i, key := range keys {
  1472  		if i != 0 {
  1473  			w.Write(LEVEL_2, initialIndent)
  1474  			w.Write(LEVEL_2, innerIndent)
  1475  		}
  1476  		line := fmt.Sprintf("%s=%s", key, attributes[key])
  1477  		if len(line) > maxAnnotationLen {
  1478  			w.Write(LEVEL_2, "%s...\n", line[:maxAnnotationLen])
  1479  		} else {
  1480  			w.Write(LEVEL_2, "%s\n", line)
  1481  		}
  1482  	}
  1483  }
  1484  
  1485  type PersistentVolumeDescriber struct {
  1486  	clientset.Interface
  1487  }
  1488  
  1489  func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  1490  	c := d.CoreV1().PersistentVolumes()
  1491  
  1492  	pv, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  1493  	if err != nil {
  1494  		return "", err
  1495  	}
  1496  
  1497  	var events *corev1.EventList
  1498  	if describerSettings.ShowEvents {
  1499  		events, _ = searchEvents(d.CoreV1(), pv, describerSettings.ChunkSize)
  1500  	}
  1501  
  1502  	return describePersistentVolume(pv, events)
  1503  }
  1504  
  1505  func printVolumeNodeAffinity(w PrefixWriter, affinity *corev1.VolumeNodeAffinity) {
  1506  	w.Write(LEVEL_0, "Node Affinity:\t")
  1507  	if affinity == nil || affinity.Required == nil {
  1508  		w.WriteLine("<none>")
  1509  		return
  1510  	}
  1511  	w.WriteLine("")
  1512  
  1513  	if affinity.Required != nil {
  1514  		w.Write(LEVEL_1, "Required Terms:\t")
  1515  		if len(affinity.Required.NodeSelectorTerms) == 0 {
  1516  			w.WriteLine("<none>")
  1517  		} else {
  1518  			w.WriteLine("")
  1519  			for i, term := range affinity.Required.NodeSelectorTerms {
  1520  				printNodeSelectorTermsMultilineWithIndent(w, LEVEL_2, fmt.Sprintf("Term %v", i), "\t", term.MatchExpressions)
  1521  			}
  1522  		}
  1523  	}
  1524  }
  1525  
  1526  // printLabelsMultiline prints multiple labels with a user-defined alignment.
  1527  func printNodeSelectorTermsMultilineWithIndent(w PrefixWriter, indentLevel int, title, innerIndent string, reqs []corev1.NodeSelectorRequirement) {
  1528  	w.Write(indentLevel, "%s:%s", title, innerIndent)
  1529  
  1530  	if len(reqs) == 0 {
  1531  		w.WriteLine("<none>")
  1532  		return
  1533  	}
  1534  
  1535  	for i, req := range reqs {
  1536  		if i != 0 {
  1537  			w.Write(indentLevel, "%s", innerIndent)
  1538  		}
  1539  		exprStr := fmt.Sprintf("%s %s", req.Key, strings.ToLower(string(req.Operator)))
  1540  		if len(req.Values) > 0 {
  1541  			exprStr = fmt.Sprintf("%s [%s]", exprStr, strings.Join(req.Values, ", "))
  1542  		}
  1543  		w.Write(LEVEL_0, "%s\n", exprStr)
  1544  	}
  1545  }
  1546  
  1547  func describePersistentVolume(pv *corev1.PersistentVolume, events *corev1.EventList) (string, error) {
  1548  	return tabbedString(func(out io.Writer) error {
  1549  		w := NewPrefixWriter(out)
  1550  		w.Write(LEVEL_0, "Name:\t%s\n", pv.Name)
  1551  		printLabelsMultiline(w, "Labels", pv.ObjectMeta.Labels)
  1552  		printAnnotationsMultiline(w, "Annotations", pv.ObjectMeta.Annotations)
  1553  		w.Write(LEVEL_0, "Finalizers:\t%v\n", pv.ObjectMeta.Finalizers)
  1554  		w.Write(LEVEL_0, "StorageClass:\t%s\n", storageutil.GetPersistentVolumeClass(pv))
  1555  		if pv.ObjectMeta.DeletionTimestamp != nil {
  1556  			w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampSince(*pv.ObjectMeta.DeletionTimestamp))
  1557  		} else {
  1558  			w.Write(LEVEL_0, "Status:\t%v\n", pv.Status.Phase)
  1559  		}
  1560  		if pv.Spec.ClaimRef != nil {
  1561  			w.Write(LEVEL_0, "Claim:\t%s\n", pv.Spec.ClaimRef.Namespace+"/"+pv.Spec.ClaimRef.Name)
  1562  		} else {
  1563  			w.Write(LEVEL_0, "Claim:\t%s\n", "")
  1564  		}
  1565  		w.Write(LEVEL_0, "Reclaim Policy:\t%v\n", pv.Spec.PersistentVolumeReclaimPolicy)
  1566  		w.Write(LEVEL_0, "Access Modes:\t%s\n", storageutil.GetAccessModesAsString(pv.Spec.AccessModes))
  1567  		if pv.Spec.VolumeMode != nil {
  1568  			w.Write(LEVEL_0, "VolumeMode:\t%v\n", *pv.Spec.VolumeMode)
  1569  		}
  1570  		storage := pv.Spec.Capacity[corev1.ResourceStorage]
  1571  		w.Write(LEVEL_0, "Capacity:\t%s\n", storage.String())
  1572  		printVolumeNodeAffinity(w, pv.Spec.NodeAffinity)
  1573  		w.Write(LEVEL_0, "Message:\t%s\n", pv.Status.Message)
  1574  		w.Write(LEVEL_0, "Source:\n")
  1575  
  1576  		switch {
  1577  		case pv.Spec.HostPath != nil:
  1578  			printHostPathVolumeSource(pv.Spec.HostPath, w)
  1579  		case pv.Spec.GCEPersistentDisk != nil:
  1580  			printGCEPersistentDiskVolumeSource(pv.Spec.GCEPersistentDisk, w)
  1581  		case pv.Spec.AWSElasticBlockStore != nil:
  1582  			printAWSElasticBlockStoreVolumeSource(pv.Spec.AWSElasticBlockStore, w)
  1583  		case pv.Spec.NFS != nil:
  1584  			printNFSVolumeSource(pv.Spec.NFS, w)
  1585  		case pv.Spec.ISCSI != nil:
  1586  			printISCSIPersistentVolumeSource(pv.Spec.ISCSI, w)
  1587  		case pv.Spec.Glusterfs != nil:
  1588  			printGlusterfsPersistentVolumeSource(pv.Spec.Glusterfs, w)
  1589  		case pv.Spec.RBD != nil:
  1590  			printRBDPersistentVolumeSource(pv.Spec.RBD, w)
  1591  		case pv.Spec.Quobyte != nil:
  1592  			printQuobyteVolumeSource(pv.Spec.Quobyte, w)
  1593  		case pv.Spec.VsphereVolume != nil:
  1594  			printVsphereVolumeSource(pv.Spec.VsphereVolume, w)
  1595  		case pv.Spec.Cinder != nil:
  1596  			printCinderPersistentVolumeSource(pv.Spec.Cinder, w)
  1597  		case pv.Spec.AzureDisk != nil:
  1598  			printAzureDiskVolumeSource(pv.Spec.AzureDisk, w)
  1599  		case pv.Spec.PhotonPersistentDisk != nil:
  1600  			printPhotonPersistentDiskVolumeSource(pv.Spec.PhotonPersistentDisk, w)
  1601  		case pv.Spec.PortworxVolume != nil:
  1602  			printPortworxVolumeSource(pv.Spec.PortworxVolume, w)
  1603  		case pv.Spec.ScaleIO != nil:
  1604  			printScaleIOPersistentVolumeSource(pv.Spec.ScaleIO, w)
  1605  		case pv.Spec.Local != nil:
  1606  			printLocalVolumeSource(pv.Spec.Local, w)
  1607  		case pv.Spec.CephFS != nil:
  1608  			printCephFSPersistentVolumeSource(pv.Spec.CephFS, w)
  1609  		case pv.Spec.StorageOS != nil:
  1610  			printStorageOSPersistentVolumeSource(pv.Spec.StorageOS, w)
  1611  		case pv.Spec.FC != nil:
  1612  			printFCVolumeSource(pv.Spec.FC, w)
  1613  		case pv.Spec.AzureFile != nil:
  1614  			printAzureFilePersistentVolumeSource(pv.Spec.AzureFile, w)
  1615  		case pv.Spec.FlexVolume != nil:
  1616  			printFlexPersistentVolumeSource(pv.Spec.FlexVolume, w)
  1617  		case pv.Spec.Flocker != nil:
  1618  			printFlockerVolumeSource(pv.Spec.Flocker, w)
  1619  		case pv.Spec.CSI != nil:
  1620  			printCSIPersistentVolumeSource(pv.Spec.CSI, w)
  1621  		default:
  1622  			w.Write(LEVEL_1, "<unknown>\n")
  1623  		}
  1624  
  1625  		if events != nil {
  1626  			DescribeEvents(events, w)
  1627  		}
  1628  
  1629  		return nil
  1630  	})
  1631  }
  1632  
  1633  type PersistentVolumeClaimDescriber struct {
  1634  	clientset.Interface
  1635  }
  1636  
  1637  func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  1638  	c := d.CoreV1().PersistentVolumeClaims(namespace)
  1639  
  1640  	pvc, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  1641  	if err != nil {
  1642  		return "", err
  1643  	}
  1644  
  1645  	pc := d.CoreV1().Pods(namespace)
  1646  
  1647  	pods, err := getPodsForPVC(pc, pvc, describerSettings)
  1648  	if err != nil {
  1649  		return "", err
  1650  	}
  1651  
  1652  	var events *corev1.EventList
  1653  	if describerSettings.ShowEvents {
  1654  		events, _ = searchEvents(d.CoreV1(), pvc, describerSettings.ChunkSize)
  1655  	}
  1656  
  1657  	return describePersistentVolumeClaim(pvc, events, pods)
  1658  }
  1659  
  1660  func getPodsForPVC(c corev1client.PodInterface, pvc *corev1.PersistentVolumeClaim, settings DescriberSettings) ([]corev1.Pod, error) {
  1661  	nsPods, err := getPodsInChunks(c, metav1.ListOptions{Limit: settings.ChunkSize})
  1662  	if err != nil {
  1663  		return []corev1.Pod{}, err
  1664  	}
  1665  
  1666  	var pods []corev1.Pod
  1667  
  1668  	for _, pod := range nsPods.Items {
  1669  		for _, volume := range pod.Spec.Volumes {
  1670  			if volume.VolumeSource.PersistentVolumeClaim != nil && volume.VolumeSource.PersistentVolumeClaim.ClaimName == pvc.Name {
  1671  				pods = append(pods, pod)
  1672  			}
  1673  		}
  1674  	}
  1675  
  1676  ownersLoop:
  1677  	for _, ownerRef := range pvc.ObjectMeta.OwnerReferences {
  1678  		if ownerRef.Kind != "Pod" {
  1679  			continue
  1680  		}
  1681  
  1682  		podIndex := -1
  1683  		for i, pod := range nsPods.Items {
  1684  			if pod.UID == ownerRef.UID {
  1685  				podIndex = i
  1686  				break
  1687  			}
  1688  		}
  1689  		if podIndex == -1 {
  1690  			// Maybe the pod has been deleted
  1691  			continue
  1692  		}
  1693  
  1694  		for _, pod := range pods {
  1695  			if pod.UID == nsPods.Items[podIndex].UID {
  1696  				// This owner pod is already recorded, look for pods between other owners
  1697  				continue ownersLoop
  1698  			}
  1699  		}
  1700  
  1701  		pods = append(pods, nsPods.Items[podIndex])
  1702  	}
  1703  
  1704  	return pods, nil
  1705  }
  1706  
  1707  func describePersistentVolumeClaim(pvc *corev1.PersistentVolumeClaim, events *corev1.EventList, pods []corev1.Pod) (string, error) {
  1708  	return tabbedString(func(out io.Writer) error {
  1709  		w := NewPrefixWriter(out)
  1710  		printPersistentVolumeClaim(w, pvc, true)
  1711  		printPodsMultiline(w, "Used By", pods)
  1712  
  1713  		if len(pvc.Status.Conditions) > 0 {
  1714  			w.Write(LEVEL_0, "Conditions:\n")
  1715  			w.Write(LEVEL_1, "Type\tStatus\tLastProbeTime\tLastTransitionTime\tReason\tMessage\n")
  1716  			w.Write(LEVEL_1, "----\t------\t-----------------\t------------------\t------\t-------\n")
  1717  			for _, c := range pvc.Status.Conditions {
  1718  				w.Write(LEVEL_1, "%v \t%v \t%s \t%s \t%v \t%v\n",
  1719  					c.Type,
  1720  					c.Status,
  1721  					c.LastProbeTime.Time.Format(time.RFC1123Z),
  1722  					c.LastTransitionTime.Time.Format(time.RFC1123Z),
  1723  					c.Reason,
  1724  					c.Message)
  1725  			}
  1726  		}
  1727  		if events != nil {
  1728  			DescribeEvents(events, w)
  1729  		}
  1730  
  1731  		return nil
  1732  	})
  1733  }
  1734  
  1735  // printPersistentVolumeClaim is used for both PVCs and PersistentVolumeClaimTemplate. For the latter,
  1736  // we need to skip some fields which have no meaning.
  1737  func printPersistentVolumeClaim(w PrefixWriter, pvc *corev1.PersistentVolumeClaim, isFullPVC bool) {
  1738  	if isFullPVC {
  1739  		w.Write(LEVEL_0, "Name:\t%s\n", pvc.Name)
  1740  		w.Write(LEVEL_0, "Namespace:\t%s\n", pvc.Namespace)
  1741  	}
  1742  	w.Write(LEVEL_0, "StorageClass:\t%s\n", storageutil.GetPersistentVolumeClaimClass(pvc))
  1743  	if isFullPVC {
  1744  		if pvc.ObjectMeta.DeletionTimestamp != nil {
  1745  			w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampSince(*pvc.ObjectMeta.DeletionTimestamp))
  1746  		} else {
  1747  			w.Write(LEVEL_0, "Status:\t%v\n", pvc.Status.Phase)
  1748  		}
  1749  	}
  1750  	w.Write(LEVEL_0, "Volume:\t%s\n", pvc.Spec.VolumeName)
  1751  	printLabelsMultiline(w, "Labels", pvc.Labels)
  1752  	printAnnotationsMultiline(w, "Annotations", pvc.Annotations)
  1753  	if isFullPVC {
  1754  		w.Write(LEVEL_0, "Finalizers:\t%v\n", pvc.ObjectMeta.Finalizers)
  1755  	}
  1756  	storage := pvc.Spec.Resources.Requests[corev1.ResourceStorage]
  1757  	capacity := ""
  1758  	accessModes := ""
  1759  	if pvc.Spec.VolumeName != "" {
  1760  		accessModes = storageutil.GetAccessModesAsString(pvc.Status.AccessModes)
  1761  		storage = pvc.Status.Capacity[corev1.ResourceStorage]
  1762  		capacity = storage.String()
  1763  	}
  1764  	w.Write(LEVEL_0, "Capacity:\t%s\n", capacity)
  1765  	w.Write(LEVEL_0, "Access Modes:\t%s\n", accessModes)
  1766  	if pvc.Spec.VolumeMode != nil {
  1767  		w.Write(LEVEL_0, "VolumeMode:\t%v\n", *pvc.Spec.VolumeMode)
  1768  	}
  1769  	if pvc.Spec.DataSource != nil {
  1770  		w.Write(LEVEL_0, "DataSource:\n")
  1771  		if pvc.Spec.DataSource.APIGroup != nil {
  1772  			w.Write(LEVEL_1, "APIGroup:\t%v\n", *pvc.Spec.DataSource.APIGroup)
  1773  		}
  1774  		w.Write(LEVEL_1, "Kind:\t%v\n", pvc.Spec.DataSource.Kind)
  1775  		w.Write(LEVEL_1, "Name:\t%v\n", pvc.Spec.DataSource.Name)
  1776  	}
  1777  }
  1778  
  1779  func describeContainers(label string, containers []corev1.Container, containerStatuses []corev1.ContainerStatus,
  1780  	resolverFn EnvVarResolverFunc, w PrefixWriter, space string) {
  1781  	statuses := map[string]corev1.ContainerStatus{}
  1782  	for _, status := range containerStatuses {
  1783  		statuses[status.Name] = status
  1784  	}
  1785  
  1786  	describeContainersLabel(containers, label, space, w)
  1787  
  1788  	for _, container := range containers {
  1789  		status, ok := statuses[container.Name]
  1790  		describeContainerBasicInfo(container, status, ok, space, w)
  1791  		describeContainerCommand(container, w)
  1792  		if ok {
  1793  			describeContainerState(status, w)
  1794  		}
  1795  		describeContainerResource(container, w)
  1796  		describeContainerProbe(container, w)
  1797  		if len(container.EnvFrom) > 0 {
  1798  			describeContainerEnvFrom(container, resolverFn, w)
  1799  		}
  1800  		describeContainerEnvVars(container, resolverFn, w)
  1801  		describeContainerVolumes(container, w)
  1802  	}
  1803  }
  1804  
  1805  func describeContainersLabel(containers []corev1.Container, label, space string, w PrefixWriter) {
  1806  	none := ""
  1807  	if len(containers) == 0 {
  1808  		none = " <none>"
  1809  	}
  1810  	w.Write(LEVEL_0, "%s%s:%s\n", space, label, none)
  1811  }
  1812  
  1813  func describeContainerBasicInfo(container corev1.Container, status corev1.ContainerStatus, ok bool, space string, w PrefixWriter) {
  1814  	nameIndent := ""
  1815  	if len(space) > 0 {
  1816  		nameIndent = " "
  1817  	}
  1818  	w.Write(LEVEL_1, "%s%v:\n", nameIndent, container.Name)
  1819  	if ok {
  1820  		w.Write(LEVEL_2, "Container ID:\t%s\n", status.ContainerID)
  1821  	}
  1822  	w.Write(LEVEL_2, "Image:\t%s\n", container.Image)
  1823  	if ok {
  1824  		w.Write(LEVEL_2, "Image ID:\t%s\n", status.ImageID)
  1825  	}
  1826  	portString := describeContainerPorts(container.Ports)
  1827  	if strings.Contains(portString, ",") {
  1828  		w.Write(LEVEL_2, "Ports:\t%s\n", portString)
  1829  	} else {
  1830  		w.Write(LEVEL_2, "Port:\t%s\n", stringOrNone(portString))
  1831  	}
  1832  	hostPortString := describeContainerHostPorts(container.Ports)
  1833  	if strings.Contains(hostPortString, ",") {
  1834  		w.Write(LEVEL_2, "Host Ports:\t%s\n", hostPortString)
  1835  	} else {
  1836  		w.Write(LEVEL_2, "Host Port:\t%s\n", stringOrNone(hostPortString))
  1837  	}
  1838  	if container.SecurityContext != nil && container.SecurityContext.SeccompProfile != nil {
  1839  		w.Write(LEVEL_2, "SeccompProfile:\t%s\n", container.SecurityContext.SeccompProfile.Type)
  1840  		if container.SecurityContext.SeccompProfile.Type == corev1.SeccompProfileTypeLocalhost {
  1841  			w.Write(LEVEL_3, "LocalhostProfile:\t%s\n", *container.SecurityContext.SeccompProfile.LocalhostProfile)
  1842  		}
  1843  	}
  1844  }
  1845  
  1846  func describeContainerPorts(cPorts []corev1.ContainerPort) string {
  1847  	ports := make([]string, 0, len(cPorts))
  1848  	for _, cPort := range cPorts {
  1849  		ports = append(ports, fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol))
  1850  	}
  1851  	return strings.Join(ports, ", ")
  1852  }
  1853  
  1854  func describeContainerHostPorts(cPorts []corev1.ContainerPort) string {
  1855  	ports := make([]string, 0, len(cPorts))
  1856  	for _, cPort := range cPorts {
  1857  		ports = append(ports, fmt.Sprintf("%d/%s", cPort.HostPort, cPort.Protocol))
  1858  	}
  1859  	return strings.Join(ports, ", ")
  1860  }
  1861  
  1862  func describeContainerCommand(container corev1.Container, w PrefixWriter) {
  1863  	if len(container.Command) > 0 {
  1864  		w.Write(LEVEL_2, "Command:\n")
  1865  		for _, c := range container.Command {
  1866  			for _, s := range strings.Split(c, "\n") {
  1867  				w.Write(LEVEL_3, "%s\n", s)
  1868  			}
  1869  		}
  1870  	}
  1871  	if len(container.Args) > 0 {
  1872  		w.Write(LEVEL_2, "Args:\n")
  1873  		for _, arg := range container.Args {
  1874  			for _, s := range strings.Split(arg, "\n") {
  1875  				w.Write(LEVEL_3, "%s\n", s)
  1876  			}
  1877  		}
  1878  	}
  1879  }
  1880  
  1881  func describeContainerResource(container corev1.Container, w PrefixWriter) {
  1882  	resources := container.Resources
  1883  	if len(resources.Limits) > 0 {
  1884  		w.Write(LEVEL_2, "Limits:\n")
  1885  	}
  1886  	for _, name := range SortedResourceNames(resources.Limits) {
  1887  		quantity := resources.Limits[name]
  1888  		w.Write(LEVEL_3, "%s:\t%s\n", name, quantity.String())
  1889  	}
  1890  
  1891  	if len(resources.Requests) > 0 {
  1892  		w.Write(LEVEL_2, "Requests:\n")
  1893  	}
  1894  	for _, name := range SortedResourceNames(resources.Requests) {
  1895  		quantity := resources.Requests[name]
  1896  		w.Write(LEVEL_3, "%s:\t%s\n", name, quantity.String())
  1897  	}
  1898  }
  1899  
  1900  func describeContainerState(status corev1.ContainerStatus, w PrefixWriter) {
  1901  	describeStatus("State", status.State, w)
  1902  	if status.LastTerminationState.Terminated != nil {
  1903  		describeStatus("Last State", status.LastTerminationState, w)
  1904  	}
  1905  	w.Write(LEVEL_2, "Ready:\t%v\n", printBool(status.Ready))
  1906  	w.Write(LEVEL_2, "Restart Count:\t%d\n", status.RestartCount)
  1907  }
  1908  
  1909  func describeContainerProbe(container corev1.Container, w PrefixWriter) {
  1910  	if container.LivenessProbe != nil {
  1911  		probe := DescribeProbe(container.LivenessProbe)
  1912  		w.Write(LEVEL_2, "Liveness:\t%s\n", probe)
  1913  	}
  1914  	if container.ReadinessProbe != nil {
  1915  		probe := DescribeProbe(container.ReadinessProbe)
  1916  		w.Write(LEVEL_2, "Readiness:\t%s\n", probe)
  1917  	}
  1918  	if container.StartupProbe != nil {
  1919  		probe := DescribeProbe(container.StartupProbe)
  1920  		w.Write(LEVEL_2, "Startup:\t%s\n", probe)
  1921  	}
  1922  }
  1923  
  1924  func describeContainerVolumes(container corev1.Container, w PrefixWriter) {
  1925  	// Show volumeMounts
  1926  	none := ""
  1927  	if len(container.VolumeMounts) == 0 {
  1928  		none = "\t<none>"
  1929  	}
  1930  	w.Write(LEVEL_2, "Mounts:%s\n", none)
  1931  	sort.Sort(SortableVolumeMounts(container.VolumeMounts))
  1932  	for _, mount := range container.VolumeMounts {
  1933  		flags := []string{}
  1934  		if mount.ReadOnly {
  1935  			flags = append(flags, "ro")
  1936  		} else {
  1937  			flags = append(flags, "rw")
  1938  		}
  1939  		if len(mount.SubPath) > 0 {
  1940  			flags = append(flags, fmt.Sprintf("path=%q", mount.SubPath))
  1941  		}
  1942  		w.Write(LEVEL_3, "%s from %s (%s)\n", mount.MountPath, mount.Name, strings.Join(flags, ","))
  1943  	}
  1944  	// Show volumeDevices if exists
  1945  	if len(container.VolumeDevices) > 0 {
  1946  		w.Write(LEVEL_2, "Devices:%s\n", none)
  1947  		sort.Sort(SortableVolumeDevices(container.VolumeDevices))
  1948  		for _, device := range container.VolumeDevices {
  1949  			w.Write(LEVEL_3, "%s from %s\n", device.DevicePath, device.Name)
  1950  		}
  1951  	}
  1952  }
  1953  
  1954  func describeContainerEnvVars(container corev1.Container, resolverFn EnvVarResolverFunc, w PrefixWriter) {
  1955  	none := ""
  1956  	if len(container.Env) == 0 {
  1957  		none = "\t<none>"
  1958  	}
  1959  	w.Write(LEVEL_2, "Environment:%s\n", none)
  1960  
  1961  	for _, e := range container.Env {
  1962  		if e.ValueFrom == nil {
  1963  			for i, s := range strings.Split(e.Value, "\n") {
  1964  				if i == 0 {
  1965  					w.Write(LEVEL_3, "%s:\t%s\n", e.Name, s)
  1966  				} else {
  1967  					w.Write(LEVEL_3, "\t%s\n", s)
  1968  				}
  1969  			}
  1970  			continue
  1971  		}
  1972  
  1973  		switch {
  1974  		case e.ValueFrom.FieldRef != nil:
  1975  			var valueFrom string
  1976  			if resolverFn != nil {
  1977  				valueFrom = resolverFn(e)
  1978  			}
  1979  			w.Write(LEVEL_3, "%s:\t%s (%s:%s)\n", e.Name, valueFrom, e.ValueFrom.FieldRef.APIVersion, e.ValueFrom.FieldRef.FieldPath)
  1980  		case e.ValueFrom.ResourceFieldRef != nil:
  1981  			valueFrom, err := resourcehelper.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container)
  1982  			if err != nil {
  1983  				valueFrom = ""
  1984  			}
  1985  			resource := e.ValueFrom.ResourceFieldRef.Resource
  1986  			if valueFrom == "0" && (resource == "limits.cpu" || resource == "limits.memory") {
  1987  				valueFrom = "node allocatable"
  1988  			}
  1989  			w.Write(LEVEL_3, "%s:\t%s (%s)\n", e.Name, valueFrom, resource)
  1990  		case e.ValueFrom.SecretKeyRef != nil:
  1991  			optional := e.ValueFrom.SecretKeyRef.Optional != nil && *e.ValueFrom.SecretKeyRef.Optional
  1992  			w.Write(LEVEL_3, "%s:\t<set to the key '%s' in secret '%s'>\tOptional: %t\n", e.Name, e.ValueFrom.SecretKeyRef.Key, e.ValueFrom.SecretKeyRef.Name, optional)
  1993  		case e.ValueFrom.ConfigMapKeyRef != nil:
  1994  			optional := e.ValueFrom.ConfigMapKeyRef.Optional != nil && *e.ValueFrom.ConfigMapKeyRef.Optional
  1995  			w.Write(LEVEL_3, "%s:\t<set to the key '%s' of config map '%s'>\tOptional: %t\n", e.Name, e.ValueFrom.ConfigMapKeyRef.Key, e.ValueFrom.ConfigMapKeyRef.Name, optional)
  1996  		}
  1997  	}
  1998  }
  1999  
  2000  func describeContainerEnvFrom(container corev1.Container, resolverFn EnvVarResolverFunc, w PrefixWriter) {
  2001  	none := ""
  2002  	if len(container.EnvFrom) == 0 {
  2003  		none = "\t<none>"
  2004  	}
  2005  	w.Write(LEVEL_2, "Environment Variables from:%s\n", none)
  2006  
  2007  	for _, e := range container.EnvFrom {
  2008  		from := ""
  2009  		name := ""
  2010  		optional := false
  2011  		if e.ConfigMapRef != nil {
  2012  			from = "ConfigMap"
  2013  			name = e.ConfigMapRef.Name
  2014  			optional = e.ConfigMapRef.Optional != nil && *e.ConfigMapRef.Optional
  2015  		} else if e.SecretRef != nil {
  2016  			from = "Secret"
  2017  			name = e.SecretRef.Name
  2018  			optional = e.SecretRef.Optional != nil && *e.SecretRef.Optional
  2019  		}
  2020  		if len(e.Prefix) == 0 {
  2021  			w.Write(LEVEL_3, "%s\t%s\tOptional: %t\n", name, from, optional)
  2022  		} else {
  2023  			w.Write(LEVEL_3, "%s\t%s with prefix '%s'\tOptional: %t\n", name, from, e.Prefix, optional)
  2024  		}
  2025  	}
  2026  }
  2027  
  2028  // DescribeProbe is exported for consumers in other API groups that have probes
  2029  func DescribeProbe(probe *corev1.Probe) string {
  2030  	attrs := fmt.Sprintf("delay=%ds timeout=%ds period=%ds #success=%d #failure=%d", probe.InitialDelaySeconds, probe.TimeoutSeconds, probe.PeriodSeconds, probe.SuccessThreshold, probe.FailureThreshold)
  2031  	switch {
  2032  	case probe.Exec != nil:
  2033  		return fmt.Sprintf("exec %v %s", probe.Exec.Command, attrs)
  2034  	case probe.HTTPGet != nil:
  2035  		url := &url.URL{}
  2036  		url.Scheme = strings.ToLower(string(probe.HTTPGet.Scheme))
  2037  		if len(probe.HTTPGet.Port.String()) > 0 {
  2038  			url.Host = net.JoinHostPort(probe.HTTPGet.Host, probe.HTTPGet.Port.String())
  2039  		} else {
  2040  			url.Host = probe.HTTPGet.Host
  2041  		}
  2042  		url.Path = probe.HTTPGet.Path
  2043  		return fmt.Sprintf("http-get %s %s", url.String(), attrs)
  2044  	case probe.TCPSocket != nil:
  2045  		return fmt.Sprintf("tcp-socket %s:%s %s", probe.TCPSocket.Host, probe.TCPSocket.Port.String(), attrs)
  2046  
  2047  	case probe.GRPC != nil:
  2048  		return fmt.Sprintf("grpc <pod>:%d %s %s", probe.GRPC.Port, *(probe.GRPC.Service), attrs)
  2049  	}
  2050  	return fmt.Sprintf("unknown %s", attrs)
  2051  }
  2052  
  2053  type EnvVarResolverFunc func(e corev1.EnvVar) string
  2054  
  2055  // EnvValueFrom is exported for use by describers in other packages
  2056  func EnvValueRetriever(pod *corev1.Pod) EnvVarResolverFunc {
  2057  	return func(e corev1.EnvVar) string {
  2058  		gv, err := schema.ParseGroupVersion(e.ValueFrom.FieldRef.APIVersion)
  2059  		if err != nil {
  2060  			return ""
  2061  		}
  2062  		gvk := gv.WithKind("Pod")
  2063  		internalFieldPath, _, err := scheme.Scheme.ConvertFieldLabel(gvk, e.ValueFrom.FieldRef.FieldPath, "")
  2064  		if err != nil {
  2065  			return "" // pod validation should catch this on create
  2066  		}
  2067  
  2068  		valueFrom, err := fieldpath.ExtractFieldPathAsString(pod, internalFieldPath)
  2069  		if err != nil {
  2070  			return "" // pod validation should catch this on create
  2071  		}
  2072  
  2073  		return valueFrom
  2074  	}
  2075  }
  2076  
  2077  func describeStatus(stateName string, state corev1.ContainerState, w PrefixWriter) {
  2078  	switch {
  2079  	case state.Running != nil:
  2080  		w.Write(LEVEL_2, "%s:\tRunning\n", stateName)
  2081  		w.Write(LEVEL_3, "Started:\t%v\n", state.Running.StartedAt.Time.Format(time.RFC1123Z))
  2082  	case state.Waiting != nil:
  2083  		w.Write(LEVEL_2, "%s:\tWaiting\n", stateName)
  2084  		if state.Waiting.Reason != "" {
  2085  			w.Write(LEVEL_3, "Reason:\t%s\n", state.Waiting.Reason)
  2086  		}
  2087  	case state.Terminated != nil:
  2088  		w.Write(LEVEL_2, "%s:\tTerminated\n", stateName)
  2089  		if state.Terminated.Reason != "" {
  2090  			w.Write(LEVEL_3, "Reason:\t%s\n", state.Terminated.Reason)
  2091  		}
  2092  		if state.Terminated.Message != "" {
  2093  			w.Write(LEVEL_3, "Message:\t%s\n", state.Terminated.Message)
  2094  		}
  2095  		w.Write(LEVEL_3, "Exit Code:\t%d\n", state.Terminated.ExitCode)
  2096  		if state.Terminated.Signal > 0 {
  2097  			w.Write(LEVEL_3, "Signal:\t%d\n", state.Terminated.Signal)
  2098  		}
  2099  		w.Write(LEVEL_3, "Started:\t%s\n", state.Terminated.StartedAt.Time.Format(time.RFC1123Z))
  2100  		w.Write(LEVEL_3, "Finished:\t%s\n", state.Terminated.FinishedAt.Time.Format(time.RFC1123Z))
  2101  	default:
  2102  		w.Write(LEVEL_2, "%s:\tWaiting\n", stateName)
  2103  	}
  2104  }
  2105  
  2106  func describeVolumeClaimTemplates(templates []corev1.PersistentVolumeClaim, w PrefixWriter) {
  2107  	if len(templates) == 0 {
  2108  		w.Write(LEVEL_0, "Volume Claims:\t<none>\n")
  2109  		return
  2110  	}
  2111  	w.Write(LEVEL_0, "Volume Claims:\n")
  2112  	for _, pvc := range templates {
  2113  		w.Write(LEVEL_1, "Name:\t%s\n", pvc.Name)
  2114  		w.Write(LEVEL_1, "StorageClass:\t%s\n", storageutil.GetPersistentVolumeClaimClass(&pvc))
  2115  		printLabelsMultilineWithIndent(w, "  ", "Labels", "\t", pvc.Labels, sets.NewString())
  2116  		printLabelsMultilineWithIndent(w, "  ", "Annotations", "\t", pvc.Annotations, sets.NewString())
  2117  		if capacity, ok := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; ok {
  2118  			w.Write(LEVEL_1, "Capacity:\t%s\n", capacity.String())
  2119  		} else {
  2120  			w.Write(LEVEL_1, "Capacity:\t%s\n", "<default>")
  2121  		}
  2122  		w.Write(LEVEL_1, "Access Modes:\t%s\n", pvc.Spec.AccessModes)
  2123  	}
  2124  }
  2125  
  2126  func printBoolPtr(value *bool) string {
  2127  	if value != nil {
  2128  		return printBool(*value)
  2129  	}
  2130  
  2131  	return "<unset>"
  2132  }
  2133  
  2134  func printBool(value bool) string {
  2135  	if value {
  2136  		return "True"
  2137  	}
  2138  
  2139  	return "False"
  2140  }
  2141  
  2142  // ReplicationControllerDescriber generates information about a replication controller
  2143  // and the pods it has created.
  2144  type ReplicationControllerDescriber struct {
  2145  	clientset.Interface
  2146  }
  2147  
  2148  func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2149  	rc := d.CoreV1().ReplicationControllers(namespace)
  2150  	pc := d.CoreV1().Pods(namespace)
  2151  
  2152  	controller, err := rc.Get(context.TODO(), name, metav1.GetOptions{})
  2153  	if err != nil {
  2154  		return "", err
  2155  	}
  2156  
  2157  	selector := labels.SelectorFromSet(controller.Spec.Selector)
  2158  	running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, controller.UID, describerSettings)
  2159  	if err != nil {
  2160  		return "", err
  2161  	}
  2162  
  2163  	var events *corev1.EventList
  2164  	if describerSettings.ShowEvents {
  2165  		events, _ = searchEvents(d.CoreV1(), controller, describerSettings.ChunkSize)
  2166  	}
  2167  
  2168  	return describeReplicationController(controller, events, running, waiting, succeeded, failed)
  2169  }
  2170  
  2171  func describeReplicationController(controller *corev1.ReplicationController, events *corev1.EventList, running, waiting, succeeded, failed int) (string, error) {
  2172  	return tabbedString(func(out io.Writer) error {
  2173  		w := NewPrefixWriter(out)
  2174  		w.Write(LEVEL_0, "Name:\t%s\n", controller.Name)
  2175  		w.Write(LEVEL_0, "Namespace:\t%s\n", controller.Namespace)
  2176  		w.Write(LEVEL_0, "Selector:\t%s\n", labels.FormatLabels(controller.Spec.Selector))
  2177  		printLabelsMultiline(w, "Labels", controller.Labels)
  2178  		printAnnotationsMultiline(w, "Annotations", controller.Annotations)
  2179  		w.Write(LEVEL_0, "Replicas:\t%d current / %d desired\n", controller.Status.Replicas, *controller.Spec.Replicas)
  2180  		w.Write(LEVEL_0, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
  2181  		DescribePodTemplate(controller.Spec.Template, w)
  2182  		if len(controller.Status.Conditions) > 0 {
  2183  			w.Write(LEVEL_0, "Conditions:\n  Type\tStatus\tReason\n")
  2184  			w.Write(LEVEL_1, "----\t------\t------\n")
  2185  			for _, c := range controller.Status.Conditions {
  2186  				w.Write(LEVEL_1, "%v \t%v\t%v\n", c.Type, c.Status, c.Reason)
  2187  			}
  2188  		}
  2189  		if events != nil {
  2190  			DescribeEvents(events, w)
  2191  		}
  2192  		return nil
  2193  	})
  2194  }
  2195  
  2196  func DescribePodTemplate(template *corev1.PodTemplateSpec, w PrefixWriter) {
  2197  	w.Write(LEVEL_0, "Pod Template:\n")
  2198  	if template == nil {
  2199  		w.Write(LEVEL_1, "<unset>")
  2200  		return
  2201  	}
  2202  	printLabelsMultiline(w, "  Labels", template.Labels)
  2203  	if len(template.Annotations) > 0 {
  2204  		printAnnotationsMultiline(w, "  Annotations", template.Annotations)
  2205  	}
  2206  	if len(template.Spec.ServiceAccountName) > 0 {
  2207  		w.Write(LEVEL_1, "Service Account:\t%s\n", template.Spec.ServiceAccountName)
  2208  	}
  2209  	if len(template.Spec.InitContainers) > 0 {
  2210  		describeContainers("Init Containers", template.Spec.InitContainers, nil, nil, w, "  ")
  2211  	}
  2212  	describeContainers("Containers", template.Spec.Containers, nil, nil, w, "  ")
  2213  	describeVolumes(template.Spec.Volumes, w, "  ")
  2214  	describeTopologySpreadConstraints(template.Spec.TopologySpreadConstraints, w, "  ")
  2215  	if len(template.Spec.PriorityClassName) > 0 {
  2216  		w.Write(LEVEL_1, "Priority Class Name:\t%s\n", template.Spec.PriorityClassName)
  2217  	}
  2218  	printLabelsMultiline(w, "  Node-Selectors", template.Spec.NodeSelector)
  2219  	printPodTolerationsMultiline(w, "  Tolerations", template.Spec.Tolerations)
  2220  }
  2221  
  2222  // ReplicaSetDescriber generates information about a ReplicaSet and the pods it has created.
  2223  type ReplicaSetDescriber struct {
  2224  	clientset.Interface
  2225  }
  2226  
  2227  func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2228  	rsc := d.AppsV1().ReplicaSets(namespace)
  2229  	pc := d.CoreV1().Pods(namespace)
  2230  
  2231  	rs, err := rsc.Get(context.TODO(), name, metav1.GetOptions{})
  2232  	if err != nil {
  2233  		return "", err
  2234  	}
  2235  
  2236  	selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
  2237  	if err != nil {
  2238  		return "", err
  2239  	}
  2240  
  2241  	running, waiting, succeeded, failed, getPodErr := getPodStatusForController(pc, selector, rs.UID, describerSettings)
  2242  
  2243  	var events *corev1.EventList
  2244  	if describerSettings.ShowEvents {
  2245  		events, _ = searchEvents(d.CoreV1(), rs, describerSettings.ChunkSize)
  2246  	}
  2247  
  2248  	return describeReplicaSet(rs, events, running, waiting, succeeded, failed, getPodErr)
  2249  }
  2250  
  2251  func describeReplicaSet(rs *appsv1.ReplicaSet, events *corev1.EventList, running, waiting, succeeded, failed int, getPodErr error) (string, error) {
  2252  	return tabbedString(func(out io.Writer) error {
  2253  		w := NewPrefixWriter(out)
  2254  		w.Write(LEVEL_0, "Name:\t%s\n", rs.Name)
  2255  		w.Write(LEVEL_0, "Namespace:\t%s\n", rs.Namespace)
  2256  		w.Write(LEVEL_0, "Selector:\t%s\n", metav1.FormatLabelSelector(rs.Spec.Selector))
  2257  		printLabelsMultiline(w, "Labels", rs.Labels)
  2258  		printAnnotationsMultiline(w, "Annotations", rs.Annotations)
  2259  		if controlledBy := printController(rs); len(controlledBy) > 0 {
  2260  			w.Write(LEVEL_0, "Controlled By:\t%s\n", controlledBy)
  2261  		}
  2262  		w.Write(LEVEL_0, "Replicas:\t%d current / %d desired\n", rs.Status.Replicas, *rs.Spec.Replicas)
  2263  		w.Write(LEVEL_0, "Pods Status:\t")
  2264  		if getPodErr != nil {
  2265  			w.Write(LEVEL_0, "error in fetching pods: %s\n", getPodErr)
  2266  		} else {
  2267  			w.Write(LEVEL_0, "%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
  2268  		}
  2269  		DescribePodTemplate(&rs.Spec.Template, w)
  2270  		if len(rs.Status.Conditions) > 0 {
  2271  			w.Write(LEVEL_0, "Conditions:\n  Type\tStatus\tReason\n")
  2272  			w.Write(LEVEL_1, "----\t------\t------\n")
  2273  			for _, c := range rs.Status.Conditions {
  2274  				w.Write(LEVEL_1, "%v \t%v\t%v\n", c.Type, c.Status, c.Reason)
  2275  			}
  2276  		}
  2277  		if events != nil {
  2278  			DescribeEvents(events, w)
  2279  		}
  2280  		return nil
  2281  	})
  2282  }
  2283  
  2284  // JobDescriber generates information about a job and the pods it has created.
  2285  type JobDescriber struct {
  2286  	clientset.Interface
  2287  }
  2288  
  2289  func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2290  	job, err := d.BatchV1().Jobs(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  2291  	if err != nil {
  2292  		return "", err
  2293  	}
  2294  
  2295  	var events *corev1.EventList
  2296  	if describerSettings.ShowEvents {
  2297  		events, _ = searchEvents(d.CoreV1(), job, describerSettings.ChunkSize)
  2298  	}
  2299  
  2300  	return describeJob(job, events)
  2301  }
  2302  
  2303  func describeJob(job *batchv1.Job, events *corev1.EventList) (string, error) {
  2304  	return tabbedString(func(out io.Writer) error {
  2305  		w := NewPrefixWriter(out)
  2306  		w.Write(LEVEL_0, "Name:\t%s\n", job.Name)
  2307  		w.Write(LEVEL_0, "Namespace:\t%s\n", job.Namespace)
  2308  		if selector, err := metav1.LabelSelectorAsSelector(job.Spec.Selector); err == nil {
  2309  			w.Write(LEVEL_0, "Selector:\t%s\n", selector)
  2310  		} else {
  2311  			w.Write(LEVEL_0, "Selector:\tFailed to get selector: %s\n", err)
  2312  		}
  2313  		printLabelsMultiline(w, "Labels", job.Labels)
  2314  		printAnnotationsMultiline(w, "Annotations", job.Annotations)
  2315  		if controlledBy := printController(job); len(controlledBy) > 0 {
  2316  			w.Write(LEVEL_0, "Controlled By:\t%s\n", controlledBy)
  2317  		}
  2318  		if job.Spec.Parallelism != nil {
  2319  			w.Write(LEVEL_0, "Parallelism:\t%d\n", *job.Spec.Parallelism)
  2320  		}
  2321  		if job.Spec.Completions != nil {
  2322  			w.Write(LEVEL_0, "Completions:\t%d\n", *job.Spec.Completions)
  2323  		} else {
  2324  			w.Write(LEVEL_0, "Completions:\t<unset>\n")
  2325  		}
  2326  		if job.Spec.CompletionMode != nil {
  2327  			w.Write(LEVEL_0, "Completion Mode:\t%s\n", *job.Spec.CompletionMode)
  2328  		}
  2329  		if job.Spec.Suspend != nil {
  2330  			w.Write(LEVEL_0, "Suspend:\t%v\n", *job.Spec.Suspend)
  2331  		}
  2332  		if job.Spec.BackoffLimit != nil {
  2333  			w.Write(LEVEL_0, "Backoff Limit:\t%v\n", *job.Spec.BackoffLimit)
  2334  		}
  2335  		if job.Spec.TTLSecondsAfterFinished != nil {
  2336  			w.Write(LEVEL_0, "TTL Seconds After Finished:\t%v\n", *job.Spec.TTLSecondsAfterFinished)
  2337  		}
  2338  		if job.Status.StartTime != nil {
  2339  			w.Write(LEVEL_0, "Start Time:\t%s\n", job.Status.StartTime.Time.Format(time.RFC1123Z))
  2340  		}
  2341  		if job.Status.CompletionTime != nil {
  2342  			w.Write(LEVEL_0, "Completed At:\t%s\n", job.Status.CompletionTime.Time.Format(time.RFC1123Z))
  2343  		}
  2344  		if job.Status.StartTime != nil && job.Status.CompletionTime != nil {
  2345  			w.Write(LEVEL_0, "Duration:\t%s\n", duration.HumanDuration(job.Status.CompletionTime.Sub(job.Status.StartTime.Time)))
  2346  		}
  2347  		if job.Spec.ActiveDeadlineSeconds != nil {
  2348  			w.Write(LEVEL_0, "Active Deadline Seconds:\t%ds\n", *job.Spec.ActiveDeadlineSeconds)
  2349  		}
  2350  		if job.Status.Ready == nil {
  2351  			w.Write(LEVEL_0, "Pods Statuses:\t%d Active / %d Succeeded / %d Failed\n", job.Status.Active, job.Status.Succeeded, job.Status.Failed)
  2352  		} else {
  2353  			w.Write(LEVEL_0, "Pods Statuses:\t%d Active (%d Ready) / %d Succeeded / %d Failed\n", job.Status.Active, *job.Status.Ready, job.Status.Succeeded, job.Status.Failed)
  2354  		}
  2355  		if job.Spec.CompletionMode != nil && *job.Spec.CompletionMode == batchv1.IndexedCompletion {
  2356  			w.Write(LEVEL_0, "Completed Indexes:\t%s\n", capIndexesListOrNone(job.Status.CompletedIndexes, 50))
  2357  		}
  2358  		DescribePodTemplate(&job.Spec.Template, w)
  2359  		if events != nil {
  2360  			DescribeEvents(events, w)
  2361  		}
  2362  		return nil
  2363  	})
  2364  }
  2365  
  2366  func capIndexesListOrNone(indexes string, softLimit int) string {
  2367  	if len(indexes) == 0 {
  2368  		return "<none>"
  2369  	}
  2370  	ix := softLimit
  2371  	for ; ix < len(indexes); ix++ {
  2372  		if indexes[ix] == ',' {
  2373  			break
  2374  		}
  2375  	}
  2376  	if ix >= len(indexes) {
  2377  		return indexes
  2378  	}
  2379  	return indexes[:ix+1] + "..."
  2380  }
  2381  
  2382  // CronJobDescriber generates information about a cron job and the jobs it has created.
  2383  type CronJobDescriber struct {
  2384  	client clientset.Interface
  2385  }
  2386  
  2387  func (d *CronJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2388  	var events *corev1.EventList
  2389  
  2390  	cronJob, err := d.client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  2391  	if err != nil {
  2392  		return "", err
  2393  	}
  2394  
  2395  	if describerSettings.ShowEvents {
  2396  		events, _ = searchEvents(d.client.CoreV1(), cronJob, describerSettings.ChunkSize)
  2397  	}
  2398  	return describeCronJob(cronJob, events)
  2399  }
  2400  
  2401  func describeCronJob(cronJob *batchv1.CronJob, events *corev1.EventList) (string, error) {
  2402  	return tabbedString(func(out io.Writer) error {
  2403  		w := NewPrefixWriter(out)
  2404  		w.Write(LEVEL_0, "Name:\t%s\n", cronJob.Name)
  2405  		w.Write(LEVEL_0, "Namespace:\t%s\n", cronJob.Namespace)
  2406  		printLabelsMultiline(w, "Labels", cronJob.Labels)
  2407  		printAnnotationsMultiline(w, "Annotations", cronJob.Annotations)
  2408  		w.Write(LEVEL_0, "Schedule:\t%s\n", cronJob.Spec.Schedule)
  2409  		w.Write(LEVEL_0, "Concurrency Policy:\t%s\n", cronJob.Spec.ConcurrencyPolicy)
  2410  		w.Write(LEVEL_0, "Suspend:\t%s\n", printBoolPtr(cronJob.Spec.Suspend))
  2411  		if cronJob.Spec.SuccessfulJobsHistoryLimit != nil {
  2412  			w.Write(LEVEL_0, "Successful Job History Limit:\t%d\n", *cronJob.Spec.SuccessfulJobsHistoryLimit)
  2413  		} else {
  2414  			w.Write(LEVEL_0, "Successful Job History Limit:\t<unset>\n")
  2415  		}
  2416  		if cronJob.Spec.FailedJobsHistoryLimit != nil {
  2417  			w.Write(LEVEL_0, "Failed Job History Limit:\t%d\n", *cronJob.Spec.FailedJobsHistoryLimit)
  2418  		} else {
  2419  			w.Write(LEVEL_0, "Failed Job History Limit:\t<unset>\n")
  2420  		}
  2421  		if cronJob.Spec.StartingDeadlineSeconds != nil {
  2422  			w.Write(LEVEL_0, "Starting Deadline Seconds:\t%ds\n", *cronJob.Spec.StartingDeadlineSeconds)
  2423  		} else {
  2424  			w.Write(LEVEL_0, "Starting Deadline Seconds:\t<unset>\n")
  2425  		}
  2426  		describeJobTemplate(cronJob.Spec.JobTemplate, w)
  2427  		if cronJob.Status.LastScheduleTime != nil {
  2428  			w.Write(LEVEL_0, "Last Schedule Time:\t%s\n", cronJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z))
  2429  		} else {
  2430  			w.Write(LEVEL_0, "Last Schedule Time:\t<unset>\n")
  2431  		}
  2432  		printActiveJobs(w, "Active Jobs", cronJob.Status.Active)
  2433  		if events != nil {
  2434  			DescribeEvents(events, w)
  2435  		}
  2436  		return nil
  2437  	})
  2438  }
  2439  
  2440  func describeJobTemplate(jobTemplate batchv1.JobTemplateSpec, w PrefixWriter) {
  2441  	if jobTemplate.Spec.Selector != nil {
  2442  		if selector, err := metav1.LabelSelectorAsSelector(jobTemplate.Spec.Selector); err == nil {
  2443  			w.Write(LEVEL_0, "Selector:\t%s\n", selector)
  2444  		} else {
  2445  			w.Write(LEVEL_0, "Selector:\tFailed to get selector: %s\n", err)
  2446  		}
  2447  	} else {
  2448  		w.Write(LEVEL_0, "Selector:\t<unset>\n")
  2449  	}
  2450  	if jobTemplate.Spec.Parallelism != nil {
  2451  		w.Write(LEVEL_0, "Parallelism:\t%d\n", *jobTemplate.Spec.Parallelism)
  2452  	} else {
  2453  		w.Write(LEVEL_0, "Parallelism:\t<unset>\n")
  2454  	}
  2455  	if jobTemplate.Spec.Completions != nil {
  2456  		w.Write(LEVEL_0, "Completions:\t%d\n", *jobTemplate.Spec.Completions)
  2457  	} else {
  2458  		w.Write(LEVEL_0, "Completions:\t<unset>\n")
  2459  	}
  2460  	if jobTemplate.Spec.ActiveDeadlineSeconds != nil {
  2461  		w.Write(LEVEL_0, "Active Deadline Seconds:\t%ds\n", *jobTemplate.Spec.ActiveDeadlineSeconds)
  2462  	}
  2463  	DescribePodTemplate(&jobTemplate.Spec.Template, w)
  2464  }
  2465  
  2466  func printActiveJobs(w PrefixWriter, title string, jobs []corev1.ObjectReference) {
  2467  	w.Write(LEVEL_0, "%s:\t", title)
  2468  	if len(jobs) == 0 {
  2469  		w.WriteLine("<none>")
  2470  		return
  2471  	}
  2472  
  2473  	for i, job := range jobs {
  2474  		if i != 0 {
  2475  			w.Write(LEVEL_0, ", ")
  2476  		}
  2477  		w.Write(LEVEL_0, "%s", job.Name)
  2478  	}
  2479  	w.WriteLine("")
  2480  }
  2481  
  2482  // DaemonSetDescriber generates information about a daemon set and the pods it has created.
  2483  type DaemonSetDescriber struct {
  2484  	clientset.Interface
  2485  }
  2486  
  2487  func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2488  	dc := d.AppsV1().DaemonSets(namespace)
  2489  	pc := d.CoreV1().Pods(namespace)
  2490  
  2491  	daemon, err := dc.Get(context.TODO(), name, metav1.GetOptions{})
  2492  	if err != nil {
  2493  		return "", err
  2494  	}
  2495  
  2496  	selector, err := metav1.LabelSelectorAsSelector(daemon.Spec.Selector)
  2497  	if err != nil {
  2498  		return "", err
  2499  	}
  2500  	running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, daemon.UID, describerSettings)
  2501  	if err != nil {
  2502  		return "", err
  2503  	}
  2504  
  2505  	var events *corev1.EventList
  2506  	if describerSettings.ShowEvents {
  2507  		events, _ = searchEvents(d.CoreV1(), daemon, describerSettings.ChunkSize)
  2508  	}
  2509  
  2510  	return describeDaemonSet(daemon, events, running, waiting, succeeded, failed)
  2511  }
  2512  
  2513  func describeDaemonSet(daemon *appsv1.DaemonSet, events *corev1.EventList, running, waiting, succeeded, failed int) (string, error) {
  2514  	return tabbedString(func(out io.Writer) error {
  2515  		w := NewPrefixWriter(out)
  2516  		w.Write(LEVEL_0, "Name:\t%s\n", daemon.Name)
  2517  		selector, err := metav1.LabelSelectorAsSelector(daemon.Spec.Selector)
  2518  		if err != nil {
  2519  			// this shouldn't happen if LabelSelector passed validation
  2520  			return err
  2521  		}
  2522  		w.Write(LEVEL_0, "Selector:\t%s\n", selector)
  2523  		w.Write(LEVEL_0, "Node-Selector:\t%s\n", labels.FormatLabels(daemon.Spec.Template.Spec.NodeSelector))
  2524  		printLabelsMultiline(w, "Labels", daemon.Labels)
  2525  		printAnnotationsMultiline(w, "Annotations", daemon.Annotations)
  2526  		w.Write(LEVEL_0, "Desired Number of Nodes Scheduled: %d\n", daemon.Status.DesiredNumberScheduled)
  2527  		w.Write(LEVEL_0, "Current Number of Nodes Scheduled: %d\n", daemon.Status.CurrentNumberScheduled)
  2528  		w.Write(LEVEL_0, "Number of Nodes Scheduled with Up-to-date Pods: %d\n", daemon.Status.UpdatedNumberScheduled)
  2529  		w.Write(LEVEL_0, "Number of Nodes Scheduled with Available Pods: %d\n", daemon.Status.NumberAvailable)
  2530  		w.Write(LEVEL_0, "Number of Nodes Misscheduled: %d\n", daemon.Status.NumberMisscheduled)
  2531  		w.Write(LEVEL_0, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
  2532  		DescribePodTemplate(&daemon.Spec.Template, w)
  2533  		if events != nil {
  2534  			DescribeEvents(events, w)
  2535  		}
  2536  		return nil
  2537  	})
  2538  }
  2539  
  2540  // SecretDescriber generates information about a secret
  2541  type SecretDescriber struct {
  2542  	clientset.Interface
  2543  }
  2544  
  2545  func (d *SecretDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2546  	c := d.CoreV1().Secrets(namespace)
  2547  
  2548  	secret, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  2549  	if err != nil {
  2550  		return "", err
  2551  	}
  2552  
  2553  	return describeSecret(secret)
  2554  }
  2555  
  2556  func describeSecret(secret *corev1.Secret) (string, error) {
  2557  	return tabbedString(func(out io.Writer) error {
  2558  		w := NewPrefixWriter(out)
  2559  		w.Write(LEVEL_0, "Name:\t%s\n", secret.Name)
  2560  		w.Write(LEVEL_0, "Namespace:\t%s\n", secret.Namespace)
  2561  		printLabelsMultiline(w, "Labels", secret.Labels)
  2562  		printAnnotationsMultiline(w, "Annotations", secret.Annotations)
  2563  
  2564  		w.Write(LEVEL_0, "\nType:\t%s\n", secret.Type)
  2565  
  2566  		w.Write(LEVEL_0, "\nData\n====\n")
  2567  		for k, v := range secret.Data {
  2568  			switch {
  2569  			case k == corev1.ServiceAccountTokenKey && secret.Type == corev1.SecretTypeServiceAccountToken:
  2570  				w.Write(LEVEL_0, "%s:\t%s\n", k, string(v))
  2571  			default:
  2572  				w.Write(LEVEL_0, "%s:\t%d bytes\n", k, len(v))
  2573  			}
  2574  		}
  2575  
  2576  		return nil
  2577  	})
  2578  }
  2579  
  2580  type IngressDescriber struct {
  2581  	client clientset.Interface
  2582  }
  2583  
  2584  func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2585  	var events *corev1.EventList
  2586  
  2587  	// try ingress/v1 first (v1.19) and fallback to ingress/v1beta if an err occurs
  2588  	netV1, err := i.client.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  2589  	if err == nil {
  2590  		if describerSettings.ShowEvents {
  2591  			events, _ = searchEvents(i.client.CoreV1(), netV1, describerSettings.ChunkSize)
  2592  		}
  2593  		return i.describeIngressV1(netV1, events)
  2594  	}
  2595  	netV1beta1, err := i.client.NetworkingV1beta1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  2596  	if err == nil {
  2597  		if describerSettings.ShowEvents {
  2598  			events, _ = searchEvents(i.client.CoreV1(), netV1beta1, describerSettings.ChunkSize)
  2599  		}
  2600  		return i.describeIngressV1beta1(netV1beta1, events)
  2601  	}
  2602  	return "", err
  2603  }
  2604  
  2605  func (i *IngressDescriber) describeBackendV1beta1(ns string, backend *networkingv1beta1.IngressBackend) string {
  2606  	endpoints, err := i.client.CoreV1().Endpoints(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{})
  2607  	if err != nil {
  2608  		return fmt.Sprintf("<error: %v>", err)
  2609  	}
  2610  	service, err := i.client.CoreV1().Services(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{})
  2611  	if err != nil {
  2612  		return fmt.Sprintf("<error: %v>", err)
  2613  	}
  2614  	spName := ""
  2615  	for i := range service.Spec.Ports {
  2616  		sp := &service.Spec.Ports[i]
  2617  		switch backend.ServicePort.Type {
  2618  		case intstr.String:
  2619  			if backend.ServicePort.StrVal == sp.Name {
  2620  				spName = sp.Name
  2621  			}
  2622  		case intstr.Int:
  2623  			if int32(backend.ServicePort.IntVal) == sp.Port {
  2624  				spName = sp.Name
  2625  			}
  2626  		}
  2627  	}
  2628  	return formatEndpoints(endpoints, sets.NewString(spName))
  2629  }
  2630  
  2631  func (i *IngressDescriber) describeBackendV1(ns string, backend *networkingv1.IngressBackend) string {
  2632  
  2633  	if backend.Service != nil {
  2634  		sb := serviceBackendStringer(backend.Service)
  2635  		endpoints, err := i.client.CoreV1().Endpoints(ns).Get(context.TODO(), backend.Service.Name, metav1.GetOptions{})
  2636  		if err != nil {
  2637  			return fmt.Sprintf("%v (<error: %v>)", sb, err)
  2638  		}
  2639  		service, err := i.client.CoreV1().Services(ns).Get(context.TODO(), backend.Service.Name, metav1.GetOptions{})
  2640  		if err != nil {
  2641  			return fmt.Sprintf("%v(<error: %v>)", sb, err)
  2642  		}
  2643  		spName := ""
  2644  		for i := range service.Spec.Ports {
  2645  			sp := &service.Spec.Ports[i]
  2646  			if backend.Service.Port.Number != 0 && backend.Service.Port.Number == sp.Port {
  2647  				spName = sp.Name
  2648  			} else if len(backend.Service.Port.Name) > 0 && backend.Service.Port.Name == sp.Name {
  2649  				spName = sp.Name
  2650  			}
  2651  		}
  2652  		ep := formatEndpoints(endpoints, sets.NewString(spName))
  2653  		return fmt.Sprintf("%s (%s)", sb, ep)
  2654  	}
  2655  	if backend.Resource != nil {
  2656  		ic := backend.Resource
  2657  		apiGroup := "<none>"
  2658  		if ic.APIGroup != nil {
  2659  			apiGroup = fmt.Sprintf("%v", *ic.APIGroup)
  2660  		}
  2661  		return fmt.Sprintf("APIGroup: %v, Kind: %v, Name: %v", apiGroup, ic.Kind, ic.Name)
  2662  	}
  2663  	return ""
  2664  }
  2665  
  2666  func (i *IngressDescriber) describeIngressV1(ing *networkingv1.Ingress, events *corev1.EventList) (string, error) {
  2667  	return tabbedString(func(out io.Writer) error {
  2668  		w := NewPrefixWriter(out)
  2669  		w.Write(LEVEL_0, "Name:\t%v\n", ing.Name)
  2670  		printLabelsMultiline(w, "Labels", ing.Labels)
  2671  		w.Write(LEVEL_0, "Namespace:\t%v\n", ing.Namespace)
  2672  		w.Write(LEVEL_0, "Address:\t%v\n", ingressLoadBalancerStatusStringerV1(ing.Status.LoadBalancer, true))
  2673  		ingressClassName := "<none>"
  2674  		if ing.Spec.IngressClassName != nil {
  2675  			ingressClassName = *ing.Spec.IngressClassName
  2676  		}
  2677  		w.Write(LEVEL_0, "Ingress Class:\t%v\n", ingressClassName)
  2678  		def := ing.Spec.DefaultBackend
  2679  		ns := ing.Namespace
  2680  		defaultBackendDescribe := "<default>"
  2681  		if def != nil {
  2682  			defaultBackendDescribe = i.describeBackendV1(ns, def)
  2683  		}
  2684  		w.Write(LEVEL_0, "Default backend:\t%s\n", defaultBackendDescribe)
  2685  		if len(ing.Spec.TLS) != 0 {
  2686  			describeIngressTLSV1(w, ing.Spec.TLS)
  2687  		}
  2688  		w.Write(LEVEL_0, "Rules:\n  Host\tPath\tBackends\n")
  2689  		w.Write(LEVEL_1, "----\t----\t--------\n")
  2690  		count := 0
  2691  		for _, rules := range ing.Spec.Rules {
  2692  
  2693  			if rules.HTTP == nil {
  2694  				continue
  2695  			}
  2696  			count++
  2697  			host := rules.Host
  2698  			if len(host) == 0 {
  2699  				host = "*"
  2700  			}
  2701  			w.Write(LEVEL_1, "%s\t\n", host)
  2702  			for _, path := range rules.HTTP.Paths {
  2703  				w.Write(LEVEL_2, "\t%s \t%s\n", path.Path, i.describeBackendV1(ing.Namespace, &path.Backend))
  2704  			}
  2705  		}
  2706  		if count == 0 {
  2707  			w.Write(LEVEL_1, "%s\t%s\t%s\n", "*", "*", defaultBackendDescribe)
  2708  		}
  2709  		printAnnotationsMultiline(w, "Annotations", ing.Annotations)
  2710  
  2711  		if events != nil {
  2712  			DescribeEvents(events, w)
  2713  		}
  2714  		return nil
  2715  	})
  2716  }
  2717  
  2718  func (i *IngressDescriber) describeIngressV1beta1(ing *networkingv1beta1.Ingress, events *corev1.EventList) (string, error) {
  2719  	return tabbedString(func(out io.Writer) error {
  2720  		w := NewPrefixWriter(out)
  2721  		w.Write(LEVEL_0, "Name:\t%v\n", ing.Name)
  2722  		printLabelsMultiline(w, "Labels", ing.Labels)
  2723  		w.Write(LEVEL_0, "Namespace:\t%v\n", ing.Namespace)
  2724  		w.Write(LEVEL_0, "Address:\t%v\n", ingressLoadBalancerStatusStringerV1beta1(ing.Status.LoadBalancer, true))
  2725  		ingressClassName := "<none>"
  2726  		if ing.Spec.IngressClassName != nil {
  2727  			ingressClassName = *ing.Spec.IngressClassName
  2728  		}
  2729  		w.Write(LEVEL_0, "Ingress Class:\t%v\n", ingressClassName)
  2730  		def := ing.Spec.Backend
  2731  		ns := ing.Namespace
  2732  		if def == nil {
  2733  			w.Write(LEVEL_0, "Default backend:\t<default>\n")
  2734  		} else {
  2735  			w.Write(LEVEL_0, "Default backend:\t%s\n", i.describeBackendV1beta1(ns, def))
  2736  		}
  2737  		if len(ing.Spec.TLS) != 0 {
  2738  			describeIngressTLSV1beta1(w, ing.Spec.TLS)
  2739  		}
  2740  		w.Write(LEVEL_0, "Rules:\n  Host\tPath\tBackends\n")
  2741  		w.Write(LEVEL_1, "----\t----\t--------\n")
  2742  		count := 0
  2743  		for _, rules := range ing.Spec.Rules {
  2744  
  2745  			if rules.HTTP == nil {
  2746  				continue
  2747  			}
  2748  			count++
  2749  			host := rules.Host
  2750  			if len(host) == 0 {
  2751  				host = "*"
  2752  			}
  2753  			w.Write(LEVEL_1, "%s\t\n", host)
  2754  			for _, path := range rules.HTTP.Paths {
  2755  				w.Write(LEVEL_2, "\t%s \t%s (%s)\n", path.Path, backendStringer(&path.Backend), i.describeBackendV1beta1(ing.Namespace, &path.Backend))
  2756  			}
  2757  		}
  2758  		if count == 0 {
  2759  			w.Write(LEVEL_1, "%s\t%s \t<default>\n", "*", "*")
  2760  		}
  2761  		printAnnotationsMultiline(w, "Annotations", ing.Annotations)
  2762  
  2763  		if events != nil {
  2764  			DescribeEvents(events, w)
  2765  		}
  2766  		return nil
  2767  	})
  2768  }
  2769  
  2770  func describeIngressTLSV1beta1(w PrefixWriter, ingTLS []networkingv1beta1.IngressTLS) {
  2771  	w.Write(LEVEL_0, "TLS:\n")
  2772  	for _, t := range ingTLS {
  2773  		if t.SecretName == "" {
  2774  			w.Write(LEVEL_1, "SNI routes %v\n", strings.Join(t.Hosts, ","))
  2775  		} else {
  2776  			w.Write(LEVEL_1, "%v terminates %v\n", t.SecretName, strings.Join(t.Hosts, ","))
  2777  		}
  2778  	}
  2779  }
  2780  
  2781  func describeIngressTLSV1(w PrefixWriter, ingTLS []networkingv1.IngressTLS) {
  2782  	w.Write(LEVEL_0, "TLS:\n")
  2783  	for _, t := range ingTLS {
  2784  		if t.SecretName == "" {
  2785  			w.Write(LEVEL_1, "SNI routes %v\n", strings.Join(t.Hosts, ","))
  2786  		} else {
  2787  			w.Write(LEVEL_1, "%v terminates %v\n", t.SecretName, strings.Join(t.Hosts, ","))
  2788  		}
  2789  	}
  2790  }
  2791  
  2792  type IngressClassDescriber struct {
  2793  	client clientset.Interface
  2794  }
  2795  
  2796  func (i *IngressClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2797  	var events *corev1.EventList
  2798  	// try IngressClass/v1 first (v1.19) and fallback to IngressClass/v1beta if an err occurs
  2799  	netV1, err := i.client.NetworkingV1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{})
  2800  	if err == nil {
  2801  		if describerSettings.ShowEvents {
  2802  			events, _ = searchEvents(i.client.CoreV1(), netV1, describerSettings.ChunkSize)
  2803  		}
  2804  		return i.describeIngressClassV1(netV1, events)
  2805  	}
  2806  	netV1beta1, err := i.client.NetworkingV1beta1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{})
  2807  	if err == nil {
  2808  		if describerSettings.ShowEvents {
  2809  			events, _ = searchEvents(i.client.CoreV1(), netV1beta1, describerSettings.ChunkSize)
  2810  		}
  2811  		return i.describeIngressClassV1beta1(netV1beta1, events)
  2812  	}
  2813  	return "", err
  2814  }
  2815  
  2816  func (i *IngressClassDescriber) describeIngressClassV1beta1(ic *networkingv1beta1.IngressClass, events *corev1.EventList) (string, error) {
  2817  	return tabbedString(func(out io.Writer) error {
  2818  		w := NewPrefixWriter(out)
  2819  		w.Write(LEVEL_0, "Name:\t%s\n", ic.Name)
  2820  		printLabelsMultiline(w, "Labels", ic.Labels)
  2821  		printAnnotationsMultiline(w, "Annotations", ic.Annotations)
  2822  		w.Write(LEVEL_0, "Controller:\t%v\n", ic.Spec.Controller)
  2823  
  2824  		if ic.Spec.Parameters != nil {
  2825  			w.Write(LEVEL_0, "Parameters:\n")
  2826  			if ic.Spec.Parameters.APIGroup != nil {
  2827  				w.Write(LEVEL_1, "APIGroup:\t%v\n", *ic.Spec.Parameters.APIGroup)
  2828  			}
  2829  			w.Write(LEVEL_1, "Kind:\t%v\n", ic.Spec.Parameters.Kind)
  2830  			w.Write(LEVEL_1, "Name:\t%v\n", ic.Spec.Parameters.Name)
  2831  		}
  2832  		if events != nil {
  2833  			DescribeEvents(events, w)
  2834  		}
  2835  		return nil
  2836  	})
  2837  }
  2838  
  2839  func (i *IngressClassDescriber) describeIngressClassV1(ic *networkingv1.IngressClass, events *corev1.EventList) (string, error) {
  2840  	return tabbedString(func(out io.Writer) error {
  2841  		w := NewPrefixWriter(out)
  2842  		w.Write(LEVEL_0, "Name:\t%s\n", ic.Name)
  2843  		printLabelsMultiline(w, "Labels", ic.Labels)
  2844  		printAnnotationsMultiline(w, "Annotations", ic.Annotations)
  2845  		w.Write(LEVEL_0, "Controller:\t%v\n", ic.Spec.Controller)
  2846  
  2847  		if ic.Spec.Parameters != nil {
  2848  			w.Write(LEVEL_0, "Parameters:\n")
  2849  			if ic.Spec.Parameters.APIGroup != nil {
  2850  				w.Write(LEVEL_1, "APIGroup:\t%v\n", *ic.Spec.Parameters.APIGroup)
  2851  			}
  2852  			w.Write(LEVEL_1, "Kind:\t%v\n", ic.Spec.Parameters.Kind)
  2853  			w.Write(LEVEL_1, "Name:\t%v\n", ic.Spec.Parameters.Name)
  2854  		}
  2855  		if events != nil {
  2856  			DescribeEvents(events, w)
  2857  		}
  2858  		return nil
  2859  	})
  2860  }
  2861  
  2862  // ServiceCIDRDescriber generates information about a ServiceCIDR.
  2863  type ServiceCIDRDescriber struct {
  2864  	client clientset.Interface
  2865  }
  2866  
  2867  func (c *ServiceCIDRDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2868  	var events *corev1.EventList
  2869  
  2870  	svcV1alpha1, err := c.client.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), name, metav1.GetOptions{})
  2871  	if err == nil {
  2872  		if describerSettings.ShowEvents {
  2873  			events, _ = searchEvents(c.client.CoreV1(), svcV1alpha1, describerSettings.ChunkSize)
  2874  		}
  2875  		return c.describeServiceCIDRV1alpha1(svcV1alpha1, events)
  2876  	}
  2877  	return "", err
  2878  }
  2879  
  2880  func (c *ServiceCIDRDescriber) describeServiceCIDRV1alpha1(svc *networkingv1alpha1.ServiceCIDR, events *corev1.EventList) (string, error) {
  2881  	return tabbedString(func(out io.Writer) error {
  2882  		w := NewPrefixWriter(out)
  2883  		w.Write(LEVEL_0, "Name:\t%v\n", svc.Name)
  2884  		printLabelsMultiline(w, "Labels", svc.Labels)
  2885  		printAnnotationsMultiline(w, "Annotations", svc.Annotations)
  2886  
  2887  		w.Write(LEVEL_0, "CIDRs:\t%v\n", strings.Join(svc.Spec.CIDRs, ", "))
  2888  
  2889  		if len(svc.Status.Conditions) > 0 {
  2890  			w.Write(LEVEL_0, "Status:\n")
  2891  			w.Write(LEVEL_0, "Conditions:\n")
  2892  			w.Write(LEVEL_1, "Type\tStatus\tLastTransitionTime\tReason\tMessage\n")
  2893  			w.Write(LEVEL_1, "----\t------\t------------------\t------\t-------\n")
  2894  			for _, c := range svc.Status.Conditions {
  2895  				w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
  2896  					c.Type,
  2897  					c.Status,
  2898  					c.LastTransitionTime.Time.Format(time.RFC1123Z),
  2899  					c.Reason,
  2900  					c.Message)
  2901  			}
  2902  		}
  2903  
  2904  		if events != nil {
  2905  			DescribeEvents(events, w)
  2906  		}
  2907  		return nil
  2908  	})
  2909  }
  2910  
  2911  // IPAddressDescriber generates information about an IPAddress.
  2912  type IPAddressDescriber struct {
  2913  	client clientset.Interface
  2914  }
  2915  
  2916  func (c *IPAddressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2917  	var events *corev1.EventList
  2918  
  2919  	ipV1alpha1, err := c.client.NetworkingV1alpha1().IPAddresses().Get(context.TODO(), name, metav1.GetOptions{})
  2920  	if err == nil {
  2921  		if describerSettings.ShowEvents {
  2922  			events, _ = searchEvents(c.client.CoreV1(), ipV1alpha1, describerSettings.ChunkSize)
  2923  		}
  2924  		return c.describeIPAddressV1alpha1(ipV1alpha1, events)
  2925  	}
  2926  	return "", err
  2927  }
  2928  
  2929  func (c *IPAddressDescriber) describeIPAddressV1alpha1(ip *networkingv1alpha1.IPAddress, events *corev1.EventList) (string, error) {
  2930  	return tabbedString(func(out io.Writer) error {
  2931  		w := NewPrefixWriter(out)
  2932  		w.Write(LEVEL_0, "Name:\t%v\n", ip.Name)
  2933  		printLabelsMultiline(w, "Labels", ip.Labels)
  2934  		printAnnotationsMultiline(w, "Annotations", ip.Annotations)
  2935  
  2936  		if ip.Spec.ParentRef != nil {
  2937  			w.Write(LEVEL_0, "Parent Reference:\n")
  2938  			w.Write(LEVEL_1, "Group:\t%v\n", ip.Spec.ParentRef.Group)
  2939  			w.Write(LEVEL_1, "Resource:\t%v\n", ip.Spec.ParentRef.Resource)
  2940  			w.Write(LEVEL_1, "Namespace:\t%v\n", ip.Spec.ParentRef.Namespace)
  2941  			w.Write(LEVEL_1, "Name:\t%v\n", ip.Spec.ParentRef.Name)
  2942  		}
  2943  
  2944  		if events != nil {
  2945  			DescribeEvents(events, w)
  2946  		}
  2947  		return nil
  2948  	})
  2949  }
  2950  
  2951  // ServiceDescriber generates information about a service.
  2952  type ServiceDescriber struct {
  2953  	clientset.Interface
  2954  }
  2955  
  2956  func (d *ServiceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  2957  	c := d.CoreV1().Services(namespace)
  2958  
  2959  	service, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  2960  	if err != nil {
  2961  		return "", err
  2962  	}
  2963  
  2964  	endpoints, _ := d.CoreV1().Endpoints(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  2965  	var events *corev1.EventList
  2966  	if describerSettings.ShowEvents {
  2967  		events, _ = searchEvents(d.CoreV1(), service, describerSettings.ChunkSize)
  2968  	}
  2969  	return describeService(service, endpoints, events)
  2970  }
  2971  
  2972  func buildIngressString(ingress []corev1.LoadBalancerIngress) string {
  2973  	var buffer bytes.Buffer
  2974  
  2975  	for i := range ingress {
  2976  		if i != 0 {
  2977  			buffer.WriteString(", ")
  2978  		}
  2979  		if ingress[i].IP != "" {
  2980  			buffer.WriteString(ingress[i].IP)
  2981  		} else {
  2982  			buffer.WriteString(ingress[i].Hostname)
  2983  		}
  2984  	}
  2985  	return buffer.String()
  2986  }
  2987  
  2988  func describeService(service *corev1.Service, endpoints *corev1.Endpoints, events *corev1.EventList) (string, error) {
  2989  	if endpoints == nil {
  2990  		endpoints = &corev1.Endpoints{}
  2991  	}
  2992  	return tabbedString(func(out io.Writer) error {
  2993  		w := NewPrefixWriter(out)
  2994  		w.Write(LEVEL_0, "Name:\t%s\n", service.Name)
  2995  		w.Write(LEVEL_0, "Namespace:\t%s\n", service.Namespace)
  2996  		printLabelsMultiline(w, "Labels", service.Labels)
  2997  		printAnnotationsMultiline(w, "Annotations", service.Annotations)
  2998  		w.Write(LEVEL_0, "Selector:\t%s\n", labels.FormatLabels(service.Spec.Selector))
  2999  		w.Write(LEVEL_0, "Type:\t%s\n", service.Spec.Type)
  3000  
  3001  		if service.Spec.IPFamilyPolicy != nil {
  3002  			w.Write(LEVEL_0, "IP Family Policy:\t%s\n", *(service.Spec.IPFamilyPolicy))
  3003  		}
  3004  
  3005  		if len(service.Spec.IPFamilies) > 0 {
  3006  			ipfamiliesStrings := make([]string, 0, len(service.Spec.IPFamilies))
  3007  			for _, family := range service.Spec.IPFamilies {
  3008  				ipfamiliesStrings = append(ipfamiliesStrings, string(family))
  3009  			}
  3010  
  3011  			w.Write(LEVEL_0, "IP Families:\t%s\n", strings.Join(ipfamiliesStrings, ","))
  3012  		} else {
  3013  			w.Write(LEVEL_0, "IP Families:\t%s\n", "<none>")
  3014  		}
  3015  
  3016  		w.Write(LEVEL_0, "IP:\t%s\n", service.Spec.ClusterIP)
  3017  		if len(service.Spec.ClusterIPs) > 0 {
  3018  			w.Write(LEVEL_0, "IPs:\t%s\n", strings.Join(service.Spec.ClusterIPs, ","))
  3019  		} else {
  3020  			w.Write(LEVEL_0, "IPs:\t%s\n", "<none>")
  3021  		}
  3022  
  3023  		if len(service.Spec.ExternalIPs) > 0 {
  3024  			w.Write(LEVEL_0, "External IPs:\t%v\n", strings.Join(service.Spec.ExternalIPs, ","))
  3025  		}
  3026  		if service.Spec.LoadBalancerIP != "" {
  3027  			w.Write(LEVEL_0, "IP:\t%s\n", service.Spec.LoadBalancerIP)
  3028  		}
  3029  		if service.Spec.ExternalName != "" {
  3030  			w.Write(LEVEL_0, "External Name:\t%s\n", service.Spec.ExternalName)
  3031  		}
  3032  		if len(service.Status.LoadBalancer.Ingress) > 0 {
  3033  			list := buildIngressString(service.Status.LoadBalancer.Ingress)
  3034  			w.Write(LEVEL_0, "LoadBalancer Ingress:\t%s\n", list)
  3035  		}
  3036  		for i := range service.Spec.Ports {
  3037  			sp := &service.Spec.Ports[i]
  3038  
  3039  			name := sp.Name
  3040  			if name == "" {
  3041  				name = "<unset>"
  3042  			}
  3043  			w.Write(LEVEL_0, "Port:\t%s\t%d/%s\n", name, sp.Port, sp.Protocol)
  3044  			if sp.TargetPort.Type == intstr.Type(intstr.Int) {
  3045  				w.Write(LEVEL_0, "TargetPort:\t%d/%s\n", sp.TargetPort.IntVal, sp.Protocol)
  3046  			} else {
  3047  				w.Write(LEVEL_0, "TargetPort:\t%s/%s\n", sp.TargetPort.StrVal, sp.Protocol)
  3048  			}
  3049  			if sp.NodePort != 0 {
  3050  				w.Write(LEVEL_0, "NodePort:\t%s\t%d/%s\n", name, sp.NodePort, sp.Protocol)
  3051  			}
  3052  			w.Write(LEVEL_0, "Endpoints:\t%s\n", formatEndpoints(endpoints, sets.NewString(sp.Name)))
  3053  		}
  3054  		w.Write(LEVEL_0, "Session Affinity:\t%s\n", service.Spec.SessionAffinity)
  3055  		if service.Spec.ExternalTrafficPolicy != "" {
  3056  			w.Write(LEVEL_0, "External Traffic Policy:\t%s\n", service.Spec.ExternalTrafficPolicy)
  3057  		}
  3058  		if service.Spec.HealthCheckNodePort != 0 {
  3059  			w.Write(LEVEL_0, "HealthCheck NodePort:\t%d\n", service.Spec.HealthCheckNodePort)
  3060  		}
  3061  		if len(service.Spec.LoadBalancerSourceRanges) > 0 {
  3062  			w.Write(LEVEL_0, "LoadBalancer Source Ranges:\t%v\n", strings.Join(service.Spec.LoadBalancerSourceRanges, ","))
  3063  		}
  3064  		if events != nil {
  3065  			DescribeEvents(events, w)
  3066  		}
  3067  		return nil
  3068  	})
  3069  }
  3070  
  3071  // EndpointsDescriber generates information about an Endpoint.
  3072  type EndpointsDescriber struct {
  3073  	clientset.Interface
  3074  }
  3075  
  3076  func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3077  	c := d.CoreV1().Endpoints(namespace)
  3078  
  3079  	ep, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  3080  	if err != nil {
  3081  		return "", err
  3082  	}
  3083  
  3084  	var events *corev1.EventList
  3085  	if describerSettings.ShowEvents {
  3086  		events, _ = searchEvents(d.CoreV1(), ep, describerSettings.ChunkSize)
  3087  	}
  3088  
  3089  	return describeEndpoints(ep, events)
  3090  }
  3091  
  3092  func describeEndpoints(ep *corev1.Endpoints, events *corev1.EventList) (string, error) {
  3093  	return tabbedString(func(out io.Writer) error {
  3094  		w := NewPrefixWriter(out)
  3095  		w.Write(LEVEL_0, "Name:\t%s\n", ep.Name)
  3096  		w.Write(LEVEL_0, "Namespace:\t%s\n", ep.Namespace)
  3097  		printLabelsMultiline(w, "Labels", ep.Labels)
  3098  		printAnnotationsMultiline(w, "Annotations", ep.Annotations)
  3099  
  3100  		w.Write(LEVEL_0, "Subsets:\n")
  3101  		for i := range ep.Subsets {
  3102  			subset := &ep.Subsets[i]
  3103  
  3104  			addresses := make([]string, 0, len(subset.Addresses))
  3105  			for _, addr := range subset.Addresses {
  3106  				addresses = append(addresses, addr.IP)
  3107  			}
  3108  			addressesString := strings.Join(addresses, ",")
  3109  			if len(addressesString) == 0 {
  3110  				addressesString = "<none>"
  3111  			}
  3112  			w.Write(LEVEL_1, "Addresses:\t%s\n", addressesString)
  3113  
  3114  			notReadyAddresses := make([]string, 0, len(subset.NotReadyAddresses))
  3115  			for _, addr := range subset.NotReadyAddresses {
  3116  				notReadyAddresses = append(notReadyAddresses, addr.IP)
  3117  			}
  3118  			notReadyAddressesString := strings.Join(notReadyAddresses, ",")
  3119  			if len(notReadyAddressesString) == 0 {
  3120  				notReadyAddressesString = "<none>"
  3121  			}
  3122  			w.Write(LEVEL_1, "NotReadyAddresses:\t%s\n", notReadyAddressesString)
  3123  
  3124  			if len(subset.Ports) > 0 {
  3125  				w.Write(LEVEL_1, "Ports:\n")
  3126  				w.Write(LEVEL_2, "Name\tPort\tProtocol\n")
  3127  				w.Write(LEVEL_2, "----\t----\t--------\n")
  3128  				for _, port := range subset.Ports {
  3129  					name := port.Name
  3130  					if len(name) == 0 {
  3131  						name = "<unset>"
  3132  					}
  3133  					w.Write(LEVEL_2, "%s\t%d\t%s\n", name, port.Port, port.Protocol)
  3134  				}
  3135  			}
  3136  			w.Write(LEVEL_0, "\n")
  3137  		}
  3138  
  3139  		if events != nil {
  3140  			DescribeEvents(events, w)
  3141  		}
  3142  		return nil
  3143  	})
  3144  }
  3145  
  3146  // EndpointSliceDescriber generates information about an EndpointSlice.
  3147  type EndpointSliceDescriber struct {
  3148  	clientset.Interface
  3149  }
  3150  
  3151  func (d *EndpointSliceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3152  	var events *corev1.EventList
  3153  	// try endpointslice/v1 first (v1.21) and fallback to v1beta1 if error occurs
  3154  
  3155  	epsV1, err := d.DiscoveryV1().EndpointSlices(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3156  	if err == nil {
  3157  		if describerSettings.ShowEvents {
  3158  			events, _ = searchEvents(d.CoreV1(), epsV1, describerSettings.ChunkSize)
  3159  		}
  3160  		return describeEndpointSliceV1(epsV1, events)
  3161  	}
  3162  
  3163  	epsV1beta1, err := d.DiscoveryV1beta1().EndpointSlices(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3164  	if err != nil {
  3165  		return "", err
  3166  	}
  3167  
  3168  	if describerSettings.ShowEvents {
  3169  		events, _ = searchEvents(d.CoreV1(), epsV1beta1, describerSettings.ChunkSize)
  3170  	}
  3171  
  3172  	return describeEndpointSliceV1beta1(epsV1beta1, events)
  3173  }
  3174  
  3175  func describeEndpointSliceV1(eps *discoveryv1.EndpointSlice, events *corev1.EventList) (string, error) {
  3176  	return tabbedString(func(out io.Writer) error {
  3177  		w := NewPrefixWriter(out)
  3178  		w.Write(LEVEL_0, "Name:\t%s\n", eps.Name)
  3179  		w.Write(LEVEL_0, "Namespace:\t%s\n", eps.Namespace)
  3180  		printLabelsMultiline(w, "Labels", eps.Labels)
  3181  		printAnnotationsMultiline(w, "Annotations", eps.Annotations)
  3182  
  3183  		w.Write(LEVEL_0, "AddressType:\t%s\n", string(eps.AddressType))
  3184  
  3185  		if len(eps.Ports) == 0 {
  3186  			w.Write(LEVEL_0, "Ports: <unset>\n")
  3187  		} else {
  3188  			w.Write(LEVEL_0, "Ports:\n")
  3189  			w.Write(LEVEL_1, "Name\tPort\tProtocol\n")
  3190  			w.Write(LEVEL_1, "----\t----\t--------\n")
  3191  			for _, port := range eps.Ports {
  3192  				portName := "<unset>"
  3193  				if port.Name != nil && len(*port.Name) > 0 {
  3194  					portName = *port.Name
  3195  				}
  3196  
  3197  				portNum := "<unset>"
  3198  				if port.Port != nil {
  3199  					portNum = strconv.Itoa(int(*port.Port))
  3200  				}
  3201  
  3202  				w.Write(LEVEL_1, "%s\t%s\t%s\n", portName, portNum, *port.Protocol)
  3203  			}
  3204  		}
  3205  
  3206  		if len(eps.Endpoints) == 0 {
  3207  			w.Write(LEVEL_0, "Endpoints: <none>\n")
  3208  		} else {
  3209  			w.Write(LEVEL_0, "Endpoints:\n")
  3210  			for i := range eps.Endpoints {
  3211  				endpoint := &eps.Endpoints[i]
  3212  
  3213  				addressesString := strings.Join(endpoint.Addresses, ", ")
  3214  				if len(addressesString) == 0 {
  3215  					addressesString = "<none>"
  3216  				}
  3217  				w.Write(LEVEL_1, "- Addresses:\t%s\n", addressesString)
  3218  
  3219  				w.Write(LEVEL_2, "Conditions:\n")
  3220  				readyText := "<unset>"
  3221  				if endpoint.Conditions.Ready != nil {
  3222  					readyText = strconv.FormatBool(*endpoint.Conditions.Ready)
  3223  				}
  3224  				w.Write(LEVEL_3, "Ready:\t%s\n", readyText)
  3225  
  3226  				hostnameText := "<unset>"
  3227  				if endpoint.Hostname != nil {
  3228  					hostnameText = *endpoint.Hostname
  3229  				}
  3230  				w.Write(LEVEL_2, "Hostname:\t%s\n", hostnameText)
  3231  
  3232  				if endpoint.TargetRef != nil {
  3233  					w.Write(LEVEL_2, "TargetRef:\t%s/%s\n", endpoint.TargetRef.Kind, endpoint.TargetRef.Name)
  3234  				}
  3235  
  3236  				nodeNameText := "<unset>"
  3237  				if endpoint.NodeName != nil {
  3238  					nodeNameText = *endpoint.NodeName
  3239  				}
  3240  				w.Write(LEVEL_2, "NodeName:\t%s\n", nodeNameText)
  3241  
  3242  				zoneText := "<unset>"
  3243  				if endpoint.Zone != nil {
  3244  					zoneText = *endpoint.Zone
  3245  				}
  3246  				w.Write(LEVEL_2, "Zone:\t%s\n", zoneText)
  3247  			}
  3248  		}
  3249  
  3250  		if events != nil {
  3251  			DescribeEvents(events, w)
  3252  		}
  3253  		return nil
  3254  	})
  3255  }
  3256  
  3257  func describeEndpointSliceV1beta1(eps *discoveryv1beta1.EndpointSlice, events *corev1.EventList) (string, error) {
  3258  	return tabbedString(func(out io.Writer) error {
  3259  		w := NewPrefixWriter(out)
  3260  		w.Write(LEVEL_0, "Name:\t%s\n", eps.Name)
  3261  		w.Write(LEVEL_0, "Namespace:\t%s\n", eps.Namespace)
  3262  		printLabelsMultiline(w, "Labels", eps.Labels)
  3263  		printAnnotationsMultiline(w, "Annotations", eps.Annotations)
  3264  
  3265  		w.Write(LEVEL_0, "AddressType:\t%s\n", string(eps.AddressType))
  3266  
  3267  		if len(eps.Ports) == 0 {
  3268  			w.Write(LEVEL_0, "Ports: <unset>\n")
  3269  		} else {
  3270  			w.Write(LEVEL_0, "Ports:\n")
  3271  			w.Write(LEVEL_1, "Name\tPort\tProtocol\n")
  3272  			w.Write(LEVEL_1, "----\t----\t--------\n")
  3273  			for _, port := range eps.Ports {
  3274  				portName := "<unset>"
  3275  				if port.Name != nil && len(*port.Name) > 0 {
  3276  					portName = *port.Name
  3277  				}
  3278  
  3279  				portNum := "<unset>"
  3280  				if port.Port != nil {
  3281  					portNum = strconv.Itoa(int(*port.Port))
  3282  				}
  3283  
  3284  				w.Write(LEVEL_1, "%s\t%s\t%s\n", portName, portNum, *port.Protocol)
  3285  			}
  3286  		}
  3287  
  3288  		if len(eps.Endpoints) == 0 {
  3289  			w.Write(LEVEL_0, "Endpoints: <none>\n")
  3290  		} else {
  3291  			w.Write(LEVEL_0, "Endpoints:\n")
  3292  			for i := range eps.Endpoints {
  3293  				endpoint := &eps.Endpoints[i]
  3294  
  3295  				addressesString := strings.Join(endpoint.Addresses, ",")
  3296  				if len(addressesString) == 0 {
  3297  					addressesString = "<none>"
  3298  				}
  3299  				w.Write(LEVEL_1, "- Addresses:\t%s\n", addressesString)
  3300  
  3301  				w.Write(LEVEL_2, "Conditions:\n")
  3302  				readyText := "<unset>"
  3303  				if endpoint.Conditions.Ready != nil {
  3304  					readyText = strconv.FormatBool(*endpoint.Conditions.Ready)
  3305  				}
  3306  				w.Write(LEVEL_3, "Ready:\t%s\n", readyText)
  3307  
  3308  				hostnameText := "<unset>"
  3309  				if endpoint.Hostname != nil {
  3310  					hostnameText = *endpoint.Hostname
  3311  				}
  3312  				w.Write(LEVEL_2, "Hostname:\t%s\n", hostnameText)
  3313  
  3314  				if endpoint.TargetRef != nil {
  3315  					w.Write(LEVEL_2, "TargetRef:\t%s/%s\n", endpoint.TargetRef.Kind, endpoint.TargetRef.Name)
  3316  				}
  3317  
  3318  				printLabelsMultilineWithIndent(w, "    ", "Topology", "\t", endpoint.Topology, sets.NewString())
  3319  			}
  3320  		}
  3321  
  3322  		if events != nil {
  3323  			DescribeEvents(events, w)
  3324  		}
  3325  		return nil
  3326  	})
  3327  }
  3328  
  3329  // ServiceAccountDescriber generates information about a service.
  3330  type ServiceAccountDescriber struct {
  3331  	clientset.Interface
  3332  }
  3333  
  3334  func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3335  	c := d.CoreV1().ServiceAccounts(namespace)
  3336  
  3337  	serviceAccount, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  3338  	if err != nil {
  3339  		return "", err
  3340  	}
  3341  
  3342  	tokens := []corev1.Secret{}
  3343  
  3344  	// missingSecrets is the set of all secrets present in the
  3345  	// serviceAccount but not present in the set of existing secrets.
  3346  	missingSecrets := sets.NewString()
  3347  	secrets := corev1.SecretList{}
  3348  	err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
  3349  		func(options metav1.ListOptions) (runtime.Object, error) {
  3350  			newList, err := d.CoreV1().Secrets(namespace).List(context.TODO(), options)
  3351  			if err != nil {
  3352  				return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceSecrets.String())
  3353  			}
  3354  			secrets.Items = append(secrets.Items, newList.Items...)
  3355  			return newList, nil
  3356  		})
  3357  
  3358  	// errors are tolerated here in order to describe the serviceAccount with all
  3359  	// of the secrets that it references, even if those secrets cannot be fetched.
  3360  	if err == nil {
  3361  		// existingSecrets is the set of all secrets remaining on a
  3362  		// service account that are not present in the "tokens" slice.
  3363  		existingSecrets := sets.NewString()
  3364  
  3365  		for _, s := range secrets.Items {
  3366  			if s.Type == corev1.SecretTypeServiceAccountToken {
  3367  				name := s.Annotations[corev1.ServiceAccountNameKey]
  3368  				uid := s.Annotations[corev1.ServiceAccountUIDKey]
  3369  				if name == serviceAccount.Name && uid == string(serviceAccount.UID) {
  3370  					tokens = append(tokens, s)
  3371  				}
  3372  			}
  3373  			existingSecrets.Insert(s.Name)
  3374  		}
  3375  
  3376  		for _, s := range serviceAccount.Secrets {
  3377  			if !existingSecrets.Has(s.Name) {
  3378  				missingSecrets.Insert(s.Name)
  3379  			}
  3380  		}
  3381  		for _, s := range serviceAccount.ImagePullSecrets {
  3382  			if !existingSecrets.Has(s.Name) {
  3383  				missingSecrets.Insert(s.Name)
  3384  			}
  3385  		}
  3386  	}
  3387  
  3388  	var events *corev1.EventList
  3389  	if describerSettings.ShowEvents {
  3390  		events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
  3391  	}
  3392  
  3393  	return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
  3394  }
  3395  
  3396  func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.String, events *corev1.EventList) (string, error) {
  3397  	return tabbedString(func(out io.Writer) error {
  3398  		w := NewPrefixWriter(out)
  3399  		w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
  3400  		w.Write(LEVEL_0, "Namespace:\t%s\n", serviceAccount.Namespace)
  3401  		printLabelsMultiline(w, "Labels", serviceAccount.Labels)
  3402  		printAnnotationsMultiline(w, "Annotations", serviceAccount.Annotations)
  3403  
  3404  		var (
  3405  			emptyHeader = "                   "
  3406  			pullHeader  = "Image pull secrets:"
  3407  			mountHeader = "Mountable secrets: "
  3408  			tokenHeader = "Tokens:            "
  3409  
  3410  			pullSecretNames  = []string{}
  3411  			mountSecretNames = []string{}
  3412  			tokenSecretNames = []string{}
  3413  		)
  3414  
  3415  		for _, s := range serviceAccount.ImagePullSecrets {
  3416  			pullSecretNames = append(pullSecretNames, s.Name)
  3417  		}
  3418  		for _, s := range serviceAccount.Secrets {
  3419  			mountSecretNames = append(mountSecretNames, s.Name)
  3420  		}
  3421  		for _, s := range tokens {
  3422  			tokenSecretNames = append(tokenSecretNames, s.Name)
  3423  		}
  3424  
  3425  		types := map[string][]string{
  3426  			pullHeader:  pullSecretNames,
  3427  			mountHeader: mountSecretNames,
  3428  			tokenHeader: tokenSecretNames,
  3429  		}
  3430  		for _, header := range sets.StringKeySet(types).List() {
  3431  			names := types[header]
  3432  			if len(names) == 0 {
  3433  				w.Write(LEVEL_0, "%s\t<none>\n", header)
  3434  			} else {
  3435  				prefix := header
  3436  				for _, name := range names {
  3437  					if missingSecrets.Has(name) {
  3438  						w.Write(LEVEL_0, "%s\t%s (not found)\n", prefix, name)
  3439  					} else {
  3440  						w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
  3441  					}
  3442  					prefix = emptyHeader
  3443  				}
  3444  			}
  3445  		}
  3446  
  3447  		if events != nil {
  3448  			DescribeEvents(events, w)
  3449  		}
  3450  
  3451  		return nil
  3452  	})
  3453  }
  3454  
  3455  // RoleDescriber generates information about a node.
  3456  type RoleDescriber struct {
  3457  	clientset.Interface
  3458  }
  3459  
  3460  func (d *RoleDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3461  	role, err := d.RbacV1().Roles(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3462  	if err != nil {
  3463  		return "", err
  3464  	}
  3465  
  3466  	breakdownRules := []rbacv1.PolicyRule{}
  3467  	for _, rule := range role.Rules {
  3468  		breakdownRules = append(breakdownRules, rbac.BreakdownRule(rule)...)
  3469  	}
  3470  
  3471  	compactRules, err := rbac.CompactRules(breakdownRules)
  3472  	if err != nil {
  3473  		return "", err
  3474  	}
  3475  	sort.Stable(rbac.SortableRuleSlice(compactRules))
  3476  
  3477  	return tabbedString(func(out io.Writer) error {
  3478  		w := NewPrefixWriter(out)
  3479  		w.Write(LEVEL_0, "Name:\t%s\n", role.Name)
  3480  		printLabelsMultiline(w, "Labels", role.Labels)
  3481  		printAnnotationsMultiline(w, "Annotations", role.Annotations)
  3482  
  3483  		w.Write(LEVEL_0, "PolicyRule:\n")
  3484  		w.Write(LEVEL_1, "Resources\tNon-Resource URLs\tResource Names\tVerbs\n")
  3485  		w.Write(LEVEL_1, "---------\t-----------------\t--------------\t-----\n")
  3486  		for _, r := range compactRules {
  3487  			w.Write(LEVEL_1, "%s\t%v\t%v\t%v\n", CombineResourceGroup(r.Resources, r.APIGroups), r.NonResourceURLs, r.ResourceNames, r.Verbs)
  3488  		}
  3489  
  3490  		return nil
  3491  	})
  3492  }
  3493  
  3494  // ClusterRoleDescriber generates information about a node.
  3495  type ClusterRoleDescriber struct {
  3496  	clientset.Interface
  3497  }
  3498  
  3499  func (d *ClusterRoleDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3500  	role, err := d.RbacV1().ClusterRoles().Get(context.TODO(), name, metav1.GetOptions{})
  3501  	if err != nil {
  3502  		return "", err
  3503  	}
  3504  
  3505  	breakdownRules := []rbacv1.PolicyRule{}
  3506  	for _, rule := range role.Rules {
  3507  		breakdownRules = append(breakdownRules, rbac.BreakdownRule(rule)...)
  3508  	}
  3509  
  3510  	compactRules, err := rbac.CompactRules(breakdownRules)
  3511  	if err != nil {
  3512  		return "", err
  3513  	}
  3514  	sort.Stable(rbac.SortableRuleSlice(compactRules))
  3515  
  3516  	return tabbedString(func(out io.Writer) error {
  3517  		w := NewPrefixWriter(out)
  3518  		w.Write(LEVEL_0, "Name:\t%s\n", role.Name)
  3519  		printLabelsMultiline(w, "Labels", role.Labels)
  3520  		printAnnotationsMultiline(w, "Annotations", role.Annotations)
  3521  
  3522  		w.Write(LEVEL_0, "PolicyRule:\n")
  3523  		w.Write(LEVEL_1, "Resources\tNon-Resource URLs\tResource Names\tVerbs\n")
  3524  		w.Write(LEVEL_1, "---------\t-----------------\t--------------\t-----\n")
  3525  		for _, r := range compactRules {
  3526  			w.Write(LEVEL_1, "%s\t%v\t%v\t%v\n", CombineResourceGroup(r.Resources, r.APIGroups), r.NonResourceURLs, r.ResourceNames, r.Verbs)
  3527  		}
  3528  
  3529  		return nil
  3530  	})
  3531  }
  3532  
  3533  func CombineResourceGroup(resource, group []string) string {
  3534  	if len(resource) == 0 {
  3535  		return ""
  3536  	}
  3537  	parts := strings.SplitN(resource[0], "/", 2)
  3538  	combine := parts[0]
  3539  
  3540  	if len(group) > 0 && group[0] != "" {
  3541  		combine = combine + "." + group[0]
  3542  	}
  3543  
  3544  	if len(parts) == 2 {
  3545  		combine = combine + "/" + parts[1]
  3546  	}
  3547  	return combine
  3548  }
  3549  
  3550  // RoleBindingDescriber generates information about a node.
  3551  type RoleBindingDescriber struct {
  3552  	clientset.Interface
  3553  }
  3554  
  3555  func (d *RoleBindingDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3556  	binding, err := d.RbacV1().RoleBindings(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3557  	if err != nil {
  3558  		return "", err
  3559  	}
  3560  
  3561  	return tabbedString(func(out io.Writer) error {
  3562  		w := NewPrefixWriter(out)
  3563  		w.Write(LEVEL_0, "Name:\t%s\n", binding.Name)
  3564  		printLabelsMultiline(w, "Labels", binding.Labels)
  3565  		printAnnotationsMultiline(w, "Annotations", binding.Annotations)
  3566  
  3567  		w.Write(LEVEL_0, "Role:\n")
  3568  		w.Write(LEVEL_1, "Kind:\t%s\n", binding.RoleRef.Kind)
  3569  		w.Write(LEVEL_1, "Name:\t%s\n", binding.RoleRef.Name)
  3570  
  3571  		w.Write(LEVEL_0, "Subjects:\n")
  3572  		w.Write(LEVEL_1, "Kind\tName\tNamespace\n")
  3573  		w.Write(LEVEL_1, "----\t----\t---------\n")
  3574  		for _, s := range binding.Subjects {
  3575  			w.Write(LEVEL_1, "%s\t%s\t%s\n", s.Kind, s.Name, s.Namespace)
  3576  		}
  3577  
  3578  		return nil
  3579  	})
  3580  }
  3581  
  3582  // ClusterRoleBindingDescriber generates information about a node.
  3583  type ClusterRoleBindingDescriber struct {
  3584  	clientset.Interface
  3585  }
  3586  
  3587  func (d *ClusterRoleBindingDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3588  	binding, err := d.RbacV1().ClusterRoleBindings().Get(context.TODO(), name, metav1.GetOptions{})
  3589  	if err != nil {
  3590  		return "", err
  3591  	}
  3592  
  3593  	return tabbedString(func(out io.Writer) error {
  3594  		w := NewPrefixWriter(out)
  3595  		w.Write(LEVEL_0, "Name:\t%s\n", binding.Name)
  3596  		printLabelsMultiline(w, "Labels", binding.Labels)
  3597  		printAnnotationsMultiline(w, "Annotations", binding.Annotations)
  3598  
  3599  		w.Write(LEVEL_0, "Role:\n")
  3600  		w.Write(LEVEL_1, "Kind:\t%s\n", binding.RoleRef.Kind)
  3601  		w.Write(LEVEL_1, "Name:\t%s\n", binding.RoleRef.Name)
  3602  
  3603  		w.Write(LEVEL_0, "Subjects:\n")
  3604  		w.Write(LEVEL_1, "Kind\tName\tNamespace\n")
  3605  		w.Write(LEVEL_1, "----\t----\t---------\n")
  3606  		for _, s := range binding.Subjects {
  3607  			w.Write(LEVEL_1, "%s\t%s\t%s\n", s.Kind, s.Name, s.Namespace)
  3608  		}
  3609  
  3610  		return nil
  3611  	})
  3612  }
  3613  
  3614  // NodeDescriber generates information about a node.
  3615  type NodeDescriber struct {
  3616  	clientset.Interface
  3617  }
  3618  
  3619  func (d *NodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3620  	mc := d.CoreV1().Nodes()
  3621  	node, err := mc.Get(context.TODO(), name, metav1.GetOptions{})
  3622  	if err != nil {
  3623  		return "", err
  3624  	}
  3625  
  3626  	fieldSelector, err := fields.ParseSelector("spec.nodeName=" + name + ",status.phase!=" + string(corev1.PodSucceeded) + ",status.phase!=" + string(corev1.PodFailed))
  3627  	if err != nil {
  3628  		return "", err
  3629  	}
  3630  	// in a policy aware setting, users may have access to a node, but not all pods
  3631  	// in that case, we note that the user does not have access to the pods
  3632  	canViewPods := true
  3633  	initialOpts := metav1.ListOptions{
  3634  		FieldSelector: fieldSelector.String(),
  3635  		Limit:         describerSettings.ChunkSize,
  3636  	}
  3637  	nodeNonTerminatedPodsList, err := getPodsInChunks(d.CoreV1().Pods(namespace), initialOpts)
  3638  	if err != nil {
  3639  		if !apierrors.IsForbidden(err) {
  3640  			return "", err
  3641  		}
  3642  		canViewPods = false
  3643  	}
  3644  
  3645  	var events *corev1.EventList
  3646  	if describerSettings.ShowEvents {
  3647  		if ref, err := reference.GetReference(scheme.Scheme, node); err != nil {
  3648  			klog.Errorf("Unable to construct reference to '%#v': %v", node, err)
  3649  		} else {
  3650  			// TODO: We haven't decided the namespace for Node object yet.
  3651  			// there are two UIDs for host events:
  3652  			// controller use node.uid
  3653  			// kubelet use node.name
  3654  			// TODO: Uniform use of UID
  3655  			events, _ = searchEvents(d.CoreV1(), ref, describerSettings.ChunkSize)
  3656  
  3657  			ref.UID = types.UID(ref.Name)
  3658  			eventsInvName, _ := searchEvents(d.CoreV1(), ref, describerSettings.ChunkSize)
  3659  
  3660  			// Merge the results of two queries
  3661  			events.Items = append(events.Items, eventsInvName.Items...)
  3662  		}
  3663  	}
  3664  
  3665  	return describeNode(node, nodeNonTerminatedPodsList, events, canViewPods, &LeaseDescriber{d})
  3666  }
  3667  
  3668  type LeaseDescriber struct {
  3669  	client clientset.Interface
  3670  }
  3671  
  3672  func describeNode(node *corev1.Node, nodeNonTerminatedPodsList *corev1.PodList, events *corev1.EventList,
  3673  	canViewPods bool, ld *LeaseDescriber) (string, error) {
  3674  	return tabbedString(func(out io.Writer) error {
  3675  		w := NewPrefixWriter(out)
  3676  		w.Write(LEVEL_0, "Name:\t%s\n", node.Name)
  3677  		if roles := findNodeRoles(node); len(roles) > 0 {
  3678  			w.Write(LEVEL_0, "Roles:\t%s\n", strings.Join(roles, ","))
  3679  		} else {
  3680  			w.Write(LEVEL_0, "Roles:\t%s\n", "<none>")
  3681  		}
  3682  		printLabelsMultiline(w, "Labels", node.Labels)
  3683  		printAnnotationsMultiline(w, "Annotations", node.Annotations)
  3684  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", node.CreationTimestamp.Time.Format(time.RFC1123Z))
  3685  		printNodeTaintsMultiline(w, "Taints", node.Spec.Taints)
  3686  		w.Write(LEVEL_0, "Unschedulable:\t%v\n", node.Spec.Unschedulable)
  3687  
  3688  		if ld != nil {
  3689  			if lease, err := ld.client.CoordinationV1().Leases(corev1.NamespaceNodeLease).Get(context.TODO(), node.Name, metav1.GetOptions{}); err == nil {
  3690  				describeNodeLease(lease, w)
  3691  			} else {
  3692  				w.Write(LEVEL_0, "Lease:\tFailed to get lease: %s\n", err)
  3693  			}
  3694  		}
  3695  
  3696  		if len(node.Status.Conditions) > 0 {
  3697  			w.Write(LEVEL_0, "Conditions:\n  Type\tStatus\tLastHeartbeatTime\tLastTransitionTime\tReason\tMessage\n")
  3698  			w.Write(LEVEL_1, "----\t------\t-----------------\t------------------\t------\t-------\n")
  3699  			for _, c := range node.Status.Conditions {
  3700  				w.Write(LEVEL_1, "%v \t%v \t%s \t%s \t%v \t%v\n",
  3701  					c.Type,
  3702  					c.Status,
  3703  					c.LastHeartbeatTime.Time.Format(time.RFC1123Z),
  3704  					c.LastTransitionTime.Time.Format(time.RFC1123Z),
  3705  					c.Reason,
  3706  					c.Message)
  3707  			}
  3708  		}
  3709  
  3710  		w.Write(LEVEL_0, "Addresses:\n")
  3711  		for _, address := range node.Status.Addresses {
  3712  			w.Write(LEVEL_1, "%s:\t%s\n", address.Type, address.Address)
  3713  		}
  3714  
  3715  		printResourceList := func(resourceList corev1.ResourceList) {
  3716  			resources := make([]corev1.ResourceName, 0, len(resourceList))
  3717  			for resource := range resourceList {
  3718  				resources = append(resources, resource)
  3719  			}
  3720  			sort.Sort(SortableResourceNames(resources))
  3721  			for _, resource := range resources {
  3722  				value := resourceList[resource]
  3723  				w.Write(LEVEL_0, "  %s:\t%s\n", resource, value.String())
  3724  			}
  3725  		}
  3726  
  3727  		if len(node.Status.Capacity) > 0 {
  3728  			w.Write(LEVEL_0, "Capacity:\n")
  3729  			printResourceList(node.Status.Capacity)
  3730  		}
  3731  		if len(node.Status.Allocatable) > 0 {
  3732  			w.Write(LEVEL_0, "Allocatable:\n")
  3733  			printResourceList(node.Status.Allocatable)
  3734  		}
  3735  
  3736  		w.Write(LEVEL_0, "System Info:\n")
  3737  		w.Write(LEVEL_0, "  Machine ID:\t%s\n", node.Status.NodeInfo.MachineID)
  3738  		w.Write(LEVEL_0, "  System UUID:\t%s\n", node.Status.NodeInfo.SystemUUID)
  3739  		w.Write(LEVEL_0, "  Boot ID:\t%s\n", node.Status.NodeInfo.BootID)
  3740  		w.Write(LEVEL_0, "  Kernel Version:\t%s\n", node.Status.NodeInfo.KernelVersion)
  3741  		w.Write(LEVEL_0, "  OS Image:\t%s\n", node.Status.NodeInfo.OSImage)
  3742  		w.Write(LEVEL_0, "  Operating System:\t%s\n", node.Status.NodeInfo.OperatingSystem)
  3743  		w.Write(LEVEL_0, "  Architecture:\t%s\n", node.Status.NodeInfo.Architecture)
  3744  		w.Write(LEVEL_0, "  Container Runtime Version:\t%s\n", node.Status.NodeInfo.ContainerRuntimeVersion)
  3745  		w.Write(LEVEL_0, "  Kubelet Version:\t%s\n", node.Status.NodeInfo.KubeletVersion)
  3746  		w.Write(LEVEL_0, "  Kube-Proxy Version:\t%s\n", node.Status.NodeInfo.KubeProxyVersion)
  3747  
  3748  		// remove when .PodCIDR is deprecated
  3749  		if len(node.Spec.PodCIDR) > 0 {
  3750  			w.Write(LEVEL_0, "PodCIDR:\t%s\n", node.Spec.PodCIDR)
  3751  		}
  3752  
  3753  		if len(node.Spec.PodCIDRs) > 0 {
  3754  			w.Write(LEVEL_0, "PodCIDRs:\t%s\n", strings.Join(node.Spec.PodCIDRs, ","))
  3755  		}
  3756  		if len(node.Spec.ProviderID) > 0 {
  3757  			w.Write(LEVEL_0, "ProviderID:\t%s\n", node.Spec.ProviderID)
  3758  		}
  3759  		if canViewPods && nodeNonTerminatedPodsList != nil {
  3760  			describeNodeResource(nodeNonTerminatedPodsList, node, w)
  3761  		} else {
  3762  			w.Write(LEVEL_0, "Pods:\tnot authorized\n")
  3763  		}
  3764  		if events != nil {
  3765  			DescribeEvents(events, w)
  3766  		}
  3767  		return nil
  3768  	})
  3769  }
  3770  
  3771  func describeNodeLease(lease *coordinationv1.Lease, w PrefixWriter) {
  3772  	w.Write(LEVEL_0, "Lease:\n")
  3773  	holderIdentity := "<unset>"
  3774  	if lease != nil && lease.Spec.HolderIdentity != nil {
  3775  		holderIdentity = *lease.Spec.HolderIdentity
  3776  	}
  3777  	w.Write(LEVEL_1, "HolderIdentity:\t%s\n", holderIdentity)
  3778  	acquireTime := "<unset>"
  3779  	if lease != nil && lease.Spec.AcquireTime != nil {
  3780  		acquireTime = lease.Spec.AcquireTime.Time.Format(time.RFC1123Z)
  3781  	}
  3782  	w.Write(LEVEL_1, "AcquireTime:\t%s\n", acquireTime)
  3783  	renewTime := "<unset>"
  3784  	if lease != nil && lease.Spec.RenewTime != nil {
  3785  		renewTime = lease.Spec.RenewTime.Time.Format(time.RFC1123Z)
  3786  	}
  3787  	w.Write(LEVEL_1, "RenewTime:\t%s\n", renewTime)
  3788  }
  3789  
  3790  type StatefulSetDescriber struct {
  3791  	client clientset.Interface
  3792  }
  3793  
  3794  func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3795  	ps, err := p.client.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3796  	if err != nil {
  3797  		return "", err
  3798  	}
  3799  	pc := p.client.CoreV1().Pods(namespace)
  3800  
  3801  	selector, err := metav1.LabelSelectorAsSelector(ps.Spec.Selector)
  3802  	if err != nil {
  3803  		return "", err
  3804  	}
  3805  
  3806  	running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, ps.UID, describerSettings)
  3807  	if err != nil {
  3808  		return "", err
  3809  	}
  3810  
  3811  	var events *corev1.EventList
  3812  	if describerSettings.ShowEvents {
  3813  		events, _ = searchEvents(p.client.CoreV1(), ps, describerSettings.ChunkSize)
  3814  	}
  3815  
  3816  	return describeStatefulSet(ps, selector, events, running, waiting, succeeded, failed)
  3817  }
  3818  
  3819  func describeStatefulSet(ps *appsv1.StatefulSet, selector labels.Selector, events *corev1.EventList, running, waiting, succeeded, failed int) (string, error) {
  3820  	return tabbedString(func(out io.Writer) error {
  3821  		w := NewPrefixWriter(out)
  3822  		w.Write(LEVEL_0, "Name:\t%s\n", ps.ObjectMeta.Name)
  3823  		w.Write(LEVEL_0, "Namespace:\t%s\n", ps.ObjectMeta.Namespace)
  3824  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", ps.CreationTimestamp.Time.Format(time.RFC1123Z))
  3825  		w.Write(LEVEL_0, "Selector:\t%s\n", selector)
  3826  		printLabelsMultiline(w, "Labels", ps.Labels)
  3827  		printAnnotationsMultiline(w, "Annotations", ps.Annotations)
  3828  		w.Write(LEVEL_0, "Replicas:\t%d desired | %d total\n", *ps.Spec.Replicas, ps.Status.Replicas)
  3829  		w.Write(LEVEL_0, "Update Strategy:\t%s\n", ps.Spec.UpdateStrategy.Type)
  3830  		if ps.Spec.UpdateStrategy.RollingUpdate != nil {
  3831  			ru := ps.Spec.UpdateStrategy.RollingUpdate
  3832  			if ru.Partition != nil {
  3833  				w.Write(LEVEL_1, "Partition:\t%d\n", *ru.Partition)
  3834  				if ru.MaxUnavailable != nil {
  3835  					w.Write(LEVEL_1, "MaxUnavailable:\t%s\n", ru.MaxUnavailable.String())
  3836  				}
  3837  			}
  3838  		}
  3839  
  3840  		w.Write(LEVEL_0, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
  3841  		DescribePodTemplate(&ps.Spec.Template, w)
  3842  		describeVolumeClaimTemplates(ps.Spec.VolumeClaimTemplates, w)
  3843  		if events != nil {
  3844  			DescribeEvents(events, w)
  3845  		}
  3846  
  3847  		return nil
  3848  	})
  3849  }
  3850  
  3851  type CertificateSigningRequestDescriber struct {
  3852  	client clientset.Interface
  3853  }
  3854  
  3855  func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3856  
  3857  	var (
  3858  		crBytes           []byte
  3859  		metadata          metav1.ObjectMeta
  3860  		status            string
  3861  		signerName        string
  3862  		expirationSeconds *int32
  3863  		username          string
  3864  		events            *corev1.EventList
  3865  	)
  3866  
  3867  	if csr, err := p.client.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{}); err == nil {
  3868  		crBytes = csr.Spec.Request
  3869  		metadata = csr.ObjectMeta
  3870  		conditionTypes := []string{}
  3871  		for _, c := range csr.Status.Conditions {
  3872  			conditionTypes = append(conditionTypes, string(c.Type))
  3873  		}
  3874  		status = extractCSRStatus(conditionTypes, csr.Status.Certificate)
  3875  		signerName = csr.Spec.SignerName
  3876  		expirationSeconds = csr.Spec.ExpirationSeconds
  3877  		username = csr.Spec.Username
  3878  		if describerSettings.ShowEvents {
  3879  			events, _ = searchEvents(p.client.CoreV1(), csr, describerSettings.ChunkSize)
  3880  		}
  3881  	} else if csr, err := p.client.CertificatesV1beta1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{}); err == nil {
  3882  		crBytes = csr.Spec.Request
  3883  		metadata = csr.ObjectMeta
  3884  		conditionTypes := []string{}
  3885  		for _, c := range csr.Status.Conditions {
  3886  			conditionTypes = append(conditionTypes, string(c.Type))
  3887  		}
  3888  		status = extractCSRStatus(conditionTypes, csr.Status.Certificate)
  3889  		if csr.Spec.SignerName != nil {
  3890  			signerName = *csr.Spec.SignerName
  3891  		}
  3892  		expirationSeconds = csr.Spec.ExpirationSeconds
  3893  		username = csr.Spec.Username
  3894  		if describerSettings.ShowEvents {
  3895  			events, _ = searchEvents(p.client.CoreV1(), csr, describerSettings.ChunkSize)
  3896  		}
  3897  	} else {
  3898  		return "", err
  3899  	}
  3900  
  3901  	cr, err := certificate.ParseCSR(crBytes)
  3902  	if err != nil {
  3903  		return "", fmt.Errorf("Error parsing CSR: %v", err)
  3904  	}
  3905  
  3906  	return describeCertificateSigningRequest(metadata, signerName, expirationSeconds, username, cr, status, events)
  3907  }
  3908  
  3909  func describeCertificateSigningRequest(csr metav1.ObjectMeta, signerName string, expirationSeconds *int32, username string, cr *x509.CertificateRequest, status string, events *corev1.EventList) (string, error) {
  3910  	printListHelper := func(w PrefixWriter, prefix, name string, values []string) {
  3911  		if len(values) == 0 {
  3912  			return
  3913  		}
  3914  		w.Write(LEVEL_0, prefix+name+":\t")
  3915  		w.Write(LEVEL_0, strings.Join(values, "\n"+prefix+"\t"))
  3916  		w.Write(LEVEL_0, "\n")
  3917  	}
  3918  
  3919  	return tabbedString(func(out io.Writer) error {
  3920  		w := NewPrefixWriter(out)
  3921  		w.Write(LEVEL_0, "Name:\t%s\n", csr.Name)
  3922  		w.Write(LEVEL_0, "Labels:\t%s\n", labels.FormatLabels(csr.Labels))
  3923  		w.Write(LEVEL_0, "Annotations:\t%s\n", labels.FormatLabels(csr.Annotations))
  3924  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", csr.CreationTimestamp.Time.Format(time.RFC1123Z))
  3925  		w.Write(LEVEL_0, "Requesting User:\t%s\n", username)
  3926  		if len(signerName) > 0 {
  3927  			w.Write(LEVEL_0, "Signer:\t%s\n", signerName)
  3928  		}
  3929  		if expirationSeconds != nil {
  3930  			w.Write(LEVEL_0, "Requested Duration:\t%s\n", duration.HumanDuration(utilcsr.ExpirationSecondsToDuration(*expirationSeconds)))
  3931  		}
  3932  		w.Write(LEVEL_0, "Status:\t%s\n", status)
  3933  
  3934  		w.Write(LEVEL_0, "Subject:\n")
  3935  		w.Write(LEVEL_0, "\tCommon Name:\t%s\n", cr.Subject.CommonName)
  3936  		w.Write(LEVEL_0, "\tSerial Number:\t%s\n", cr.Subject.SerialNumber)
  3937  		printListHelper(w, "\t", "Organization", cr.Subject.Organization)
  3938  		printListHelper(w, "\t", "Organizational Unit", cr.Subject.OrganizationalUnit)
  3939  		printListHelper(w, "\t", "Country", cr.Subject.Country)
  3940  		printListHelper(w, "\t", "Locality", cr.Subject.Locality)
  3941  		printListHelper(w, "\t", "Province", cr.Subject.Province)
  3942  		printListHelper(w, "\t", "StreetAddress", cr.Subject.StreetAddress)
  3943  		printListHelper(w, "\t", "PostalCode", cr.Subject.PostalCode)
  3944  
  3945  		if len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses)+len(cr.URIs) > 0 {
  3946  			w.Write(LEVEL_0, "Subject Alternative Names:\n")
  3947  			printListHelper(w, "\t", "DNS Names", cr.DNSNames)
  3948  			printListHelper(w, "\t", "Email Addresses", cr.EmailAddresses)
  3949  			var uris []string
  3950  			for _, uri := range cr.URIs {
  3951  				uris = append(uris, uri.String())
  3952  			}
  3953  			printListHelper(w, "\t", "URIs", uris)
  3954  			var ipaddrs []string
  3955  			for _, ipaddr := range cr.IPAddresses {
  3956  				ipaddrs = append(ipaddrs, ipaddr.String())
  3957  			}
  3958  			printListHelper(w, "\t", "IP Addresses", ipaddrs)
  3959  		}
  3960  
  3961  		if events != nil {
  3962  			DescribeEvents(events, w)
  3963  		}
  3964  
  3965  		return nil
  3966  	})
  3967  }
  3968  
  3969  // HorizontalPodAutoscalerDescriber generates information about a horizontal pod autoscaler.
  3970  type HorizontalPodAutoscalerDescriber struct {
  3971  	client clientset.Interface
  3972  }
  3973  
  3974  func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  3975  	var events *corev1.EventList
  3976  
  3977  	// autoscaling/v2 is introduced since v1.23 and autoscaling/v1 does not have full backward compatibility
  3978  	// with autoscaling/v2, so describer will try to get and describe hpa v2 object firstly, if it fails,
  3979  	// describer will fall back to do with hpa v1 object
  3980  	hpaV2, err := d.client.AutoscalingV2().HorizontalPodAutoscalers(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3981  	if err == nil {
  3982  		if describerSettings.ShowEvents {
  3983  			events, _ = searchEvents(d.client.CoreV1(), hpaV2, describerSettings.ChunkSize)
  3984  		}
  3985  		return describeHorizontalPodAutoscalerV2(hpaV2, events, d)
  3986  	}
  3987  
  3988  	hpaV1, err := d.client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  3989  	if err == nil {
  3990  		if describerSettings.ShowEvents {
  3991  			events, _ = searchEvents(d.client.CoreV1(), hpaV1, describerSettings.ChunkSize)
  3992  		}
  3993  		return describeHorizontalPodAutoscalerV1(hpaV1, events, d)
  3994  	}
  3995  
  3996  	return "", err
  3997  }
  3998  
  3999  func describeHorizontalPodAutoscalerV2(hpa *autoscalingv2.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) {
  4000  	return tabbedString(func(out io.Writer) error {
  4001  		w := NewPrefixWriter(out)
  4002  		w.Write(LEVEL_0, "Name:\t%s\n", hpa.Name)
  4003  		w.Write(LEVEL_0, "Namespace:\t%s\n", hpa.Namespace)
  4004  		printLabelsMultiline(w, "Labels", hpa.Labels)
  4005  		printAnnotationsMultiline(w, "Annotations", hpa.Annotations)
  4006  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", hpa.CreationTimestamp.Time.Format(time.RFC1123Z))
  4007  		w.Write(LEVEL_0, "Reference:\t%s/%s\n",
  4008  			hpa.Spec.ScaleTargetRef.Kind,
  4009  			hpa.Spec.ScaleTargetRef.Name)
  4010  		w.Write(LEVEL_0, "Metrics:\t( current / target )\n")
  4011  		for i, metric := range hpa.Spec.Metrics {
  4012  			switch metric.Type {
  4013  			case autoscalingv2.ExternalMetricSourceType:
  4014  				if metric.External.Target.AverageValue != nil {
  4015  					current := "<unknown>"
  4016  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].External != nil &&
  4017  						hpa.Status.CurrentMetrics[i].External.Current.AverageValue != nil {
  4018  						current = hpa.Status.CurrentMetrics[i].External.Current.AverageValue.String()
  4019  					}
  4020  					w.Write(LEVEL_1, "%q (target average value):\t%s / %s\n", metric.External.Metric.Name, current, metric.External.Target.AverageValue.String())
  4021  				} else {
  4022  					current := "<unknown>"
  4023  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].External != nil {
  4024  						current = hpa.Status.CurrentMetrics[i].External.Current.Value.String()
  4025  					}
  4026  					w.Write(LEVEL_1, "%q (target value):\t%s / %s\n", metric.External.Metric.Name, current, metric.External.Target.Value.String())
  4027  
  4028  				}
  4029  			case autoscalingv2.PodsMetricSourceType:
  4030  				current := "<unknown>"
  4031  				if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Pods != nil {
  4032  					current = hpa.Status.CurrentMetrics[i].Pods.Current.AverageValue.String()
  4033  				}
  4034  				w.Write(LEVEL_1, "%q on pods:\t%s / %s\n", metric.Pods.Metric.Name, current, metric.Pods.Target.AverageValue.String())
  4035  			case autoscalingv2.ObjectMetricSourceType:
  4036  				w.Write(LEVEL_1, "\"%s\" on %s/%s ", metric.Object.Metric.Name, metric.Object.DescribedObject.Kind, metric.Object.DescribedObject.Name)
  4037  				if metric.Object.Target.Type == autoscalingv2.AverageValueMetricType {
  4038  					current := "<unknown>"
  4039  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Object != nil {
  4040  						current = hpa.Status.CurrentMetrics[i].Object.Current.AverageValue.String()
  4041  					}
  4042  					w.Write(LEVEL_0, "(target average value):\t%s / %s\n", current, metric.Object.Target.AverageValue.String())
  4043  				} else {
  4044  					current := "<unknown>"
  4045  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Object != nil {
  4046  						current = hpa.Status.CurrentMetrics[i].Object.Current.Value.String()
  4047  					}
  4048  					w.Write(LEVEL_0, "(target value):\t%s / %s\n", current, metric.Object.Target.Value.String())
  4049  				}
  4050  			case autoscalingv2.ResourceMetricSourceType:
  4051  				w.Write(LEVEL_1, "resource %s on pods", string(metric.Resource.Name))
  4052  				if metric.Resource.Target.AverageValue != nil {
  4053  					current := "<unknown>"
  4054  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Resource != nil {
  4055  						current = hpa.Status.CurrentMetrics[i].Resource.Current.AverageValue.String()
  4056  					}
  4057  					w.Write(LEVEL_0, ":\t%s / %s\n", current, metric.Resource.Target.AverageValue.String())
  4058  				} else {
  4059  					current := "<unknown>"
  4060  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Resource != nil && hpa.Status.CurrentMetrics[i].Resource.Current.AverageUtilization != nil {
  4061  						current = fmt.Sprintf("%d%% (%s)", *hpa.Status.CurrentMetrics[i].Resource.Current.AverageUtilization, hpa.Status.CurrentMetrics[i].Resource.Current.AverageValue.String())
  4062  					}
  4063  
  4064  					target := "<auto>"
  4065  					if metric.Resource.Target.AverageUtilization != nil {
  4066  						target = fmt.Sprintf("%d%%", *metric.Resource.Target.AverageUtilization)
  4067  					}
  4068  					w.Write(LEVEL_1, "(as a percentage of request):\t%s / %s\n", current, target)
  4069  				}
  4070  			case autoscalingv2.ContainerResourceMetricSourceType:
  4071  				w.Write(LEVEL_1, "resource %s of container \"%s\" on pods", string(metric.ContainerResource.Name), metric.ContainerResource.Container)
  4072  				if metric.ContainerResource.Target.AverageValue != nil {
  4073  					current := "<unknown>"
  4074  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].ContainerResource != nil {
  4075  						current = hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageValue.String()
  4076  					}
  4077  					w.Write(LEVEL_0, ":\t%s / %s\n", current, metric.ContainerResource.Target.AverageValue.String())
  4078  				} else {
  4079  					current := "<unknown>"
  4080  					if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].ContainerResource != nil && hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageUtilization != nil {
  4081  						current = fmt.Sprintf("%d%% (%s)", *hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageUtilization, hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageValue.String())
  4082  					}
  4083  
  4084  					target := "<auto>"
  4085  					if metric.ContainerResource.Target.AverageUtilization != nil {
  4086  						target = fmt.Sprintf("%d%%", *metric.ContainerResource.Target.AverageUtilization)
  4087  					}
  4088  					w.Write(LEVEL_1, "(as a percentage of request):\t%s / %s\n", current, target)
  4089  				}
  4090  			default:
  4091  				w.Write(LEVEL_1, "<unknown metric type %q>\n", string(metric.Type))
  4092  			}
  4093  		}
  4094  		minReplicas := "<unset>"
  4095  		if hpa.Spec.MinReplicas != nil {
  4096  			minReplicas = fmt.Sprintf("%d", *hpa.Spec.MinReplicas)
  4097  		}
  4098  		w.Write(LEVEL_0, "Min replicas:\t%s\n", minReplicas)
  4099  		w.Write(LEVEL_0, "Max replicas:\t%d\n", hpa.Spec.MaxReplicas)
  4100  		// only print the hpa behavior if present
  4101  		if hpa.Spec.Behavior != nil {
  4102  			w.Write(LEVEL_0, "Behavior:\n")
  4103  			printDirectionBehavior(w, "Scale Up", hpa.Spec.Behavior.ScaleUp)
  4104  			printDirectionBehavior(w, "Scale Down", hpa.Spec.Behavior.ScaleDown)
  4105  		}
  4106  		w.Write(LEVEL_0, "%s pods:\t", hpa.Spec.ScaleTargetRef.Kind)
  4107  		w.Write(LEVEL_0, "%d current / %d desired\n", hpa.Status.CurrentReplicas, hpa.Status.DesiredReplicas)
  4108  
  4109  		if len(hpa.Status.Conditions) > 0 {
  4110  			w.Write(LEVEL_0, "Conditions:\n")
  4111  			w.Write(LEVEL_1, "Type\tStatus\tReason\tMessage\n")
  4112  			w.Write(LEVEL_1, "----\t------\t------\t-------\n")
  4113  			for _, c := range hpa.Status.Conditions {
  4114  				w.Write(LEVEL_1, "%v\t%v\t%v\t%v\n", c.Type, c.Status, c.Reason, c.Message)
  4115  			}
  4116  		}
  4117  
  4118  		if events != nil {
  4119  			DescribeEvents(events, w)
  4120  		}
  4121  
  4122  		return nil
  4123  	})
  4124  }
  4125  
  4126  func printDirectionBehavior(w PrefixWriter, direction string, rules *autoscalingv2.HPAScalingRules) {
  4127  	if rules != nil {
  4128  		w.Write(LEVEL_1, "%s:\n", direction)
  4129  		if rules.StabilizationWindowSeconds != nil {
  4130  			w.Write(LEVEL_2, "Stabilization Window: %d seconds\n", *rules.StabilizationWindowSeconds)
  4131  		}
  4132  		if len(rules.Policies) > 0 {
  4133  			if rules.SelectPolicy != nil {
  4134  				w.Write(LEVEL_2, "Select Policy: %s\n", *rules.SelectPolicy)
  4135  			} else {
  4136  				w.Write(LEVEL_2, "Select Policy: %s\n", autoscalingv2.MaxChangePolicySelect)
  4137  			}
  4138  			w.Write(LEVEL_2, "Policies:\n")
  4139  			for _, p := range rules.Policies {
  4140  				w.Write(LEVEL_3, "- Type: %s\tValue: %d\tPeriod: %d seconds\n", p.Type, p.Value, p.PeriodSeconds)
  4141  			}
  4142  		}
  4143  	}
  4144  }
  4145  
  4146  func describeHorizontalPodAutoscalerV1(hpa *autoscalingv1.HorizontalPodAutoscaler, events *corev1.EventList, d *HorizontalPodAutoscalerDescriber) (string, error) {
  4147  	return tabbedString(func(out io.Writer) error {
  4148  		w := NewPrefixWriter(out)
  4149  		w.Write(LEVEL_0, "Name:\t%s\n", hpa.Name)
  4150  		w.Write(LEVEL_0, "Namespace:\t%s\n", hpa.Namespace)
  4151  		printLabelsMultiline(w, "Labels", hpa.Labels)
  4152  		printAnnotationsMultiline(w, "Annotations", hpa.Annotations)
  4153  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", hpa.CreationTimestamp.Time.Format(time.RFC1123Z))
  4154  		w.Write(LEVEL_0, "Reference:\t%s/%s\n",
  4155  			hpa.Spec.ScaleTargetRef.Kind,
  4156  			hpa.Spec.ScaleTargetRef.Name)
  4157  
  4158  		if hpa.Spec.TargetCPUUtilizationPercentage != nil {
  4159  			w.Write(LEVEL_0, "Target CPU utilization:\t%d%%\n", *hpa.Spec.TargetCPUUtilizationPercentage)
  4160  			current := "<unknown>"
  4161  			if hpa.Status.CurrentCPUUtilizationPercentage != nil {
  4162  				current = fmt.Sprintf("%d", *hpa.Status.CurrentCPUUtilizationPercentage)
  4163  			}
  4164  			w.Write(LEVEL_0, "Current CPU utilization:\t%s%%\n", current)
  4165  		}
  4166  
  4167  		minReplicas := "<unset>"
  4168  		if hpa.Spec.MinReplicas != nil {
  4169  			minReplicas = fmt.Sprintf("%d", *hpa.Spec.MinReplicas)
  4170  		}
  4171  		w.Write(LEVEL_0, "Min replicas:\t%s\n", minReplicas)
  4172  		w.Write(LEVEL_0, "Max replicas:\t%d\n", hpa.Spec.MaxReplicas)
  4173  		w.Write(LEVEL_0, "%s pods:\t", hpa.Spec.ScaleTargetRef.Kind)
  4174  		w.Write(LEVEL_0, "%d current / %d desired\n", hpa.Status.CurrentReplicas, hpa.Status.DesiredReplicas)
  4175  
  4176  		if events != nil {
  4177  			DescribeEvents(events, w)
  4178  		}
  4179  
  4180  		return nil
  4181  	})
  4182  }
  4183  
  4184  func describeNodeResource(nodeNonTerminatedPodsList *corev1.PodList, node *corev1.Node, w PrefixWriter) {
  4185  	w.Write(LEVEL_0, "Non-terminated Pods:\t(%d in total)\n", len(nodeNonTerminatedPodsList.Items))
  4186  	w.Write(LEVEL_1, "Namespace\tName\t\tCPU Requests\tCPU Limits\tMemory Requests\tMemory Limits\tAge\n")
  4187  	w.Write(LEVEL_1, "---------\t----\t\t------------\t----------\t---------------\t-------------\t---\n")
  4188  	allocatable := node.Status.Capacity
  4189  	if len(node.Status.Allocatable) > 0 {
  4190  		allocatable = node.Status.Allocatable
  4191  	}
  4192  
  4193  	for _, pod := range nodeNonTerminatedPodsList.Items {
  4194  		req, limit := resourcehelper.PodRequestsAndLimits(&pod)
  4195  		cpuReq, cpuLimit, memoryReq, memoryLimit := req[corev1.ResourceCPU], limit[corev1.ResourceCPU], req[corev1.ResourceMemory], limit[corev1.ResourceMemory]
  4196  		fractionCpuReq := float64(cpuReq.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
  4197  		fractionCpuLimit := float64(cpuLimit.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
  4198  		fractionMemoryReq := float64(memoryReq.Value()) / float64(allocatable.Memory().Value()) * 100
  4199  		fractionMemoryLimit := float64(memoryLimit.Value()) / float64(allocatable.Memory().Value()) * 100
  4200  		w.Write(LEVEL_1, "%s\t%s\t\t%s (%d%%)\t%s (%d%%)\t%s (%d%%)\t%s (%d%%)\t%s\n", pod.Namespace, pod.Name,
  4201  			cpuReq.String(), int64(fractionCpuReq), cpuLimit.String(), int64(fractionCpuLimit),
  4202  			memoryReq.String(), int64(fractionMemoryReq), memoryLimit.String(), int64(fractionMemoryLimit), translateTimestampSince(pod.CreationTimestamp))
  4203  	}
  4204  
  4205  	w.Write(LEVEL_0, "Allocated resources:\n  (Total limits may be over 100 percent, i.e., overcommitted.)\n")
  4206  	w.Write(LEVEL_1, "Resource\tRequests\tLimits\n")
  4207  	w.Write(LEVEL_1, "--------\t--------\t------\n")
  4208  	reqs, limits := getPodsTotalRequestsAndLimits(nodeNonTerminatedPodsList)
  4209  	cpuReqs, cpuLimits, memoryReqs, memoryLimits, ephemeralstorageReqs, ephemeralstorageLimits :=
  4210  		reqs[corev1.ResourceCPU], limits[corev1.ResourceCPU], reqs[corev1.ResourceMemory], limits[corev1.ResourceMemory], reqs[corev1.ResourceEphemeralStorage], limits[corev1.ResourceEphemeralStorage]
  4211  	fractionCpuReqs := float64(0)
  4212  	fractionCpuLimits := float64(0)
  4213  	if allocatable.Cpu().MilliValue() != 0 {
  4214  		fractionCpuReqs = float64(cpuReqs.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
  4215  		fractionCpuLimits = float64(cpuLimits.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
  4216  	}
  4217  	fractionMemoryReqs := float64(0)
  4218  	fractionMemoryLimits := float64(0)
  4219  	if allocatable.Memory().Value() != 0 {
  4220  		fractionMemoryReqs = float64(memoryReqs.Value()) / float64(allocatable.Memory().Value()) * 100
  4221  		fractionMemoryLimits = float64(memoryLimits.Value()) / float64(allocatable.Memory().Value()) * 100
  4222  	}
  4223  	fractionEphemeralStorageReqs := float64(0)
  4224  	fractionEphemeralStorageLimits := float64(0)
  4225  	if allocatable.StorageEphemeral().Value() != 0 {
  4226  		fractionEphemeralStorageReqs = float64(ephemeralstorageReqs.Value()) / float64(allocatable.StorageEphemeral().Value()) * 100
  4227  		fractionEphemeralStorageLimits = float64(ephemeralstorageLimits.Value()) / float64(allocatable.StorageEphemeral().Value()) * 100
  4228  	}
  4229  	w.Write(LEVEL_1, "%s\t%s (%d%%)\t%s (%d%%)\n",
  4230  		corev1.ResourceCPU, cpuReqs.String(), int64(fractionCpuReqs), cpuLimits.String(), int64(fractionCpuLimits))
  4231  	w.Write(LEVEL_1, "%s\t%s (%d%%)\t%s (%d%%)\n",
  4232  		corev1.ResourceMemory, memoryReqs.String(), int64(fractionMemoryReqs), memoryLimits.String(), int64(fractionMemoryLimits))
  4233  	w.Write(LEVEL_1, "%s\t%s (%d%%)\t%s (%d%%)\n",
  4234  		corev1.ResourceEphemeralStorage, ephemeralstorageReqs.String(), int64(fractionEphemeralStorageReqs), ephemeralstorageLimits.String(), int64(fractionEphemeralStorageLimits))
  4235  
  4236  	extResources := make([]string, 0, len(allocatable))
  4237  	hugePageResources := make([]string, 0, len(allocatable))
  4238  	for resource := range allocatable {
  4239  		if resourcehelper.IsHugePageResourceName(resource) {
  4240  			hugePageResources = append(hugePageResources, string(resource))
  4241  		} else if !resourcehelper.IsStandardContainerResourceName(string(resource)) && resource != corev1.ResourcePods {
  4242  			extResources = append(extResources, string(resource))
  4243  		}
  4244  	}
  4245  
  4246  	sort.Strings(extResources)
  4247  	sort.Strings(hugePageResources)
  4248  
  4249  	for _, resource := range hugePageResources {
  4250  		hugePageSizeRequests, hugePageSizeLimits, hugePageSizeAllocable := reqs[corev1.ResourceName(resource)], limits[corev1.ResourceName(resource)], allocatable[corev1.ResourceName(resource)]
  4251  		fractionHugePageSizeRequests := float64(0)
  4252  		fractionHugePageSizeLimits := float64(0)
  4253  		if hugePageSizeAllocable.Value() != 0 {
  4254  			fractionHugePageSizeRequests = float64(hugePageSizeRequests.Value()) / float64(hugePageSizeAllocable.Value()) * 100
  4255  			fractionHugePageSizeLimits = float64(hugePageSizeLimits.Value()) / float64(hugePageSizeAllocable.Value()) * 100
  4256  		}
  4257  		w.Write(LEVEL_1, "%s\t%s (%d%%)\t%s (%d%%)\n",
  4258  			resource, hugePageSizeRequests.String(), int64(fractionHugePageSizeRequests), hugePageSizeLimits.String(), int64(fractionHugePageSizeLimits))
  4259  	}
  4260  
  4261  	for _, ext := range extResources {
  4262  		extRequests, extLimits := reqs[corev1.ResourceName(ext)], limits[corev1.ResourceName(ext)]
  4263  		w.Write(LEVEL_1, "%s\t%s\t%s\n", ext, extRequests.String(), extLimits.String())
  4264  	}
  4265  }
  4266  
  4267  func getPodsTotalRequestsAndLimits(podList *corev1.PodList) (reqs map[corev1.ResourceName]resource.Quantity, limits map[corev1.ResourceName]resource.Quantity) {
  4268  	reqs, limits = map[corev1.ResourceName]resource.Quantity{}, map[corev1.ResourceName]resource.Quantity{}
  4269  	for _, pod := range podList.Items {
  4270  		podReqs, podLimits := resourcehelper.PodRequestsAndLimits(&pod)
  4271  		for podReqName, podReqValue := range podReqs {
  4272  			if value, ok := reqs[podReqName]; !ok {
  4273  				reqs[podReqName] = podReqValue.DeepCopy()
  4274  			} else {
  4275  				value.Add(podReqValue)
  4276  				reqs[podReqName] = value
  4277  			}
  4278  		}
  4279  		for podLimitName, podLimitValue := range podLimits {
  4280  			if value, ok := limits[podLimitName]; !ok {
  4281  				limits[podLimitName] = podLimitValue.DeepCopy()
  4282  			} else {
  4283  				value.Add(podLimitValue)
  4284  				limits[podLimitName] = value
  4285  			}
  4286  		}
  4287  	}
  4288  	return
  4289  }
  4290  
  4291  func DescribeEvents(el *corev1.EventList, w PrefixWriter) {
  4292  	if len(el.Items) == 0 {
  4293  		w.Write(LEVEL_0, "Events:\t<none>\n")
  4294  		return
  4295  	}
  4296  	w.Flush()
  4297  	sort.Sort(event.SortableEvents(el.Items))
  4298  	w.Write(LEVEL_0, "Events:\n  Type\tReason\tAge\tFrom\tMessage\n")
  4299  	w.Write(LEVEL_1, "----\t------\t----\t----\t-------\n")
  4300  	for _, e := range el.Items {
  4301  		var interval string
  4302  		firstTimestampSince := translateMicroTimestampSince(e.EventTime)
  4303  		if e.EventTime.IsZero() {
  4304  			firstTimestampSince = translateTimestampSince(e.FirstTimestamp)
  4305  		}
  4306  		if e.Series != nil {
  4307  			interval = fmt.Sprintf("%s (x%d over %s)", translateMicroTimestampSince(e.Series.LastObservedTime), e.Series.Count, firstTimestampSince)
  4308  		} else if e.Count > 1 {
  4309  			interval = fmt.Sprintf("%s (x%d over %s)", translateTimestampSince(e.LastTimestamp), e.Count, firstTimestampSince)
  4310  		} else {
  4311  			interval = firstTimestampSince
  4312  		}
  4313  		source := e.Source.Component
  4314  		if source == "" {
  4315  			source = e.ReportingController
  4316  		}
  4317  		w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
  4318  			e.Type,
  4319  			e.Reason,
  4320  			interval,
  4321  			source,
  4322  			strings.TrimSpace(e.Message),
  4323  		)
  4324  	}
  4325  }
  4326  
  4327  // DeploymentDescriber generates information about a deployment.
  4328  type DeploymentDescriber struct {
  4329  	client clientset.Interface
  4330  }
  4331  
  4332  func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4333  	d, err := dd.client.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  4334  	if err != nil {
  4335  		return "", err
  4336  	}
  4337  
  4338  	var events *corev1.EventList
  4339  	if describerSettings.ShowEvents {
  4340  		events, _ = searchEvents(dd.client.CoreV1(), d, describerSettings.ChunkSize)
  4341  	}
  4342  
  4343  	var oldRSs, newRSs []*appsv1.ReplicaSet
  4344  	if _, oldResult, newResult, err := deploymentutil.GetAllReplicaSetsInChunks(d, dd.client.AppsV1(), describerSettings.ChunkSize); err == nil {
  4345  		oldRSs = oldResult
  4346  		if newResult != nil {
  4347  			newRSs = append(newRSs, newResult)
  4348  		}
  4349  	}
  4350  
  4351  	return describeDeployment(d, oldRSs, newRSs, events)
  4352  }
  4353  
  4354  func describeDeployment(d *appsv1.Deployment, oldRSs []*appsv1.ReplicaSet, newRSs []*appsv1.ReplicaSet, events *corev1.EventList) (string, error) {
  4355  	selector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector)
  4356  	if err != nil {
  4357  		return "", err
  4358  	}
  4359  	return tabbedString(func(out io.Writer) error {
  4360  		w := NewPrefixWriter(out)
  4361  		w.Write(LEVEL_0, "Name:\t%s\n", d.ObjectMeta.Name)
  4362  		w.Write(LEVEL_0, "Namespace:\t%s\n", d.ObjectMeta.Namespace)
  4363  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", d.CreationTimestamp.Time.Format(time.RFC1123Z))
  4364  		printLabelsMultiline(w, "Labels", d.Labels)
  4365  		printAnnotationsMultiline(w, "Annotations", d.Annotations)
  4366  		w.Write(LEVEL_0, "Selector:\t%s\n", selector)
  4367  		w.Write(LEVEL_0, "Replicas:\t%d desired | %d updated | %d total | %d available | %d unavailable\n", *(d.Spec.Replicas), d.Status.UpdatedReplicas, d.Status.Replicas, d.Status.AvailableReplicas, d.Status.UnavailableReplicas)
  4368  		w.Write(LEVEL_0, "StrategyType:\t%s\n", d.Spec.Strategy.Type)
  4369  		w.Write(LEVEL_0, "MinReadySeconds:\t%d\n", d.Spec.MinReadySeconds)
  4370  		if d.Spec.Strategy.RollingUpdate != nil {
  4371  			ru := d.Spec.Strategy.RollingUpdate
  4372  			w.Write(LEVEL_0, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String())
  4373  		}
  4374  		DescribePodTemplate(&d.Spec.Template, w)
  4375  		if len(d.Status.Conditions) > 0 {
  4376  			w.Write(LEVEL_0, "Conditions:\n  Type\tStatus\tReason\n")
  4377  			w.Write(LEVEL_1, "----\t------\t------\n")
  4378  			for _, c := range d.Status.Conditions {
  4379  				w.Write(LEVEL_1, "%v \t%v\t%v\n", c.Type, c.Status, c.Reason)
  4380  			}
  4381  		}
  4382  
  4383  		if len(oldRSs) > 0 || len(newRSs) > 0 {
  4384  			w.Write(LEVEL_0, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs))
  4385  			w.Write(LEVEL_0, "NewReplicaSet:\t%s\n", printReplicaSetsByLabels(newRSs))
  4386  		}
  4387  		if events != nil {
  4388  			DescribeEvents(events, w)
  4389  		}
  4390  
  4391  		return nil
  4392  	})
  4393  }
  4394  
  4395  func printReplicaSetsByLabels(matchingRSs []*appsv1.ReplicaSet) string {
  4396  	// Format the matching ReplicaSets into strings.
  4397  	rsStrings := make([]string, 0, len(matchingRSs))
  4398  	for _, rs := range matchingRSs {
  4399  		rsStrings = append(rsStrings, fmt.Sprintf("%s (%d/%d replicas created)", rs.Name, rs.Status.Replicas, *rs.Spec.Replicas))
  4400  	}
  4401  
  4402  	list := strings.Join(rsStrings, ", ")
  4403  	if list == "" {
  4404  		return "<none>"
  4405  	}
  4406  	return list
  4407  }
  4408  
  4409  func getPodStatusForController(c corev1client.PodInterface, selector labels.Selector, uid types.UID, settings DescriberSettings) (
  4410  	running, waiting, succeeded, failed int, err error) {
  4411  	initialOpts := metav1.ListOptions{LabelSelector: selector.String(), Limit: settings.ChunkSize}
  4412  	rcPods, err := getPodsInChunks(c, initialOpts)
  4413  	if err != nil {
  4414  		return
  4415  	}
  4416  	for _, pod := range rcPods.Items {
  4417  		controllerRef := metav1.GetControllerOf(&pod)
  4418  		// Skip pods that are orphans or owned by other controllers.
  4419  		if controllerRef == nil || controllerRef.UID != uid {
  4420  			continue
  4421  		}
  4422  		switch pod.Status.Phase {
  4423  		case corev1.PodRunning:
  4424  			running++
  4425  		case corev1.PodPending:
  4426  			waiting++
  4427  		case corev1.PodSucceeded:
  4428  			succeeded++
  4429  		case corev1.PodFailed:
  4430  			failed++
  4431  		}
  4432  	}
  4433  	return
  4434  }
  4435  
  4436  func getPodsInChunks(c corev1client.PodInterface, initialOpts metav1.ListOptions) (*corev1.PodList, error) {
  4437  	podList := &corev1.PodList{}
  4438  	err := runtimeresource.FollowContinue(&initialOpts,
  4439  		func(options metav1.ListOptions) (runtime.Object, error) {
  4440  			newList, err := c.List(context.TODO(), options)
  4441  			if err != nil {
  4442  				return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourcePods.String())
  4443  			}
  4444  			podList.Items = append(podList.Items, newList.Items...)
  4445  			return newList, nil
  4446  		})
  4447  	return podList, err
  4448  }
  4449  
  4450  // ConfigMapDescriber generates information about a ConfigMap
  4451  type ConfigMapDescriber struct {
  4452  	clientset.Interface
  4453  }
  4454  
  4455  func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4456  	c := d.CoreV1().ConfigMaps(namespace)
  4457  
  4458  	configMap, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  4459  	if err != nil {
  4460  		return "", err
  4461  	}
  4462  
  4463  	return tabbedString(func(out io.Writer) error {
  4464  		w := NewPrefixWriter(out)
  4465  		w.Write(LEVEL_0, "Name:\t%s\n", configMap.Name)
  4466  		w.Write(LEVEL_0, "Namespace:\t%s\n", configMap.Namespace)
  4467  		printLabelsMultiline(w, "Labels", configMap.Labels)
  4468  		printAnnotationsMultiline(w, "Annotations", configMap.Annotations)
  4469  
  4470  		w.Write(LEVEL_0, "\nData\n====\n")
  4471  		for k, v := range configMap.Data {
  4472  			w.Write(LEVEL_0, "%s:\n----\n", k)
  4473  			w.Write(LEVEL_0, "%s\n", string(v))
  4474  		}
  4475  		w.Write(LEVEL_0, "\nBinaryData\n====\n")
  4476  		for k, v := range configMap.BinaryData {
  4477  			w.Write(LEVEL_0, "%s: %s bytes\n", k, strconv.Itoa(len(v)))
  4478  		}
  4479  		w.Write(LEVEL_0, "\n")
  4480  
  4481  		if describerSettings.ShowEvents {
  4482  			events, err := searchEvents(d.CoreV1(), configMap, describerSettings.ChunkSize)
  4483  			if err != nil {
  4484  				return err
  4485  			}
  4486  			if events != nil {
  4487  				DescribeEvents(events, w)
  4488  			}
  4489  		}
  4490  		return nil
  4491  	})
  4492  }
  4493  
  4494  // NetworkPolicyDescriber generates information about a networkingv1.NetworkPolicy
  4495  type NetworkPolicyDescriber struct {
  4496  	clientset.Interface
  4497  }
  4498  
  4499  func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4500  	c := d.NetworkingV1().NetworkPolicies(namespace)
  4501  
  4502  	networkPolicy, err := c.Get(context.TODO(), name, metav1.GetOptions{})
  4503  	if err != nil {
  4504  		return "", err
  4505  	}
  4506  
  4507  	return describeNetworkPolicy(networkPolicy)
  4508  }
  4509  
  4510  func describeNetworkPolicy(networkPolicy *networkingv1.NetworkPolicy) (string, error) {
  4511  	return tabbedString(func(out io.Writer) error {
  4512  		w := NewPrefixWriter(out)
  4513  		w.Write(LEVEL_0, "Name:\t%s\n", networkPolicy.Name)
  4514  		w.Write(LEVEL_0, "Namespace:\t%s\n", networkPolicy.Namespace)
  4515  		w.Write(LEVEL_0, "Created on:\t%s\n", networkPolicy.CreationTimestamp)
  4516  		printLabelsMultiline(w, "Labels", networkPolicy.Labels)
  4517  		printAnnotationsMultiline(w, "Annotations", networkPolicy.Annotations)
  4518  		describeNetworkPolicySpec(networkPolicy.Spec, w)
  4519  		return nil
  4520  	})
  4521  }
  4522  
  4523  func describeNetworkPolicySpec(nps networkingv1.NetworkPolicySpec, w PrefixWriter) {
  4524  	w.Write(LEVEL_0, "Spec:\n")
  4525  	w.Write(LEVEL_1, "PodSelector: ")
  4526  	if len(nps.PodSelector.MatchLabels) == 0 && len(nps.PodSelector.MatchExpressions) == 0 {
  4527  		w.Write(LEVEL_2, "<none> (Allowing the specific traffic to all pods in this namespace)\n")
  4528  	} else {
  4529  		w.Write(LEVEL_2, "%s\n", metav1.FormatLabelSelector(&nps.PodSelector))
  4530  	}
  4531  
  4532  	ingressEnabled, egressEnabled := getPolicyType(nps)
  4533  	if ingressEnabled {
  4534  		w.Write(LEVEL_1, "Allowing ingress traffic:\n")
  4535  		printNetworkPolicySpecIngressFrom(nps.Ingress, "    ", w)
  4536  	} else {
  4537  		w.Write(LEVEL_1, "Not affecting ingress traffic\n")
  4538  	}
  4539  	if egressEnabled {
  4540  		w.Write(LEVEL_1, "Allowing egress traffic:\n")
  4541  		printNetworkPolicySpecEgressTo(nps.Egress, "    ", w)
  4542  	} else {
  4543  		w.Write(LEVEL_1, "Not affecting egress traffic\n")
  4544  
  4545  	}
  4546  	w.Write(LEVEL_1, "Policy Types: %v\n", policyTypesToString(nps.PolicyTypes))
  4547  }
  4548  
  4549  func getPolicyType(nps networkingv1.NetworkPolicySpec) (bool, bool) {
  4550  	var ingress, egress bool
  4551  	for _, pt := range nps.PolicyTypes {
  4552  		switch pt {
  4553  		case networkingv1.PolicyTypeIngress:
  4554  			ingress = true
  4555  		case networkingv1.PolicyTypeEgress:
  4556  			egress = true
  4557  		}
  4558  	}
  4559  
  4560  	return ingress, egress
  4561  }
  4562  
  4563  func printNetworkPolicySpecIngressFrom(npirs []networkingv1.NetworkPolicyIngressRule, initialIndent string, w PrefixWriter) {
  4564  	if len(npirs) == 0 {
  4565  		w.Write(LEVEL_0, "%s%s\n", initialIndent, "<none> (Selected pods are isolated for ingress connectivity)")
  4566  		return
  4567  	}
  4568  	for i, npir := range npirs {
  4569  		if len(npir.Ports) == 0 {
  4570  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "To Port: <any> (traffic allowed to all ports)")
  4571  		} else {
  4572  			for _, port := range npir.Ports {
  4573  				var proto corev1.Protocol
  4574  				if port.Protocol != nil {
  4575  					proto = *port.Protocol
  4576  				} else {
  4577  					proto = corev1.ProtocolTCP
  4578  				}
  4579  				w.Write(LEVEL_0, "%s%s: %s/%s\n", initialIndent, "To Port", port.Port, proto)
  4580  			}
  4581  		}
  4582  		if len(npir.From) == 0 {
  4583  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "From: <any> (traffic not restricted by source)")
  4584  		} else {
  4585  			for _, from := range npir.From {
  4586  				w.Write(LEVEL_0, "%s%s\n", initialIndent, "From:")
  4587  				if from.PodSelector != nil && from.NamespaceSelector != nil {
  4588  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "NamespaceSelector", metav1.FormatLabelSelector(from.NamespaceSelector))
  4589  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "PodSelector", metav1.FormatLabelSelector(from.PodSelector))
  4590  				} else if from.PodSelector != nil {
  4591  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "PodSelector", metav1.FormatLabelSelector(from.PodSelector))
  4592  				} else if from.NamespaceSelector != nil {
  4593  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "NamespaceSelector", metav1.FormatLabelSelector(from.NamespaceSelector))
  4594  				} else if from.IPBlock != nil {
  4595  					w.Write(LEVEL_1, "%sIPBlock:\n", initialIndent)
  4596  					w.Write(LEVEL_2, "%sCIDR: %s\n", initialIndent, from.IPBlock.CIDR)
  4597  					w.Write(LEVEL_2, "%sExcept: %v\n", initialIndent, strings.Join(from.IPBlock.Except, ", "))
  4598  				}
  4599  			}
  4600  		}
  4601  		if i != len(npirs)-1 {
  4602  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "----------")
  4603  		}
  4604  	}
  4605  }
  4606  
  4607  func printNetworkPolicySpecEgressTo(npers []networkingv1.NetworkPolicyEgressRule, initialIndent string, w PrefixWriter) {
  4608  	if len(npers) == 0 {
  4609  		w.Write(LEVEL_0, "%s%s\n", initialIndent, "<none> (Selected pods are isolated for egress connectivity)")
  4610  		return
  4611  	}
  4612  	for i, nper := range npers {
  4613  		if len(nper.Ports) == 0 {
  4614  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "To Port: <any> (traffic allowed to all ports)")
  4615  		} else {
  4616  			for _, port := range nper.Ports {
  4617  				var proto corev1.Protocol
  4618  				if port.Protocol != nil {
  4619  					proto = *port.Protocol
  4620  				} else {
  4621  					proto = corev1.ProtocolTCP
  4622  				}
  4623  				w.Write(LEVEL_0, "%s%s: %s/%s\n", initialIndent, "To Port", port.Port, proto)
  4624  			}
  4625  		}
  4626  		if len(nper.To) == 0 {
  4627  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "To: <any> (traffic not restricted by destination)")
  4628  		} else {
  4629  			for _, to := range nper.To {
  4630  				w.Write(LEVEL_0, "%s%s\n", initialIndent, "To:")
  4631  				if to.PodSelector != nil && to.NamespaceSelector != nil {
  4632  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "NamespaceSelector", metav1.FormatLabelSelector(to.NamespaceSelector))
  4633  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "PodSelector", metav1.FormatLabelSelector(to.PodSelector))
  4634  				} else if to.PodSelector != nil {
  4635  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "PodSelector", metav1.FormatLabelSelector(to.PodSelector))
  4636  				} else if to.NamespaceSelector != nil {
  4637  					w.Write(LEVEL_1, "%s%s: %s\n", initialIndent, "NamespaceSelector", metav1.FormatLabelSelector(to.NamespaceSelector))
  4638  				} else if to.IPBlock != nil {
  4639  					w.Write(LEVEL_1, "%sIPBlock:\n", initialIndent)
  4640  					w.Write(LEVEL_2, "%sCIDR: %s\n", initialIndent, to.IPBlock.CIDR)
  4641  					w.Write(LEVEL_2, "%sExcept: %v\n", initialIndent, strings.Join(to.IPBlock.Except, ", "))
  4642  				}
  4643  			}
  4644  		}
  4645  		if i != len(npers)-1 {
  4646  			w.Write(LEVEL_0, "%s%s\n", initialIndent, "----------")
  4647  		}
  4648  	}
  4649  }
  4650  
  4651  type StorageClassDescriber struct {
  4652  	clientset.Interface
  4653  }
  4654  
  4655  func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4656  	sc, err := s.StorageV1().StorageClasses().Get(context.TODO(), name, metav1.GetOptions{})
  4657  	if err != nil {
  4658  		return "", err
  4659  	}
  4660  
  4661  	var events *corev1.EventList
  4662  	if describerSettings.ShowEvents {
  4663  		events, _ = searchEvents(s.CoreV1(), sc, describerSettings.ChunkSize)
  4664  	}
  4665  
  4666  	return describeStorageClass(sc, events)
  4667  }
  4668  
  4669  func describeStorageClass(sc *storagev1.StorageClass, events *corev1.EventList) (string, error) {
  4670  	return tabbedString(func(out io.Writer) error {
  4671  		w := NewPrefixWriter(out)
  4672  		w.Write(LEVEL_0, "Name:\t%s\n", sc.Name)
  4673  		w.Write(LEVEL_0, "IsDefaultClass:\t%s\n", storageutil.IsDefaultAnnotationText(sc.ObjectMeta))
  4674  		w.Write(LEVEL_0, "Annotations:\t%s\n", labels.FormatLabels(sc.Annotations))
  4675  		w.Write(LEVEL_0, "Provisioner:\t%s\n", sc.Provisioner)
  4676  		w.Write(LEVEL_0, "Parameters:\t%s\n", labels.FormatLabels(sc.Parameters))
  4677  		w.Write(LEVEL_0, "AllowVolumeExpansion:\t%s\n", printBoolPtr(sc.AllowVolumeExpansion))
  4678  		if len(sc.MountOptions) == 0 {
  4679  			w.Write(LEVEL_0, "MountOptions:\t<none>\n")
  4680  		} else {
  4681  			w.Write(LEVEL_0, "MountOptions:\n")
  4682  			for _, option := range sc.MountOptions {
  4683  				w.Write(LEVEL_1, "%s\n", option)
  4684  			}
  4685  		}
  4686  		if sc.ReclaimPolicy != nil {
  4687  			w.Write(LEVEL_0, "ReclaimPolicy:\t%s\n", *sc.ReclaimPolicy)
  4688  		}
  4689  		if sc.VolumeBindingMode != nil {
  4690  			w.Write(LEVEL_0, "VolumeBindingMode:\t%s\n", *sc.VolumeBindingMode)
  4691  		}
  4692  		if sc.AllowedTopologies != nil {
  4693  			printAllowedTopologies(w, sc.AllowedTopologies)
  4694  		}
  4695  		if events != nil {
  4696  			DescribeEvents(events, w)
  4697  		}
  4698  
  4699  		return nil
  4700  	})
  4701  }
  4702  
  4703  type VolumeAttributesClassDescriber struct {
  4704  	clientset.Interface
  4705  }
  4706  
  4707  func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4708  	vac, err := d.StorageV1alpha1().VolumeAttributesClasses().Get(context.TODO(), name, metav1.GetOptions{})
  4709  	if err != nil {
  4710  		return "", err
  4711  	}
  4712  
  4713  	var events *corev1.EventList
  4714  	if describerSettings.ShowEvents {
  4715  		events, _ = searchEvents(d.CoreV1(), vac, describerSettings.ChunkSize)
  4716  	}
  4717  
  4718  	return describeVolumeAttributesClass(vac, events)
  4719  }
  4720  
  4721  func describeVolumeAttributesClass(vac *storagev1alpha1.VolumeAttributesClass, events *corev1.EventList) (string, error) {
  4722  	return tabbedString(func(out io.Writer) error {
  4723  		w := NewPrefixWriter(out)
  4724  		w.Write(LEVEL_0, "Name:\t%s\n", vac.Name)
  4725  		w.Write(LEVEL_0, "Annotations:\t%s\n", labels.FormatLabels(vac.Annotations))
  4726  		w.Write(LEVEL_0, "DriverName:\t%s\n", vac.DriverName)
  4727  		w.Write(LEVEL_0, "Parameters:\t%s\n", labels.FormatLabels(vac.Parameters))
  4728  
  4729  		if events != nil {
  4730  			DescribeEvents(events, w)
  4731  		}
  4732  
  4733  		return nil
  4734  	})
  4735  }
  4736  
  4737  type CSINodeDescriber struct {
  4738  	clientset.Interface
  4739  }
  4740  
  4741  func (c *CSINodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4742  	csi, err := c.StorageV1().CSINodes().Get(context.TODO(), name, metav1.GetOptions{})
  4743  	if err != nil {
  4744  		return "", err
  4745  	}
  4746  
  4747  	var events *corev1.EventList
  4748  	if describerSettings.ShowEvents {
  4749  		events, _ = searchEvents(c.CoreV1(), csi, describerSettings.ChunkSize)
  4750  	}
  4751  
  4752  	return describeCSINode(csi, events)
  4753  }
  4754  
  4755  func describeCSINode(csi *storagev1.CSINode, events *corev1.EventList) (output string, err error) {
  4756  	return tabbedString(func(out io.Writer) error {
  4757  		w := NewPrefixWriter(out)
  4758  		w.Write(LEVEL_0, "Name:\t%s\n", csi.GetName())
  4759  		printLabelsMultiline(w, "Labels", csi.GetLabels())
  4760  		printAnnotationsMultiline(w, "Annotations", csi.GetAnnotations())
  4761  		w.Write(LEVEL_0, "CreationTimestamp:\t%s\n", csi.CreationTimestamp.Time.Format(time.RFC1123Z))
  4762  		w.Write(LEVEL_0, "Spec:\n")
  4763  		if csi.Spec.Drivers != nil {
  4764  			w.Write(LEVEL_1, "Drivers:\n")
  4765  			for _, driver := range csi.Spec.Drivers {
  4766  				w.Write(LEVEL_2, "%s:\n", driver.Name)
  4767  				w.Write(LEVEL_3, "Node ID:\t%s\n", driver.NodeID)
  4768  				if driver.Allocatable != nil && driver.Allocatable.Count != nil {
  4769  					w.Write(LEVEL_3, "Allocatables:\n")
  4770  					w.Write(LEVEL_4, "Count:\t%d\n", *driver.Allocatable.Count)
  4771  				}
  4772  				if driver.TopologyKeys != nil {
  4773  					w.Write(LEVEL_3, "Topology Keys:\t%s\n", driver.TopologyKeys)
  4774  				}
  4775  			}
  4776  		}
  4777  		if events != nil {
  4778  			DescribeEvents(events, w)
  4779  		}
  4780  		return nil
  4781  	})
  4782  }
  4783  
  4784  func printAllowedTopologies(w PrefixWriter, topologies []corev1.TopologySelectorTerm) {
  4785  	w.Write(LEVEL_0, "AllowedTopologies:\t")
  4786  	if len(topologies) == 0 {
  4787  		w.WriteLine("<none>")
  4788  		return
  4789  	}
  4790  	w.WriteLine("")
  4791  	for i, term := range topologies {
  4792  		printTopologySelectorTermsMultilineWithIndent(w, LEVEL_1, fmt.Sprintf("Term %d", i), "\t", term.MatchLabelExpressions)
  4793  	}
  4794  }
  4795  
  4796  func printTopologySelectorTermsMultilineWithIndent(w PrefixWriter, indentLevel int, title, innerIndent string, reqs []corev1.TopologySelectorLabelRequirement) {
  4797  	w.Write(indentLevel, "%s:%s", title, innerIndent)
  4798  
  4799  	if len(reqs) == 0 {
  4800  		w.WriteLine("<none>")
  4801  		return
  4802  	}
  4803  
  4804  	for i, req := range reqs {
  4805  		if i != 0 {
  4806  			w.Write(indentLevel, "%s", innerIndent)
  4807  		}
  4808  		exprStr := fmt.Sprintf("%s %s", req.Key, "in")
  4809  		if len(req.Values) > 0 {
  4810  			exprStr = fmt.Sprintf("%s [%s]", exprStr, strings.Join(req.Values, ", "))
  4811  		}
  4812  		w.Write(LEVEL_0, "%s\n", exprStr)
  4813  	}
  4814  }
  4815  
  4816  type PodDisruptionBudgetDescriber struct {
  4817  	clientset.Interface
  4818  }
  4819  
  4820  func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4821  	var (
  4822  		pdbv1      *policyv1.PodDisruptionBudget
  4823  		pdbv1beta1 *policyv1beta1.PodDisruptionBudget
  4824  		err        error
  4825  	)
  4826  
  4827  	pdbv1, err = p.PolicyV1().PodDisruptionBudgets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  4828  	if err == nil {
  4829  		var events *corev1.EventList
  4830  		if describerSettings.ShowEvents {
  4831  			events, _ = searchEvents(p.CoreV1(), pdbv1, describerSettings.ChunkSize)
  4832  		}
  4833  		return describePodDisruptionBudgetV1(pdbv1, events)
  4834  	}
  4835  
  4836  	// try falling back to v1beta1 in NotFound error cases
  4837  	if apierrors.IsNotFound(err) {
  4838  		pdbv1beta1, err = p.PolicyV1beta1().PodDisruptionBudgets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
  4839  	}
  4840  	if err == nil {
  4841  		var events *corev1.EventList
  4842  		if describerSettings.ShowEvents {
  4843  			events, _ = searchEvents(p.CoreV1(), pdbv1beta1, describerSettings.ChunkSize)
  4844  		}
  4845  		return describePodDisruptionBudgetV1beta1(pdbv1beta1, events)
  4846  	}
  4847  
  4848  	return "", err
  4849  }
  4850  
  4851  func describePodDisruptionBudgetV1(pdb *policyv1.PodDisruptionBudget, events *corev1.EventList) (string, error) {
  4852  	return tabbedString(func(out io.Writer) error {
  4853  		w := NewPrefixWriter(out)
  4854  		w.Write(LEVEL_0, "Name:\t%s\n", pdb.Name)
  4855  		w.Write(LEVEL_0, "Namespace:\t%s\n", pdb.Namespace)
  4856  
  4857  		if pdb.Spec.MinAvailable != nil {
  4858  			w.Write(LEVEL_0, "Min available:\t%s\n", pdb.Spec.MinAvailable.String())
  4859  		} else if pdb.Spec.MaxUnavailable != nil {
  4860  			w.Write(LEVEL_0, "Max unavailable:\t%s\n", pdb.Spec.MaxUnavailable.String())
  4861  		}
  4862  
  4863  		if pdb.Spec.Selector != nil {
  4864  			w.Write(LEVEL_0, "Selector:\t%s\n", metav1.FormatLabelSelector(pdb.Spec.Selector))
  4865  		} else {
  4866  			w.Write(LEVEL_0, "Selector:\t<unset>\n")
  4867  		}
  4868  		w.Write(LEVEL_0, "Status:\n")
  4869  		w.Write(LEVEL_2, "Allowed disruptions:\t%d\n", pdb.Status.DisruptionsAllowed)
  4870  		w.Write(LEVEL_2, "Current:\t%d\n", pdb.Status.CurrentHealthy)
  4871  		w.Write(LEVEL_2, "Desired:\t%d\n", pdb.Status.DesiredHealthy)
  4872  		w.Write(LEVEL_2, "Total:\t%d\n", pdb.Status.ExpectedPods)
  4873  		if events != nil {
  4874  			DescribeEvents(events, w)
  4875  		}
  4876  
  4877  		return nil
  4878  	})
  4879  }
  4880  
  4881  func describePodDisruptionBudgetV1beta1(pdb *policyv1beta1.PodDisruptionBudget, events *corev1.EventList) (string, error) {
  4882  	return tabbedString(func(out io.Writer) error {
  4883  		w := NewPrefixWriter(out)
  4884  		w.Write(LEVEL_0, "Name:\t%s\n", pdb.Name)
  4885  		w.Write(LEVEL_0, "Namespace:\t%s\n", pdb.Namespace)
  4886  
  4887  		if pdb.Spec.MinAvailable != nil {
  4888  			w.Write(LEVEL_0, "Min available:\t%s\n", pdb.Spec.MinAvailable.String())
  4889  		} else if pdb.Spec.MaxUnavailable != nil {
  4890  			w.Write(LEVEL_0, "Max unavailable:\t%s\n", pdb.Spec.MaxUnavailable.String())
  4891  		}
  4892  
  4893  		if pdb.Spec.Selector != nil {
  4894  			w.Write(LEVEL_0, "Selector:\t%s\n", metav1.FormatLabelSelector(pdb.Spec.Selector))
  4895  		} else {
  4896  			w.Write(LEVEL_0, "Selector:\t<unset>\n")
  4897  		}
  4898  		w.Write(LEVEL_0, "Status:\n")
  4899  		w.Write(LEVEL_2, "Allowed disruptions:\t%d\n", pdb.Status.DisruptionsAllowed)
  4900  		w.Write(LEVEL_2, "Current:\t%d\n", pdb.Status.CurrentHealthy)
  4901  		w.Write(LEVEL_2, "Desired:\t%d\n", pdb.Status.DesiredHealthy)
  4902  		w.Write(LEVEL_2, "Total:\t%d\n", pdb.Status.ExpectedPods)
  4903  		if events != nil {
  4904  			DescribeEvents(events, w)
  4905  		}
  4906  
  4907  		return nil
  4908  	})
  4909  }
  4910  
  4911  // PriorityClassDescriber generates information about a PriorityClass.
  4912  type PriorityClassDescriber struct {
  4913  	clientset.Interface
  4914  }
  4915  
  4916  func (s *PriorityClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
  4917  	pc, err := s.SchedulingV1().PriorityClasses().Get(context.TODO(), name, metav1.GetOptions{})
  4918  	if err != nil {
  4919  		return "", err
  4920  	}
  4921  
  4922  	var events *corev1.EventList
  4923  	if describerSettings.ShowEvents {
  4924  		events, _ = searchEvents(s.CoreV1(), pc, describerSettings.ChunkSize)
  4925  	}
  4926  
  4927  	return describePriorityClass(pc, events)
  4928  }
  4929  
  4930  func describePriorityClass(pc *schedulingv1.PriorityClass, events *corev1.EventList) (string, error) {
  4931  	return tabbedString(func(out io.Writer) error {
  4932  		w := NewPrefixWriter(out)
  4933  		w.Write(LEVEL_0, "Name:\t%s\n", pc.Name)
  4934  		w.Write(LEVEL_0, "Value:\t%v\n", pc.Value)
  4935  		w.Write(LEVEL_0, "GlobalDefault:\t%v\n", pc.GlobalDefault)
  4936  		w.Write(LEVEL_0, "PreemptionPolicy:\t%s\n", *pc.PreemptionPolicy)
  4937  		w.Write(LEVEL_0, "Description:\t%s\n", pc.Description)
  4938  
  4939  		w.Write(LEVEL_0, "Annotations:\t%s\n", labels.FormatLabels(pc.Annotations))
  4940  		if events != nil {
  4941  			DescribeEvents(events, w)
  4942  		}
  4943  
  4944  		return nil
  4945  	})
  4946  }
  4947  
  4948  func stringOrNone(s string) string {
  4949  	return stringOrDefaultValue(s, "<none>")
  4950  }
  4951  
  4952  func stringOrDefaultValue(s, defaultValue string) string {
  4953  	if len(s) > 0 {
  4954  		return s
  4955  	}
  4956  	return defaultValue
  4957  }
  4958  
  4959  func policyTypesToString(pts []networkingv1.PolicyType) string {
  4960  	formattedString := ""
  4961  	if pts != nil {
  4962  		strPts := []string{}
  4963  		for _, p := range pts {
  4964  			strPts = append(strPts, string(p))
  4965  		}
  4966  		formattedString = strings.Join(strPts, ", ")
  4967  	}
  4968  	return stringOrNone(formattedString)
  4969  }
  4970  
  4971  // newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
  4972  func newErrNoDescriber(types ...reflect.Type) error {
  4973  	names := make([]string, 0, len(types))
  4974  	for _, t := range types {
  4975  		names = append(names, t.String())
  4976  	}
  4977  	return ErrNoDescriber{Types: names}
  4978  }
  4979  
  4980  // Describers implements ObjectDescriber against functions registered via Add. Those functions can
  4981  // be strongly typed. Types are exactly matched (no conversion or assignable checks).
  4982  type Describers struct {
  4983  	searchFns map[reflect.Type][]typeFunc
  4984  }
  4985  
  4986  // DescribeObject implements ObjectDescriber and will attempt to print the provided object to a string,
  4987  // if at least one describer function has been registered with the exact types passed, or if any
  4988  // describer can print the exact object in its first argument (the remainder will be provided empty
  4989  // values). If no function registered with Add can satisfy the passed objects, an ErrNoDescriber will
  4990  // be returned
  4991  // TODO: reorder and partial match extra.
  4992  func (d *Describers) DescribeObject(exact interface{}, extra ...interface{}) (string, error) {
  4993  	exactType := reflect.TypeOf(exact)
  4994  	fns, ok := d.searchFns[exactType]
  4995  	if !ok {
  4996  		return "", newErrNoDescriber(exactType)
  4997  	}
  4998  	if len(extra) == 0 {
  4999  		for _, typeFn := range fns {
  5000  			if len(typeFn.Extra) == 0 {
  5001  				return typeFn.Describe(exact, extra...)
  5002  			}
  5003  		}
  5004  		typeFn := fns[0]
  5005  		for _, t := range typeFn.Extra {
  5006  			v := reflect.New(t).Elem()
  5007  			extra = append(extra, v.Interface())
  5008  		}
  5009  		return fns[0].Describe(exact, extra...)
  5010  	}
  5011  
  5012  	types := make([]reflect.Type, 0, len(extra))
  5013  	for _, obj := range extra {
  5014  		types = append(types, reflect.TypeOf(obj))
  5015  	}
  5016  	for _, typeFn := range fns {
  5017  		if typeFn.Matches(types) {
  5018  			return typeFn.Describe(exact, extra...)
  5019  		}
  5020  	}
  5021  	return "", newErrNoDescriber(append([]reflect.Type{exactType}, types...)...)
  5022  }
  5023  
  5024  // Add adds one or more describer functions to the Describer. The passed function must
  5025  // match the signature:
  5026  //
  5027  //	func(...) (string, error)
  5028  //
  5029  // Any number of arguments may be provided.
  5030  func (d *Describers) Add(fns ...interface{}) error {
  5031  	for _, fn := range fns {
  5032  		fv := reflect.ValueOf(fn)
  5033  		ft := fv.Type()
  5034  		if ft.Kind() != reflect.Func {
  5035  			return fmt.Errorf("expected func, got: %v", ft)
  5036  		}
  5037  		numIn := ft.NumIn()
  5038  		if numIn == 0 {
  5039  			return fmt.Errorf("expected at least one 'in' params, got: %v", ft)
  5040  		}
  5041  		if ft.NumOut() != 2 {
  5042  			return fmt.Errorf("expected two 'out' params - (string, error), got: %v", ft)
  5043  		}
  5044  		types := make([]reflect.Type, 0, numIn)
  5045  		for i := 0; i < numIn; i++ {
  5046  			types = append(types, ft.In(i))
  5047  		}
  5048  		if ft.Out(0) != reflect.TypeOf(string("")) {
  5049  			return fmt.Errorf("expected string return, got: %v", ft)
  5050  		}
  5051  		var forErrorType error
  5052  		// This convolution is necessary, otherwise TypeOf picks up on the fact
  5053  		// that forErrorType is nil.
  5054  		errorType := reflect.TypeOf(&forErrorType).Elem()
  5055  		if ft.Out(1) != errorType {
  5056  			return fmt.Errorf("expected error return, got: %v", ft)
  5057  		}
  5058  
  5059  		exact := types[0]
  5060  		extra := types[1:]
  5061  		if d.searchFns == nil {
  5062  			d.searchFns = make(map[reflect.Type][]typeFunc)
  5063  		}
  5064  		fns := d.searchFns[exact]
  5065  		fn := typeFunc{Extra: extra, Fn: fv}
  5066  		fns = append(fns, fn)
  5067  		d.searchFns[exact] = fns
  5068  	}
  5069  	return nil
  5070  }
  5071  
  5072  // typeFunc holds information about a describer function and the types it accepts
  5073  type typeFunc struct {
  5074  	Extra []reflect.Type
  5075  	Fn    reflect.Value
  5076  }
  5077  
  5078  // Matches returns true when the passed types exactly match the Extra list.
  5079  func (fn typeFunc) Matches(types []reflect.Type) bool {
  5080  	if len(fn.Extra) != len(types) {
  5081  		return false
  5082  	}
  5083  	// reorder the items in array types and fn.Extra
  5084  	// convert the type into string and sort them, check if they are matched
  5085  	varMap := make(map[reflect.Type]bool)
  5086  	for i := range fn.Extra {
  5087  		varMap[fn.Extra[i]] = true
  5088  	}
  5089  	for i := range types {
  5090  		if _, found := varMap[types[i]]; !found {
  5091  			return false
  5092  		}
  5093  	}
  5094  	return true
  5095  }
  5096  
  5097  // Describe invokes the nested function with the exact number of arguments.
  5098  func (fn typeFunc) Describe(exact interface{}, extra ...interface{}) (string, error) {
  5099  	values := []reflect.Value{reflect.ValueOf(exact)}
  5100  	for i, obj := range extra {
  5101  		if obj != nil {
  5102  			values = append(values, reflect.ValueOf(obj))
  5103  		} else {
  5104  			values = append(values, reflect.New(fn.Extra[i]).Elem())
  5105  		}
  5106  	}
  5107  	out := fn.Fn.Call(values)
  5108  	s := out[0].Interface().(string)
  5109  	var err error
  5110  	if !out[1].IsNil() {
  5111  		err = out[1].Interface().(error)
  5112  	}
  5113  	return s, err
  5114  }
  5115  
  5116  // printLabelsMultiline prints multiple labels with a proper alignment.
  5117  func printLabelsMultiline(w PrefixWriter, title string, labels map[string]string) {
  5118  	printLabelsMultilineWithIndent(w, "", title, "\t", labels, sets.NewString())
  5119  }
  5120  
  5121  // printLabelsMultiline prints multiple labels with a user-defined alignment.
  5122  func printLabelsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, labels map[string]string, skip sets.String) {
  5123  	w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
  5124  
  5125  	if len(labels) == 0 {
  5126  		w.WriteLine("<none>")
  5127  		return
  5128  	}
  5129  
  5130  	// to print labels in the sorted order
  5131  	keys := make([]string, 0, len(labels))
  5132  	for key := range labels {
  5133  		if skip.Has(key) {
  5134  			continue
  5135  		}
  5136  		keys = append(keys, key)
  5137  	}
  5138  	if len(keys) == 0 {
  5139  		w.WriteLine("<none>")
  5140  		return
  5141  	}
  5142  	sort.Strings(keys)
  5143  
  5144  	for i, key := range keys {
  5145  		if i != 0 {
  5146  			w.Write(LEVEL_0, "%s", initialIndent)
  5147  			w.Write(LEVEL_0, "%s", innerIndent)
  5148  		}
  5149  		w.Write(LEVEL_0, "%s=%s\n", key, labels[key])
  5150  	}
  5151  }
  5152  
  5153  // printTaintsMultiline prints multiple taints with a proper alignment.
  5154  func printNodeTaintsMultiline(w PrefixWriter, title string, taints []corev1.Taint) {
  5155  	printTaintsMultilineWithIndent(w, "", title, "\t", taints)
  5156  }
  5157  
  5158  // printTaintsMultilineWithIndent prints multiple taints with a user-defined alignment.
  5159  func printTaintsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, taints []corev1.Taint) {
  5160  	w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
  5161  
  5162  	if len(taints) == 0 {
  5163  		w.WriteLine("<none>")
  5164  		return
  5165  	}
  5166  
  5167  	// to print taints in the sorted order
  5168  	sort.Slice(taints, func(i, j int) bool {
  5169  		cmpKey := func(taint corev1.Taint) string {
  5170  			return string(taint.Effect) + "," + taint.Key
  5171  		}
  5172  		return cmpKey(taints[i]) < cmpKey(taints[j])
  5173  	})
  5174  
  5175  	for i, taint := range taints {
  5176  		if i != 0 {
  5177  			w.Write(LEVEL_0, "%s", initialIndent)
  5178  			w.Write(LEVEL_0, "%s", innerIndent)
  5179  		}
  5180  		w.Write(LEVEL_0, "%s\n", taint.ToString())
  5181  	}
  5182  }
  5183  
  5184  // printPodsMultiline prints multiple pods with a proper alignment.
  5185  func printPodsMultiline(w PrefixWriter, title string, pods []corev1.Pod) {
  5186  	printPodsMultilineWithIndent(w, "", title, "\t", pods)
  5187  }
  5188  
  5189  // printPodsMultilineWithIndent prints multiple pods with a user-defined alignment.
  5190  func printPodsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, pods []corev1.Pod) {
  5191  	w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
  5192  
  5193  	if len(pods) == 0 {
  5194  		w.WriteLine("<none>")
  5195  		return
  5196  	}
  5197  
  5198  	// to print pods in the sorted order
  5199  	sort.Slice(pods, func(i, j int) bool {
  5200  		cmpKey := func(pod corev1.Pod) string {
  5201  			return pod.Name
  5202  		}
  5203  		return cmpKey(pods[i]) < cmpKey(pods[j])
  5204  	})
  5205  
  5206  	for i, pod := range pods {
  5207  		if i != 0 {
  5208  			w.Write(LEVEL_0, "%s", initialIndent)
  5209  			w.Write(LEVEL_0, "%s", innerIndent)
  5210  		}
  5211  		w.Write(LEVEL_0, "%s\n", pod.Name)
  5212  	}
  5213  }
  5214  
  5215  // printPodTolerationsMultiline prints multiple tolerations with a proper alignment.
  5216  func printPodTolerationsMultiline(w PrefixWriter, title string, tolerations []corev1.Toleration) {
  5217  	printTolerationsMultilineWithIndent(w, "", title, "\t", tolerations)
  5218  }
  5219  
  5220  // printTolerationsMultilineWithIndent prints multiple tolerations with a user-defined alignment.
  5221  func printTolerationsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, tolerations []corev1.Toleration) {
  5222  	w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
  5223  
  5224  	if len(tolerations) == 0 {
  5225  		w.WriteLine("<none>")
  5226  		return
  5227  	}
  5228  
  5229  	// to print tolerations in the sorted order
  5230  	sort.Slice(tolerations, func(i, j int) bool {
  5231  		return tolerations[i].Key < tolerations[j].Key
  5232  	})
  5233  
  5234  	for i, toleration := range tolerations {
  5235  		if i != 0 {
  5236  			w.Write(LEVEL_0, "%s", initialIndent)
  5237  			w.Write(LEVEL_0, "%s", innerIndent)
  5238  		}
  5239  		w.Write(LEVEL_0, "%s", toleration.Key)
  5240  		if len(toleration.Value) != 0 {
  5241  			w.Write(LEVEL_0, "=%s", toleration.Value)
  5242  		}
  5243  		if len(toleration.Effect) != 0 {
  5244  			w.Write(LEVEL_0, ":%s", toleration.Effect)
  5245  		}
  5246  		// tolerations:
  5247  		// - operator: "Exists"
  5248  		// is a special case which tolerates everything
  5249  		if toleration.Operator == corev1.TolerationOpExists && len(toleration.Value) == 0 {
  5250  			if len(toleration.Key) != 0 || len(toleration.Effect) != 0 {
  5251  				w.Write(LEVEL_0, " op=Exists")
  5252  			} else {
  5253  				w.Write(LEVEL_0, "op=Exists")
  5254  			}
  5255  		}
  5256  
  5257  		if toleration.TolerationSeconds != nil {
  5258  			w.Write(LEVEL_0, " for %ds", *toleration.TolerationSeconds)
  5259  		}
  5260  		w.Write(LEVEL_0, "\n")
  5261  	}
  5262  }
  5263  
  5264  type flusher interface {
  5265  	Flush()
  5266  }
  5267  
  5268  func tabbedString(f func(io.Writer) error) (string, error) {
  5269  	out := new(tabwriter.Writer)
  5270  	buf := &bytes.Buffer{}
  5271  	out.Init(buf, 0, 8, 2, ' ', 0)
  5272  
  5273  	err := f(out)
  5274  	if err != nil {
  5275  		return "", err
  5276  	}
  5277  
  5278  	out.Flush()
  5279  	return buf.String(), nil
  5280  }
  5281  
  5282  type SortableResourceNames []corev1.ResourceName
  5283  
  5284  func (list SortableResourceNames) Len() int {
  5285  	return len(list)
  5286  }
  5287  
  5288  func (list SortableResourceNames) Swap(i, j int) {
  5289  	list[i], list[j] = list[j], list[i]
  5290  }
  5291  
  5292  func (list SortableResourceNames) Less(i, j int) bool {
  5293  	return list[i] < list[j]
  5294  }
  5295  
  5296  // SortedResourceNames returns the sorted resource names of a resource list.
  5297  func SortedResourceNames(list corev1.ResourceList) []corev1.ResourceName {
  5298  	resources := make([]corev1.ResourceName, 0, len(list))
  5299  	for res := range list {
  5300  		resources = append(resources, res)
  5301  	}
  5302  	sort.Sort(SortableResourceNames(resources))
  5303  	return resources
  5304  }
  5305  
  5306  type SortableResourceQuotas []corev1.ResourceQuota
  5307  
  5308  func (list SortableResourceQuotas) Len() int {
  5309  	return len(list)
  5310  }
  5311  
  5312  func (list SortableResourceQuotas) Swap(i, j int) {
  5313  	list[i], list[j] = list[j], list[i]
  5314  }
  5315  
  5316  func (list SortableResourceQuotas) Less(i, j int) bool {
  5317  	return list[i].Name < list[j].Name
  5318  }
  5319  
  5320  type SortableVolumeMounts []corev1.VolumeMount
  5321  
  5322  func (list SortableVolumeMounts) Len() int {
  5323  	return len(list)
  5324  }
  5325  
  5326  func (list SortableVolumeMounts) Swap(i, j int) {
  5327  	list[i], list[j] = list[j], list[i]
  5328  }
  5329  
  5330  func (list SortableVolumeMounts) Less(i, j int) bool {
  5331  	return list[i].MountPath < list[j].MountPath
  5332  }
  5333  
  5334  type SortableVolumeDevices []corev1.VolumeDevice
  5335  
  5336  func (list SortableVolumeDevices) Len() int {
  5337  	return len(list)
  5338  }
  5339  
  5340  func (list SortableVolumeDevices) Swap(i, j int) {
  5341  	list[i], list[j] = list[j], list[i]
  5342  }
  5343  
  5344  func (list SortableVolumeDevices) Less(i, j int) bool {
  5345  	return list[i].DevicePath < list[j].DevicePath
  5346  }
  5347  
  5348  var maxAnnotationLen = 140
  5349  
  5350  // printAnnotationsMultiline prints multiple annotations with a proper alignment.
  5351  // If annotation string is too long, we omit chars more than 200 length.
  5352  func printAnnotationsMultiline(w PrefixWriter, title string, annotations map[string]string) {
  5353  	w.Write(LEVEL_0, "%s:\t", title)
  5354  
  5355  	// to print labels in the sorted order
  5356  	keys := make([]string, 0, len(annotations))
  5357  	for key := range annotations {
  5358  		if skipAnnotations.Has(key) {
  5359  			continue
  5360  		}
  5361  		keys = append(keys, key)
  5362  	}
  5363  	if len(keys) == 0 {
  5364  		w.WriteLine("<none>")
  5365  		return
  5366  	}
  5367  	sort.Strings(keys)
  5368  	indent := "\t"
  5369  	for i, key := range keys {
  5370  		if i != 0 {
  5371  			w.Write(LEVEL_0, indent)
  5372  		}
  5373  		value := strings.TrimSuffix(annotations[key], "\n")
  5374  		if (len(value)+len(key)+2) > maxAnnotationLen || strings.Contains(value, "\n") {
  5375  			w.Write(LEVEL_0, "%s:\n", key)
  5376  			for _, s := range strings.Split(value, "\n") {
  5377  				w.Write(LEVEL_0, "%s  %s\n", indent, shorten(s, maxAnnotationLen-2))
  5378  			}
  5379  		} else {
  5380  			w.Write(LEVEL_0, "%s: %s\n", key, value)
  5381  		}
  5382  	}
  5383  }
  5384  
  5385  func shorten(s string, maxLength int) string {
  5386  	if len(s) > maxLength {
  5387  		return s[:maxLength] + "..."
  5388  	}
  5389  	return s
  5390  }
  5391  
  5392  // translateMicroTimestampSince returns the elapsed time since timestamp in
  5393  // human-readable approximation.
  5394  func translateMicroTimestampSince(timestamp metav1.MicroTime) string {
  5395  	if timestamp.IsZero() {
  5396  		return "<unknown>"
  5397  	}
  5398  
  5399  	return duration.HumanDuration(time.Since(timestamp.Time))
  5400  }
  5401  
  5402  // translateTimestampSince returns the elapsed time since timestamp in
  5403  // human-readable approximation.
  5404  func translateTimestampSince(timestamp metav1.Time) string {
  5405  	if timestamp.IsZero() {
  5406  		return "<unknown>"
  5407  	}
  5408  
  5409  	return duration.HumanDuration(time.Since(timestamp.Time))
  5410  }
  5411  
  5412  // Pass ports=nil for all ports.
  5413  func formatEndpoints(endpoints *corev1.Endpoints, ports sets.String) string {
  5414  	if len(endpoints.Subsets) == 0 {
  5415  		return "<none>"
  5416  	}
  5417  	list := []string{}
  5418  	max := 3
  5419  	more := false
  5420  	count := 0
  5421  	for i := range endpoints.Subsets {
  5422  		ss := &endpoints.Subsets[i]
  5423  		if len(ss.Ports) == 0 {
  5424  			// It's possible to have headless services with no ports.
  5425  			for i := range ss.Addresses {
  5426  				if len(list) == max {
  5427  					more = true
  5428  				}
  5429  				if !more {
  5430  					list = append(list, ss.Addresses[i].IP)
  5431  				}
  5432  				count++
  5433  			}
  5434  		} else {
  5435  			// "Normal" services with ports defined.
  5436  			for i := range ss.Ports {
  5437  				port := &ss.Ports[i]
  5438  				if ports == nil || ports.Has(port.Name) {
  5439  					for i := range ss.Addresses {
  5440  						if len(list) == max {
  5441  							more = true
  5442  						}
  5443  						addr := &ss.Addresses[i]
  5444  						if !more {
  5445  							hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port.Port)))
  5446  							list = append(list, hostPort)
  5447  						}
  5448  						count++
  5449  					}
  5450  				}
  5451  			}
  5452  		}
  5453  	}
  5454  	ret := strings.Join(list, ",")
  5455  	if more {
  5456  		return fmt.Sprintf("%s + %d more...", ret, count-max)
  5457  	}
  5458  	return ret
  5459  }
  5460  
  5461  func extractCSRStatus(conditions []string, certificateBytes []byte) string {
  5462  	var approved, denied, failed bool
  5463  	for _, c := range conditions {
  5464  		switch c {
  5465  		case string(certificatesv1beta1.CertificateApproved):
  5466  			approved = true
  5467  		case string(certificatesv1beta1.CertificateDenied):
  5468  			denied = true
  5469  		case string(certificatesv1beta1.CertificateFailed):
  5470  			failed = true
  5471  		}
  5472  	}
  5473  	var status string
  5474  	// must be in order of precedence
  5475  	if denied {
  5476  		status += "Denied"
  5477  	} else if approved {
  5478  		status += "Approved"
  5479  	} else {
  5480  		status += "Pending"
  5481  	}
  5482  	if failed {
  5483  		status += ",Failed"
  5484  	}
  5485  	if len(certificateBytes) > 0 {
  5486  		status += ",Issued"
  5487  	}
  5488  	return status
  5489  }
  5490  
  5491  // backendStringer behaves just like a string interface and converts the given backend to a string.
  5492  func serviceBackendStringer(backend *networkingv1.IngressServiceBackend) string {
  5493  	if backend == nil {
  5494  		return ""
  5495  	}
  5496  	var bPort string
  5497  	if backend.Port.Number != 0 {
  5498  		sNum := int64(backend.Port.Number)
  5499  		bPort = strconv.FormatInt(sNum, 10)
  5500  	} else {
  5501  		bPort = backend.Port.Name
  5502  	}
  5503  	return fmt.Sprintf("%v:%v", backend.Name, bPort)
  5504  }
  5505  
  5506  // backendStringer behaves just like a string interface and converts the given backend to a string.
  5507  func backendStringer(backend *networkingv1beta1.IngressBackend) string {
  5508  	if backend == nil {
  5509  		return ""
  5510  	}
  5511  	return fmt.Sprintf("%v:%v", backend.ServiceName, backend.ServicePort.String())
  5512  }
  5513  
  5514  // findNodeRoles returns the roles of a given node.
  5515  // The roles are determined by looking for:
  5516  // * a node-role.kubernetes.io/<role>="" label
  5517  // * a kubernetes.io/role="<role>" label
  5518  func findNodeRoles(node *corev1.Node) []string {
  5519  	roles := sets.NewString()
  5520  	for k, v := range node.Labels {
  5521  		switch {
  5522  		case strings.HasPrefix(k, LabelNodeRolePrefix):
  5523  			if role := strings.TrimPrefix(k, LabelNodeRolePrefix); len(role) > 0 {
  5524  				roles.Insert(role)
  5525  			}
  5526  
  5527  		case k == NodeLabelRole && v != "":
  5528  			roles.Insert(v)
  5529  		}
  5530  	}
  5531  	return roles.List()
  5532  }
  5533  
  5534  // ingressLoadBalancerStatusStringerV1 behaves mostly like a string interface and converts the given status to a string.
  5535  // `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
  5536  func ingressLoadBalancerStatusStringerV1(s networkingv1.IngressLoadBalancerStatus, wide bool) string {
  5537  	ingress := s.Ingress
  5538  	result := sets.NewString()
  5539  	for i := range ingress {
  5540  		if ingress[i].IP != "" {
  5541  			result.Insert(ingress[i].IP)
  5542  		} else if ingress[i].Hostname != "" {
  5543  			result.Insert(ingress[i].Hostname)
  5544  		}
  5545  	}
  5546  
  5547  	r := strings.Join(result.List(), ",")
  5548  	if !wide && len(r) > LoadBalancerWidth {
  5549  		r = r[0:(LoadBalancerWidth-3)] + "..."
  5550  	}
  5551  	return r
  5552  }
  5553  
  5554  // ingressLoadBalancerStatusStringerV1beta1 behaves mostly like a string interface and converts the given status to a string.
  5555  // `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
  5556  func ingressLoadBalancerStatusStringerV1beta1(s networkingv1beta1.IngressLoadBalancerStatus, wide bool) string {
  5557  	ingress := s.Ingress
  5558  	result := sets.NewString()
  5559  	for i := range ingress {
  5560  		if ingress[i].IP != "" {
  5561  			result.Insert(ingress[i].IP)
  5562  		} else if ingress[i].Hostname != "" {
  5563  			result.Insert(ingress[i].Hostname)
  5564  		}
  5565  	}
  5566  
  5567  	r := strings.Join(result.List(), ",")
  5568  	if !wide && len(r) > LoadBalancerWidth {
  5569  		r = r[0:(LoadBalancerWidth-3)] + "..."
  5570  	}
  5571  	return r
  5572  }
  5573  
  5574  // searchEvents finds events about the specified object.
  5575  // It is very similar to CoreV1.Events.Search, but supports the Limit parameter.
  5576  func searchEvents(client corev1client.EventsGetter, objOrRef runtime.Object, limit int64) (*corev1.EventList, error) {
  5577  	ref, err := reference.GetReference(scheme.Scheme, objOrRef)
  5578  	if err != nil {
  5579  		return nil, err
  5580  	}
  5581  	stringRefKind := string(ref.Kind)
  5582  	var refKind *string
  5583  	if len(stringRefKind) > 0 {
  5584  		refKind = &stringRefKind
  5585  	}
  5586  	stringRefUID := string(ref.UID)
  5587  	var refUID *string
  5588  	if len(stringRefUID) > 0 {
  5589  		refUID = &stringRefUID
  5590  	}
  5591  
  5592  	e := client.Events(ref.Namespace)
  5593  	fieldSelector := e.GetFieldSelector(&ref.Name, &ref.Namespace, refKind, refUID)
  5594  	initialOpts := metav1.ListOptions{FieldSelector: fieldSelector.String(), Limit: limit}
  5595  	eventList := &corev1.EventList{}
  5596  	err = runtimeresource.FollowContinue(&initialOpts,
  5597  		func(options metav1.ListOptions) (runtime.Object, error) {
  5598  			newEvents, err := e.List(context.TODO(), options)
  5599  			if err != nil {
  5600  				return nil, runtimeresource.EnhanceListError(err, options, "events")
  5601  			}
  5602  			eventList.Items = append(eventList.Items, newEvents.Items...)
  5603  			return newEvents, nil
  5604  		})
  5605  	return eventList, err
  5606  }
  5607  

View as plain text