1
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
91 const (
92 LEVEL_0 = iota
93 LEVEL_1
94 LEVEL_2
95 LEVEL_3
96 LEVEL_4
97 )
98
99 var (
100
101 skipAnnotations = sets.NewString(corev1.LastAppliedConfigAnnotation)
102
103
104 DescriberFn DescriberFunc = Describer
105 )
106
107
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
114 if describer, ok := DescriberFor(mapping.GroupVersionKind.GroupKind(), clientConfig); ok {
115 return describer, nil
116 }
117
118 if genericDescriber, ok := GenericDescriberFor(mapping, clientConfig); ok {
119 return genericDescriber, nil
120 }
121
122 return nil, fmt.Errorf("no description has been implemented for %s", mapping.GroupVersionKind.String())
123 }
124
125
126 type PrefixWriter interface {
127
128 Write(level int, format string, a ...interface{})
129
130 WriteLine(a ...interface{})
131
132 Flush()
133 }
134
135
136 type prefixWriter struct {
137 out io.Writer
138 }
139
140 var _ PrefixWriter = &prefixWriter{}
141
142
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
169
170 type nestedPrefixWriter struct {
171 PrefixWriter
172 indent int
173 }
174
175 var _ PrefixWriter = &prefixWriter{}
176
177
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
246
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
259
260 func GenericDescriberFor(mapping *meta.RESTMapping, clientConfig *rest.Config) (ResourceDescriber, bool) {
261
262 dynamicClient, err := dynamic.NewForConfig(clientConfig)
263 if err != nil {
264 return nil, false
265 }
266
267
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
354
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
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
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
447
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
467
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
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
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
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
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
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
733
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
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 )
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
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
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
1691 continue
1692 }
1693
1694 for _, pod := range pods {
1695 if pod.UID == nsPods.Items[podIndex].UID {
1696
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
1736
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
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
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
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
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 ""
2066 }
2067
2068 valueFrom, err := fieldpath.ExtractFieldPathAsString(pod, internalFieldPath)
2069 if err != nil {
2070 return ""
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
2143
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
3345
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
3359
3360 if err == nil {
3361
3362
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
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
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
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
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
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
3631
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
3651
3652
3653
3654
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
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
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
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
3978
3979
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
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
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
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
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
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
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
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
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
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
4981
4982 type Describers struct {
4983 searchFns map[reflect.Type][]typeFunc
4984 }
4985
4986
4987
4988
4989
4990
4991
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
5025
5026
5027
5028
5029
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
5053
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
5073 type typeFunc struct {
5074 Extra []reflect.Type
5075 Fn reflect.Value
5076 }
5077
5078
5079 func (fn typeFunc) Matches(types []reflect.Type) bool {
5080 if len(fn.Extra) != len(types) {
5081 return false
5082 }
5083
5084
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
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
5117 func printLabelsMultiline(w PrefixWriter, title string, labels map[string]string) {
5118 printLabelsMultilineWithIndent(w, "", title, "\t", labels, sets.NewString())
5119 }
5120
5121
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
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
5154 func printNodeTaintsMultiline(w PrefixWriter, title string, taints []corev1.Taint) {
5155 printTaintsMultilineWithIndent(w, "", title, "\t", taints)
5156 }
5157
5158
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
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
5185 func printPodsMultiline(w PrefixWriter, title string, pods []corev1.Pod) {
5186 printPodsMultilineWithIndent(w, "", title, "\t", pods)
5187 }
5188
5189
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
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
5216 func printPodTolerationsMultiline(w PrefixWriter, title string, tolerations []corev1.Toleration) {
5217 printTolerationsMultilineWithIndent(w, "", title, "\t", tolerations)
5218 }
5219
5220
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
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
5247
5248
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
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
5351
5352 func printAnnotationsMultiline(w PrefixWriter, title string, annotations map[string]string) {
5353 w.Write(LEVEL_0, "%s:\t", title)
5354
5355
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
5393
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
5403
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
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
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
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
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
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
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
5515
5516
5517
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
5535
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
5555
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
5575
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