...

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

Documentation: k8s.io/kubectl/pkg/describe

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package describe
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"reflect"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	autoscalingv1 "k8s.io/api/autoscaling/v1"
    30  	autoscalingv2 "k8s.io/api/autoscaling/v2"
    31  	batchv1 "k8s.io/api/batch/v1"
    32  	coordinationv1 "k8s.io/api/coordination/v1"
    33  	corev1 "k8s.io/api/core/v1"
    34  	discoveryv1 "k8s.io/api/discovery/v1"
    35  	discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
    36  	networkingv1 "k8s.io/api/networking/v1"
    37  	networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
    38  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    39  	policyv1 "k8s.io/api/policy/v1"
    40  	policyv1beta1 "k8s.io/api/policy/v1beta1"
    41  	schedulingv1 "k8s.io/api/scheduling/v1"
    42  	storagev1 "k8s.io/api/storage/v1"
    43  	storagev1alpha1 "k8s.io/api/storage/v1alpha1"
    44  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    45  	"k8s.io/apimachinery/pkg/api/resource"
    46  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    47  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    48  	"k8s.io/apimachinery/pkg/runtime"
    49  	"k8s.io/apimachinery/pkg/util/intstr"
    50  	"k8s.io/client-go/kubernetes"
    51  	"k8s.io/client-go/kubernetes/fake"
    52  	utilpointer "k8s.io/utils/pointer"
    53  	"k8s.io/utils/ptr"
    54  )
    55  
    56  type describeClient struct {
    57  	T         *testing.T
    58  	Namespace string
    59  	Err       error
    60  	kubernetes.Interface
    61  }
    62  
    63  func TestDescribePod(t *testing.T) {
    64  	deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}
    65  	gracePeriod := int64(1234)
    66  	condition1 := corev1.PodConditionType("condition1")
    67  	condition2 := corev1.PodConditionType("condition2")
    68  	fake := fake.NewSimpleClientset(&corev1.Pod{
    69  		ObjectMeta: metav1.ObjectMeta{
    70  			Name:                       "bar",
    71  			Namespace:                  "foo",
    72  			DeletionTimestamp:          &deletionTimestamp,
    73  			DeletionGracePeriodSeconds: &gracePeriod,
    74  		},
    75  		Spec: corev1.PodSpec{
    76  			ReadinessGates: []corev1.PodReadinessGate{
    77  				{
    78  					ConditionType: condition1,
    79  				},
    80  				{
    81  					ConditionType: condition2,
    82  				},
    83  			},
    84  		},
    85  		Status: corev1.PodStatus{
    86  			Conditions: []corev1.PodCondition{
    87  				{
    88  					Type:   condition1,
    89  					Status: corev1.ConditionTrue,
    90  				},
    91  			},
    92  		},
    93  	})
    94  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
    95  	d := PodDescriber{c}
    96  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
    97  	if err != nil {
    98  		t.Errorf("unexpected error: %v", err)
    99  	}
   100  	if !strings.Contains(out, "bar") || !strings.Contains(out, "Status:") {
   101  		t.Errorf("unexpected out: %s", out)
   102  	}
   103  	if !strings.Contains(out, "Terminating (lasts 10y)") || !strings.Contains(out, "1234s") {
   104  		t.Errorf("unexpected out: %s", out)
   105  	}
   106  }
   107  
   108  func TestDescribePodServiceAccount(t *testing.T) {
   109  	fake := fake.NewSimpleClientset(&corev1.Pod{
   110  		ObjectMeta: metav1.ObjectMeta{
   111  			Name:      "bar",
   112  			Namespace: "foo",
   113  		},
   114  		Spec: corev1.PodSpec{
   115  			ServiceAccountName: "fooaccount",
   116  		},
   117  	})
   118  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   119  	d := PodDescriber{c}
   120  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
   121  	if err != nil {
   122  		t.Errorf("unexpected error: %v", err)
   123  	}
   124  	if !strings.Contains(out, "Service Account:") {
   125  		t.Errorf("unexpected out: %s", out)
   126  	}
   127  	if !strings.Contains(out, "fooaccount") {
   128  		t.Errorf("unexpected out: %s", out)
   129  	}
   130  }
   131  
   132  func TestDescribePodEphemeralContainers(t *testing.T) {
   133  	fake := fake.NewSimpleClientset(&corev1.Pod{
   134  		ObjectMeta: metav1.ObjectMeta{
   135  			Name:      "bar",
   136  			Namespace: "foo",
   137  		},
   138  		Spec: corev1.PodSpec{
   139  			EphemeralContainers: []corev1.EphemeralContainer{
   140  				{
   141  					EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   142  						Name:  "debugger",
   143  						Image: "busybox",
   144  					},
   145  				},
   146  			},
   147  		},
   148  		Status: corev1.PodStatus{
   149  			EphemeralContainerStatuses: []corev1.ContainerStatus{
   150  				{
   151  					Name: "debugger",
   152  					State: corev1.ContainerState{
   153  						Running: &corev1.ContainerStateRunning{
   154  							StartedAt: metav1.NewTime(time.Now()),
   155  						},
   156  					},
   157  					Ready:        false,
   158  					RestartCount: 0,
   159  				},
   160  			},
   161  		},
   162  	})
   163  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   164  	d := PodDescriber{c}
   165  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
   166  	if err != nil {
   167  		t.Errorf("unexpected error: %v", err)
   168  	}
   169  	if !strings.Contains(out, "debugger:") {
   170  		t.Errorf("unexpected out: %s", out)
   171  	}
   172  	if !strings.Contains(out, "busybox") {
   173  		t.Errorf("unexpected out: %s", out)
   174  	}
   175  }
   176  
   177  func TestDescribePodNode(t *testing.T) {
   178  	fake := fake.NewSimpleClientset(&corev1.Pod{
   179  		ObjectMeta: metav1.ObjectMeta{
   180  			Name:      "bar",
   181  			Namespace: "foo",
   182  		},
   183  		Spec: corev1.PodSpec{
   184  			NodeName: "all-in-one",
   185  		},
   186  		Status: corev1.PodStatus{
   187  			HostIP:            "127.0.0.1",
   188  			NominatedNodeName: "nodeA",
   189  		},
   190  	})
   191  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   192  	d := PodDescriber{c}
   193  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
   194  	if err != nil {
   195  		t.Errorf("unexpected error: %v", err)
   196  	}
   197  	if !strings.Contains(out, "all-in-one/127.0.0.1") {
   198  		t.Errorf("unexpected out: %s", out)
   199  	}
   200  	if !strings.Contains(out, "nodeA") {
   201  		t.Errorf("unexpected out: %s", out)
   202  	}
   203  }
   204  
   205  func TestDescribePodTolerations(t *testing.T) {
   206  	fake := fake.NewSimpleClientset(&corev1.Pod{
   207  		ObjectMeta: metav1.ObjectMeta{
   208  			Name:      "bar",
   209  			Namespace: "foo",
   210  		},
   211  		Spec: corev1.PodSpec{
   212  			Tolerations: []corev1.Toleration{
   213  				{Operator: corev1.TolerationOpExists},
   214  				{Effect: corev1.TaintEffectNoSchedule, Operator: corev1.TolerationOpExists},
   215  				{Key: "key0", Operator: corev1.TolerationOpExists},
   216  				{Key: "key1", Value: "value1"},
   217  				{Key: "key2", Operator: corev1.TolerationOpEqual, Value: "value2", Effect: corev1.TaintEffectNoSchedule},
   218  				{Key: "key3", Value: "value3", Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{300}[0]},
   219  				{Key: "key4", Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0]},
   220  			},
   221  		},
   222  	})
   223  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   224  	d := PodDescriber{c}
   225  	out, err := d.Describe("foo", "bar", DescriberSettings{})
   226  	if err != nil {
   227  		t.Errorf("unexpected error: %v", err)
   228  	}
   229  	if !strings.Contains(out, "  op=Exists\n") ||
   230  		!strings.Contains(out, ":NoSchedule op=Exists\n") ||
   231  		!strings.Contains(out, "key0 op=Exists\n") ||
   232  		!strings.Contains(out, "key1=value1\n") ||
   233  		!strings.Contains(out, "key2=value2:NoSchedule\n") ||
   234  		!strings.Contains(out, "key3=value3:NoExecute for 300s\n") ||
   235  		!strings.Contains(out, "key4:NoExecute for 60s\n") ||
   236  		!strings.Contains(out, "Tolerations:") {
   237  		t.Errorf("unexpected out:\n%s", out)
   238  	}
   239  }
   240  
   241  func TestDescribeTopologySpreadConstraints(t *testing.T) {
   242  	fake := fake.NewSimpleClientset(&corev1.Pod{
   243  		ObjectMeta: metav1.ObjectMeta{
   244  			Name:      "bar",
   245  			Namespace: "foo",
   246  		},
   247  		Spec: corev1.PodSpec{
   248  			TopologySpreadConstraints: []corev1.TopologySpreadConstraint{
   249  				{
   250  					MaxSkew:           3,
   251  					TopologyKey:       "topology.kubernetes.io/test1",
   252  					WhenUnsatisfiable: "DoNotSchedule",
   253  					LabelSelector:     &metav1.LabelSelector{MatchLabels: map[string]string{"key1": "val1", "key2": "val2"}},
   254  				},
   255  				{
   256  					MaxSkew:           1,
   257  					TopologyKey:       "topology.kubernetes.io/test2",
   258  					WhenUnsatisfiable: "ScheduleAnyway",
   259  				},
   260  			},
   261  		},
   262  	})
   263  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   264  	d := PodDescriber{c}
   265  	out, err := d.Describe("foo", "bar", DescriberSettings{})
   266  	if err != nil {
   267  		t.Errorf("unexpected error: %v", err)
   268  	}
   269  	if !strings.Contains(out, "topology.kubernetes.io/test1:DoNotSchedule when max skew 3 is exceeded for selector key1=val1,key2=val2\n") ||
   270  		!strings.Contains(out, "topology.kubernetes.io/test2:ScheduleAnyway when max skew 1 is exceeded\n") {
   271  		t.Errorf("unexpected out:\n%s", out)
   272  	}
   273  }
   274  
   275  func TestDescribeSecret(t *testing.T) {
   276  	fake := fake.NewSimpleClientset(&corev1.Secret{
   277  		ObjectMeta: metav1.ObjectMeta{
   278  			Name:      "bar",
   279  			Namespace: "foo",
   280  		},
   281  		Data: map[string][]byte{
   282  			"username": []byte("YWRtaW4="),
   283  			"password": []byte("MWYyZDFlMmU2N2Rm"),
   284  		},
   285  	})
   286  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   287  	d := SecretDescriber{c}
   288  	out, err := d.Describe("foo", "bar", DescriberSettings{})
   289  	if err != nil {
   290  		t.Errorf("unexpected error: %v", err)
   291  	}
   292  	if !strings.Contains(out, "bar") || !strings.Contains(out, "foo") || !strings.Contains(out, "username") || !strings.Contains(out, "8 bytes") || !strings.Contains(out, "password") || !strings.Contains(out, "16 bytes") {
   293  		t.Errorf("unexpected out: %s", out)
   294  	}
   295  	if strings.Contains(out, "YWRtaW4=") || strings.Contains(out, "MWYyZDFlMmU2N2Rm") {
   296  		t.Errorf("sensitive data should not be shown, unexpected out: %s", out)
   297  	}
   298  }
   299  
   300  func TestDescribeNamespace(t *testing.T) {
   301  	exampleNamespaceName := "example"
   302  
   303  	testCases := []struct {
   304  		name      string
   305  		namespace *corev1.Namespace
   306  		expect    []string
   307  	}{
   308  		{
   309  			name: "no quotas or limit ranges",
   310  			namespace: &corev1.Namespace{
   311  				ObjectMeta: metav1.ObjectMeta{
   312  					Name: exampleNamespaceName,
   313  				},
   314  				Status: corev1.NamespaceStatus{
   315  					Phase: corev1.NamespaceActive,
   316  				},
   317  			},
   318  			expect: []string{
   319  				"Name",
   320  				exampleNamespaceName,
   321  				"Status",
   322  				string(corev1.NamespaceActive),
   323  				"No resource quota",
   324  				"No LimitRange resource.",
   325  			},
   326  		},
   327  		{
   328  			name: "has conditions",
   329  			namespace: &corev1.Namespace{
   330  				ObjectMeta: metav1.ObjectMeta{
   331  					Name: exampleNamespaceName,
   332  				},
   333  				Status: corev1.NamespaceStatus{
   334  					Phase: corev1.NamespaceTerminating,
   335  					Conditions: []corev1.NamespaceCondition{
   336  						{
   337  							LastTransitionTime: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
   338  							Message:            "example message",
   339  							Reason:             "example reason",
   340  							Status:             corev1.ConditionTrue,
   341  							Type:               corev1.NamespaceDeletionContentFailure,
   342  						},
   343  					},
   344  				},
   345  			},
   346  			expect: []string{
   347  				"Name",
   348  				exampleNamespaceName,
   349  				"Status",
   350  				string(corev1.NamespaceTerminating),
   351  				"Conditions",
   352  				"Type",
   353  				string(corev1.NamespaceDeletionContentFailure),
   354  				"Status",
   355  				string(corev1.ConditionTrue),
   356  				"Reason",
   357  				"example reason",
   358  				"Message",
   359  				"example message",
   360  				"No resource quota",
   361  				"No LimitRange resource.",
   362  			},
   363  		},
   364  	}
   365  
   366  	for _, testCase := range testCases {
   367  		t.Run(testCase.name, func(t *testing.T) {
   368  			fake := fake.NewSimpleClientset(testCase.namespace)
   369  			c := &describeClient{T: t, Namespace: "", Interface: fake}
   370  			d := NamespaceDescriber{c}
   371  
   372  			out, err := d.Describe("", testCase.namespace.Name, DescriberSettings{ShowEvents: true})
   373  			if err != nil {
   374  				t.Errorf("unexpected error: %v", err)
   375  			}
   376  
   377  			for _, expected := range testCase.expect {
   378  				if !strings.Contains(out, expected) {
   379  					t.Errorf("expected to find %q in output: %q", expected, out)
   380  				}
   381  			}
   382  		})
   383  	}
   384  }
   385  
   386  func TestDescribePodPriority(t *testing.T) {
   387  	priority := int32(1000)
   388  	fake := fake.NewSimpleClientset(&corev1.Pod{
   389  		ObjectMeta: metav1.ObjectMeta{
   390  			Name: "bar",
   391  		},
   392  		Spec: corev1.PodSpec{
   393  			PriorityClassName: "high-priority",
   394  			Priority:          &priority,
   395  		},
   396  	})
   397  	c := &describeClient{T: t, Namespace: "", Interface: fake}
   398  	d := PodDescriber{c}
   399  	out, err := d.Describe("", "bar", DescriberSettings{ShowEvents: true})
   400  	if err != nil {
   401  		t.Errorf("unexpected error: %v", err)
   402  	}
   403  	if !strings.Contains(out, "high-priority") || !strings.Contains(out, "1000") {
   404  		t.Errorf("unexpected out: %s", out)
   405  	}
   406  }
   407  
   408  func TestDescribePodRuntimeClass(t *testing.T) {
   409  	runtimeClassNames := []string{"test1", ""}
   410  	testCases := []struct {
   411  		name     string
   412  		pod      *corev1.Pod
   413  		expect   []string
   414  		unexpect []string
   415  	}{
   416  		{
   417  			name: "test1",
   418  			pod: &corev1.Pod{
   419  				ObjectMeta: metav1.ObjectMeta{
   420  					Name: "bar",
   421  				},
   422  				Spec: corev1.PodSpec{
   423  					RuntimeClassName: &runtimeClassNames[0],
   424  				},
   425  			},
   426  			expect: []string{
   427  				"Name", "bar",
   428  				"Runtime Class Name", "test1",
   429  			},
   430  			unexpect: []string{},
   431  		},
   432  		{
   433  			name: "test2",
   434  			pod: &corev1.Pod{
   435  				ObjectMeta: metav1.ObjectMeta{
   436  					Name: "bar",
   437  				},
   438  				Spec: corev1.PodSpec{
   439  					RuntimeClassName: &runtimeClassNames[1],
   440  				},
   441  			},
   442  			expect: []string{
   443  				"Name", "bar",
   444  			},
   445  			unexpect: []string{
   446  				"Runtime Class Name",
   447  			},
   448  		},
   449  		{
   450  			name: "test3",
   451  			pod: &corev1.Pod{
   452  				ObjectMeta: metav1.ObjectMeta{
   453  					Name: "bar",
   454  				},
   455  				Spec: corev1.PodSpec{},
   456  			},
   457  			expect: []string{
   458  				"Name", "bar",
   459  			},
   460  			unexpect: []string{
   461  				"Runtime Class Name",
   462  			},
   463  		},
   464  	}
   465  	for _, testCase := range testCases {
   466  		t.Run(testCase.name, func(t *testing.T) {
   467  			fake := fake.NewSimpleClientset(testCase.pod)
   468  			c := &describeClient{T: t, Interface: fake}
   469  			d := PodDescriber{c}
   470  			out, err := d.Describe("", "bar", DescriberSettings{ShowEvents: true})
   471  			if err != nil {
   472  				t.Errorf("unexpected error: %v", err)
   473  			}
   474  			for _, expected := range testCase.expect {
   475  				if !strings.Contains(out, expected) {
   476  					t.Errorf("expected to find %q in output: %q", expected, out)
   477  				}
   478  			}
   479  			for _, unexpected := range testCase.unexpect {
   480  				if strings.Contains(out, unexpected) {
   481  					t.Errorf("unexpected to find %q in output: %q", unexpected, out)
   482  				}
   483  			}
   484  		})
   485  	}
   486  }
   487  
   488  func TestDescribePriorityClass(t *testing.T) {
   489  	preemptLowerPriority := corev1.PreemptLowerPriority
   490  	preemptNever := corev1.PreemptNever
   491  
   492  	testCases := []struct {
   493  		name          string
   494  		priorityClass *schedulingv1.PriorityClass
   495  		expect        []string
   496  	}{
   497  		{
   498  			name: "test1",
   499  			priorityClass: &schedulingv1.PriorityClass{
   500  				ObjectMeta: metav1.ObjectMeta{
   501  					Name: "bar",
   502  				},
   503  				Value:            10,
   504  				GlobalDefault:    false,
   505  				PreemptionPolicy: &preemptLowerPriority,
   506  				Description:      "test1",
   507  			},
   508  			expect: []string{
   509  				"Name", "bar",
   510  				"Value", "10",
   511  				"GlobalDefault", "false",
   512  				"PreemptionPolicy", "PreemptLowerPriority",
   513  				"Description", "test1",
   514  				"Annotations", "",
   515  			},
   516  		},
   517  		{
   518  			name: "test2",
   519  			priorityClass: &schedulingv1.PriorityClass{
   520  				ObjectMeta: metav1.ObjectMeta{
   521  					Name: "bar",
   522  				},
   523  				Value:            100,
   524  				GlobalDefault:    true,
   525  				PreemptionPolicy: &preemptNever,
   526  				Description:      "test2",
   527  			},
   528  			expect: []string{
   529  				"Name", "bar",
   530  				"Value", "100",
   531  				"GlobalDefault", "true",
   532  				"PreemptionPolicy", "Never",
   533  				"Description", "test2",
   534  				"Annotations", "",
   535  			},
   536  		},
   537  	}
   538  	for _, testCase := range testCases {
   539  		t.Run(testCase.name, func(t *testing.T) {
   540  			fake := fake.NewSimpleClientset(testCase.priorityClass)
   541  			c := &describeClient{T: t, Interface: fake}
   542  			d := PriorityClassDescriber{c}
   543  			out, err := d.Describe("", "bar", DescriberSettings{ShowEvents: true})
   544  			if err != nil {
   545  				t.Errorf("unexpected error: %v", err)
   546  			}
   547  			for _, expected := range testCase.expect {
   548  				if !strings.Contains(out, expected) {
   549  					t.Errorf("expected to find %q in output: %q", expected, out)
   550  				}
   551  			}
   552  		})
   553  	}
   554  }
   555  
   556  func TestDescribeConfigMap(t *testing.T) {
   557  	fake := fake.NewSimpleClientset(&corev1.ConfigMap{
   558  		ObjectMeta: metav1.ObjectMeta{
   559  			Name:      "mycm",
   560  			Namespace: "foo",
   561  		},
   562  		Data: map[string]string{
   563  			"key1": "value1",
   564  			"key2": "value2",
   565  		},
   566  		BinaryData: map[string][]byte{
   567  			"binarykey1": {0xFF, 0xFE, 0xFD, 0xFC, 0xFB},
   568  			"binarykey2": {0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA},
   569  		},
   570  	})
   571  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   572  	d := ConfigMapDescriber{c}
   573  	out, err := d.Describe("foo", "mycm", DescriberSettings{ShowEvents: true})
   574  	if err != nil {
   575  		t.Errorf("unexpected error: %v", err)
   576  	}
   577  	if !strings.Contains(out, "foo") || !strings.Contains(out, "mycm") {
   578  		t.Errorf("unexpected out: %s", out)
   579  	}
   580  	if !strings.Contains(out, "key1") || !strings.Contains(out, "value1") || !strings.Contains(out, "key2") || !strings.Contains(out, "value2") {
   581  		t.Errorf("unexpected out: %s", out)
   582  	}
   583  	if !strings.Contains(out, "binarykey1") || !strings.Contains(out, "5 bytes") || !strings.Contains(out, "binarykey2") || !strings.Contains(out, "6 bytes") {
   584  		t.Errorf("unexpected out: %s", out)
   585  	}
   586  }
   587  
   588  func TestDescribeLimitRange(t *testing.T) {
   589  	fake := fake.NewSimpleClientset(&corev1.LimitRange{
   590  		ObjectMeta: metav1.ObjectMeta{
   591  			Name:      "mylr",
   592  			Namespace: "foo",
   593  		},
   594  		Spec: corev1.LimitRangeSpec{
   595  			Limits: []corev1.LimitRangeItem{
   596  				{
   597  					Type:                 corev1.LimitTypePod,
   598  					Max:                  getResourceList("100m", "10000Mi"),
   599  					Min:                  getResourceList("5m", "100Mi"),
   600  					MaxLimitRequestRatio: getResourceList("10", ""),
   601  				},
   602  				{
   603  					Type:                 corev1.LimitTypeContainer,
   604  					Max:                  getResourceList("100m", "10000Mi"),
   605  					Min:                  getResourceList("5m", "100Mi"),
   606  					Default:              getResourceList("50m", "500Mi"),
   607  					DefaultRequest:       getResourceList("10m", "200Mi"),
   608  					MaxLimitRequestRatio: getResourceList("10", ""),
   609  				},
   610  				{
   611  					Type: corev1.LimitTypePersistentVolumeClaim,
   612  					Max:  getStorageResourceList("10Gi"),
   613  					Min:  getStorageResourceList("5Gi"),
   614  				},
   615  			},
   616  		},
   617  	})
   618  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   619  	d := LimitRangeDescriber{c}
   620  	out, err := d.Describe("foo", "mylr", DescriberSettings{ShowEvents: true})
   621  	if err != nil {
   622  		t.Errorf("unexpected error: %v", err)
   623  	}
   624  
   625  	checks := []string{"foo", "mylr", "Pod", "cpu", "5m", "100m", "memory", "100Mi", "10000Mi", "10", "Container", "cpu", "10m", "50m", "200Mi", "500Mi", "PersistentVolumeClaim", "storage", "5Gi", "10Gi"}
   626  	for _, check := range checks {
   627  		if !strings.Contains(out, check) {
   628  			t.Errorf("unexpected out: %s", out)
   629  		}
   630  	}
   631  }
   632  
   633  func getStorageResourceList(storage string) corev1.ResourceList {
   634  	res := corev1.ResourceList{}
   635  	if storage != "" {
   636  		res[corev1.ResourceStorage] = resource.MustParse(storage)
   637  	}
   638  	return res
   639  }
   640  
   641  func getResourceList(cpu, memory string) corev1.ResourceList {
   642  	res := corev1.ResourceList{}
   643  	if cpu != "" {
   644  		res[corev1.ResourceCPU] = resource.MustParse(cpu)
   645  	}
   646  	if memory != "" {
   647  		res[corev1.ResourceMemory] = resource.MustParse(memory)
   648  	}
   649  	return res
   650  }
   651  
   652  func TestDescribeService(t *testing.T) {
   653  	singleStack := corev1.IPFamilyPolicySingleStack
   654  	testCases := []struct {
   655  		name    string
   656  		service *corev1.Service
   657  		expect  []string
   658  	}{
   659  		{
   660  			name: "test1",
   661  			service: &corev1.Service{
   662  				ObjectMeta: metav1.ObjectMeta{
   663  					Name:      "bar",
   664  					Namespace: "foo",
   665  				},
   666  				Spec: corev1.ServiceSpec{
   667  					Type: corev1.ServiceTypeLoadBalancer,
   668  					Ports: []corev1.ServicePort{{
   669  						Name:       "port-tcp",
   670  						Port:       8080,
   671  						Protocol:   corev1.ProtocolTCP,
   672  						TargetPort: intstr.FromInt32(9527),
   673  						NodePort:   31111,
   674  					}},
   675  					Selector:              map[string]string{"blah": "heh"},
   676  					ClusterIP:             "1.2.3.4",
   677  					IPFamilies:            []corev1.IPFamily{corev1.IPv4Protocol},
   678  					LoadBalancerIP:        "5.6.7.8",
   679  					SessionAffinity:       "None",
   680  					ExternalTrafficPolicy: "Local",
   681  					HealthCheckNodePort:   32222,
   682  				},
   683  			},
   684  			expect: []string{
   685  				"Name", "bar",
   686  				"Namespace", "foo",
   687  				"Selector", "blah=heh",
   688  				"Type", "LoadBalancer",
   689  				"IP", "1.2.3.4",
   690  				"Port", "port-tcp", "8080/TCP",
   691  				"TargetPort", "9527/TCP",
   692  				"NodePort", "port-tcp", "31111/TCP",
   693  				"Session Affinity", "None",
   694  				"External Traffic Policy", "Local",
   695  				"HealthCheck NodePort", "32222",
   696  			},
   697  		},
   698  		{
   699  			name: "test2",
   700  			service: &corev1.Service{
   701  				ObjectMeta: metav1.ObjectMeta{
   702  					Name:      "bar",
   703  					Namespace: "foo",
   704  				},
   705  				Spec: corev1.ServiceSpec{
   706  					Type: corev1.ServiceTypeLoadBalancer,
   707  					Ports: []corev1.ServicePort{{
   708  						Name:       "port-tcp",
   709  						Port:       8080,
   710  						Protocol:   corev1.ProtocolTCP,
   711  						TargetPort: intstr.FromString("targetPort"),
   712  						NodePort:   31111,
   713  					}},
   714  					Selector:              map[string]string{"blah": "heh"},
   715  					ClusterIP:             "1.2.3.4",
   716  					IPFamilies:            []corev1.IPFamily{corev1.IPv4Protocol},
   717  					LoadBalancerIP:        "5.6.7.8",
   718  					SessionAffinity:       "None",
   719  					ExternalTrafficPolicy: "Local",
   720  					HealthCheckNodePort:   32222,
   721  				},
   722  			},
   723  			expect: []string{
   724  				"Name", "bar",
   725  				"Namespace", "foo",
   726  				"Selector", "blah=heh",
   727  				"Type", "LoadBalancer",
   728  				"IP", "1.2.3.4",
   729  				"Port", "port-tcp", "8080/TCP",
   730  				"TargetPort", "targetPort/TCP",
   731  				"NodePort", "port-tcp", "31111/TCP",
   732  				"Session Affinity", "None",
   733  				"External Traffic Policy", "Local",
   734  				"HealthCheck NodePort", "32222",
   735  			},
   736  		},
   737  		{
   738  			name: "test-ServiceIPFamily",
   739  			service: &corev1.Service{
   740  				ObjectMeta: metav1.ObjectMeta{
   741  					Name:      "bar",
   742  					Namespace: "foo",
   743  				},
   744  				Spec: corev1.ServiceSpec{
   745  					Type: corev1.ServiceTypeLoadBalancer,
   746  					Ports: []corev1.ServicePort{{
   747  						Name:       "port-tcp",
   748  						Port:       8080,
   749  						Protocol:   corev1.ProtocolTCP,
   750  						TargetPort: intstr.FromString("targetPort"),
   751  						NodePort:   31111,
   752  					}},
   753  					Selector:              map[string]string{"blah": "heh"},
   754  					ClusterIP:             "1.2.3.4",
   755  					IPFamilies:            []corev1.IPFamily{corev1.IPv4Protocol},
   756  					LoadBalancerIP:        "5.6.7.8",
   757  					SessionAffinity:       "None",
   758  					ExternalTrafficPolicy: "Local",
   759  					HealthCheckNodePort:   32222,
   760  				},
   761  			},
   762  			expect: []string{
   763  				"Name", "bar",
   764  				"Namespace", "foo",
   765  				"Selector", "blah=heh",
   766  				"Type", "LoadBalancer",
   767  				"IP", "1.2.3.4",
   768  				"IP Families", "IPv4",
   769  				"Port", "port-tcp", "8080/TCP",
   770  				"TargetPort", "targetPort/TCP",
   771  				"NodePort", "port-tcp", "31111/TCP",
   772  				"Session Affinity", "None",
   773  				"External Traffic Policy", "Local",
   774  				"HealthCheck NodePort", "32222",
   775  			},
   776  		},
   777  		{
   778  			name: "test-ServiceIPFamilyPolicy+ClusterIPs",
   779  			service: &corev1.Service{
   780  				ObjectMeta: metav1.ObjectMeta{
   781  					Name:      "bar",
   782  					Namespace: "foo",
   783  				},
   784  				Spec: corev1.ServiceSpec{
   785  					Type: corev1.ServiceTypeLoadBalancer,
   786  					Ports: []corev1.ServicePort{{
   787  						Name:       "port-tcp",
   788  						Port:       8080,
   789  						Protocol:   corev1.ProtocolTCP,
   790  						TargetPort: intstr.FromString("targetPort"),
   791  						NodePort:   31111,
   792  					}},
   793  					Selector:              map[string]string{"blah": "heh"},
   794  					ClusterIP:             "1.2.3.4",
   795  					IPFamilies:            []corev1.IPFamily{corev1.IPv4Protocol},
   796  					IPFamilyPolicy:        &singleStack,
   797  					ClusterIPs:            []string{"1.2.3.4"},
   798  					LoadBalancerIP:        "5.6.7.8",
   799  					SessionAffinity:       "None",
   800  					ExternalTrafficPolicy: "Local",
   801  					HealthCheckNodePort:   32222,
   802  				},
   803  			},
   804  			expect: []string{
   805  				"Name", "bar",
   806  				"Namespace", "foo",
   807  				"Selector", "blah=heh",
   808  				"Type", "LoadBalancer",
   809  				"IP", "1.2.3.4",
   810  				"IP Families", "IPv4",
   811  				"IP Family Policy", "SingleStack",
   812  				"IPs", "1.2.3.4",
   813  				"Port", "port-tcp", "8080/TCP",
   814  				"TargetPort", "targetPort/TCP",
   815  				"NodePort", "port-tcp", "31111/TCP",
   816  				"Session Affinity", "None",
   817  				"External Traffic Policy", "Local",
   818  				"HealthCheck NodePort", "32222",
   819  			},
   820  		},
   821  	}
   822  	for _, testCase := range testCases {
   823  		t.Run(testCase.name, func(t *testing.T) {
   824  			fake := fake.NewSimpleClientset(testCase.service)
   825  			c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   826  			d := ServiceDescriber{c}
   827  			out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
   828  			if err != nil {
   829  				t.Errorf("unexpected error: %v", err)
   830  			}
   831  			for _, expected := range testCase.expect {
   832  				if !strings.Contains(out, expected) {
   833  					t.Errorf("expected to find %q in output: %q", expected, out)
   834  				}
   835  			}
   836  		})
   837  	}
   838  }
   839  
   840  func TestPodDescribeResultsSorted(t *testing.T) {
   841  	// Arrange
   842  	fake := fake.NewSimpleClientset(
   843  		&corev1.EventList{
   844  			Items: []corev1.Event{
   845  				{
   846  					ObjectMeta:     metav1.ObjectMeta{Name: "one"},
   847  					Source:         corev1.EventSource{Component: "kubelet"},
   848  					Message:        "Item 1",
   849  					FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
   850  					LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
   851  					Count:          1,
   852  					Type:           corev1.EventTypeNormal,
   853  				},
   854  				{
   855  					ObjectMeta:     metav1.ObjectMeta{Name: "two"},
   856  					Source:         corev1.EventSource{Component: "scheduler"},
   857  					Message:        "Item 2",
   858  					FirstTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
   859  					LastTimestamp:  metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
   860  					Count:          1,
   861  					Type:           corev1.EventTypeNormal,
   862  				},
   863  				{
   864  					ObjectMeta:     metav1.ObjectMeta{Name: "three"},
   865  					Source:         corev1.EventSource{Component: "kubelet"},
   866  					Message:        "Item 3",
   867  					FirstTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
   868  					LastTimestamp:  metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
   869  					Count:          1,
   870  					Type:           corev1.EventTypeNormal,
   871  				},
   872  			},
   873  		},
   874  		&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}},
   875  	)
   876  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
   877  	d := PodDescriber{c}
   878  
   879  	// Act
   880  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
   881  
   882  	// Assert
   883  	if err != nil {
   884  		t.Errorf("unexpected error: %v", err)
   885  	}
   886  	VerifyDatesInOrder(out, "\n" /* rowDelimiter */, "\t" /* columnDelimiter */, t)
   887  }
   888  
   889  // VerifyDatesInOrder checks the start of each line for a RFC1123Z date
   890  // and posts error if all subsequent dates are not equal or increasing
   891  func VerifyDatesInOrder(
   892  	resultToTest, rowDelimiter, columnDelimiter string, t *testing.T) {
   893  	lines := strings.Split(resultToTest, rowDelimiter)
   894  	var previousTime time.Time
   895  	for _, str := range lines {
   896  		columns := strings.Split(str, columnDelimiter)
   897  		if len(columns) > 0 {
   898  			currentTime, err := time.Parse(time.RFC1123Z, columns[0])
   899  			if err == nil {
   900  				if previousTime.After(currentTime) {
   901  					t.Errorf(
   902  						"Output is not sorted by time. %s should be listed after %s. Complete output: %s",
   903  						previousTime.Format(time.RFC1123Z),
   904  						currentTime.Format(time.RFC1123Z),
   905  						resultToTest)
   906  				}
   907  				previousTime = currentTime
   908  			}
   909  		}
   910  	}
   911  }
   912  
   913  func TestDescribeContainers(t *testing.T) {
   914  	trueVal := true
   915  	testCases := []struct {
   916  		container        corev1.Container
   917  		status           corev1.ContainerStatus
   918  		expectedElements []string
   919  	}{
   920  		// Running state.
   921  		{
   922  			container: corev1.Container{Name: "test", Image: "image"},
   923  			status: corev1.ContainerStatus{
   924  				Name: "test",
   925  				State: corev1.ContainerState{
   926  					Running: &corev1.ContainerStateRunning{
   927  						StartedAt: metav1.NewTime(time.Now()),
   928  					},
   929  				},
   930  				Ready:        true,
   931  				RestartCount: 7,
   932  			},
   933  			expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"},
   934  		},
   935  		// Waiting state.
   936  		{
   937  			container: corev1.Container{Name: "test", Image: "image"},
   938  			status: corev1.ContainerStatus{
   939  				Name: "test",
   940  				State: corev1.ContainerState{
   941  					Waiting: &corev1.ContainerStateWaiting{
   942  						Reason: "potato",
   943  					},
   944  				},
   945  				Ready:        true,
   946  				RestartCount: 7,
   947  			},
   948  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"},
   949  		},
   950  		// Terminated state.
   951  		{
   952  			container: corev1.Container{Name: "test", Image: "image"},
   953  			status: corev1.ContainerStatus{
   954  				Name: "test",
   955  				State: corev1.ContainerState{
   956  					Terminated: &corev1.ContainerStateTerminated{
   957  						StartedAt:  metav1.NewTime(time.Now()),
   958  						FinishedAt: metav1.NewTime(time.Now()),
   959  						Reason:     "potato",
   960  						ExitCode:   2,
   961  					},
   962  				},
   963  				Ready:        true,
   964  				RestartCount: 7,
   965  			},
   966  			expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"},
   967  		},
   968  		// Last Terminated
   969  		{
   970  			container: corev1.Container{Name: "test", Image: "image"},
   971  			status: corev1.ContainerStatus{
   972  				Name: "test",
   973  				State: corev1.ContainerState{
   974  					Running: &corev1.ContainerStateRunning{
   975  						StartedAt: metav1.NewTime(time.Now()),
   976  					},
   977  				},
   978  				LastTerminationState: corev1.ContainerState{
   979  					Terminated: &corev1.ContainerStateTerminated{
   980  						StartedAt:  metav1.NewTime(time.Now().Add(time.Second * 3)),
   981  						FinishedAt: metav1.NewTime(time.Now()),
   982  						Reason:     "crashing",
   983  						ExitCode:   3,
   984  					},
   985  				},
   986  				Ready:        true,
   987  				RestartCount: 7,
   988  			},
   989  			expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Started", "Finished", "Exit Code", "2", "crashing", "3"},
   990  		},
   991  		// No state defaults to waiting.
   992  		{
   993  			container: corev1.Container{Name: "test", Image: "image"},
   994  			status: corev1.ContainerStatus{
   995  				Name:         "test",
   996  				Ready:        true,
   997  				RestartCount: 7,
   998  			},
   999  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"},
  1000  		},
  1001  		// Env
  1002  		{
  1003  			container: corev1.Container{Name: "test", Image: "image", Env: []corev1.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []corev1.EnvFromSource{{ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "a123"}}}}},
  1004  			status: corev1.ContainerStatus{
  1005  				Name:         "test",
  1006  				Ready:        true,
  1007  				RestartCount: 7,
  1008  			},
  1009  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: false"},
  1010  		},
  1011  		{
  1012  			container: corev1.Container{Name: "test", Image: "image", Env: []corev1.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []corev1.EnvFromSource{{Prefix: "p_", ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "a123"}}}}},
  1013  			status: corev1.ContainerStatus{
  1014  				Name:         "test",
  1015  				Ready:        true,
  1016  				RestartCount: 7,
  1017  			},
  1018  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap with prefix 'p_'\tOptional: false"},
  1019  		},
  1020  		{
  1021  			container: corev1.Container{Name: "test", Image: "image", Env: []corev1.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []corev1.EnvFromSource{{ConfigMapRef: &corev1.ConfigMapEnvSource{Optional: &trueVal, LocalObjectReference: corev1.LocalObjectReference{Name: "a123"}}}}},
  1022  			status: corev1.ContainerStatus{
  1023  				Name:         "test",
  1024  				Ready:        true,
  1025  				RestartCount: 7,
  1026  			},
  1027  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: true"},
  1028  		},
  1029  		{
  1030  			container: corev1.Container{Name: "test", Image: "image", Env: []corev1.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []corev1.EnvFromSource{{SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "a123"}, Optional: &trueVal}}}},
  1031  			status: corev1.ContainerStatus{
  1032  				Name:         "test",
  1033  				Ready:        true,
  1034  				RestartCount: 7,
  1035  			},
  1036  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret\tOptional: true"},
  1037  		},
  1038  		{
  1039  			container: corev1.Container{Name: "test", Image: "image", Env: []corev1.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []corev1.EnvFromSource{{Prefix: "p_", SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "a123"}}}}},
  1040  			status: corev1.ContainerStatus{
  1041  				Name:         "test",
  1042  				Ready:        true,
  1043  				RestartCount: 7,
  1044  			},
  1045  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret with prefix 'p_'\tOptional: false"},
  1046  		},
  1047  		// Command
  1048  		{
  1049  			container: corev1.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}},
  1050  			status: corev1.ContainerStatus{
  1051  				Name:         "test",
  1052  				Ready:        true,
  1053  				RestartCount: 7,
  1054  			},
  1055  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "sleep", "1000"},
  1056  		},
  1057  		// Command with newline
  1058  		{
  1059  			container: corev1.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000\n2000"}},
  1060  			status: corev1.ContainerStatus{
  1061  				Name:         "test",
  1062  				Ready:        true,
  1063  				RestartCount: 7,
  1064  			},
  1065  			expectedElements: []string{"1000\n      2000"},
  1066  		},
  1067  		// Args
  1068  		{
  1069  			container: corev1.Container{Name: "test", Image: "image", Args: []string{"time", "1000"}},
  1070  			status: corev1.ContainerStatus{
  1071  				Name:         "test",
  1072  				Ready:        true,
  1073  				RestartCount: 7,
  1074  			},
  1075  			expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "time", "1000"},
  1076  		},
  1077  		// Args with newline
  1078  		{
  1079  			container: corev1.Container{Name: "test", Image: "image", Args: []string{"time", "1000\n2000"}},
  1080  			status: corev1.ContainerStatus{
  1081  				Name:         "test",
  1082  				Ready:        true,
  1083  				RestartCount: 7,
  1084  			},
  1085  			expectedElements: []string{"1000\n      2000"},
  1086  		},
  1087  		// Using limits.
  1088  		{
  1089  			container: corev1.Container{
  1090  				Name:  "test",
  1091  				Image: "image",
  1092  				Resources: corev1.ResourceRequirements{
  1093  					Limits: corev1.ResourceList{
  1094  						corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("1000"),
  1095  						corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("4G"),
  1096  						corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("20G"),
  1097  					},
  1098  				},
  1099  			},
  1100  			status: corev1.ContainerStatus{
  1101  				Name:         "test",
  1102  				Ready:        true,
  1103  				RestartCount: 7,
  1104  			},
  1105  			expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"},
  1106  		},
  1107  		// Using requests.
  1108  		{
  1109  			container: corev1.Container{
  1110  				Name:  "test",
  1111  				Image: "image",
  1112  				Resources: corev1.ResourceRequirements{
  1113  					Requests: corev1.ResourceList{
  1114  						corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("1000"),
  1115  						corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("4G"),
  1116  						corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("20G"),
  1117  					},
  1118  				},
  1119  			},
  1120  			expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"},
  1121  		},
  1122  		// volumeMounts read/write
  1123  		{
  1124  			container: corev1.Container{
  1125  				Name:  "test",
  1126  				Image: "image",
  1127  				VolumeMounts: []corev1.VolumeMount{
  1128  					{
  1129  						Name:      "mounted-volume",
  1130  						MountPath: "/opt/",
  1131  					},
  1132  				},
  1133  			},
  1134  			expectedElements: []string{"mounted-volume", "/opt/", "(rw)"},
  1135  		},
  1136  		// volumeMounts readonly
  1137  		{
  1138  			container: corev1.Container{
  1139  				Name:  "test",
  1140  				Image: "image",
  1141  				VolumeMounts: []corev1.VolumeMount{
  1142  					{
  1143  						Name:      "mounted-volume",
  1144  						MountPath: "/opt/",
  1145  						ReadOnly:  true,
  1146  					},
  1147  				},
  1148  			},
  1149  			expectedElements: []string{"Mounts", "mounted-volume", "/opt/", "(ro)"},
  1150  		},
  1151  
  1152  		// volumeMounts subPath
  1153  		{
  1154  			container: corev1.Container{
  1155  				Name:  "test",
  1156  				Image: "image",
  1157  				VolumeMounts: []corev1.VolumeMount{
  1158  					{
  1159  						Name:      "mounted-volume",
  1160  						MountPath: "/opt/",
  1161  						SubPath:   "foo",
  1162  					},
  1163  				},
  1164  			},
  1165  			expectedElements: []string{"Mounts", "mounted-volume", "/opt/", "(rw,path=\"foo\")"},
  1166  		},
  1167  
  1168  		// volumeDevices
  1169  		{
  1170  			container: corev1.Container{
  1171  				Name:  "test",
  1172  				Image: "image",
  1173  				VolumeDevices: []corev1.VolumeDevice{
  1174  					{
  1175  						Name:       "volume-device",
  1176  						DevicePath: "/dev/xvda",
  1177  					},
  1178  				},
  1179  			},
  1180  			expectedElements: []string{"Devices", "volume-device", "/dev/xvda"},
  1181  		},
  1182  	}
  1183  
  1184  	for i, testCase := range testCases {
  1185  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
  1186  			out := new(bytes.Buffer)
  1187  			pod := corev1.Pod{
  1188  				Spec: corev1.PodSpec{
  1189  					Containers: []corev1.Container{testCase.container},
  1190  				},
  1191  				Status: corev1.PodStatus{
  1192  					ContainerStatuses: []corev1.ContainerStatus{testCase.status},
  1193  				},
  1194  			}
  1195  			writer := NewPrefixWriter(out)
  1196  			describeContainers("Containers", pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(&pod), writer, "")
  1197  			output := out.String()
  1198  			for _, expected := range testCase.expectedElements {
  1199  				if !strings.Contains(output, expected) {
  1200  					t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output)
  1201  				}
  1202  			}
  1203  		})
  1204  	}
  1205  }
  1206  
  1207  func TestDescribers(t *testing.T) {
  1208  	first := &corev1.Event{}
  1209  	second := &corev1.Pod{}
  1210  	var third *corev1.Pod
  1211  	testErr := fmt.Errorf("test")
  1212  	d := Describers{}
  1213  	d.Add(
  1214  		func(e *corev1.Event, p *corev1.Pod) (string, error) {
  1215  			if e != first {
  1216  				t.Errorf("first argument not equal: %#v", e)
  1217  			}
  1218  			if p != second {
  1219  				t.Errorf("second argument not equal: %#v", p)
  1220  			}
  1221  			return "test", testErr
  1222  		},
  1223  	)
  1224  	if out, err := d.DescribeObject(first, second); out != "test" || err != testErr {
  1225  		t.Errorf("unexpected result: %s %v", out, err)
  1226  	}
  1227  
  1228  	if out, err := d.DescribeObject(first, second, third); out != "" || err == nil {
  1229  		t.Errorf("unexpected result: %s %v", out, err)
  1230  	} else {
  1231  		if noDescriber, ok := err.(ErrNoDescriber); ok {
  1232  			if !reflect.DeepEqual(noDescriber.Types, []string{"*v1.Event", "*v1.Pod", "*v1.Pod"}) {
  1233  				t.Errorf("unexpected describer: %v", err)
  1234  			}
  1235  		} else {
  1236  			t.Errorf("unexpected error type: %v", err)
  1237  		}
  1238  	}
  1239  
  1240  	d.Add(
  1241  		func(e *corev1.Event) (string, error) {
  1242  			if e != first {
  1243  				t.Errorf("first argument not equal: %#v", e)
  1244  			}
  1245  			return "simpler", testErr
  1246  		},
  1247  	)
  1248  	if out, err := d.DescribeObject(first); out != "simpler" || err != testErr {
  1249  		t.Errorf("unexpected result: %s %v", out, err)
  1250  	}
  1251  }
  1252  
  1253  func TestDefaultDescribers(t *testing.T) {
  1254  	out, err := DefaultObjectDescriber.DescribeObject(&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
  1255  	if err != nil {
  1256  		t.Fatalf("unexpected error: %v", err)
  1257  	}
  1258  	if !strings.Contains(out, "foo") {
  1259  		t.Errorf("missing Pod `foo` in output: %s", out)
  1260  	}
  1261  
  1262  	out, err = DefaultObjectDescriber.DescribeObject(&corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
  1263  	if err != nil {
  1264  		t.Fatalf("unexpected error: %v", err)
  1265  	}
  1266  	if !strings.Contains(out, "foo") {
  1267  		t.Errorf("missing Service `foo` in output: %s", out)
  1268  	}
  1269  
  1270  	out, err = DefaultObjectDescriber.DescribeObject(&corev1.ReplicationController{
  1271  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  1272  		Spec:       corev1.ReplicationControllerSpec{Replicas: utilpointer.Int32(1)},
  1273  	})
  1274  	if err != nil {
  1275  		t.Fatalf("unexpected error: %v", err)
  1276  	}
  1277  	if !strings.Contains(out, "foo") {
  1278  		t.Errorf("missing Replication Controller `foo` in output: %s", out)
  1279  	}
  1280  
  1281  	out, err = DefaultObjectDescriber.DescribeObject(&corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
  1282  	if err != nil {
  1283  		t.Fatalf("unexpected error: %v", err)
  1284  	}
  1285  	if !strings.Contains(out, "foo") {
  1286  		t.Errorf("missing Node `foo` output: %s", out)
  1287  	}
  1288  
  1289  	out, err = DefaultObjectDescriber.DescribeObject(&appsv1.StatefulSet{
  1290  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  1291  		Spec:       appsv1.StatefulSetSpec{Replicas: utilpointer.Int32(1)},
  1292  	})
  1293  	if err != nil {
  1294  		t.Fatalf("unexpected error: %v", err)
  1295  	}
  1296  	if !strings.Contains(out, "foo") {
  1297  		t.Errorf("missing StatefulSet `foo` in output: %s", out)
  1298  	}
  1299  }
  1300  
  1301  func TestGetPodsTotalRequests(t *testing.T) {
  1302  	testCases := []struct {
  1303  		name         string
  1304  		pods         *corev1.PodList
  1305  		expectedReqs map[corev1.ResourceName]resource.Quantity
  1306  	}{
  1307  		{
  1308  			name: "test1",
  1309  			pods: &corev1.PodList{
  1310  				Items: []corev1.Pod{
  1311  					{
  1312  						Spec: corev1.PodSpec{
  1313  							Containers: []corev1.Container{
  1314  								{
  1315  									Resources: corev1.ResourceRequirements{
  1316  										Requests: corev1.ResourceList{
  1317  											corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("1"),
  1318  											corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("300Mi"),
  1319  											corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("1G"),
  1320  										},
  1321  									},
  1322  								},
  1323  								{
  1324  									Resources: corev1.ResourceRequirements{
  1325  										Requests: corev1.ResourceList{
  1326  											corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("90m"),
  1327  											corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("120Mi"),
  1328  											corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("200M"),
  1329  										},
  1330  									},
  1331  								},
  1332  							},
  1333  						},
  1334  					},
  1335  					{
  1336  						Spec: corev1.PodSpec{
  1337  							Containers: []corev1.Container{
  1338  								{
  1339  									Resources: corev1.ResourceRequirements{
  1340  										Requests: corev1.ResourceList{
  1341  											corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("60m"),
  1342  											corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("43Mi"),
  1343  											corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("500M"),
  1344  										},
  1345  									},
  1346  								},
  1347  								{
  1348  									Resources: corev1.ResourceRequirements{
  1349  										Requests: corev1.ResourceList{
  1350  											corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("34m"),
  1351  											corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("83Mi"),
  1352  											corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("700M"),
  1353  										},
  1354  									},
  1355  								},
  1356  							},
  1357  						},
  1358  					},
  1359  				},
  1360  			},
  1361  			expectedReqs: map[corev1.ResourceName]resource.Quantity{
  1362  				corev1.ResourceName(corev1.ResourceCPU):     resource.MustParse("1.184"),
  1363  				corev1.ResourceName(corev1.ResourceMemory):  resource.MustParse("546Mi"),
  1364  				corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("2.4G"),
  1365  			},
  1366  		},
  1367  	}
  1368  
  1369  	for _, testCase := range testCases {
  1370  		t.Run(testCase.name, func(t *testing.T) {
  1371  			reqs, _ := getPodsTotalRequestsAndLimits(testCase.pods)
  1372  			if !apiequality.Semantic.DeepEqual(reqs, testCase.expectedReqs) {
  1373  				t.Errorf("Expected %v, got %v", testCase.expectedReqs, reqs)
  1374  			}
  1375  		})
  1376  	}
  1377  }
  1378  
  1379  func TestPersistentVolumeDescriber(t *testing.T) {
  1380  	block := corev1.PersistentVolumeBlock
  1381  	file := corev1.PersistentVolumeFilesystem
  1382  	foo := "glusterfsendpointname"
  1383  	deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}
  1384  	testCases := []struct {
  1385  		name               string
  1386  		plugin             string
  1387  		pv                 *corev1.PersistentVolume
  1388  		expectedElements   []string
  1389  		unexpectedElements []string
  1390  	}{
  1391  		{
  1392  			name:   "test0",
  1393  			plugin: "hostpath",
  1394  			pv: &corev1.PersistentVolume{
  1395  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1396  				Spec: corev1.PersistentVolumeSpec{
  1397  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1398  						HostPath: &corev1.HostPathVolumeSource{Type: new(corev1.HostPathType)},
  1399  					},
  1400  				},
  1401  			},
  1402  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1403  		},
  1404  		{
  1405  			name:   "test1",
  1406  			plugin: "gce",
  1407  			pv: &corev1.PersistentVolume{
  1408  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1409  				Spec: corev1.PersistentVolumeSpec{
  1410  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1411  						GCEPersistentDisk: &corev1.GCEPersistentDiskVolumeSource{},
  1412  					},
  1413  					VolumeMode: &file,
  1414  				},
  1415  			},
  1416  			expectedElements: []string{"VolumeMode", "Filesystem"},
  1417  		},
  1418  		{
  1419  			name:   "test2",
  1420  			plugin: "ebs",
  1421  			pv: &corev1.PersistentVolume{
  1422  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1423  				Spec: corev1.PersistentVolumeSpec{
  1424  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1425  						AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{},
  1426  					},
  1427  				},
  1428  			},
  1429  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1430  		},
  1431  		{
  1432  			name:   "test3",
  1433  			plugin: "nfs",
  1434  			pv: &corev1.PersistentVolume{
  1435  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1436  				Spec: corev1.PersistentVolumeSpec{
  1437  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1438  						NFS: &corev1.NFSVolumeSource{},
  1439  					},
  1440  				},
  1441  			},
  1442  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1443  		},
  1444  		{
  1445  			name:   "test4",
  1446  			plugin: "iscsi",
  1447  			pv: &corev1.PersistentVolume{
  1448  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1449  				Spec: corev1.PersistentVolumeSpec{
  1450  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1451  						ISCSI: &corev1.ISCSIPersistentVolumeSource{},
  1452  					},
  1453  					VolumeMode: &block,
  1454  				},
  1455  			},
  1456  			expectedElements: []string{"VolumeMode", "Block"},
  1457  		},
  1458  		{
  1459  			name:   "test5",
  1460  			plugin: "gluster",
  1461  			pv: &corev1.PersistentVolume{
  1462  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1463  				Spec: corev1.PersistentVolumeSpec{
  1464  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1465  						Glusterfs: &corev1.GlusterfsPersistentVolumeSource{},
  1466  					},
  1467  				},
  1468  			},
  1469  			expectedElements:   []string{"EndpointsNamespace", "<unset>"},
  1470  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1471  		},
  1472  		{
  1473  			name:   "test6",
  1474  			plugin: "rbd",
  1475  			pv: &corev1.PersistentVolume{
  1476  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1477  				Spec: corev1.PersistentVolumeSpec{
  1478  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1479  						RBD: &corev1.RBDPersistentVolumeSource{},
  1480  					},
  1481  				},
  1482  			},
  1483  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1484  		},
  1485  		{
  1486  			name:   "test7",
  1487  			plugin: "quobyte",
  1488  			pv: &corev1.PersistentVolume{
  1489  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1490  				Spec: corev1.PersistentVolumeSpec{
  1491  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1492  						Quobyte: &corev1.QuobyteVolumeSource{},
  1493  					},
  1494  				},
  1495  			},
  1496  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1497  		},
  1498  		{
  1499  			name:   "test8",
  1500  			plugin: "cinder",
  1501  			pv: &corev1.PersistentVolume{
  1502  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1503  				Spec: corev1.PersistentVolumeSpec{
  1504  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1505  						Cinder: &corev1.CinderPersistentVolumeSource{},
  1506  					},
  1507  				},
  1508  			},
  1509  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1510  		},
  1511  		{
  1512  			name:   "test9",
  1513  			plugin: "fc",
  1514  			pv: &corev1.PersistentVolume{
  1515  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1516  				Spec: corev1.PersistentVolumeSpec{
  1517  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1518  						FC: &corev1.FCVolumeSource{},
  1519  					},
  1520  					VolumeMode: &block,
  1521  				},
  1522  			},
  1523  			expectedElements: []string{"VolumeMode", "Block"},
  1524  		},
  1525  		{
  1526  			name:   "test10",
  1527  			plugin: "local",
  1528  			pv: &corev1.PersistentVolume{
  1529  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1530  				Spec: corev1.PersistentVolumeSpec{
  1531  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1532  						Local: &corev1.LocalVolumeSource{},
  1533  					},
  1534  				},
  1535  			},
  1536  			expectedElements:   []string{"Node Affinity:   <none>"},
  1537  			unexpectedElements: []string{"Required Terms", "Term "},
  1538  		},
  1539  		{
  1540  			name:   "test11",
  1541  			plugin: "local",
  1542  			pv: &corev1.PersistentVolume{
  1543  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1544  				Spec: corev1.PersistentVolumeSpec{
  1545  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1546  						Local: &corev1.LocalVolumeSource{},
  1547  					},
  1548  					NodeAffinity: &corev1.VolumeNodeAffinity{},
  1549  				},
  1550  			},
  1551  			expectedElements:   []string{"Node Affinity:   <none>"},
  1552  			unexpectedElements: []string{"Required Terms", "Term "},
  1553  		},
  1554  		{
  1555  			name:   "test12",
  1556  			plugin: "local",
  1557  			pv: &corev1.PersistentVolume{
  1558  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1559  				Spec: corev1.PersistentVolumeSpec{
  1560  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1561  						Local: &corev1.LocalVolumeSource{},
  1562  					},
  1563  					NodeAffinity: &corev1.VolumeNodeAffinity{
  1564  						Required: &corev1.NodeSelector{},
  1565  					},
  1566  				},
  1567  			},
  1568  			expectedElements:   []string{"Node Affinity", "Required Terms:  <none>"},
  1569  			unexpectedElements: []string{"Term "},
  1570  		},
  1571  		{
  1572  			name:   "test13",
  1573  			plugin: "local",
  1574  			pv: &corev1.PersistentVolume{
  1575  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1576  				Spec: corev1.PersistentVolumeSpec{
  1577  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1578  						Local: &corev1.LocalVolumeSource{},
  1579  					},
  1580  					NodeAffinity: &corev1.VolumeNodeAffinity{
  1581  						Required: &corev1.NodeSelector{
  1582  							NodeSelectorTerms: []corev1.NodeSelectorTerm{
  1583  								{
  1584  									MatchExpressions: []corev1.NodeSelectorRequirement{},
  1585  								},
  1586  								{
  1587  									MatchExpressions: []corev1.NodeSelectorRequirement{},
  1588  								},
  1589  							},
  1590  						},
  1591  					},
  1592  				},
  1593  			},
  1594  			expectedElements: []string{"Node Affinity", "Required Terms", "Term 0", "Term 1"},
  1595  		},
  1596  		{
  1597  			name:   "test14",
  1598  			plugin: "local",
  1599  			pv: &corev1.PersistentVolume{
  1600  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1601  				Spec: corev1.PersistentVolumeSpec{
  1602  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1603  						Local: &corev1.LocalVolumeSource{},
  1604  					},
  1605  					NodeAffinity: &corev1.VolumeNodeAffinity{
  1606  						Required: &corev1.NodeSelector{
  1607  							NodeSelectorTerms: []corev1.NodeSelectorTerm{
  1608  								{
  1609  									MatchExpressions: []corev1.NodeSelectorRequirement{
  1610  										{
  1611  											Key:      "foo",
  1612  											Operator: "In",
  1613  											Values:   []string{"val1", "val2"},
  1614  										},
  1615  										{
  1616  											Key:      "foo",
  1617  											Operator: "Exists",
  1618  										},
  1619  									},
  1620  								},
  1621  							},
  1622  						},
  1623  					},
  1624  				},
  1625  			},
  1626  			expectedElements: []string{"Node Affinity", "Required Terms", "Term 0",
  1627  				"foo in [val1, val2]",
  1628  				"foo exists"},
  1629  		},
  1630  		{
  1631  			name:   "test15",
  1632  			plugin: "local",
  1633  			pv: &corev1.PersistentVolume{
  1634  				ObjectMeta: metav1.ObjectMeta{
  1635  					Name:              "bar",
  1636  					DeletionTimestamp: &deletionTimestamp,
  1637  				},
  1638  				Spec: corev1.PersistentVolumeSpec{
  1639  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1640  						Local: &corev1.LocalVolumeSource{},
  1641  					},
  1642  				},
  1643  			},
  1644  			expectedElements: []string{"Terminating (lasts 10y)"},
  1645  		},
  1646  		{
  1647  			name:   "test16",
  1648  			plugin: "local",
  1649  			pv: &corev1.PersistentVolume{
  1650  				ObjectMeta: metav1.ObjectMeta{
  1651  					Name:                       "bar",
  1652  					GenerateName:               "test-GenerateName",
  1653  					UID:                        "test-UID",
  1654  					CreationTimestamp:          metav1.Time{Time: time.Now()},
  1655  					DeletionTimestamp:          &metav1.Time{Time: time.Now()},
  1656  					DeletionGracePeriodSeconds: new(int64),
  1657  					Labels:                     map[string]string{"label1": "label1", "label2": "label2", "label3": "label3"},
  1658  					Annotations:                map[string]string{"annotation1": "annotation1", "annotation2": "annotation2", "annotation3": "annotation3"},
  1659  				},
  1660  				Spec: corev1.PersistentVolumeSpec{
  1661  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1662  						Local: &corev1.LocalVolumeSource{},
  1663  					},
  1664  					NodeAffinity: &corev1.VolumeNodeAffinity{
  1665  						Required: &corev1.NodeSelector{
  1666  							NodeSelectorTerms: []corev1.NodeSelectorTerm{
  1667  								{
  1668  									MatchExpressions: []corev1.NodeSelectorRequirement{
  1669  										{
  1670  											Key:      "foo",
  1671  											Operator: "In",
  1672  											Values:   []string{"val1", "val2"},
  1673  										},
  1674  										{
  1675  											Key:      "foo",
  1676  											Operator: "Exists",
  1677  										},
  1678  									},
  1679  								},
  1680  							},
  1681  						},
  1682  					},
  1683  				},
  1684  			},
  1685  			expectedElements: []string{"Node Affinity", "Required Terms", "Term 0",
  1686  				"foo in [val1, val2]",
  1687  				"foo exists"},
  1688  		},
  1689  		{
  1690  			name:   "test17",
  1691  			plugin: "local",
  1692  			pv: &corev1.PersistentVolume{
  1693  				ObjectMeta: metav1.ObjectMeta{
  1694  					Name:                       "bar",
  1695  					GenerateName:               "test-GenerateName",
  1696  					UID:                        "test-UID",
  1697  					CreationTimestamp:          metav1.Time{Time: time.Now()},
  1698  					DeletionTimestamp:          &metav1.Time{Time: time.Now()},
  1699  					DeletionGracePeriodSeconds: new(int64),
  1700  					Labels:                     map[string]string{"label1": "label1", "label2": "label2", "label3": "label3"},
  1701  					Annotations:                map[string]string{"annotation1": "annotation1", "annotation2": "annotation2", "annotation3": "annotation3"},
  1702  				},
  1703  				Spec: corev1.PersistentVolumeSpec{
  1704  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1705  						CSI: &corev1.CSIPersistentVolumeSource{
  1706  							Driver:       "drive",
  1707  							VolumeHandle: "handler",
  1708  							ReadOnly:     true,
  1709  							VolumeAttributes: map[string]string{
  1710  								"Attribute1": "Value1",
  1711  								"Attribute2": "Value2",
  1712  								"Attribute3": "Value3",
  1713  							},
  1714  						},
  1715  					},
  1716  				},
  1717  			},
  1718  			expectedElements: []string{"Driver", "VolumeHandle", "ReadOnly", "VolumeAttributes"},
  1719  		},
  1720  		{
  1721  			name:   "test19",
  1722  			plugin: "gluster",
  1723  			pv: &corev1.PersistentVolume{
  1724  				ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  1725  				Spec: corev1.PersistentVolumeSpec{
  1726  					PersistentVolumeSource: corev1.PersistentVolumeSource{
  1727  						Glusterfs: &corev1.GlusterfsPersistentVolumeSource{
  1728  							EndpointsNamespace: &foo,
  1729  						},
  1730  					},
  1731  				},
  1732  			},
  1733  			expectedElements:   []string{"EndpointsNamespace", "glusterfsendpointname"},
  1734  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1735  		},
  1736  	}
  1737  
  1738  	for _, test := range testCases {
  1739  		t.Run(test.name, func(t *testing.T) {
  1740  			fake := fake.NewSimpleClientset(test.pv)
  1741  			c := PersistentVolumeDescriber{fake}
  1742  			str, err := c.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  1743  			if err != nil {
  1744  				t.Errorf("Unexpected error for test %s: %v", test.plugin, err)
  1745  			}
  1746  			if str == "" {
  1747  				t.Errorf("Unexpected empty string for test %s.  Expected PV Describer output", test.plugin)
  1748  			}
  1749  			for _, expected := range test.expectedElements {
  1750  				if !strings.Contains(str, expected) {
  1751  					t.Errorf("expected to find %q in output: %q", expected, str)
  1752  				}
  1753  			}
  1754  			for _, unexpected := range test.unexpectedElements {
  1755  				if strings.Contains(str, unexpected) {
  1756  					t.Errorf("unexpected to find %q in output: %q", unexpected, str)
  1757  				}
  1758  			}
  1759  		})
  1760  	}
  1761  }
  1762  
  1763  func TestPersistentVolumeClaimDescriber(t *testing.T) {
  1764  	block := corev1.PersistentVolumeBlock
  1765  	file := corev1.PersistentVolumeFilesystem
  1766  	goldClassName := "gold"
  1767  	now := time.Now()
  1768  	deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}
  1769  	snapshotAPIGroup := "snapshot.storage.k8s.io"
  1770  	defaultDescriberSettings := &DescriberSettings{ShowEvents: true}
  1771  	testCases := []struct {
  1772  		name               string
  1773  		pvc                *corev1.PersistentVolumeClaim
  1774  		describerSettings  *DescriberSettings
  1775  		expectedElements   []string
  1776  		unexpectedElements []string
  1777  	}{
  1778  		{
  1779  			name: "default",
  1780  			pvc: &corev1.PersistentVolumeClaim{
  1781  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1782  				Spec: corev1.PersistentVolumeClaimSpec{
  1783  					VolumeName:       "volume1",
  1784  					StorageClassName: &goldClassName,
  1785  				},
  1786  				Status: corev1.PersistentVolumeClaimStatus{
  1787  					Phase: corev1.ClaimBound,
  1788  				},
  1789  			},
  1790  			expectedElements:   []string{"Events"},
  1791  			unexpectedElements: []string{"VolumeMode", "Filesystem"},
  1792  		},
  1793  		{
  1794  			name: "filesystem",
  1795  			pvc: &corev1.PersistentVolumeClaim{
  1796  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1797  				Spec: corev1.PersistentVolumeClaimSpec{
  1798  					VolumeName:       "volume2",
  1799  					StorageClassName: &goldClassName,
  1800  					VolumeMode:       &file,
  1801  				},
  1802  				Status: corev1.PersistentVolumeClaimStatus{
  1803  					Phase: corev1.ClaimBound,
  1804  				},
  1805  			},
  1806  			expectedElements: []string{"VolumeMode", "Filesystem"},
  1807  		},
  1808  		{
  1809  			name: "block",
  1810  			pvc: &corev1.PersistentVolumeClaim{
  1811  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1812  				Spec: corev1.PersistentVolumeClaimSpec{
  1813  					VolumeName:       "volume3",
  1814  					StorageClassName: &goldClassName,
  1815  					VolumeMode:       &block,
  1816  				},
  1817  				Status: corev1.PersistentVolumeClaimStatus{
  1818  					Phase: corev1.ClaimBound,
  1819  				},
  1820  			},
  1821  			expectedElements: []string{"VolumeMode", "Block"},
  1822  		},
  1823  		// Tests for Status.Condition.
  1824  		{
  1825  			name: "condition-type",
  1826  			pvc: &corev1.PersistentVolumeClaim{
  1827  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1828  				Spec: corev1.PersistentVolumeClaimSpec{
  1829  					VolumeName:       "volume4",
  1830  					StorageClassName: &goldClassName,
  1831  				},
  1832  				Status: corev1.PersistentVolumeClaimStatus{
  1833  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1834  						{Type: corev1.PersistentVolumeClaimResizing},
  1835  					},
  1836  				},
  1837  			},
  1838  			expectedElements: []string{"Conditions", "Type", "Resizing"},
  1839  		},
  1840  		{
  1841  			name: "condition-status",
  1842  			pvc: &corev1.PersistentVolumeClaim{
  1843  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1844  				Spec: corev1.PersistentVolumeClaimSpec{
  1845  					VolumeName:       "volume5",
  1846  					StorageClassName: &goldClassName,
  1847  				},
  1848  				Status: corev1.PersistentVolumeClaimStatus{
  1849  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1850  						{Status: corev1.ConditionTrue},
  1851  					},
  1852  				},
  1853  			},
  1854  			expectedElements: []string{"Conditions", "Status", "True"},
  1855  		},
  1856  		{
  1857  			name: "condition-last-probe-time",
  1858  			pvc: &corev1.PersistentVolumeClaim{
  1859  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1860  				Spec: corev1.PersistentVolumeClaimSpec{
  1861  					VolumeName:       "volume6",
  1862  					StorageClassName: &goldClassName,
  1863  				},
  1864  				Status: corev1.PersistentVolumeClaimStatus{
  1865  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1866  						{LastProbeTime: metav1.Time{Time: now}},
  1867  					},
  1868  				},
  1869  			},
  1870  			expectedElements: []string{"Conditions", "LastProbeTime", now.Format(time.RFC1123Z)},
  1871  		},
  1872  		{
  1873  			name: "condition-last-transition-time",
  1874  			pvc: &corev1.PersistentVolumeClaim{
  1875  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1876  				Spec: corev1.PersistentVolumeClaimSpec{
  1877  					VolumeName:       "volume7",
  1878  					StorageClassName: &goldClassName,
  1879  				},
  1880  				Status: corev1.PersistentVolumeClaimStatus{
  1881  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1882  						{LastTransitionTime: metav1.Time{Time: now}},
  1883  					},
  1884  				},
  1885  			},
  1886  			expectedElements: []string{"Conditions", "LastTransitionTime", now.Format(time.RFC1123Z)},
  1887  		},
  1888  		{
  1889  			name: "condition-reason",
  1890  			pvc: &corev1.PersistentVolumeClaim{
  1891  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1892  				Spec: corev1.PersistentVolumeClaimSpec{
  1893  					VolumeName:       "volume8",
  1894  					StorageClassName: &goldClassName,
  1895  				},
  1896  				Status: corev1.PersistentVolumeClaimStatus{
  1897  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1898  						{Reason: "OfflineResize"},
  1899  					},
  1900  				},
  1901  			},
  1902  			expectedElements: []string{"Conditions", "Reason", "OfflineResize"},
  1903  		},
  1904  		{
  1905  			name: "condition-message",
  1906  			pvc: &corev1.PersistentVolumeClaim{
  1907  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1908  				Spec: corev1.PersistentVolumeClaimSpec{
  1909  					VolumeName:       "volume9",
  1910  					StorageClassName: &goldClassName,
  1911  				},
  1912  				Status: corev1.PersistentVolumeClaimStatus{
  1913  					Conditions: []corev1.PersistentVolumeClaimCondition{
  1914  						{Message: "User request resize"},
  1915  					},
  1916  				},
  1917  			},
  1918  			expectedElements: []string{"Conditions", "Message", "User request resize"},
  1919  		},
  1920  		{
  1921  			name: "deletion-timestamp",
  1922  			pvc: &corev1.PersistentVolumeClaim{
  1923  				ObjectMeta: metav1.ObjectMeta{
  1924  					Namespace:         "foo",
  1925  					Name:              "bar",
  1926  					DeletionTimestamp: &deletionTimestamp,
  1927  				},
  1928  				Spec: corev1.PersistentVolumeClaimSpec{
  1929  					VolumeName:       "volume10",
  1930  					StorageClassName: &goldClassName,
  1931  				},
  1932  				Status: corev1.PersistentVolumeClaimStatus{},
  1933  			},
  1934  			expectedElements: []string{"Terminating (lasts 10y)"},
  1935  		},
  1936  		{
  1937  			name: "pvc-datasource",
  1938  			pvc: &corev1.PersistentVolumeClaim{
  1939  				ObjectMeta: metav1.ObjectMeta{
  1940  					Namespace: "foo",
  1941  					Name:      "bar",
  1942  				},
  1943  				Spec: corev1.PersistentVolumeClaimSpec{
  1944  					VolumeName:       "volume10",
  1945  					StorageClassName: &goldClassName,
  1946  					DataSource: &corev1.TypedLocalObjectReference{
  1947  						Name: "srcpvc",
  1948  						Kind: "PersistentVolumeClaim",
  1949  					},
  1950  				},
  1951  				Status: corev1.PersistentVolumeClaimStatus{},
  1952  			},
  1953  			expectedElements: []string{"\nDataSource:\n  Kind:   PersistentVolumeClaim\n  Name:   srcpvc"},
  1954  		},
  1955  		{
  1956  			name: "snapshot-datasource",
  1957  			pvc: &corev1.PersistentVolumeClaim{
  1958  				ObjectMeta: metav1.ObjectMeta{
  1959  					Namespace: "foo",
  1960  					Name:      "bar",
  1961  				},
  1962  				Spec: corev1.PersistentVolumeClaimSpec{
  1963  					VolumeName:       "volume10",
  1964  					StorageClassName: &goldClassName,
  1965  					DataSource: &corev1.TypedLocalObjectReference{
  1966  						Name:     "src-snapshot",
  1967  						Kind:     "VolumeSnapshot",
  1968  						APIGroup: &snapshotAPIGroup,
  1969  					},
  1970  				},
  1971  				Status: corev1.PersistentVolumeClaimStatus{},
  1972  			},
  1973  			expectedElements: []string{"DataSource:\n  APIGroup:  snapshot.storage.k8s.io\n  Kind:      VolumeSnapshot\n  Name:      src-snapshot\n"},
  1974  		},
  1975  		{
  1976  			name: "no-show-events",
  1977  			pvc: &corev1.PersistentVolumeClaim{
  1978  				ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
  1979  				Spec: corev1.PersistentVolumeClaimSpec{
  1980  					VolumeName:       "volume1",
  1981  					StorageClassName: &goldClassName,
  1982  				},
  1983  				Status: corev1.PersistentVolumeClaimStatus{
  1984  					Phase: corev1.ClaimBound,
  1985  				},
  1986  			},
  1987  			unexpectedElements: []string{"Events"},
  1988  			describerSettings:  &DescriberSettings{ShowEvents: false},
  1989  		},
  1990  	}
  1991  
  1992  	for _, test := range testCases {
  1993  		t.Run(test.name, func(t *testing.T) {
  1994  			fake := fake.NewSimpleClientset(test.pvc)
  1995  			c := PersistentVolumeClaimDescriber{fake}
  1996  
  1997  			var describerSettings DescriberSettings
  1998  			if test.describerSettings != nil {
  1999  				describerSettings = *test.describerSettings
  2000  			} else {
  2001  				describerSettings = *defaultDescriberSettings
  2002  			}
  2003  
  2004  			str, err := c.Describe("foo", "bar", describerSettings)
  2005  			if err != nil {
  2006  				t.Errorf("Unexpected error for test %s: %v", test.name, err)
  2007  			}
  2008  			if str == "" {
  2009  				t.Errorf("Unexpected empty string for test %s.  Expected PVC Describer output", test.name)
  2010  			}
  2011  			for _, expected := range test.expectedElements {
  2012  				if !strings.Contains(str, expected) {
  2013  					t.Errorf("expected to find %q in output: %q", expected, str)
  2014  				}
  2015  			}
  2016  			for _, unexpected := range test.unexpectedElements {
  2017  				if strings.Contains(str, unexpected) {
  2018  					t.Errorf("unexpected to find %q in output: %q", unexpected, str)
  2019  				}
  2020  			}
  2021  		})
  2022  	}
  2023  }
  2024  
  2025  func TestGetPodsForPVC(t *testing.T) {
  2026  	goldClassName := "gold"
  2027  	testCases := []struct {
  2028  		name            string
  2029  		pvc             *corev1.PersistentVolumeClaim
  2030  		requiredObjects []runtime.Object
  2031  		expectedPods    []string
  2032  	}{
  2033  		{
  2034  			name: "pvc-unused",
  2035  			pvc: &corev1.PersistentVolumeClaim{
  2036  				ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pvc-name"},
  2037  				Spec: corev1.PersistentVolumeClaimSpec{
  2038  					VolumeName:       "volume1",
  2039  					StorageClassName: &goldClassName,
  2040  				},
  2041  				Status: corev1.PersistentVolumeClaimStatus{
  2042  					Phase: corev1.ClaimBound,
  2043  				},
  2044  			},
  2045  			expectedPods: []string{},
  2046  		},
  2047  		{
  2048  			name: "pvc-in-pods-volumes-list",
  2049  			pvc: &corev1.PersistentVolumeClaim{
  2050  				ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pvc-name"},
  2051  				Spec: corev1.PersistentVolumeClaimSpec{
  2052  					VolumeName:       "volume1",
  2053  					StorageClassName: &goldClassName,
  2054  				},
  2055  				Status: corev1.PersistentVolumeClaimStatus{
  2056  					Phase: corev1.ClaimBound,
  2057  				},
  2058  			},
  2059  			requiredObjects: []runtime.Object{
  2060  				&corev1.Pod{
  2061  					ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pod-name"},
  2062  					Spec: corev1.PodSpec{
  2063  						Volumes: []corev1.Volume{
  2064  							{
  2065  								Name: "volume",
  2066  								VolumeSource: corev1.VolumeSource{
  2067  									PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
  2068  										ClaimName: "pvc-name",
  2069  									},
  2070  								},
  2071  							},
  2072  						},
  2073  					},
  2074  				},
  2075  			},
  2076  			expectedPods: []string{"pod-name"},
  2077  		},
  2078  		{
  2079  			name: "pvc-owned-by-pod",
  2080  			pvc: &corev1.PersistentVolumeClaim{
  2081  				ObjectMeta: metav1.ObjectMeta{
  2082  					Namespace: "ns",
  2083  					Name:      "pvc-name",
  2084  					OwnerReferences: []metav1.OwnerReference{
  2085  						{
  2086  							Kind: "Pod",
  2087  							Name: "pod-name",
  2088  							UID:  "pod-uid",
  2089  						},
  2090  					},
  2091  				},
  2092  				Spec: corev1.PersistentVolumeClaimSpec{
  2093  					VolumeName:       "volume1",
  2094  					StorageClassName: &goldClassName,
  2095  				},
  2096  				Status: corev1.PersistentVolumeClaimStatus{
  2097  					Phase: corev1.ClaimBound,
  2098  				},
  2099  			},
  2100  			requiredObjects: []runtime.Object{
  2101  				&corev1.Pod{
  2102  					ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "pod-name", UID: "pod-uid"},
  2103  				},
  2104  			},
  2105  			expectedPods: []string{"pod-name"},
  2106  		},
  2107  	}
  2108  
  2109  	for _, test := range testCases {
  2110  		t.Run(test.name, func(t *testing.T) {
  2111  			var objects []runtime.Object
  2112  			objects = append(objects, test.requiredObjects...)
  2113  			objects = append(objects, test.pvc)
  2114  			fake := fake.NewSimpleClientset(objects...)
  2115  
  2116  			pods, err := getPodsForPVC(fake.CoreV1().Pods(test.pvc.ObjectMeta.Namespace), test.pvc, DescriberSettings{})
  2117  			if err != nil {
  2118  				t.Errorf("Unexpected error for test %s: %v", test.name, err)
  2119  			}
  2120  
  2121  			for _, expectedPod := range test.expectedPods {
  2122  				foundPod := false
  2123  				for _, pod := range pods {
  2124  					if pod.Name == expectedPod {
  2125  						foundPod = true
  2126  						break
  2127  					}
  2128  				}
  2129  
  2130  				if !foundPod {
  2131  					t.Errorf("Expected pod %s, but it was not returned: %v", expectedPod, pods)
  2132  				}
  2133  			}
  2134  
  2135  			if len(test.expectedPods) != len(pods) {
  2136  				t.Errorf("Expected %d pods, but got %d pods", len(test.expectedPods), len(pods))
  2137  			}
  2138  		})
  2139  	}
  2140  }
  2141  
  2142  func TestDescribeDeployment(t *testing.T) {
  2143  	labels := map[string]string{"k8s-app": "bar"}
  2144  	testCases := []struct {
  2145  		name    string
  2146  		objects []runtime.Object
  2147  		expects []string
  2148  	}{
  2149  		{
  2150  			name: "deployment with two mounted volumes",
  2151  			objects: []runtime.Object{
  2152  				&appsv1.Deployment{
  2153  					ObjectMeta: metav1.ObjectMeta{
  2154  						Name:              "bar",
  2155  						Namespace:         "foo",
  2156  						Labels:            labels,
  2157  						UID:               "00000000-0000-0000-0000-000000000001",
  2158  						CreationTimestamp: metav1.NewTime(time.Date(2021, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
  2159  					},
  2160  					Spec: appsv1.DeploymentSpec{
  2161  						Replicas: utilpointer.Int32Ptr(1),
  2162  						Selector: &metav1.LabelSelector{
  2163  							MatchLabels: labels,
  2164  						},
  2165  						Template: corev1.PodTemplateSpec{
  2166  							ObjectMeta: metav1.ObjectMeta{
  2167  								Name:      "bar",
  2168  								Namespace: "foo",
  2169  								Labels:    labels,
  2170  							},
  2171  							Spec: corev1.PodSpec{
  2172  								Containers: []corev1.Container{
  2173  									{
  2174  										Image: "mytest-image:latest",
  2175  										VolumeMounts: []corev1.VolumeMount{
  2176  											{
  2177  												Name:      "vol-foo",
  2178  												MountPath: "/tmp/vol-foo",
  2179  											}, {
  2180  												Name:      "vol-bar",
  2181  												MountPath: "/tmp/vol-bar",
  2182  											},
  2183  										},
  2184  									},
  2185  								},
  2186  								Volumes: []corev1.Volume{
  2187  									{
  2188  										Name:         "vol-foo",
  2189  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2190  									},
  2191  									{
  2192  										Name:         "vol-bar",
  2193  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2194  									},
  2195  								},
  2196  							},
  2197  						},
  2198  					},
  2199  				}, &appsv1.ReplicaSet{
  2200  					ObjectMeta: metav1.ObjectMeta{
  2201  						Name:      "bar-001",
  2202  						Namespace: "foo",
  2203  						Labels:    labels,
  2204  						OwnerReferences: []metav1.OwnerReference{
  2205  							{
  2206  								Controller: utilpointer.BoolPtr(true),
  2207  								UID:        "00000000-0000-0000-0000-000000000001",
  2208  							},
  2209  						},
  2210  					},
  2211  					Spec: appsv1.ReplicaSetSpec{
  2212  						Replicas: utilpointer.Int32Ptr(1),
  2213  						Selector: &metav1.LabelSelector{
  2214  							MatchLabels: labels,
  2215  						},
  2216  						Template: corev1.PodTemplateSpec{
  2217  							ObjectMeta: metav1.ObjectMeta{
  2218  								Name:      "bar",
  2219  								Namespace: "foo",
  2220  								Labels:    labels,
  2221  							},
  2222  							Spec: corev1.PodSpec{
  2223  								Containers: []corev1.Container{
  2224  									{
  2225  										Image: "mytest-image:latest",
  2226  										VolumeMounts: []corev1.VolumeMount{
  2227  											{
  2228  												Name:      "vol-foo",
  2229  												MountPath: "/tmp/vol-foo",
  2230  											}, {
  2231  												Name:      "vol-bar",
  2232  												MountPath: "/tmp/vol-bar",
  2233  											},
  2234  										},
  2235  									},
  2236  								},
  2237  								Volumes: []corev1.Volume{
  2238  									{
  2239  										Name:         "vol-foo",
  2240  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2241  									},
  2242  									{
  2243  										Name:         "vol-bar",
  2244  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2245  									},
  2246  								},
  2247  							},
  2248  						},
  2249  					},
  2250  					Status: appsv1.ReplicaSetStatus{
  2251  						Replicas:          1,
  2252  						ReadyReplicas:     1,
  2253  						AvailableReplicas: 1,
  2254  					},
  2255  				},
  2256  			},
  2257  			expects: []string{
  2258  				"Name:               bar\nNamespace:          foo",
  2259  				"CreationTimestamp:  Fri, 01 Jan 2021 00:00:00 +0000",
  2260  				"Labels:             k8s-app=bar",
  2261  				"Selector:           k8s-app=bar",
  2262  				"Replicas:           1 desired | 0 updated | 0 total | 0 available | 0 unavailable",
  2263  				"Image:        mytest-image:latest",
  2264  				"Mounts:\n      /tmp/vol-bar from vol-bar (rw)\n      /tmp/vol-foo from vol-foo (rw)",
  2265  				"OldReplicaSets:    <none>",
  2266  				"NewReplicaSet:     bar-001 (1/1 replicas created)",
  2267  				"Events:            <none>",
  2268  				"Node-Selectors:  <none>",
  2269  				"Tolerations:     <none>",
  2270  			},
  2271  		},
  2272  		{
  2273  			name: "deployment during the process of rolling out",
  2274  			objects: []runtime.Object{
  2275  				&appsv1.Deployment{
  2276  					ObjectMeta: metav1.ObjectMeta{
  2277  						Name:              "bar",
  2278  						Namespace:         "foo",
  2279  						Labels:            labels,
  2280  						UID:               "00000000-0000-0000-0000-000000000001",
  2281  						CreationTimestamp: metav1.NewTime(time.Date(2021, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
  2282  					},
  2283  					Spec: appsv1.DeploymentSpec{
  2284  						Replicas: utilpointer.Int32Ptr(2),
  2285  						Selector: &metav1.LabelSelector{
  2286  							MatchLabels: labels,
  2287  						},
  2288  						Template: corev1.PodTemplateSpec{
  2289  							ObjectMeta: metav1.ObjectMeta{
  2290  								Name:      "bar",
  2291  								Namespace: "foo",
  2292  								Labels:    labels,
  2293  							},
  2294  							Spec: corev1.PodSpec{
  2295  								Containers: []corev1.Container{
  2296  									{
  2297  										Image: "mytest-image:v2.0",
  2298  										VolumeMounts: []corev1.VolumeMount{
  2299  											{
  2300  												Name:      "vol-foo",
  2301  												MountPath: "/tmp/vol-foo",
  2302  											}, {
  2303  												Name:      "vol-bar",
  2304  												MountPath: "/tmp/vol-bar",
  2305  											},
  2306  										},
  2307  									},
  2308  								},
  2309  								Volumes: []corev1.Volume{
  2310  									{
  2311  										Name:         "vol-foo",
  2312  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2313  									},
  2314  									{
  2315  										Name:         "vol-bar",
  2316  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2317  									},
  2318  								},
  2319  							},
  2320  						},
  2321  					},
  2322  					Status: appsv1.DeploymentStatus{
  2323  						Replicas:            3,
  2324  						UpdatedReplicas:     1,
  2325  						AvailableReplicas:   2,
  2326  						UnavailableReplicas: 1,
  2327  					},
  2328  				}, &appsv1.ReplicaSet{
  2329  					ObjectMeta: metav1.ObjectMeta{
  2330  						Name:      "bar-001",
  2331  						Namespace: "foo",
  2332  						Labels:    labels,
  2333  						UID:       "00000000-0000-0000-0000-000000000001",
  2334  						OwnerReferences: []metav1.OwnerReference{
  2335  							{
  2336  								Controller: utilpointer.BoolPtr(true),
  2337  								UID:        "00000000-0000-0000-0000-000000000001",
  2338  							},
  2339  						},
  2340  					},
  2341  					Spec: appsv1.ReplicaSetSpec{
  2342  						Replicas: utilpointer.Int32Ptr(2),
  2343  						Selector: &metav1.LabelSelector{
  2344  							MatchLabels: labels,
  2345  						},
  2346  						Template: corev1.PodTemplateSpec{
  2347  							ObjectMeta: metav1.ObjectMeta{
  2348  								Name:      "bar",
  2349  								Namespace: "foo",
  2350  								Labels:    labels,
  2351  							},
  2352  							Spec: corev1.PodSpec{
  2353  								Containers: []corev1.Container{
  2354  									{
  2355  										Image: "mytest-image:v1.0",
  2356  										VolumeMounts: []corev1.VolumeMount{
  2357  											{
  2358  												Name:      "vol-foo",
  2359  												MountPath: "/tmp/vol-foo",
  2360  											}, {
  2361  												Name:      "vol-bar",
  2362  												MountPath: "/tmp/vol-bar",
  2363  											},
  2364  										},
  2365  									},
  2366  								},
  2367  								Volumes: []corev1.Volume{
  2368  									{
  2369  										Name:         "vol-foo",
  2370  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2371  									},
  2372  									{
  2373  										Name:         "vol-bar",
  2374  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2375  									},
  2376  								},
  2377  							},
  2378  						},
  2379  					},
  2380  					Status: appsv1.ReplicaSetStatus{
  2381  						Replicas:          2,
  2382  						ReadyReplicas:     2,
  2383  						AvailableReplicas: 2,
  2384  					},
  2385  				}, &appsv1.ReplicaSet{
  2386  					ObjectMeta: metav1.ObjectMeta{
  2387  						Name:      "bar-002",
  2388  						Namespace: "foo",
  2389  						Labels:    labels,
  2390  						UID:       "00000000-0000-0000-0000-000000000002",
  2391  						OwnerReferences: []metav1.OwnerReference{
  2392  							{
  2393  								Controller: utilpointer.BoolPtr(true),
  2394  								UID:        "00000000-0000-0000-0000-000000000001",
  2395  							},
  2396  						},
  2397  					},
  2398  					Spec: appsv1.ReplicaSetSpec{
  2399  						Replicas: utilpointer.Int32Ptr(1),
  2400  						Selector: &metav1.LabelSelector{
  2401  							MatchLabels: labels,
  2402  						},
  2403  						Template: corev1.PodTemplateSpec{
  2404  							ObjectMeta: metav1.ObjectMeta{
  2405  								Name:      "bar",
  2406  								Namespace: "foo",
  2407  								Labels:    labels,
  2408  							},
  2409  							Spec: corev1.PodSpec{
  2410  								Containers: []corev1.Container{
  2411  									{
  2412  										Image: "mytest-image:v2.0",
  2413  										VolumeMounts: []corev1.VolumeMount{
  2414  											{
  2415  												Name:      "vol-foo",
  2416  												MountPath: "/tmp/vol-foo",
  2417  											}, {
  2418  												Name:      "vol-bar",
  2419  												MountPath: "/tmp/vol-bar",
  2420  											},
  2421  										},
  2422  									},
  2423  								},
  2424  								Volumes: []corev1.Volume{
  2425  									{
  2426  										Name:         "vol-foo",
  2427  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2428  									},
  2429  									{
  2430  										Name:         "vol-bar",
  2431  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2432  									},
  2433  								},
  2434  							},
  2435  						},
  2436  					},
  2437  					Status: appsv1.ReplicaSetStatus{
  2438  						Replicas:          1,
  2439  						ReadyReplicas:     0,
  2440  						AvailableReplicas: 1,
  2441  					},
  2442  				}, &corev1.Event{
  2443  					ObjectMeta: metav1.ObjectMeta{
  2444  						Name:      "bar-000",
  2445  						Namespace: "foo",
  2446  					},
  2447  					InvolvedObject: corev1.ObjectReference{
  2448  						APIVersion: "apps/v1",
  2449  						Kind:       "Deployment",
  2450  						Name:       "bar",
  2451  						Namespace:  "foo",
  2452  						UID:        "00000000-0000-0000-0000-000000000001",
  2453  					},
  2454  					Type:                corev1.EventTypeNormal,
  2455  					Reason:              "ScalingReplicaSet",
  2456  					Message:             "Scaled up replica set bar-002 to 1",
  2457  					ReportingController: "deployment-controller",
  2458  					EventTime:           metav1.NewMicroTime(time.Now().Add(-20 * time.Minute)),
  2459  					Series: &corev1.EventSeries{
  2460  						Count:            3,
  2461  						LastObservedTime: metav1.NewMicroTime(time.Now().Add(-12 * time.Minute)),
  2462  					},
  2463  				}, &corev1.Event{
  2464  					ObjectMeta: metav1.ObjectMeta{
  2465  						Name:      "bar-001",
  2466  						Namespace: "foo",
  2467  					},
  2468  					InvolvedObject: corev1.ObjectReference{
  2469  						APIVersion: "apps/v1",
  2470  						Kind:       "Deployment",
  2471  						Name:       "bar",
  2472  						Namespace:  "foo",
  2473  						UID:        "00000000-0000-0000-0000-000000000001",
  2474  					},
  2475  					Type:    corev1.EventTypeNormal,
  2476  					Reason:  "ScalingReplicaSet",
  2477  					Message: "Scaled up replica set bar-001 to 2",
  2478  					Source: corev1.EventSource{
  2479  						Component: "deployment-controller",
  2480  					},
  2481  					FirstTimestamp: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
  2482  				}, &corev1.Event{
  2483  					ObjectMeta: metav1.ObjectMeta{
  2484  						Name:      "bar-002",
  2485  						Namespace: "foo",
  2486  					},
  2487  					InvolvedObject: corev1.ObjectReference{
  2488  						APIVersion: "apps/v1",
  2489  						Kind:       "Deployment",
  2490  						Name:       "bar",
  2491  						Namespace:  "foo",
  2492  						UID:        "00000000-0000-0000-0000-000000000001",
  2493  					},
  2494  					Type:    corev1.EventTypeNormal,
  2495  					Reason:  "ScalingReplicaSet",
  2496  					Message: "Scaled up replica set bar-002 to 1",
  2497  					Source: corev1.EventSource{
  2498  						Component: "deployment-controller",
  2499  					},
  2500  					FirstTimestamp: metav1.NewTime(time.Now().Add(-2 * time.Minute)),
  2501  				}, &corev1.Event{
  2502  					ObjectMeta: metav1.ObjectMeta{
  2503  						Name:      "bar-003",
  2504  						Namespace: "foo",
  2505  					},
  2506  					InvolvedObject: corev1.ObjectReference{
  2507  						APIVersion: "apps/v1",
  2508  						Kind:       "Deployment",
  2509  						Name:       "bar",
  2510  						Namespace:  "foo",
  2511  						UID:        "00000000-0000-0000-0000-000000000001",
  2512  					},
  2513  					Type:                corev1.EventTypeNormal,
  2514  					Reason:              "ScalingReplicaSet",
  2515  					Message:             "Scaled down replica set bar-002 to 1",
  2516  					ReportingController: "deployment-controller",
  2517  					EventTime:           metav1.NewMicroTime(time.Now().Add(-1 * time.Minute)),
  2518  				},
  2519  			},
  2520  			expects: []string{
  2521  				"Replicas:           2 desired | 1 updated | 3 total | 2 available | 1 unavailable",
  2522  				"Image:        mytest-image:v2.0",
  2523  				"OldReplicaSets:    bar-001 (2/2 replicas created)",
  2524  				"NewReplicaSet:     bar-002 (1/1 replicas created)",
  2525  				"Events:\n",
  2526  				"Normal  ScalingReplicaSet  12m (x3 over 20m)  deployment-controller  Scaled up replica set bar-002 to 1",
  2527  				"Normal  ScalingReplicaSet  10m                deployment-controller  Scaled up replica set bar-001 to 2",
  2528  				"Normal  ScalingReplicaSet  2m                 deployment-controller  Scaled up replica set bar-002 to 1",
  2529  				"Normal  ScalingReplicaSet  60s                deployment-controller  Scaled down replica set bar-002 to 1",
  2530  			},
  2531  		},
  2532  		{
  2533  			name: "deployment after successful rollout",
  2534  			objects: []runtime.Object{
  2535  				&appsv1.Deployment{
  2536  					ObjectMeta: metav1.ObjectMeta{
  2537  						Name:              "bar",
  2538  						Namespace:         "foo",
  2539  						Labels:            labels,
  2540  						UID:               "00000000-0000-0000-0000-000000000001",
  2541  						CreationTimestamp: metav1.NewTime(time.Date(2021, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
  2542  					},
  2543  					Spec: appsv1.DeploymentSpec{
  2544  						Replicas: utilpointer.Int32Ptr(2),
  2545  						Selector: &metav1.LabelSelector{
  2546  							MatchLabels: labels,
  2547  						},
  2548  						Template: corev1.PodTemplateSpec{
  2549  							ObjectMeta: metav1.ObjectMeta{
  2550  								Name:      "bar",
  2551  								Namespace: "foo",
  2552  								Labels:    labels,
  2553  							},
  2554  							Spec: corev1.PodSpec{
  2555  								Containers: []corev1.Container{
  2556  									{
  2557  										Image: "mytest-image:v2.0",
  2558  										VolumeMounts: []corev1.VolumeMount{
  2559  											{
  2560  												Name:      "vol-foo",
  2561  												MountPath: "/tmp/vol-foo",
  2562  											}, {
  2563  												Name:      "vol-bar",
  2564  												MountPath: "/tmp/vol-bar",
  2565  											},
  2566  										},
  2567  									},
  2568  								},
  2569  								Volumes: []corev1.Volume{
  2570  									{
  2571  										Name:         "vol-foo",
  2572  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2573  									},
  2574  									{
  2575  										Name:         "vol-bar",
  2576  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2577  									},
  2578  								},
  2579  							},
  2580  						},
  2581  					},
  2582  					Status: appsv1.DeploymentStatus{
  2583  						Replicas:            2,
  2584  						UpdatedReplicas:     2,
  2585  						AvailableReplicas:   2,
  2586  						UnavailableReplicas: 0,
  2587  					},
  2588  				}, &appsv1.ReplicaSet{
  2589  					ObjectMeta: metav1.ObjectMeta{
  2590  						Name:      "bar-001",
  2591  						Namespace: "foo",
  2592  						Labels:    labels,
  2593  						UID:       "00000000-0000-0000-0000-000000000001",
  2594  						OwnerReferences: []metav1.OwnerReference{
  2595  							{
  2596  								Controller: utilpointer.BoolPtr(true),
  2597  								UID:        "00000000-0000-0000-0000-000000000001",
  2598  							},
  2599  						},
  2600  					},
  2601  					Spec: appsv1.ReplicaSetSpec{
  2602  						Replicas: utilpointer.Int32Ptr(0),
  2603  						Selector: &metav1.LabelSelector{
  2604  							MatchLabels: labels,
  2605  						},
  2606  						Template: corev1.PodTemplateSpec{
  2607  							ObjectMeta: metav1.ObjectMeta{
  2608  								Name:      "bar",
  2609  								Namespace: "foo",
  2610  								Labels:    labels,
  2611  							},
  2612  							Spec: corev1.PodSpec{
  2613  								Containers: []corev1.Container{
  2614  									{
  2615  										Image: "mytest-image:v1.0",
  2616  										VolumeMounts: []corev1.VolumeMount{
  2617  											{
  2618  												Name:      "vol-foo",
  2619  												MountPath: "/tmp/vol-foo",
  2620  											}, {
  2621  												Name:      "vol-bar",
  2622  												MountPath: "/tmp/vol-bar",
  2623  											},
  2624  										},
  2625  									},
  2626  								},
  2627  								Volumes: []corev1.Volume{
  2628  									{
  2629  										Name:         "vol-foo",
  2630  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2631  									},
  2632  									{
  2633  										Name:         "vol-bar",
  2634  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2635  									},
  2636  								},
  2637  							},
  2638  						},
  2639  					},
  2640  					Status: appsv1.ReplicaSetStatus{
  2641  						Replicas:          0,
  2642  						ReadyReplicas:     0,
  2643  						AvailableReplicas: 0,
  2644  					},
  2645  				}, &appsv1.ReplicaSet{
  2646  					ObjectMeta: metav1.ObjectMeta{
  2647  						Name:      "bar-002",
  2648  						Namespace: "foo",
  2649  						Labels:    labels,
  2650  						UID:       "00000000-0000-0000-0000-000000000002",
  2651  						OwnerReferences: []metav1.OwnerReference{
  2652  							{
  2653  								Controller: utilpointer.BoolPtr(true),
  2654  								UID:        "00000000-0000-0000-0000-000000000001",
  2655  							},
  2656  						},
  2657  					},
  2658  					Spec: appsv1.ReplicaSetSpec{
  2659  						Replicas: utilpointer.Int32Ptr(2),
  2660  						Selector: &metav1.LabelSelector{
  2661  							MatchLabels: labels,
  2662  						},
  2663  						Template: corev1.PodTemplateSpec{
  2664  							ObjectMeta: metav1.ObjectMeta{
  2665  								Name:      "bar",
  2666  								Namespace: "foo",
  2667  								Labels:    labels,
  2668  							},
  2669  							Spec: corev1.PodSpec{
  2670  								Containers: []corev1.Container{
  2671  									{
  2672  										Image: "mytest-image:v2.0",
  2673  										VolumeMounts: []corev1.VolumeMount{
  2674  											{
  2675  												Name:      "vol-foo",
  2676  												MountPath: "/tmp/vol-foo",
  2677  											}, {
  2678  												Name:      "vol-bar",
  2679  												MountPath: "/tmp/vol-bar",
  2680  											},
  2681  										},
  2682  									},
  2683  								},
  2684  								Volumes: []corev1.Volume{
  2685  									{
  2686  										Name:         "vol-foo",
  2687  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2688  									},
  2689  									{
  2690  										Name:         "vol-bar",
  2691  										VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
  2692  									},
  2693  								},
  2694  							},
  2695  						},
  2696  					},
  2697  					Status: appsv1.ReplicaSetStatus{
  2698  						Replicas:          2,
  2699  						ReadyReplicas:     2,
  2700  						AvailableReplicas: 2,
  2701  					},
  2702  				}, &corev1.Event{
  2703  					ObjectMeta: metav1.ObjectMeta{
  2704  						Name:      "bar-000",
  2705  						Namespace: "foo",
  2706  					},
  2707  					InvolvedObject: corev1.ObjectReference{
  2708  						APIVersion: "apps/v1",
  2709  						Kind:       "Deployment",
  2710  						Name:       "bar",
  2711  						Namespace:  "foo",
  2712  						UID:        "00000000-0000-0000-0000-000000000001",
  2713  					},
  2714  					Type:                corev1.EventTypeNormal,
  2715  					Reason:              "ScalingReplicaSet",
  2716  					Message:             "Scaled up replica set bar-002 to 1",
  2717  					ReportingController: "deployment-controller",
  2718  					EventTime:           metav1.NewMicroTime(time.Now().Add(-20 * time.Minute)),
  2719  					Series: &corev1.EventSeries{
  2720  						Count:            3,
  2721  						LastObservedTime: metav1.NewMicroTime(time.Now().Add(-12 * time.Minute)),
  2722  					},
  2723  				}, &corev1.Event{
  2724  					ObjectMeta: metav1.ObjectMeta{
  2725  						Name:      "bar-001",
  2726  						Namespace: "foo",
  2727  					},
  2728  					InvolvedObject: corev1.ObjectReference{
  2729  						APIVersion: "apps/v1",
  2730  						Kind:       "Deployment",
  2731  						Name:       "bar",
  2732  						Namespace:  "foo",
  2733  						UID:        "00000000-0000-0000-0000-000000000001",
  2734  					},
  2735  					Type:    corev1.EventTypeNormal,
  2736  					Reason:  "ScalingReplicaSet",
  2737  					Message: "Scaled up replica set bar-001 to 2",
  2738  					Source: corev1.EventSource{
  2739  						Component: "deployment-controller",
  2740  					},
  2741  					FirstTimestamp: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
  2742  				}, &corev1.Event{
  2743  					ObjectMeta: metav1.ObjectMeta{
  2744  						Name:      "bar-002",
  2745  						Namespace: "foo",
  2746  					},
  2747  					InvolvedObject: corev1.ObjectReference{
  2748  						APIVersion: "apps/v1",
  2749  						Kind:       "Deployment",
  2750  						Name:       "bar",
  2751  						Namespace:  "foo",
  2752  						UID:        "00000000-0000-0000-0000-000000000001",
  2753  					},
  2754  					Type:    corev1.EventTypeNormal,
  2755  					Reason:  "ScalingReplicaSet",
  2756  					Message: "Scaled up replica set bar-002 to 1",
  2757  					Source: corev1.EventSource{
  2758  						Component: "deployment-controller",
  2759  					},
  2760  					FirstTimestamp: metav1.NewTime(time.Now().Add(-2 * time.Minute)),
  2761  				}, &corev1.Event{
  2762  					ObjectMeta: metav1.ObjectMeta{
  2763  						Name:      "bar-003",
  2764  						Namespace: "foo",
  2765  					},
  2766  					InvolvedObject: corev1.ObjectReference{
  2767  						APIVersion: "apps/v1",
  2768  						Kind:       "Deployment",
  2769  						Name:       "bar",
  2770  						Namespace:  "foo",
  2771  						UID:        "00000000-0000-0000-0000-000000000001",
  2772  					},
  2773  					Type:                corev1.EventTypeNormal,
  2774  					Reason:              "ScalingReplicaSet",
  2775  					Message:             "Scaled down replica set bar-002 to 1",
  2776  					ReportingController: "deployment-controller",
  2777  					EventTime:           metav1.NewMicroTime(time.Now().Add(-1 * time.Minute)),
  2778  				}, &corev1.Event{
  2779  					ObjectMeta: metav1.ObjectMeta{
  2780  						Name:      "bar-004",
  2781  						Namespace: "foo",
  2782  					},
  2783  					InvolvedObject: corev1.ObjectReference{
  2784  						APIVersion: "apps/v1",
  2785  						Kind:       "Deployment",
  2786  						Name:       "bar",
  2787  						Namespace:  "foo",
  2788  						UID:        "00000000-0000-0000-0000-000000000001",
  2789  					},
  2790  					Type:                corev1.EventTypeNormal,
  2791  					Reason:              "ScalingReplicaSet",
  2792  					Message:             "Scaled up replica set bar-002 to 2",
  2793  					ReportingController: "deployment-controller",
  2794  					EventTime:           metav1.NewMicroTime(time.Now().Add(-15 * time.Second)),
  2795  				}, &corev1.Event{
  2796  					ObjectMeta: metav1.ObjectMeta{
  2797  						Name:      "bar-005",
  2798  						Namespace: "foo",
  2799  					},
  2800  					InvolvedObject: corev1.ObjectReference{
  2801  						APIVersion: "apps/v1",
  2802  						Kind:       "Deployment",
  2803  						Name:       "bar",
  2804  						Namespace:  "foo",
  2805  						UID:        "00000000-0000-0000-0000-000000000001",
  2806  					},
  2807  					Type:                corev1.EventTypeNormal,
  2808  					Reason:              "ScalingReplicaSet",
  2809  					Message:             "Scaled down replica set bar-001 to 0",
  2810  					ReportingController: "deployment-controller",
  2811  					EventTime:           metav1.NewMicroTime(time.Now().Add(-3 * time.Second)),
  2812  				},
  2813  			},
  2814  			expects: []string{
  2815  				"Replicas:           2 desired | 2 updated | 2 total | 2 available | 0 unavailable",
  2816  				"Image:        mytest-image:v2.0",
  2817  				"OldReplicaSets:    bar-001 (0/0 replicas created)",
  2818  				"NewReplicaSet:     bar-002 (2/2 replicas created)",
  2819  				"Events:\n",
  2820  				"Normal  ScalingReplicaSet  12m (x3 over 20m)  deployment-controller  Scaled up replica set bar-002 to 1",
  2821  				"Normal  ScalingReplicaSet  10m                deployment-controller  Scaled up replica set bar-001 to 2",
  2822  				"Normal  ScalingReplicaSet  2m                 deployment-controller  Scaled up replica set bar-002 to 1",
  2823  				"Normal  ScalingReplicaSet  60s                deployment-controller  Scaled down replica set bar-002 to 1",
  2824  				"Normal  ScalingReplicaSet  15s                deployment-controller  Scaled up replica set bar-002 to 2",
  2825  				"Normal  ScalingReplicaSet  3s                 deployment-controller  Scaled down replica set bar-001 to 0",
  2826  			},
  2827  		},
  2828  	}
  2829  	for _, testCase := range testCases {
  2830  		t.Run(testCase.name, func(t *testing.T) {
  2831  			fakeClient := fake.NewSimpleClientset(testCase.objects...)
  2832  			d := DeploymentDescriber{fakeClient}
  2833  			out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  2834  			if err != nil {
  2835  				t.Errorf("unexpected error: %v", err)
  2836  			}
  2837  
  2838  			for _, expect := range testCase.expects {
  2839  				if !strings.Contains(out, expect) {
  2840  					t.Errorf("expected to find \"%s\" in:\n %s", expect, out)
  2841  				}
  2842  			}
  2843  
  2844  		})
  2845  	}
  2846  }
  2847  
  2848  func TestDescribeJob(t *testing.T) {
  2849  	indexedCompletion := batchv1.IndexedCompletion
  2850  	cases := map[string]struct {
  2851  		job              *batchv1.Job
  2852  		wantElements     []string
  2853  		dontWantElements []string
  2854  	}{
  2855  		"empty job": {
  2856  			job: &batchv1.Job{
  2857  				ObjectMeta: metav1.ObjectMeta{
  2858  					Name:      "bar",
  2859  					Namespace: "foo",
  2860  				},
  2861  				Spec: batchv1.JobSpec{},
  2862  			},
  2863  			dontWantElements: []string{"Completed Indexes:", "Suspend:", "Backoff Limit:", "TTL Seconds After Finished:"},
  2864  		},
  2865  		"no completed indexes": {
  2866  			job: &batchv1.Job{
  2867  				ObjectMeta: metav1.ObjectMeta{
  2868  					Name:      "bar",
  2869  					Namespace: "foo",
  2870  				},
  2871  				Spec: batchv1.JobSpec{
  2872  					CompletionMode: &indexedCompletion,
  2873  				},
  2874  			},
  2875  			wantElements: []string{"Completed Indexes:  <none>"},
  2876  		},
  2877  		"few completed indexes": {
  2878  			job: &batchv1.Job{
  2879  				ObjectMeta: metav1.ObjectMeta{
  2880  					Name:      "bar",
  2881  					Namespace: "foo",
  2882  				},
  2883  				Spec: batchv1.JobSpec{
  2884  					CompletionMode: &indexedCompletion,
  2885  				},
  2886  				Status: batchv1.JobStatus{
  2887  					CompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32",
  2888  				},
  2889  			},
  2890  			wantElements: []string{"Completed Indexes:  0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32"},
  2891  		},
  2892  		"too many completed indexes": {
  2893  			job: &batchv1.Job{
  2894  				ObjectMeta: metav1.ObjectMeta{
  2895  					Name:      "bar",
  2896  					Namespace: "foo",
  2897  				},
  2898  				Spec: batchv1.JobSpec{
  2899  					CompletionMode: &indexedCompletion,
  2900  				},
  2901  				Status: batchv1.JobStatus{
  2902  					CompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32-34,36,37",
  2903  				},
  2904  			},
  2905  			wantElements: []string{"Completed Indexes:  0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32-34,..."},
  2906  		},
  2907  		"suspend set to true": {
  2908  			job: &batchv1.Job{
  2909  				ObjectMeta: metav1.ObjectMeta{
  2910  					Name:      "bar",
  2911  					Namespace: "foo",
  2912  				},
  2913  				Spec: batchv1.JobSpec{
  2914  					Suspend:                 ptr.To(true),
  2915  					TTLSecondsAfterFinished: ptr.To(int32(123)),
  2916  					BackoffLimit:            ptr.To(int32(1)),
  2917  				},
  2918  			},
  2919  			wantElements: []string{
  2920  				"Suspend:                     true",
  2921  				"TTL Seconds After Finished:  123",
  2922  				"Backoff Limit:               1",
  2923  			},
  2924  		},
  2925  		"suspend set to false": {
  2926  			job: &batchv1.Job{
  2927  				ObjectMeta: metav1.ObjectMeta{
  2928  					Name:      "bar",
  2929  					Namespace: "foo",
  2930  				},
  2931  				Spec: batchv1.JobSpec{
  2932  					Suspend: ptr.To(false),
  2933  				},
  2934  			},
  2935  			wantElements: []string{"Suspend:        false"},
  2936  		},
  2937  	}
  2938  	for name, tc := range cases {
  2939  		t.Run(name, func(t *testing.T) {
  2940  			client := &describeClient{
  2941  				T:         t,
  2942  				Namespace: tc.job.Namespace,
  2943  				Interface: fake.NewSimpleClientset(tc.job),
  2944  			}
  2945  			describer := JobDescriber{Interface: client}
  2946  			out, err := describer.Describe(tc.job.Namespace, tc.job.Name, DescriberSettings{ShowEvents: true})
  2947  			if err != nil {
  2948  				t.Fatalf("unexpected error describing object: %v", err)
  2949  			}
  2950  
  2951  			for _, expected := range tc.wantElements {
  2952  				if !strings.Contains(out, expected) {
  2953  					t.Errorf("expected to find %q in output:\n %s", expected, out)
  2954  				}
  2955  			}
  2956  
  2957  			for _, unexpected := range tc.dontWantElements {
  2958  				if strings.Contains(out, unexpected) {
  2959  					t.Errorf("unexpected to find %q in output:\n %s", unexpected, out)
  2960  				}
  2961  			}
  2962  		})
  2963  	}
  2964  }
  2965  
  2966  func TestDescribeIngress(t *testing.T) {
  2967  	ingresClassName := "test"
  2968  	backendV1beta1 := networkingv1beta1.IngressBackend{
  2969  		ServiceName: "default-backend",
  2970  		ServicePort: intstr.FromInt32(80),
  2971  	}
  2972  	v1beta1 := fake.NewSimpleClientset(&networkingv1beta1.Ingress{
  2973  		ObjectMeta: metav1.ObjectMeta{
  2974  			Name: "bar",
  2975  			Labels: map[string]string{
  2976  				"id1": "app1",
  2977  				"id2": "app2",
  2978  			},
  2979  			Namespace: "foo",
  2980  		},
  2981  		Spec: networkingv1beta1.IngressSpec{
  2982  			IngressClassName: &ingresClassName,
  2983  			Rules: []networkingv1beta1.IngressRule{
  2984  				{
  2985  					Host: "foo.bar.com",
  2986  					IngressRuleValue: networkingv1beta1.IngressRuleValue{
  2987  						HTTP: &networkingv1beta1.HTTPIngressRuleValue{
  2988  							Paths: []networkingv1beta1.HTTPIngressPath{
  2989  								{
  2990  									Path:    "/foo",
  2991  									Backend: backendV1beta1,
  2992  								},
  2993  							},
  2994  						},
  2995  					},
  2996  				},
  2997  			},
  2998  		},
  2999  	})
  3000  	backendV1 := networkingv1.IngressBackend{
  3001  		Service: &networkingv1.IngressServiceBackend{
  3002  			Name: "default-backend",
  3003  			Port: networkingv1.ServiceBackendPort{
  3004  				Number: 80,
  3005  			},
  3006  		},
  3007  	}
  3008  
  3009  	netv1 := fake.NewSimpleClientset(&networkingv1.Ingress{
  3010  		ObjectMeta: metav1.ObjectMeta{
  3011  			Name:      "bar",
  3012  			Namespace: "foo",
  3013  		},
  3014  		Spec: networkingv1.IngressSpec{
  3015  			IngressClassName: &ingresClassName,
  3016  			Rules: []networkingv1.IngressRule{
  3017  				{
  3018  					Host: "foo.bar.com",
  3019  					IngressRuleValue: networkingv1.IngressRuleValue{
  3020  						HTTP: &networkingv1.HTTPIngressRuleValue{
  3021  							Paths: []networkingv1.HTTPIngressPath{
  3022  								{
  3023  									Path:    "/foo",
  3024  									Backend: backendV1,
  3025  								},
  3026  							},
  3027  						},
  3028  					},
  3029  				},
  3030  			},
  3031  		},
  3032  	})
  3033  
  3034  	backendResource := networkingv1.IngressBackend{
  3035  		Resource: &corev1.TypedLocalObjectReference{
  3036  			APIGroup: utilpointer.StringPtr("example.com"),
  3037  			Kind:     "foo",
  3038  			Name:     "bar",
  3039  		},
  3040  	}
  3041  	backendResourceNoAPIGroup := networkingv1.IngressBackend{
  3042  		Resource: &corev1.TypedLocalObjectReference{
  3043  			Kind: "foo",
  3044  			Name: "bar",
  3045  		},
  3046  	}
  3047  
  3048  	tests := map[string]struct {
  3049  		input  *fake.Clientset
  3050  		output string
  3051  	}{
  3052  		"IngressRule.HTTP.Paths.Backend.Service v1beta1": {
  3053  			input: v1beta1,
  3054  			output: `Name:             bar
  3055  Labels:           id1=app1
  3056                    id2=app2
  3057  Namespace:        foo
  3058  Address:          
  3059  Ingress Class:    test
  3060  Default backend:  <default>
  3061  Rules:
  3062    Host         Path  Backends
  3063    ----         ----  --------
  3064    foo.bar.com  
  3065                 /foo   default-backend:80 (<error: endpoints "default-backend" not found>)
  3066  Annotations:   <none>
  3067  Events:        <none>` + "\n",
  3068  		},
  3069  		"IngressRule.HTTP.Paths.Backend.Service v1": {
  3070  			input: netv1,
  3071  			output: `Name:             bar
  3072  Labels:           <none>
  3073  Namespace:        foo
  3074  Address:          
  3075  Ingress Class:    test
  3076  Default backend:  <default>
  3077  Rules:
  3078    Host         Path  Backends
  3079    ----         ----  --------
  3080    foo.bar.com  
  3081                 /foo   default-backend:80 (<error: endpoints "default-backend" not found>)
  3082  Annotations:   <none>
  3083  Events:        <none>` + "\n",
  3084  		},
  3085  		"IngressRule.HTTP.Paths.Backend.Resource v1": {
  3086  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3087  				ObjectMeta: metav1.ObjectMeta{
  3088  					Name:      "bar",
  3089  					Namespace: "foo",
  3090  				},
  3091  				Spec: networkingv1.IngressSpec{
  3092  					IngressClassName: &ingresClassName,
  3093  					Rules: []networkingv1.IngressRule{
  3094  						{
  3095  							Host: "foo.bar.com",
  3096  							IngressRuleValue: networkingv1.IngressRuleValue{
  3097  								HTTP: &networkingv1.HTTPIngressRuleValue{
  3098  									Paths: []networkingv1.HTTPIngressPath{
  3099  										{
  3100  											Path:    "/foo",
  3101  											Backend: backendResource,
  3102  										},
  3103  									},
  3104  								},
  3105  							},
  3106  						},
  3107  					},
  3108  				},
  3109  			}),
  3110  			output: `Name:             bar
  3111  Labels:           <none>
  3112  Namespace:        foo
  3113  Address:          
  3114  Ingress Class:    test
  3115  Default backend:  <default>
  3116  Rules:
  3117    Host         Path  Backends
  3118    ----         ----  --------
  3119    foo.bar.com  
  3120                 /foo   APIGroup: example.com, Kind: foo, Name: bar
  3121  Annotations:   <none>
  3122  Events:        <none>` + "\n",
  3123  		},
  3124  		"IngressRule.HTTP.Paths.Backend.Resource v1 Without APIGroup": {
  3125  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3126  				ObjectMeta: metav1.ObjectMeta{
  3127  					Name:      "bar",
  3128  					Namespace: "foo",
  3129  				},
  3130  				Spec: networkingv1.IngressSpec{
  3131  					IngressClassName: &ingresClassName,
  3132  					Rules: []networkingv1.IngressRule{
  3133  						{
  3134  							Host: "foo.bar.com",
  3135  							IngressRuleValue: networkingv1.IngressRuleValue{
  3136  								HTTP: &networkingv1.HTTPIngressRuleValue{
  3137  									Paths: []networkingv1.HTTPIngressPath{
  3138  										{
  3139  											Path:    "/foo",
  3140  											Backend: backendResourceNoAPIGroup,
  3141  										},
  3142  									},
  3143  								},
  3144  							},
  3145  						},
  3146  					},
  3147  				},
  3148  			}),
  3149  			output: `Name:             bar
  3150  Labels:           <none>
  3151  Namespace:        foo
  3152  Address:          
  3153  Ingress Class:    test
  3154  Default backend:  <default>
  3155  Rules:
  3156    Host         Path  Backends
  3157    ----         ----  --------
  3158    foo.bar.com  
  3159                 /foo   APIGroup: <none>, Kind: foo, Name: bar
  3160  Annotations:   <none>
  3161  Events:        <none>` + "\n",
  3162  		},
  3163  		"Spec.DefaultBackend.Service & IngressRule.HTTP.Paths.Backend.Service v1": {
  3164  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3165  				ObjectMeta: metav1.ObjectMeta{
  3166  					Name:      "bar",
  3167  					Namespace: "foo",
  3168  				},
  3169  				Spec: networkingv1.IngressSpec{
  3170  					DefaultBackend:   &backendV1,
  3171  					IngressClassName: &ingresClassName,
  3172  					Rules: []networkingv1.IngressRule{
  3173  						{
  3174  							Host: "foo.bar.com",
  3175  							IngressRuleValue: networkingv1.IngressRuleValue{
  3176  								HTTP: &networkingv1.HTTPIngressRuleValue{
  3177  									Paths: []networkingv1.HTTPIngressPath{
  3178  										{
  3179  											Path:    "/foo",
  3180  											Backend: backendV1,
  3181  										},
  3182  									},
  3183  								},
  3184  							},
  3185  						},
  3186  					},
  3187  				},
  3188  			}),
  3189  			output: `Name:             bar
  3190  Labels:           <none>
  3191  Namespace:        foo
  3192  Address:          
  3193  Ingress Class:    test
  3194  Default backend:  default-backend:80 (<error: endpoints "default-backend" not found>)
  3195  Rules:
  3196    Host         Path  Backends
  3197    ----         ----  --------
  3198    foo.bar.com  
  3199                 /foo   default-backend:80 (<error: endpoints "default-backend" not found>)
  3200  Annotations:   <none>
  3201  Events:        <none>` + "\n",
  3202  		},
  3203  		"Spec.DefaultBackend.Resource & IngressRule.HTTP.Paths.Backend.Resource v1": {
  3204  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3205  				ObjectMeta: metav1.ObjectMeta{
  3206  					Name:      "bar",
  3207  					Namespace: "foo",
  3208  				},
  3209  				Spec: networkingv1.IngressSpec{
  3210  					DefaultBackend:   &backendResource,
  3211  					IngressClassName: &ingresClassName,
  3212  					Rules: []networkingv1.IngressRule{
  3213  						{
  3214  							Host: "foo.bar.com",
  3215  							IngressRuleValue: networkingv1.IngressRuleValue{
  3216  								HTTP: &networkingv1.HTTPIngressRuleValue{
  3217  									Paths: []networkingv1.HTTPIngressPath{
  3218  										{
  3219  											Path:    "/foo",
  3220  											Backend: backendResource,
  3221  										},
  3222  									},
  3223  								},
  3224  							},
  3225  						},
  3226  					},
  3227  				},
  3228  			}),
  3229  			output: `Name:             bar
  3230  Labels:           <none>
  3231  Namespace:        foo
  3232  Address:          
  3233  Ingress Class:    test
  3234  Default backend:  APIGroup: example.com, Kind: foo, Name: bar
  3235  Rules:
  3236    Host         Path  Backends
  3237    ----         ----  --------
  3238    foo.bar.com  
  3239                 /foo   APIGroup: example.com, Kind: foo, Name: bar
  3240  Annotations:   <none>
  3241  Events:        <none>` + "\n",
  3242  		},
  3243  		"Spec.DefaultBackend.Resource & IngressRule.HTTP.Paths.Backend.Service v1": {
  3244  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3245  				ObjectMeta: metav1.ObjectMeta{
  3246  					Name:      "bar",
  3247  					Namespace: "foo",
  3248  				},
  3249  				Spec: networkingv1.IngressSpec{
  3250  					DefaultBackend:   &backendResource,
  3251  					IngressClassName: &ingresClassName,
  3252  					Rules: []networkingv1.IngressRule{
  3253  						{
  3254  							Host: "foo.bar.com",
  3255  							IngressRuleValue: networkingv1.IngressRuleValue{
  3256  								HTTP: &networkingv1.HTTPIngressRuleValue{
  3257  									Paths: []networkingv1.HTTPIngressPath{
  3258  										{
  3259  											Path:    "/foo",
  3260  											Backend: backendV1,
  3261  										},
  3262  									},
  3263  								},
  3264  							},
  3265  						},
  3266  					},
  3267  				},
  3268  			}),
  3269  			output: `Name:             bar
  3270  Labels:           <none>
  3271  Namespace:        foo
  3272  Address:          
  3273  Ingress Class:    test
  3274  Default backend:  APIGroup: example.com, Kind: foo, Name: bar
  3275  Rules:
  3276    Host         Path  Backends
  3277    ----         ----  --------
  3278    foo.bar.com  
  3279                 /foo   default-backend:80 (<error: endpoints "default-backend" not found>)
  3280  Annotations:   <none>
  3281  Events:        <none>` + "\n",
  3282  		},
  3283  		"DefaultBackend": {
  3284  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3285  				ObjectMeta: metav1.ObjectMeta{
  3286  					Name:      "bar",
  3287  					Namespace: "foo",
  3288  				},
  3289  				Spec: networkingv1.IngressSpec{
  3290  					DefaultBackend:   &backendV1,
  3291  					IngressClassName: &ingresClassName,
  3292  				},
  3293  			}),
  3294  			output: `Name:             bar
  3295  Labels:           <none>
  3296  Namespace:        foo
  3297  Address:          
  3298  Ingress Class:    test
  3299  Default backend:  default-backend:80 (<error: endpoints "default-backend" not found>)
  3300  Rules:
  3301    Host        Path  Backends
  3302    ----        ----  --------
  3303    *           *     default-backend:80 (<error: endpoints "default-backend" not found>)
  3304  Annotations:  <none>
  3305  Events:       <none>
  3306  `,
  3307  		},
  3308  		"EmptyBackend": {
  3309  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3310  				ObjectMeta: metav1.ObjectMeta{
  3311  					Name:      "bar",
  3312  					Namespace: "foo",
  3313  				},
  3314  				Spec: networkingv1.IngressSpec{
  3315  					IngressClassName: &ingresClassName,
  3316  				},
  3317  			}),
  3318  			output: `Name:             bar
  3319  Labels:           <none>
  3320  Namespace:        foo
  3321  Address:          
  3322  Ingress Class:    test
  3323  Default backend:  <default>
  3324  Rules:
  3325    Host        Path  Backends
  3326    ----        ----  --------
  3327    *           *     <default>
  3328  Annotations:  <none>
  3329  Events:       <none>
  3330  `,
  3331  		},
  3332  		"EmptyIngressClassName": {
  3333  			input: fake.NewSimpleClientset(&networkingv1.Ingress{
  3334  				ObjectMeta: metav1.ObjectMeta{
  3335  					Name:      "bar",
  3336  					Namespace: "foo",
  3337  				},
  3338  				Spec: networkingv1.IngressSpec{
  3339  					DefaultBackend: &backendV1,
  3340  				},
  3341  			}),
  3342  			output: `Name:             bar
  3343  Labels:           <none>
  3344  Namespace:        foo
  3345  Address:          
  3346  Ingress Class:    <none>
  3347  Default backend:  default-backend:80 (<error: endpoints "default-backend" not found>)
  3348  Rules:
  3349    Host        Path  Backends
  3350    ----        ----  --------
  3351    *           *     default-backend:80 (<error: endpoints "default-backend" not found>)
  3352  Annotations:  <none>
  3353  Events:       <none>
  3354  `,
  3355  		},
  3356  	}
  3357  
  3358  	for name, test := range tests {
  3359  		t.Run(name, func(t *testing.T) {
  3360  			c := &describeClient{T: t, Namespace: "foo", Interface: test.input}
  3361  			i := IngressDescriber{c}
  3362  			out, err := i.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  3363  			if err != nil {
  3364  				t.Errorf("unexpected error: %v", err)
  3365  			}
  3366  			if out != test.output {
  3367  				t.Logf(out)
  3368  				t.Logf(test.output)
  3369  				t.Errorf("expected: \n%q\n but got output: \n%q\n", test.output, out)
  3370  			}
  3371  		})
  3372  	}
  3373  }
  3374  
  3375  func TestDescribeIngressV1(t *testing.T) {
  3376  	ingresClassName := "test"
  3377  	defaultBackend := networkingv1.IngressBackend{
  3378  		Service: &networkingv1.IngressServiceBackend{
  3379  			Name: "default-backend",
  3380  			Port: networkingv1.ServiceBackendPort{
  3381  				Number: 80,
  3382  			},
  3383  		},
  3384  	}
  3385  
  3386  	fakeClient := fake.NewSimpleClientset(&networkingv1.Ingress{
  3387  		ObjectMeta: metav1.ObjectMeta{
  3388  			Name: "bar",
  3389  			Labels: map[string]string{
  3390  				"id1": "app1",
  3391  				"id2": "app2",
  3392  			},
  3393  			Namespace: "foo",
  3394  		},
  3395  		Spec: networkingv1.IngressSpec{
  3396  			IngressClassName: &ingresClassName,
  3397  			Rules: []networkingv1.IngressRule{
  3398  				{
  3399  					Host: "foo.bar.com",
  3400  					IngressRuleValue: networkingv1.IngressRuleValue{
  3401  						HTTP: &networkingv1.HTTPIngressRuleValue{
  3402  							Paths: []networkingv1.HTTPIngressPath{
  3403  								{
  3404  									Path:    "/foo",
  3405  									Backend: defaultBackend,
  3406  								},
  3407  							},
  3408  						},
  3409  					},
  3410  				},
  3411  			},
  3412  		},
  3413  	})
  3414  	i := IngressDescriber{fakeClient}
  3415  	out, err := i.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  3416  	if err != nil {
  3417  		t.Errorf("unexpected error: %v", err)
  3418  	}
  3419  	if !strings.Contains(out, "bar") ||
  3420  		!strings.Contains(out, "foo") ||
  3421  		!strings.Contains(out, "foo.bar.com") ||
  3422  		!strings.Contains(out, "/foo") ||
  3423  		!strings.Contains(out, "app1") ||
  3424  		!strings.Contains(out, "app2") {
  3425  		t.Errorf("unexpected out: %s", out)
  3426  	}
  3427  }
  3428  
  3429  func TestDescribeStorageClass(t *testing.T) {
  3430  	reclaimPolicy := corev1.PersistentVolumeReclaimRetain
  3431  	bindingMode := storagev1.VolumeBindingMode("bindingmode")
  3432  	f := fake.NewSimpleClientset(&storagev1.StorageClass{
  3433  		ObjectMeta: metav1.ObjectMeta{
  3434  			Name:            "foo",
  3435  			ResourceVersion: "4",
  3436  			Annotations: map[string]string{
  3437  				"name": "foo",
  3438  			},
  3439  		},
  3440  		Provisioner: "my-provisioner",
  3441  		Parameters: map[string]string{
  3442  			"param1": "value1",
  3443  			"param2": "value2",
  3444  		},
  3445  		ReclaimPolicy:     &reclaimPolicy,
  3446  		VolumeBindingMode: &bindingMode,
  3447  		AllowedTopologies: []corev1.TopologySelectorTerm{
  3448  			{
  3449  				MatchLabelExpressions: []corev1.TopologySelectorLabelRequirement{
  3450  					{
  3451  						Key:    "failure-domain.beta.kubernetes.io/zone",
  3452  						Values: []string{"zone1"},
  3453  					},
  3454  					{
  3455  						Key:    "kubernetes.io/hostname",
  3456  						Values: []string{"node1"},
  3457  					},
  3458  				},
  3459  			},
  3460  			{
  3461  				MatchLabelExpressions: []corev1.TopologySelectorLabelRequirement{
  3462  					{
  3463  						Key:    "failure-domain.beta.kubernetes.io/zone",
  3464  						Values: []string{"zone2"},
  3465  					},
  3466  					{
  3467  						Key:    "kubernetes.io/hostname",
  3468  						Values: []string{"node2"},
  3469  					},
  3470  				},
  3471  			},
  3472  		},
  3473  	})
  3474  	s := StorageClassDescriber{f}
  3475  	out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true})
  3476  	if err != nil {
  3477  		t.Errorf("unexpected error: %v", err)
  3478  	}
  3479  	if !strings.Contains(out, "foo") ||
  3480  		!strings.Contains(out, "my-provisioner") ||
  3481  		!strings.Contains(out, "param1") ||
  3482  		!strings.Contains(out, "param2") ||
  3483  		!strings.Contains(out, "value1") ||
  3484  		!strings.Contains(out, "value2") ||
  3485  		!strings.Contains(out, "Retain") ||
  3486  		!strings.Contains(out, "bindingmode") ||
  3487  		!strings.Contains(out, "failure-domain.beta.kubernetes.io/zone") ||
  3488  		!strings.Contains(out, "zone1") ||
  3489  		!strings.Contains(out, "kubernetes.io/hostname") ||
  3490  		!strings.Contains(out, "node1") ||
  3491  		!strings.Contains(out, "zone2") ||
  3492  		!strings.Contains(out, "node2") {
  3493  		t.Errorf("unexpected out: %s", out)
  3494  	}
  3495  }
  3496  
  3497  func TestDescribeVolumeAttributesClass(t *testing.T) {
  3498  	expectedOut := `Name:         foo
  3499  Annotations:  name=bar
  3500  DriverName:   my-driver
  3501  Parameters:   param1=value1,param2=value2
  3502  Events:       <none>
  3503  `
  3504  
  3505  	f := fake.NewSimpleClientset(&storagev1alpha1.VolumeAttributesClass{
  3506  		ObjectMeta: metav1.ObjectMeta{
  3507  			Name:            "foo",
  3508  			ResourceVersion: "4",
  3509  			Annotations: map[string]string{
  3510  				"name": "bar",
  3511  			},
  3512  		},
  3513  		DriverName: "my-driver",
  3514  		Parameters: map[string]string{
  3515  			"param1": "value1",
  3516  			"param2": "value2",
  3517  		},
  3518  	})
  3519  	s := VolumeAttributesClassDescriber{f}
  3520  	out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true})
  3521  	if err != nil {
  3522  		t.Errorf("unexpected error: %v", err)
  3523  	}
  3524  	if out != expectedOut {
  3525  		t.Errorf("expected:\n %s\n but got output:\n %s diff:\n%s", expectedOut, out, cmp.Diff(out, expectedOut))
  3526  	}
  3527  }
  3528  
  3529  func TestDescribeCSINode(t *testing.T) {
  3530  	limit := utilpointer.Int32Ptr(int32(2))
  3531  	f := fake.NewSimpleClientset(&storagev1.CSINode{
  3532  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  3533  		Spec: storagev1.CSINodeSpec{
  3534  			Drivers: []storagev1.CSINodeDriver{
  3535  				{
  3536  					Name:   "driver1",
  3537  					NodeID: "node1",
  3538  				},
  3539  				{
  3540  					Name:        "driver2",
  3541  					NodeID:      "node2",
  3542  					Allocatable: &storagev1.VolumeNodeResources{Count: limit},
  3543  				},
  3544  			},
  3545  		},
  3546  	})
  3547  	s := CSINodeDescriber{f}
  3548  	out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true})
  3549  	if err != nil {
  3550  		t.Errorf("unexpected error: %v", err)
  3551  	}
  3552  	if !strings.Contains(out, "foo") ||
  3553  		!strings.Contains(out, "driver1") ||
  3554  		!strings.Contains(out, "node1") ||
  3555  		!strings.Contains(out, "driver2") ||
  3556  		!strings.Contains(out, "node2") {
  3557  		t.Errorf("unexpected out: %s", out)
  3558  	}
  3559  }
  3560  
  3561  func TestDescribePodDisruptionBudgetV1beta1(t *testing.T) {
  3562  	minAvailable := intstr.FromInt32(22)
  3563  	f := fake.NewSimpleClientset(&policyv1beta1.PodDisruptionBudget{
  3564  		ObjectMeta: metav1.ObjectMeta{
  3565  			Namespace:         "ns1",
  3566  			Name:              "pdb1",
  3567  			CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3568  		},
  3569  		Spec: policyv1beta1.PodDisruptionBudgetSpec{
  3570  			MinAvailable: &minAvailable,
  3571  		},
  3572  		Status: policyv1beta1.PodDisruptionBudgetStatus{
  3573  			DisruptionsAllowed: 5,
  3574  		},
  3575  	})
  3576  	s := PodDisruptionBudgetDescriber{f}
  3577  	out, err := s.Describe("ns1", "pdb1", DescriberSettings{ShowEvents: true})
  3578  	if err != nil {
  3579  		t.Errorf("unexpected error: %v", err)
  3580  	}
  3581  	if !strings.Contains(out, "pdb1") ||
  3582  		!strings.Contains(out, "ns1") ||
  3583  		!strings.Contains(out, "22") ||
  3584  		!strings.Contains(out, "5") {
  3585  		t.Errorf("unexpected out: %s", out)
  3586  	}
  3587  }
  3588  
  3589  func TestDescribePodDisruptionBudgetV1(t *testing.T) {
  3590  	minAvailable := intstr.FromInt32(22)
  3591  	f := fake.NewSimpleClientset(&policyv1.PodDisruptionBudget{
  3592  		ObjectMeta: metav1.ObjectMeta{
  3593  			Namespace:         "ns1",
  3594  			Name:              "pdb1",
  3595  			CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3596  		},
  3597  		Spec: policyv1.PodDisruptionBudgetSpec{
  3598  			MinAvailable: &minAvailable,
  3599  		},
  3600  		Status: policyv1.PodDisruptionBudgetStatus{
  3601  			DisruptionsAllowed: 5,
  3602  		},
  3603  	})
  3604  	s := PodDisruptionBudgetDescriber{f}
  3605  	out, err := s.Describe("ns1", "pdb1", DescriberSettings{ShowEvents: true})
  3606  	if err != nil {
  3607  		t.Errorf("unexpected error: %v", err)
  3608  	}
  3609  	if !strings.Contains(out, "pdb1") ||
  3610  		!strings.Contains(out, "ns1") ||
  3611  		!strings.Contains(out, "22") ||
  3612  		!strings.Contains(out, "5") {
  3613  		t.Errorf("unexpected out: %s", out)
  3614  	}
  3615  }
  3616  
  3617  func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
  3618  	minReplicasVal := int32(2)
  3619  	targetUtilizationVal := int32(80)
  3620  	currentUtilizationVal := int32(50)
  3621  	maxSelectPolicy := autoscalingv2.MaxChangePolicySelect
  3622  	metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
  3623  	if err != nil {
  3624  		t.Errorf("unable to parse label selector: %v", err)
  3625  	}
  3626  	testsv2 := []struct {
  3627  		name string
  3628  		hpa  autoscalingv2.HorizontalPodAutoscaler
  3629  	}{
  3630  		{
  3631  			"minReplicas unset",
  3632  			autoscalingv2.HorizontalPodAutoscaler{
  3633  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3634  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3635  						Name: "some-rc",
  3636  						Kind: "ReplicationController",
  3637  					},
  3638  					MaxReplicas: 10,
  3639  				},
  3640  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3641  					CurrentReplicas: 4,
  3642  					DesiredReplicas: 5,
  3643  				},
  3644  			},
  3645  		},
  3646  		{
  3647  			"external source type, target average value (no current)",
  3648  			autoscalingv2.HorizontalPodAutoscaler{
  3649  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3650  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3651  						Name: "some-rc",
  3652  						Kind: "ReplicationController",
  3653  					},
  3654  					MinReplicas: &minReplicasVal,
  3655  					MaxReplicas: 10,
  3656  					Metrics: []autoscalingv2.MetricSpec{
  3657  						{
  3658  							Type: autoscalingv2.ExternalMetricSourceType,
  3659  							External: &autoscalingv2.ExternalMetricSource{
  3660  								Metric: autoscalingv2.MetricIdentifier{
  3661  									Name:     "some-external-metric",
  3662  									Selector: metricLabelSelector,
  3663  								},
  3664  								Target: autoscalingv2.MetricTarget{
  3665  									Type:         autoscalingv2.AverageValueMetricType,
  3666  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3667  								},
  3668  							},
  3669  						},
  3670  					},
  3671  				},
  3672  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3673  					CurrentReplicas: 4,
  3674  					DesiredReplicas: 5,
  3675  				},
  3676  			},
  3677  		},
  3678  		{
  3679  			"external source type, target average value (with current)",
  3680  			autoscalingv2.HorizontalPodAutoscaler{
  3681  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3682  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3683  						Name: "some-rc",
  3684  						Kind: "ReplicationController",
  3685  					},
  3686  					MinReplicas: &minReplicasVal,
  3687  					MaxReplicas: 10,
  3688  					Metrics: []autoscalingv2.MetricSpec{
  3689  						{
  3690  							Type: autoscalingv2.ExternalMetricSourceType,
  3691  							External: &autoscalingv2.ExternalMetricSource{
  3692  								Metric: autoscalingv2.MetricIdentifier{
  3693  									Name:     "some-external-metric",
  3694  									Selector: metricLabelSelector,
  3695  								},
  3696  								Target: autoscalingv2.MetricTarget{
  3697  									Type:         autoscalingv2.AverageValueMetricType,
  3698  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3699  								},
  3700  							},
  3701  						},
  3702  					},
  3703  				},
  3704  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3705  					CurrentReplicas: 4,
  3706  					DesiredReplicas: 5,
  3707  					CurrentMetrics: []autoscalingv2.MetricStatus{
  3708  						{
  3709  							Type: autoscalingv2.ExternalMetricSourceType,
  3710  							External: &autoscalingv2.ExternalMetricStatus{
  3711  								Metric: autoscalingv2.MetricIdentifier{
  3712  									Name:     "some-external-metric",
  3713  									Selector: metricLabelSelector,
  3714  								},
  3715  								Current: autoscalingv2.MetricValueStatus{
  3716  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3717  								},
  3718  							},
  3719  						},
  3720  					},
  3721  				},
  3722  			},
  3723  		},
  3724  		{
  3725  			"external source type, target value (no current)",
  3726  			autoscalingv2.HorizontalPodAutoscaler{
  3727  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3728  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3729  						Name: "some-rc",
  3730  						Kind: "ReplicationController",
  3731  					},
  3732  					MinReplicas: &minReplicasVal,
  3733  					MaxReplicas: 10,
  3734  					Metrics: []autoscalingv2.MetricSpec{
  3735  						{
  3736  							Type: autoscalingv2.ExternalMetricSourceType,
  3737  							External: &autoscalingv2.ExternalMetricSource{
  3738  								Metric: autoscalingv2.MetricIdentifier{
  3739  									Name:     "some-external-metric",
  3740  									Selector: metricLabelSelector,
  3741  								},
  3742  								Target: autoscalingv2.MetricTarget{
  3743  									Type:  autoscalingv2.ValueMetricType,
  3744  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3745  								},
  3746  							},
  3747  						},
  3748  					},
  3749  				},
  3750  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3751  					CurrentReplicas: 4,
  3752  					DesiredReplicas: 5,
  3753  				},
  3754  			},
  3755  		},
  3756  		{
  3757  			"external source type, target value (with current)",
  3758  			autoscalingv2.HorizontalPodAutoscaler{
  3759  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3760  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3761  						Name: "some-rc",
  3762  						Kind: "ReplicationController",
  3763  					},
  3764  					MinReplicas: &minReplicasVal,
  3765  					MaxReplicas: 10,
  3766  					Metrics: []autoscalingv2.MetricSpec{
  3767  						{
  3768  							Type: autoscalingv2.ExternalMetricSourceType,
  3769  							External: &autoscalingv2.ExternalMetricSource{
  3770  								Metric: autoscalingv2.MetricIdentifier{
  3771  									Name:     "some-external-metric",
  3772  									Selector: metricLabelSelector,
  3773  								},
  3774  								Target: autoscalingv2.MetricTarget{
  3775  									Type:  autoscalingv2.ValueMetricType,
  3776  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3777  								},
  3778  							},
  3779  						},
  3780  					},
  3781  				},
  3782  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3783  					CurrentReplicas: 4,
  3784  					DesiredReplicas: 5,
  3785  					CurrentMetrics: []autoscalingv2.MetricStatus{
  3786  						{
  3787  							Type: autoscalingv2.ExternalMetricSourceType,
  3788  							External: &autoscalingv2.ExternalMetricStatus{
  3789  								Metric: autoscalingv2.MetricIdentifier{
  3790  									Name:     "some-external-metric",
  3791  									Selector: metricLabelSelector,
  3792  								},
  3793  								Current: autoscalingv2.MetricValueStatus{
  3794  									Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  3795  								},
  3796  							},
  3797  						},
  3798  					},
  3799  				},
  3800  			},
  3801  		},
  3802  		{
  3803  			"pods source type (no current)",
  3804  			autoscalingv2.HorizontalPodAutoscaler{
  3805  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3806  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3807  						Name: "some-rc",
  3808  						Kind: "ReplicationController",
  3809  					},
  3810  					MinReplicas: &minReplicasVal,
  3811  					MaxReplicas: 10,
  3812  					Metrics: []autoscalingv2.MetricSpec{
  3813  						{
  3814  							Type: autoscalingv2.PodsMetricSourceType,
  3815  							Pods: &autoscalingv2.PodsMetricSource{
  3816  								Metric: autoscalingv2.MetricIdentifier{
  3817  									Name: "some-pods-metric",
  3818  								},
  3819  								Target: autoscalingv2.MetricTarget{
  3820  									Type:         autoscalingv2.AverageValueMetricType,
  3821  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3822  								},
  3823  							},
  3824  						},
  3825  					},
  3826  				},
  3827  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3828  					CurrentReplicas: 4,
  3829  					DesiredReplicas: 5,
  3830  				},
  3831  			},
  3832  		},
  3833  		{
  3834  			"pods source type (with current)",
  3835  			autoscalingv2.HorizontalPodAutoscaler{
  3836  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3837  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3838  						Name: "some-rc",
  3839  						Kind: "ReplicationController",
  3840  					},
  3841  					MinReplicas: &minReplicasVal,
  3842  					MaxReplicas: 10,
  3843  					Metrics: []autoscalingv2.MetricSpec{
  3844  						{
  3845  							Type: autoscalingv2.PodsMetricSourceType,
  3846  							Pods: &autoscalingv2.PodsMetricSource{
  3847  								Metric: autoscalingv2.MetricIdentifier{
  3848  									Name: "some-pods-metric",
  3849  								},
  3850  								Target: autoscalingv2.MetricTarget{
  3851  									Type:         autoscalingv2.AverageValueMetricType,
  3852  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3853  								},
  3854  							},
  3855  						},
  3856  					},
  3857  				},
  3858  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3859  					CurrentReplicas: 4,
  3860  					DesiredReplicas: 5,
  3861  					CurrentMetrics: []autoscalingv2.MetricStatus{
  3862  						{
  3863  							Type: autoscalingv2.PodsMetricSourceType,
  3864  							Pods: &autoscalingv2.PodsMetricStatus{
  3865  								Metric: autoscalingv2.MetricIdentifier{
  3866  									Name: "some-pods-metric",
  3867  								},
  3868  								Current: autoscalingv2.MetricValueStatus{
  3869  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3870  								},
  3871  							},
  3872  						},
  3873  					},
  3874  				},
  3875  			},
  3876  		},
  3877  		{
  3878  			"object source type target average value (no current)",
  3879  			autoscalingv2.HorizontalPodAutoscaler{
  3880  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3881  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3882  						Name: "some-rc",
  3883  						Kind: "ReplicationController",
  3884  					},
  3885  					MinReplicas: &minReplicasVal,
  3886  					MaxReplicas: 10,
  3887  					Metrics: []autoscalingv2.MetricSpec{
  3888  						{
  3889  							Type: autoscalingv2.ObjectMetricSourceType,
  3890  							Object: &autoscalingv2.ObjectMetricSource{
  3891  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  3892  									Name: "some-service",
  3893  									Kind: "Service",
  3894  								},
  3895  								Metric: autoscalingv2.MetricIdentifier{
  3896  									Name: "some-service-metric",
  3897  								},
  3898  								Target: autoscalingv2.MetricTarget{
  3899  									Type:         autoscalingv2.AverageValueMetricType,
  3900  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3901  								},
  3902  							},
  3903  						},
  3904  					},
  3905  				},
  3906  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3907  					CurrentReplicas: 4,
  3908  					DesiredReplicas: 5,
  3909  				},
  3910  			},
  3911  		},
  3912  		{
  3913  			"object source type target average value (with current)",
  3914  			autoscalingv2.HorizontalPodAutoscaler{
  3915  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3916  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3917  						Name: "some-rc",
  3918  						Kind: "ReplicationController",
  3919  					},
  3920  					MinReplicas: &minReplicasVal,
  3921  					MaxReplicas: 10,
  3922  					Metrics: []autoscalingv2.MetricSpec{
  3923  						{
  3924  							Type: autoscalingv2.ObjectMetricSourceType,
  3925  							Object: &autoscalingv2.ObjectMetricSource{
  3926  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  3927  									Name: "some-service",
  3928  									Kind: "Service",
  3929  								},
  3930  								Metric: autoscalingv2.MetricIdentifier{
  3931  									Name: "some-service-metric",
  3932  								},
  3933  								Target: autoscalingv2.MetricTarget{
  3934  									Type:         autoscalingv2.AverageValueMetricType,
  3935  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3936  								},
  3937  							},
  3938  						},
  3939  					},
  3940  				},
  3941  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3942  					CurrentReplicas: 4,
  3943  					DesiredReplicas: 5,
  3944  					CurrentMetrics: []autoscalingv2.MetricStatus{
  3945  						{
  3946  							Type: autoscalingv2.ObjectMetricSourceType,
  3947  							Object: &autoscalingv2.ObjectMetricStatus{
  3948  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  3949  									Name: "some-service",
  3950  									Kind: "Service",
  3951  								},
  3952  								Metric: autoscalingv2.MetricIdentifier{
  3953  									Name: "some-service-metric",
  3954  								},
  3955  								Current: autoscalingv2.MetricValueStatus{
  3956  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3957  								},
  3958  							},
  3959  						},
  3960  					},
  3961  				},
  3962  			},
  3963  		},
  3964  		{
  3965  			"object source type target value (no current)",
  3966  			autoscalingv2.HorizontalPodAutoscaler{
  3967  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  3968  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  3969  						Name: "some-rc",
  3970  						Kind: "ReplicationController",
  3971  					},
  3972  					MinReplicas: &minReplicasVal,
  3973  					MaxReplicas: 10,
  3974  					Metrics: []autoscalingv2.MetricSpec{
  3975  						{
  3976  							Type: autoscalingv2.ObjectMetricSourceType,
  3977  							Object: &autoscalingv2.ObjectMetricSource{
  3978  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  3979  									Name: "some-service",
  3980  									Kind: "Service",
  3981  								},
  3982  								Metric: autoscalingv2.MetricIdentifier{
  3983  									Name: "some-service-metric",
  3984  								},
  3985  								Target: autoscalingv2.MetricTarget{
  3986  									Type:  autoscalingv2.ValueMetricType,
  3987  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3988  								},
  3989  							},
  3990  						},
  3991  					},
  3992  				},
  3993  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  3994  					CurrentReplicas: 4,
  3995  					DesiredReplicas: 5,
  3996  				},
  3997  			},
  3998  		},
  3999  		{
  4000  			"object source type target value (with current)",
  4001  			autoscalingv2.HorizontalPodAutoscaler{
  4002  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4003  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4004  						Name: "some-rc",
  4005  						Kind: "ReplicationController",
  4006  					},
  4007  					MinReplicas: &minReplicasVal,
  4008  					MaxReplicas: 10,
  4009  					Metrics: []autoscalingv2.MetricSpec{
  4010  						{
  4011  							Type: autoscalingv2.ObjectMetricSourceType,
  4012  							Object: &autoscalingv2.ObjectMetricSource{
  4013  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  4014  									Name: "some-service",
  4015  									Kind: "Service",
  4016  								},
  4017  								Metric: autoscalingv2.MetricIdentifier{
  4018  									Name: "some-service-metric",
  4019  								},
  4020  								Target: autoscalingv2.MetricTarget{
  4021  									Type:  autoscalingv2.ValueMetricType,
  4022  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  4023  								},
  4024  							},
  4025  						},
  4026  					},
  4027  				},
  4028  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4029  					CurrentReplicas: 4,
  4030  					DesiredReplicas: 5,
  4031  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4032  						{
  4033  							Type: autoscalingv2.ObjectMetricSourceType,
  4034  							Object: &autoscalingv2.ObjectMetricStatus{
  4035  								DescribedObject: autoscalingv2.CrossVersionObjectReference{
  4036  									Name: "some-service",
  4037  									Kind: "Service",
  4038  								},
  4039  								Metric: autoscalingv2.MetricIdentifier{
  4040  									Name: "some-service-metric",
  4041  								},
  4042  								Current: autoscalingv2.MetricValueStatus{
  4043  									Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  4044  								},
  4045  							},
  4046  						},
  4047  					},
  4048  				},
  4049  			},
  4050  		},
  4051  		{
  4052  			"resource source type, target average value (no current)",
  4053  			autoscalingv2.HorizontalPodAutoscaler{
  4054  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4055  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4056  						Name: "some-rc",
  4057  						Kind: "ReplicationController",
  4058  					},
  4059  					MinReplicas: &minReplicasVal,
  4060  					MaxReplicas: 10,
  4061  					Metrics: []autoscalingv2.MetricSpec{
  4062  						{
  4063  							Type: autoscalingv2.ResourceMetricSourceType,
  4064  							Resource: &autoscalingv2.ResourceMetricSource{
  4065  								Name: corev1.ResourceCPU,
  4066  								Target: autoscalingv2.MetricTarget{
  4067  									Type:         autoscalingv2.AverageValueMetricType,
  4068  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  4069  								},
  4070  							},
  4071  						},
  4072  					},
  4073  				},
  4074  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4075  					CurrentReplicas: 4,
  4076  					DesiredReplicas: 5,
  4077  				},
  4078  			},
  4079  		},
  4080  		{
  4081  			"resource source type, target average value (with current)",
  4082  			autoscalingv2.HorizontalPodAutoscaler{
  4083  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4084  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4085  						Name: "some-rc",
  4086  						Kind: "ReplicationController",
  4087  					},
  4088  					MinReplicas: &minReplicasVal,
  4089  					MaxReplicas: 10,
  4090  					Metrics: []autoscalingv2.MetricSpec{
  4091  						{
  4092  							Type: autoscalingv2.ResourceMetricSourceType,
  4093  							Resource: &autoscalingv2.ResourceMetricSource{
  4094  								Name: corev1.ResourceCPU,
  4095  								Target: autoscalingv2.MetricTarget{
  4096  									Type:         autoscalingv2.AverageValueMetricType,
  4097  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  4098  								},
  4099  							},
  4100  						},
  4101  					},
  4102  				},
  4103  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4104  					CurrentReplicas: 4,
  4105  					DesiredReplicas: 5,
  4106  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4107  						{
  4108  							Type: autoscalingv2.ResourceMetricSourceType,
  4109  							Resource: &autoscalingv2.ResourceMetricStatus{
  4110  								Name: corev1.ResourceCPU,
  4111  								Current: autoscalingv2.MetricValueStatus{
  4112  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  4113  								},
  4114  							},
  4115  						},
  4116  					},
  4117  				},
  4118  			},
  4119  		},
  4120  		{
  4121  			"resource source type, target utilization (no current)",
  4122  			autoscalingv2.HorizontalPodAutoscaler{
  4123  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4124  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4125  						Name: "some-rc",
  4126  						Kind: "ReplicationController",
  4127  					},
  4128  					MinReplicas: &minReplicasVal,
  4129  					MaxReplicas: 10,
  4130  					Metrics: []autoscalingv2.MetricSpec{
  4131  						{
  4132  							Type: autoscalingv2.ResourceMetricSourceType,
  4133  							Resource: &autoscalingv2.ResourceMetricSource{
  4134  								Name: corev1.ResourceCPU,
  4135  								Target: autoscalingv2.MetricTarget{
  4136  									Type:               autoscalingv2.UtilizationMetricType,
  4137  									AverageUtilization: &targetUtilizationVal,
  4138  								},
  4139  							},
  4140  						},
  4141  					},
  4142  				},
  4143  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4144  					CurrentReplicas: 4,
  4145  					DesiredReplicas: 5,
  4146  				},
  4147  			},
  4148  		},
  4149  		{
  4150  			"resource source type, target utilization (with current)",
  4151  			autoscalingv2.HorizontalPodAutoscaler{
  4152  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4153  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4154  						Name: "some-rc",
  4155  						Kind: "ReplicationController",
  4156  					},
  4157  					MinReplicas: &minReplicasVal,
  4158  					MaxReplicas: 10,
  4159  					Metrics: []autoscalingv2.MetricSpec{
  4160  						{
  4161  							Type: autoscalingv2.ResourceMetricSourceType,
  4162  							Resource: &autoscalingv2.ResourceMetricSource{
  4163  								Name: corev1.ResourceCPU,
  4164  								Target: autoscalingv2.MetricTarget{
  4165  									Type:               autoscalingv2.UtilizationMetricType,
  4166  									AverageUtilization: &targetUtilizationVal,
  4167  								},
  4168  							},
  4169  						},
  4170  					},
  4171  				},
  4172  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4173  					CurrentReplicas: 4,
  4174  					DesiredReplicas: 5,
  4175  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4176  						{
  4177  							Type: autoscalingv2.ResourceMetricSourceType,
  4178  							Resource: &autoscalingv2.ResourceMetricStatus{
  4179  								Name: corev1.ResourceCPU,
  4180  								Current: autoscalingv2.MetricValueStatus{
  4181  									AverageUtilization: &currentUtilizationVal,
  4182  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  4183  								},
  4184  							},
  4185  						},
  4186  					},
  4187  				},
  4188  			},
  4189  		},
  4190  		{
  4191  			"container resource source type, target average value (no current)",
  4192  			autoscalingv2.HorizontalPodAutoscaler{
  4193  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4194  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4195  						Name: "some-rc",
  4196  						Kind: "ReplicationController",
  4197  					},
  4198  					MinReplicas: &minReplicasVal,
  4199  					MaxReplicas: 10,
  4200  					Metrics: []autoscalingv2.MetricSpec{
  4201  						{
  4202  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4203  							ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
  4204  								Name:      corev1.ResourceCPU,
  4205  								Container: "application",
  4206  								Target: autoscalingv2.MetricTarget{
  4207  									Type:         autoscalingv2.AverageValueMetricType,
  4208  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  4209  								},
  4210  							},
  4211  						},
  4212  					},
  4213  				},
  4214  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4215  					CurrentReplicas: 4,
  4216  					DesiredReplicas: 5,
  4217  				},
  4218  			},
  4219  		},
  4220  		{
  4221  			"container resource source type, target average value (with current)",
  4222  			autoscalingv2.HorizontalPodAutoscaler{
  4223  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4224  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4225  						Name: "some-rc",
  4226  						Kind: "ReplicationController",
  4227  					},
  4228  					MinReplicas: &minReplicasVal,
  4229  					MaxReplicas: 10,
  4230  					Metrics: []autoscalingv2.MetricSpec{
  4231  						{
  4232  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4233  							ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
  4234  								Name:      corev1.ResourceCPU,
  4235  								Container: "application",
  4236  								Target: autoscalingv2.MetricTarget{
  4237  									Type:         autoscalingv2.AverageValueMetricType,
  4238  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  4239  								},
  4240  							},
  4241  						},
  4242  					},
  4243  				},
  4244  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4245  					CurrentReplicas: 4,
  4246  					DesiredReplicas: 5,
  4247  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4248  						{
  4249  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4250  							ContainerResource: &autoscalingv2.ContainerResourceMetricStatus{
  4251  								Name:      corev1.ResourceCPU,
  4252  								Container: "application",
  4253  								Current: autoscalingv2.MetricValueStatus{
  4254  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  4255  								},
  4256  							},
  4257  						},
  4258  					},
  4259  				},
  4260  			},
  4261  		},
  4262  		{
  4263  			"container resource source type, target utilization (no current)",
  4264  			autoscalingv2.HorizontalPodAutoscaler{
  4265  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4266  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4267  						Name: "some-rc",
  4268  						Kind: "ReplicationController",
  4269  					},
  4270  					MinReplicas: &minReplicasVal,
  4271  					MaxReplicas: 10,
  4272  					Metrics: []autoscalingv2.MetricSpec{
  4273  						{
  4274  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4275  							ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
  4276  								Name:      corev1.ResourceCPU,
  4277  								Container: "application",
  4278  								Target: autoscalingv2.MetricTarget{
  4279  									Type:               autoscalingv2.UtilizationMetricType,
  4280  									AverageUtilization: &targetUtilizationVal,
  4281  								},
  4282  							},
  4283  						},
  4284  					},
  4285  				},
  4286  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4287  					CurrentReplicas: 4,
  4288  					DesiredReplicas: 5,
  4289  				},
  4290  			},
  4291  		},
  4292  		{
  4293  			"container resource source type, target utilization (with current)",
  4294  			autoscalingv2.HorizontalPodAutoscaler{
  4295  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4296  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4297  						Name: "some-rc",
  4298  						Kind: "ReplicationController",
  4299  					},
  4300  					MinReplicas: &minReplicasVal,
  4301  					MaxReplicas: 10,
  4302  					Metrics: []autoscalingv2.MetricSpec{
  4303  						{
  4304  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4305  							ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
  4306  								Name:      corev1.ResourceCPU,
  4307  								Container: "application",
  4308  								Target: autoscalingv2.MetricTarget{
  4309  									Type:               autoscalingv2.UtilizationMetricType,
  4310  									AverageUtilization: &targetUtilizationVal,
  4311  								},
  4312  							},
  4313  						},
  4314  					},
  4315  				},
  4316  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4317  					CurrentReplicas: 4,
  4318  					DesiredReplicas: 5,
  4319  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4320  						{
  4321  							Type: autoscalingv2.ContainerResourceMetricSourceType,
  4322  							ContainerResource: &autoscalingv2.ContainerResourceMetricStatus{
  4323  								Name:      corev1.ResourceCPU,
  4324  								Container: "application",
  4325  								Current: autoscalingv2.MetricValueStatus{
  4326  									AverageUtilization: &currentUtilizationVal,
  4327  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  4328  								},
  4329  							},
  4330  						},
  4331  					},
  4332  				},
  4333  			},
  4334  		},
  4335  
  4336  		{
  4337  			"multiple metrics",
  4338  			autoscalingv2.HorizontalPodAutoscaler{
  4339  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4340  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4341  						Name: "some-rc",
  4342  						Kind: "ReplicationController",
  4343  					},
  4344  					MinReplicas: &minReplicasVal,
  4345  					MaxReplicas: 10,
  4346  					Metrics: []autoscalingv2.MetricSpec{
  4347  						{
  4348  							Type: autoscalingv2.PodsMetricSourceType,
  4349  							Pods: &autoscalingv2.PodsMetricSource{
  4350  								Metric: autoscalingv2.MetricIdentifier{
  4351  									Name: "some-pods-metric",
  4352  								},
  4353  								Target: autoscalingv2.MetricTarget{
  4354  									Type:         autoscalingv2.AverageValueMetricType,
  4355  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  4356  								},
  4357  							},
  4358  						},
  4359  						{
  4360  							Type: autoscalingv2.ResourceMetricSourceType,
  4361  							Resource: &autoscalingv2.ResourceMetricSource{
  4362  								Name: corev1.ResourceCPU,
  4363  								Target: autoscalingv2.MetricTarget{
  4364  									Type:               autoscalingv2.UtilizationMetricType,
  4365  									AverageUtilization: &targetUtilizationVal,
  4366  								},
  4367  							},
  4368  						},
  4369  						{
  4370  							Type: autoscalingv2.PodsMetricSourceType,
  4371  							Pods: &autoscalingv2.PodsMetricSource{
  4372  								Metric: autoscalingv2.MetricIdentifier{
  4373  									Name: "other-pods-metric",
  4374  								},
  4375  								Target: autoscalingv2.MetricTarget{
  4376  									Type:         autoscalingv2.AverageValueMetricType,
  4377  									AverageValue: resource.NewMilliQuantity(400, resource.DecimalSI),
  4378  								},
  4379  							},
  4380  						},
  4381  					},
  4382  				},
  4383  				Status: autoscalingv2.HorizontalPodAutoscalerStatus{
  4384  					CurrentReplicas: 4,
  4385  					DesiredReplicas: 5,
  4386  					CurrentMetrics: []autoscalingv2.MetricStatus{
  4387  						{
  4388  							Type: autoscalingv2.PodsMetricSourceType,
  4389  							Pods: &autoscalingv2.PodsMetricStatus{
  4390  								Metric: autoscalingv2.MetricIdentifier{
  4391  									Name: "some-pods-metric",
  4392  								},
  4393  								Current: autoscalingv2.MetricValueStatus{
  4394  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  4395  								},
  4396  							},
  4397  						},
  4398  						{
  4399  							Type: autoscalingv2.ResourceMetricSourceType,
  4400  							Resource: &autoscalingv2.ResourceMetricStatus{
  4401  								Name: corev1.ResourceCPU,
  4402  								Current: autoscalingv2.MetricValueStatus{
  4403  									AverageUtilization: &currentUtilizationVal,
  4404  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  4405  								},
  4406  							},
  4407  						},
  4408  					},
  4409  				},
  4410  			},
  4411  		},
  4412  		{
  4413  			"scale up behavior specified",
  4414  			autoscalingv2.HorizontalPodAutoscaler{
  4415  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4416  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4417  						Name: "behavior-target",
  4418  						Kind: "Deployment",
  4419  					},
  4420  					MinReplicas: &minReplicasVal,
  4421  					MaxReplicas: 10,
  4422  					Metrics: []autoscalingv2.MetricSpec{
  4423  						{
  4424  							Type: autoscalingv2.ResourceMetricSourceType,
  4425  							Resource: &autoscalingv2.ResourceMetricSource{
  4426  								Name: corev1.ResourceCPU,
  4427  								Target: autoscalingv2.MetricTarget{
  4428  									Type:               autoscalingv2.UtilizationMetricType,
  4429  									AverageUtilization: &targetUtilizationVal,
  4430  								},
  4431  							},
  4432  						},
  4433  					},
  4434  					Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{
  4435  						ScaleUp: &autoscalingv2.HPAScalingRules{
  4436  							StabilizationWindowSeconds: utilpointer.Int32Ptr(30),
  4437  							SelectPolicy:               &maxSelectPolicy,
  4438  							Policies: []autoscalingv2.HPAScalingPolicy{
  4439  								{Type: autoscalingv2.PodsScalingPolicy, Value: 10, PeriodSeconds: 10},
  4440  								{Type: autoscalingv2.PercentScalingPolicy, Value: 10, PeriodSeconds: 10},
  4441  							},
  4442  						},
  4443  					},
  4444  				},
  4445  			},
  4446  		},
  4447  		{
  4448  			"scale down behavior specified",
  4449  			autoscalingv2.HorizontalPodAutoscaler{
  4450  				Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
  4451  					ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
  4452  						Name: "behavior-target",
  4453  						Kind: "Deployment",
  4454  					},
  4455  					MinReplicas: &minReplicasVal,
  4456  					MaxReplicas: 10,
  4457  					Metrics: []autoscalingv2.MetricSpec{
  4458  						{
  4459  							Type: autoscalingv2.ResourceMetricSourceType,
  4460  							Resource: &autoscalingv2.ResourceMetricSource{
  4461  								Name: corev1.ResourceCPU,
  4462  								Target: autoscalingv2.MetricTarget{
  4463  									Type:               autoscalingv2.UtilizationMetricType,
  4464  									AverageUtilization: &targetUtilizationVal,
  4465  								},
  4466  							},
  4467  						},
  4468  					},
  4469  					Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{
  4470  						ScaleDown: &autoscalingv2.HPAScalingRules{
  4471  							StabilizationWindowSeconds: utilpointer.Int32Ptr(30),
  4472  							Policies: []autoscalingv2.HPAScalingPolicy{
  4473  								{Type: autoscalingv2.PodsScalingPolicy, Value: 10, PeriodSeconds: 10},
  4474  								{Type: autoscalingv2.PercentScalingPolicy, Value: 10, PeriodSeconds: 10},
  4475  							},
  4476  						},
  4477  					},
  4478  				},
  4479  			},
  4480  		},
  4481  	}
  4482  
  4483  	for _, test := range testsv2 {
  4484  		t.Run(test.name, func(t *testing.T) {
  4485  			test.hpa.ObjectMeta = metav1.ObjectMeta{
  4486  				Name:      "bar",
  4487  				Namespace: "foo",
  4488  			}
  4489  			fake := fake.NewSimpleClientset(&test.hpa)
  4490  			desc := HorizontalPodAutoscalerDescriber{fake}
  4491  			str, err := desc.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  4492  			if err != nil {
  4493  				t.Errorf("Unexpected error for test %s: %v", test.name, err)
  4494  			}
  4495  			if str == "" {
  4496  				t.Errorf("Unexpected empty string for test %s.  Expected HPA Describer output", test.name)
  4497  			}
  4498  			t.Logf("Description for %q:\n%s", test.name, str)
  4499  		})
  4500  	}
  4501  
  4502  	testsV1 := []struct {
  4503  		name string
  4504  		hpa  autoscalingv1.HorizontalPodAutoscaler
  4505  	}{
  4506  		{
  4507  			"minReplicas unset",
  4508  			autoscalingv1.HorizontalPodAutoscaler{
  4509  				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
  4510  					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
  4511  						Name: "some-rc",
  4512  						Kind: "ReplicationController",
  4513  					},
  4514  					MaxReplicas: 10,
  4515  				},
  4516  				Status: autoscalingv1.HorizontalPodAutoscalerStatus{
  4517  					CurrentReplicas: 4,
  4518  					DesiredReplicas: 5,
  4519  				},
  4520  			},
  4521  		},
  4522  		{
  4523  			"minReplicas set",
  4524  			autoscalingv1.HorizontalPodAutoscaler{
  4525  				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
  4526  					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
  4527  						Name: "some-rc",
  4528  						Kind: "ReplicationController",
  4529  					},
  4530  					MinReplicas: &minReplicasVal,
  4531  					MaxReplicas: 10,
  4532  				},
  4533  				Status: autoscalingv1.HorizontalPodAutoscalerStatus{
  4534  					CurrentReplicas: 4,
  4535  					DesiredReplicas: 5,
  4536  				},
  4537  			},
  4538  		},
  4539  		{
  4540  			"with target no current",
  4541  			autoscalingv1.HorizontalPodAutoscaler{
  4542  				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
  4543  					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
  4544  						Name: "some-rc",
  4545  						Kind: "ReplicationController",
  4546  					},
  4547  					MinReplicas:                    &minReplicasVal,
  4548  					MaxReplicas:                    10,
  4549  					TargetCPUUtilizationPercentage: &targetUtilizationVal,
  4550  				},
  4551  				Status: autoscalingv1.HorizontalPodAutoscalerStatus{
  4552  					CurrentReplicas: 4,
  4553  					DesiredReplicas: 5,
  4554  				},
  4555  			},
  4556  		},
  4557  		{
  4558  			"with target and current",
  4559  			autoscalingv1.HorizontalPodAutoscaler{
  4560  				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
  4561  					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
  4562  						Name: "some-rc",
  4563  						Kind: "ReplicationController",
  4564  					},
  4565  					MinReplicas:                    &minReplicasVal,
  4566  					MaxReplicas:                    10,
  4567  					TargetCPUUtilizationPercentage: &targetUtilizationVal,
  4568  				},
  4569  				Status: autoscalingv1.HorizontalPodAutoscalerStatus{
  4570  					CurrentReplicas:                 4,
  4571  					DesiredReplicas:                 5,
  4572  					CurrentCPUUtilizationPercentage: &currentUtilizationVal,
  4573  				},
  4574  			},
  4575  		},
  4576  	}
  4577  
  4578  	for _, test := range testsV1 {
  4579  		t.Run(test.name, func(t *testing.T) {
  4580  			test.hpa.ObjectMeta = metav1.ObjectMeta{
  4581  				Name:      "bar",
  4582  				Namespace: "foo",
  4583  			}
  4584  			fake := fake.NewSimpleClientset(&test.hpa)
  4585  			desc := HorizontalPodAutoscalerDescriber{fake}
  4586  			str, err := desc.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  4587  			if err != nil {
  4588  				t.Errorf("Unexpected error for test %s: %v", test.name, err)
  4589  			}
  4590  			if str == "" {
  4591  				t.Errorf("Unexpected empty string for test %s.  Expected HPA Describer output", test.name)
  4592  			}
  4593  			t.Logf("Description for %q:\n%s", test.name, str)
  4594  		})
  4595  	}
  4596  }
  4597  
  4598  func TestDescribeEvents(t *testing.T) {
  4599  
  4600  	events := &corev1.EventList{
  4601  		Items: []corev1.Event{
  4602  			{
  4603  				ObjectMeta: metav1.ObjectMeta{
  4604  					Name:      "event-1",
  4605  					Namespace: "foo",
  4606  				},
  4607  				Source:         corev1.EventSource{Component: "kubelet"},
  4608  				Message:        "Item 1",
  4609  				FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  4610  				LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  4611  				Count:          1,
  4612  				Type:           corev1.EventTypeNormal,
  4613  			},
  4614  			{
  4615  				ObjectMeta: metav1.ObjectMeta{
  4616  					Name:      "event-2",
  4617  					Namespace: "foo",
  4618  				},
  4619  				Source:    corev1.EventSource{Component: "kubelet"},
  4620  				Message:   "Item 1",
  4621  				EventTime: metav1.NewMicroTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  4622  				Series: &corev1.EventSeries{
  4623  					Count:            1,
  4624  					LastObservedTime: metav1.NewMicroTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  4625  				},
  4626  				Type: corev1.EventTypeNormal,
  4627  			},
  4628  		},
  4629  	}
  4630  
  4631  	m := map[string]ResourceDescriber{
  4632  		"DaemonSetDescriber": &DaemonSetDescriber{
  4633  			fake.NewSimpleClientset(&appsv1.DaemonSet{
  4634  				ObjectMeta: metav1.ObjectMeta{
  4635  					Name:      "bar",
  4636  					Namespace: "foo",
  4637  				},
  4638  			}, events),
  4639  		},
  4640  		"DeploymentDescriber": &DeploymentDescriber{
  4641  			fake.NewSimpleClientset(&appsv1.Deployment{
  4642  				ObjectMeta: metav1.ObjectMeta{
  4643  					Name:      "bar",
  4644  					Namespace: "foo",
  4645  				},
  4646  				Spec: appsv1.DeploymentSpec{
  4647  					Replicas: utilpointer.Int32Ptr(1),
  4648  					Selector: &metav1.LabelSelector{},
  4649  				},
  4650  			}, events),
  4651  		},
  4652  		"EndpointsDescriber": &EndpointsDescriber{
  4653  			fake.NewSimpleClientset(&corev1.Endpoints{
  4654  				ObjectMeta: metav1.ObjectMeta{
  4655  					Name:      "bar",
  4656  					Namespace: "foo",
  4657  				},
  4658  			}, events),
  4659  		},
  4660  		"EndpointSliceDescriber": &EndpointSliceDescriber{
  4661  			fake.NewSimpleClientset(&discoveryv1beta1.EndpointSlice{
  4662  				ObjectMeta: metav1.ObjectMeta{
  4663  					Name:      "bar",
  4664  					Namespace: "foo",
  4665  				},
  4666  			}, events),
  4667  		},
  4668  		"JobDescriber": &JobDescriber{
  4669  			fake.NewSimpleClientset(&batchv1.Job{
  4670  				ObjectMeta: metav1.ObjectMeta{
  4671  					Name:      "bar",
  4672  					Namespace: "foo",
  4673  				},
  4674  			}, events),
  4675  		},
  4676  		"IngressDescriber": &IngressDescriber{
  4677  			fake.NewSimpleClientset(&networkingv1beta1.Ingress{
  4678  				ObjectMeta: metav1.ObjectMeta{
  4679  					Name:      "bar",
  4680  					Namespace: "foo",
  4681  				},
  4682  			}, events),
  4683  		},
  4684  		"NodeDescriber": &NodeDescriber{
  4685  			fake.NewSimpleClientset(&corev1.Node{
  4686  				ObjectMeta: metav1.ObjectMeta{
  4687  					Name: "bar",
  4688  				},
  4689  			}, events),
  4690  		},
  4691  		"PersistentVolumeDescriber": &PersistentVolumeDescriber{
  4692  			fake.NewSimpleClientset(&corev1.PersistentVolume{
  4693  				ObjectMeta: metav1.ObjectMeta{
  4694  					Name: "bar",
  4695  				},
  4696  			}, events),
  4697  		},
  4698  		"PodDescriber": &PodDescriber{
  4699  			fake.NewSimpleClientset(&corev1.Pod{
  4700  				ObjectMeta: metav1.ObjectMeta{
  4701  					Name:      "bar",
  4702  					Namespace: "foo",
  4703  				},
  4704  			}, events),
  4705  		},
  4706  		"ReplicaSetDescriber": &ReplicaSetDescriber{
  4707  			fake.NewSimpleClientset(&appsv1.ReplicaSet{
  4708  				ObjectMeta: metav1.ObjectMeta{
  4709  					Name:      "bar",
  4710  					Namespace: "foo",
  4711  				},
  4712  				Spec: appsv1.ReplicaSetSpec{
  4713  					Replicas: utilpointer.Int32Ptr(1),
  4714  				},
  4715  			}, events),
  4716  		},
  4717  		"ReplicationControllerDescriber": &ReplicationControllerDescriber{
  4718  			fake.NewSimpleClientset(&corev1.ReplicationController{
  4719  				ObjectMeta: metav1.ObjectMeta{
  4720  					Name:      "bar",
  4721  					Namespace: "foo",
  4722  				},
  4723  				Spec: corev1.ReplicationControllerSpec{
  4724  					Replicas: utilpointer.Int32Ptr(1),
  4725  				},
  4726  			}, events),
  4727  		},
  4728  		"Service": &ServiceDescriber{
  4729  			fake.NewSimpleClientset(&corev1.Service{
  4730  				ObjectMeta: metav1.ObjectMeta{
  4731  					Name:      "bar",
  4732  					Namespace: "foo",
  4733  				},
  4734  			}, events),
  4735  		},
  4736  		"StorageClass": &StorageClassDescriber{
  4737  			fake.NewSimpleClientset(&storagev1.StorageClass{
  4738  				ObjectMeta: metav1.ObjectMeta{
  4739  					Name: "bar",
  4740  				},
  4741  			}, events),
  4742  		},
  4743  		"HorizontalPodAutoscaler": &HorizontalPodAutoscalerDescriber{
  4744  			fake.NewSimpleClientset(&autoscalingv2.HorizontalPodAutoscaler{
  4745  				ObjectMeta: metav1.ObjectMeta{
  4746  					Name:      "bar",
  4747  					Namespace: "foo",
  4748  				},
  4749  			}, events),
  4750  		},
  4751  		"ConfigMap": &ConfigMapDescriber{
  4752  			fake.NewSimpleClientset(&corev1.ConfigMap{
  4753  				ObjectMeta: metav1.ObjectMeta{
  4754  					Name:      "bar",
  4755  					Namespace: "foo",
  4756  				},
  4757  			}, events),
  4758  		},
  4759  	}
  4760  
  4761  	for name, d := range m {
  4762  		t.Run(name, func(t *testing.T) {
  4763  			out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  4764  			if err != nil {
  4765  				t.Errorf("unexpected error for %q: %v", name, err)
  4766  			}
  4767  			if !strings.Contains(out, "bar") {
  4768  				t.Errorf("unexpected out for %q: %s", name, out)
  4769  			}
  4770  			if !strings.Contains(out, "Events:") {
  4771  				t.Errorf("events not found for %q when ShowEvents=true: %s", name, out)
  4772  			}
  4773  
  4774  			out, err = d.Describe("foo", "bar", DescriberSettings{ShowEvents: false})
  4775  			if err != nil {
  4776  				t.Errorf("unexpected error for %q: %s", name, err)
  4777  			}
  4778  			if !strings.Contains(out, "bar") {
  4779  				t.Errorf("unexpected out for %q: %s", name, out)
  4780  			}
  4781  			if strings.Contains(out, "Events:") {
  4782  				t.Errorf("events found for %q when ShowEvents=false: %s", name, out)
  4783  			}
  4784  		})
  4785  	}
  4786  }
  4787  
  4788  func TestPrintLabelsMultiline(t *testing.T) {
  4789  	key := "MaxLenAnnotation"
  4790  	value := strings.Repeat("a", maxAnnotationLen-len(key)-2)
  4791  	testCases := []struct {
  4792  		annotations map[string]string
  4793  		expectPrint string
  4794  	}{
  4795  		{
  4796  			annotations: map[string]string{"col1": "asd", "COL2": "zxc"},
  4797  			expectPrint: "Annotations:\tCOL2: zxc\n\tcol1: asd\n",
  4798  		},
  4799  		{
  4800  			annotations: map[string]string{"MaxLenAnnotation": value},
  4801  			expectPrint: fmt.Sprintf("Annotations:\t%s: %s\n", key, value),
  4802  		},
  4803  		{
  4804  			annotations: map[string]string{"MaxLenAnnotation": value + "1"},
  4805  			expectPrint: fmt.Sprintf("Annotations:\t%s:\n\t  %s\n", key, value+"1"),
  4806  		},
  4807  		{
  4808  			annotations: map[string]string{"MaxLenAnnotation": value + value},
  4809  			expectPrint: fmt.Sprintf("Annotations:\t%s:\n\t  %s\n", key, strings.Repeat("a", maxAnnotationLen-2)+"..."),
  4810  		},
  4811  		{
  4812  			annotations: map[string]string{"key": "value\nwith\nnewlines\n"},
  4813  			expectPrint: "Annotations:\tkey:\n\t  value\n\t  with\n\t  newlines\n",
  4814  		},
  4815  		{
  4816  			annotations: map[string]string{},
  4817  			expectPrint: "Annotations:\t<none>\n",
  4818  		},
  4819  	}
  4820  	for i, testCase := range testCases {
  4821  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
  4822  			out := new(bytes.Buffer)
  4823  			writer := NewPrefixWriter(out)
  4824  			printAnnotationsMultiline(writer, "Annotations", testCase.annotations)
  4825  			output := out.String()
  4826  			if output != testCase.expectPrint {
  4827  				t.Errorf("Test case %d: expected to match:\n%q\nin output:\n%q", i, testCase.expectPrint, output)
  4828  			}
  4829  		})
  4830  	}
  4831  }
  4832  
  4833  func TestDescribeUnstructuredContent(t *testing.T) {
  4834  	testCases := []struct {
  4835  		expected   string
  4836  		unexpected string
  4837  	}{
  4838  		{
  4839  			expected: `API Version:	v1
  4840  Dummy - Dummy:	present
  4841  dummy-dummy@dummy:	present
  4842  dummy/dummy:	present
  4843  dummy2:	present
  4844  Dummy Dummy:	present
  4845  Items:
  4846    Item Bool:	true
  4847    Item Int:	42
  4848  Kind:	Test
  4849  Metadata:
  4850    Creation Timestamp:	2017-04-01T00:00:00Z
  4851    Name:	MyName
  4852    Namespace:	MyNamespace
  4853    Resource Version:	123
  4854    UID:	00000000-0000-0000-0000-000000000001
  4855  Status:	ok
  4856  URL:	http://localhost
  4857  `,
  4858  		},
  4859  		{
  4860  			unexpected: "\nDummy 1:\tpresent\n",
  4861  		},
  4862  		{
  4863  			unexpected: "Dummy 1",
  4864  		},
  4865  		{
  4866  			unexpected: "Dummy 3",
  4867  		},
  4868  		{
  4869  			unexpected: "Dummy3",
  4870  		},
  4871  		{
  4872  			unexpected: "dummy3",
  4873  		},
  4874  		{
  4875  			unexpected: "dummy 3",
  4876  		},
  4877  	}
  4878  	out := new(bytes.Buffer)
  4879  	w := NewPrefixWriter(out)
  4880  	obj := &unstructured.Unstructured{
  4881  		Object: map[string]interface{}{
  4882  			"apiVersion":        "v1",
  4883  			"kind":              "Test",
  4884  			"dummyDummy":        "present",
  4885  			"dummy/dummy":       "present",
  4886  			"dummy-dummy@dummy": "present",
  4887  			"dummy-dummy":       "present",
  4888  			"dummy1":            "present",
  4889  			"dummy2":            "present",
  4890  			"metadata": map[string]interface{}{
  4891  				"name":              "MyName",
  4892  				"namespace":         "MyNamespace",
  4893  				"creationTimestamp": "2017-04-01T00:00:00Z",
  4894  				"resourceVersion":   123,
  4895  				"uid":               "00000000-0000-0000-0000-000000000001",
  4896  				"dummy3":            "present",
  4897  			},
  4898  			"items": []interface{}{
  4899  				map[string]interface{}{
  4900  					"itemBool": true,
  4901  					"itemInt":  42,
  4902  				},
  4903  			},
  4904  			"url":    "http://localhost",
  4905  			"status": "ok",
  4906  		},
  4907  	}
  4908  	printUnstructuredContent(w, LEVEL_0, obj.UnstructuredContent(), "", ".dummy1", ".metadata.dummy3")
  4909  	output := out.String()
  4910  
  4911  	for _, test := range testCases {
  4912  		if len(test.expected) > 0 {
  4913  			if !strings.Contains(output, test.expected) {
  4914  				t.Errorf("Expected to find %q in: %q", test.expected, output)
  4915  			}
  4916  		}
  4917  		if len(test.unexpected) > 0 {
  4918  			if strings.Contains(output, test.unexpected) {
  4919  				t.Errorf("Didn't expect to find %q in: %q", test.unexpected, output)
  4920  			}
  4921  		}
  4922  	}
  4923  }
  4924  
  4925  func TestDescribeResourceQuota(t *testing.T) {
  4926  	fake := fake.NewSimpleClientset(&corev1.ResourceQuota{
  4927  		ObjectMeta: metav1.ObjectMeta{
  4928  			Name:      "bar",
  4929  			Namespace: "foo",
  4930  		},
  4931  		Status: corev1.ResourceQuotaStatus{
  4932  			Hard: corev1.ResourceList{
  4933  				corev1.ResourceName(corev1.ResourceCPU):            resource.MustParse("1"),
  4934  				corev1.ResourceName(corev1.ResourceLimitsCPU):      resource.MustParse("2"),
  4935  				corev1.ResourceName(corev1.ResourceLimitsMemory):   resource.MustParse("2G"),
  4936  				corev1.ResourceName(corev1.ResourceMemory):         resource.MustParse("1G"),
  4937  				corev1.ResourceName(corev1.ResourceRequestsCPU):    resource.MustParse("1"),
  4938  				corev1.ResourceName(corev1.ResourceRequestsMemory): resource.MustParse("1G"),
  4939  			},
  4940  			Used: corev1.ResourceList{
  4941  				corev1.ResourceName(corev1.ResourceCPU):            resource.MustParse("0"),
  4942  				corev1.ResourceName(corev1.ResourceLimitsCPU):      resource.MustParse("0"),
  4943  				corev1.ResourceName(corev1.ResourceLimitsMemory):   resource.MustParse("0G"),
  4944  				corev1.ResourceName(corev1.ResourceMemory):         resource.MustParse("0G"),
  4945  				corev1.ResourceName(corev1.ResourceRequestsCPU):    resource.MustParse("0"),
  4946  				corev1.ResourceName(corev1.ResourceRequestsMemory): resource.MustParse("1000Ki"),
  4947  			},
  4948  		},
  4949  	})
  4950  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
  4951  	d := ResourceQuotaDescriber{c}
  4952  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  4953  	if err != nil {
  4954  		t.Errorf("unexpected error: %v", err)
  4955  	}
  4956  	expectedOut := []string{"bar", "foo", "limits.cpu", "2", "limits.memory", "2G", "requests.cpu", "1", "requests.memory", "1024k", "1G"}
  4957  	for _, expected := range expectedOut {
  4958  		if !strings.Contains(out, expected) {
  4959  			t.Errorf("expected to find %q in output: %q", expected, out)
  4960  		}
  4961  	}
  4962  }
  4963  
  4964  func TestDescribeIngressClass(t *testing.T) {
  4965  	expectedOut := `Name:         example-class
  4966  Labels:       <none>
  4967  Annotations:  <none>
  4968  Controller:   example.com/controller
  4969  Parameters:
  4970    APIGroup:  v1
  4971    Kind:      ConfigMap
  4972    Name:      example-parameters` + "\n"
  4973  
  4974  	tests := map[string]struct {
  4975  		input  *fake.Clientset
  4976  		output string
  4977  	}{
  4978  		"basic IngressClass (v1beta1)": {
  4979  			input: fake.NewSimpleClientset(&networkingv1beta1.IngressClass{
  4980  				ObjectMeta: metav1.ObjectMeta{
  4981  					Name: "example-class",
  4982  				},
  4983  				Spec: networkingv1beta1.IngressClassSpec{
  4984  					Controller: "example.com/controller",
  4985  					Parameters: &networkingv1beta1.IngressClassParametersReference{
  4986  						APIGroup: utilpointer.StringPtr("v1"),
  4987  						Kind:     "ConfigMap",
  4988  						Name:     "example-parameters",
  4989  					},
  4990  				},
  4991  			}),
  4992  			output: expectedOut,
  4993  		},
  4994  		"basic IngressClass (v1)": {
  4995  			input: fake.NewSimpleClientset(&networkingv1.IngressClass{
  4996  				ObjectMeta: metav1.ObjectMeta{
  4997  					Name: "example-class",
  4998  				},
  4999  				Spec: networkingv1.IngressClassSpec{
  5000  					Controller: "example.com/controller",
  5001  					Parameters: &networkingv1.IngressClassParametersReference{
  5002  						APIGroup: utilpointer.StringPtr("v1"),
  5003  						Kind:     "ConfigMap",
  5004  						Name:     "example-parameters",
  5005  					},
  5006  				},
  5007  			}),
  5008  			output: expectedOut,
  5009  		},
  5010  	}
  5011  
  5012  	for name, test := range tests {
  5013  		t.Run(name, func(t *testing.T) {
  5014  			c := &describeClient{T: t, Namespace: "foo", Interface: test.input}
  5015  			d := IngressClassDescriber{c}
  5016  			out, err := d.Describe("", "example-class", DescriberSettings{})
  5017  			if err != nil {
  5018  				t.Errorf("unexpected error: %v", err)
  5019  			}
  5020  			if out != expectedOut {
  5021  				t.Logf(out)
  5022  				t.Errorf("expected : %q\n but got output:\n %q", test.output, out)
  5023  			}
  5024  		})
  5025  	}
  5026  }
  5027  
  5028  func TestDescribeNetworkPolicies(t *testing.T) {
  5029  	expectedTime, err := time.Parse("2006-01-02 15:04:05 Z0700 MST", "2017-06-04 21:45:56 -0700 PDT")
  5030  	if err != nil {
  5031  		t.Errorf("unable to parse time %q error: %s", "2017-06-04 21:45:56 -0700 PDT", err)
  5032  	}
  5033  	expectedOut := `Name:         network-policy-1
  5034  Namespace:    default
  5035  Created on:   2017-06-04 21:45:56 -0700 PDT
  5036  Labels:       <none>
  5037  Annotations:  <none>
  5038  Spec:
  5039    PodSelector:     foo in (bar1,bar2),foo2 notin (bar1,bar2),id1=app1,id2=app2
  5040    Allowing ingress traffic:
  5041      To Port: 80/TCP
  5042      To Port: 82/TCP
  5043      From:
  5044        NamespaceSelector: id=ns1,id2=ns2
  5045        PodSelector: id=pod1,id2=pod2
  5046      From:
  5047        PodSelector: id=app2,id2=app3
  5048      From:
  5049        NamespaceSelector: id=app2,id2=app3
  5050      From:
  5051        NamespaceSelector: foo in (bar1,bar2),id=app2,id2=app3
  5052      From:
  5053        IPBlock:
  5054          CIDR: 192.168.0.0/16
  5055          Except: 192.168.3.0/24, 192.168.4.0/24
  5056      ----------
  5057      To Port: <any> (traffic allowed to all ports)
  5058      From: <any> (traffic not restricted by source)
  5059    Allowing egress traffic:
  5060      To Port: 80/TCP
  5061      To Port: 82/TCP
  5062      To:
  5063        NamespaceSelector: id=ns1,id2=ns2
  5064        PodSelector: id=pod1,id2=pod2
  5065      To:
  5066        PodSelector: id=app2,id2=app3
  5067      To:
  5068        NamespaceSelector: id=app2,id2=app3
  5069      To:
  5070        NamespaceSelector: foo in (bar1,bar2),id=app2,id2=app3
  5071      To:
  5072        IPBlock:
  5073          CIDR: 192.168.0.0/16
  5074          Except: 192.168.3.0/24, 192.168.4.0/24
  5075      ----------
  5076      To Port: <any> (traffic allowed to all ports)
  5077      To: <any> (traffic not restricted by destination)
  5078    Policy Types: Ingress, Egress
  5079  `
  5080  
  5081  	port80 := intstr.FromInt32(80)
  5082  	port82 := intstr.FromInt32(82)
  5083  	protoTCP := corev1.ProtocolTCP
  5084  
  5085  	versionedFake := fake.NewSimpleClientset(&networkingv1.NetworkPolicy{
  5086  		ObjectMeta: metav1.ObjectMeta{
  5087  			Name:              "network-policy-1",
  5088  			Namespace:         "default",
  5089  			CreationTimestamp: metav1.NewTime(expectedTime),
  5090  		},
  5091  		Spec: networkingv1.NetworkPolicySpec{
  5092  			PodSelector: metav1.LabelSelector{
  5093  				MatchLabels: map[string]string{
  5094  					"id1": "app1",
  5095  					"id2": "app2",
  5096  				},
  5097  				MatchExpressions: []metav1.LabelSelectorRequirement{
  5098  					{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5099  					{Key: "foo2", Operator: "NotIn", Values: []string{"bar1", "bar2"}},
  5100  				},
  5101  			},
  5102  			Ingress: []networkingv1.NetworkPolicyIngressRule{
  5103  				{
  5104  					Ports: []networkingv1.NetworkPolicyPort{
  5105  						{Port: &port80},
  5106  						{Port: &port82, Protocol: &protoTCP},
  5107  					},
  5108  					From: []networkingv1.NetworkPolicyPeer{
  5109  						{
  5110  							PodSelector: &metav1.LabelSelector{
  5111  								MatchLabels: map[string]string{
  5112  									"id":  "pod1",
  5113  									"id2": "pod2",
  5114  								},
  5115  							},
  5116  							NamespaceSelector: &metav1.LabelSelector{
  5117  								MatchLabels: map[string]string{
  5118  									"id":  "ns1",
  5119  									"id2": "ns2",
  5120  								},
  5121  							},
  5122  						},
  5123  						{
  5124  							PodSelector: &metav1.LabelSelector{
  5125  								MatchLabels: map[string]string{
  5126  									"id":  "app2",
  5127  									"id2": "app3",
  5128  								},
  5129  							},
  5130  						},
  5131  						{
  5132  							NamespaceSelector: &metav1.LabelSelector{
  5133  								MatchLabels: map[string]string{
  5134  									"id":  "app2",
  5135  									"id2": "app3",
  5136  								},
  5137  							},
  5138  						},
  5139  						{
  5140  							NamespaceSelector: &metav1.LabelSelector{
  5141  								MatchLabels: map[string]string{
  5142  									"id":  "app2",
  5143  									"id2": "app3",
  5144  								},
  5145  								MatchExpressions: []metav1.LabelSelectorRequirement{
  5146  									{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5147  								},
  5148  							},
  5149  						},
  5150  						{
  5151  							IPBlock: &networkingv1.IPBlock{
  5152  								CIDR:   "192.168.0.0/16",
  5153  								Except: []string{"192.168.3.0/24", "192.168.4.0/24"},
  5154  							},
  5155  						},
  5156  					},
  5157  				},
  5158  				{},
  5159  			},
  5160  			Egress: []networkingv1.NetworkPolicyEgressRule{
  5161  				{
  5162  					Ports: []networkingv1.NetworkPolicyPort{
  5163  						{Port: &port80},
  5164  						{Port: &port82, Protocol: &protoTCP},
  5165  					},
  5166  					To: []networkingv1.NetworkPolicyPeer{
  5167  						{
  5168  							PodSelector: &metav1.LabelSelector{
  5169  								MatchLabels: map[string]string{
  5170  									"id":  "pod1",
  5171  									"id2": "pod2",
  5172  								},
  5173  							},
  5174  							NamespaceSelector: &metav1.LabelSelector{
  5175  								MatchLabels: map[string]string{
  5176  									"id":  "ns1",
  5177  									"id2": "ns2",
  5178  								},
  5179  							},
  5180  						},
  5181  						{
  5182  							PodSelector: &metav1.LabelSelector{
  5183  								MatchLabels: map[string]string{
  5184  									"id":  "app2",
  5185  									"id2": "app3",
  5186  								},
  5187  							},
  5188  						},
  5189  						{
  5190  							NamespaceSelector: &metav1.LabelSelector{
  5191  								MatchLabels: map[string]string{
  5192  									"id":  "app2",
  5193  									"id2": "app3",
  5194  								},
  5195  							},
  5196  						},
  5197  						{
  5198  							NamespaceSelector: &metav1.LabelSelector{
  5199  								MatchLabels: map[string]string{
  5200  									"id":  "app2",
  5201  									"id2": "app3",
  5202  								},
  5203  								MatchExpressions: []metav1.LabelSelectorRequirement{
  5204  									{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5205  								},
  5206  							},
  5207  						},
  5208  						{
  5209  							IPBlock: &networkingv1.IPBlock{
  5210  								CIDR:   "192.168.0.0/16",
  5211  								Except: []string{"192.168.3.0/24", "192.168.4.0/24"},
  5212  							},
  5213  						},
  5214  					},
  5215  				},
  5216  				{},
  5217  			},
  5218  			PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress},
  5219  		},
  5220  	})
  5221  	d := NetworkPolicyDescriber{versionedFake}
  5222  	out, err := d.Describe("default", "network-policy-1", DescriberSettings{})
  5223  	if err != nil {
  5224  		t.Errorf("unexpected error: %s", err)
  5225  	}
  5226  	if out != expectedOut {
  5227  		t.Errorf("want:\n%s\ngot:\n%s", expectedOut, out)
  5228  	}
  5229  }
  5230  
  5231  func TestDescribeIngressNetworkPolicies(t *testing.T) {
  5232  	expectedTime, err := time.Parse("2006-01-02 15:04:05 Z0700 MST", "2017-06-04 21:45:56 -0700 PDT")
  5233  	if err != nil {
  5234  		t.Errorf("unable to parse time %q error: %s", "2017-06-04 21:45:56 -0700 PDT", err)
  5235  	}
  5236  	expectedOut := `Name:         network-policy-1
  5237  Namespace:    default
  5238  Created on:   2017-06-04 21:45:56 -0700 PDT
  5239  Labels:       <none>
  5240  Annotations:  <none>
  5241  Spec:
  5242    PodSelector:     foo in (bar1,bar2),foo2 notin (bar1,bar2),id1=app1,id2=app2
  5243    Allowing ingress traffic:
  5244      To Port: 80/TCP
  5245      To Port: 82/TCP
  5246      From:
  5247        NamespaceSelector: id=ns1,id2=ns2
  5248        PodSelector: id=pod1,id2=pod2
  5249      From:
  5250        PodSelector: id=app2,id2=app3
  5251      From:
  5252        NamespaceSelector: id=app2,id2=app3
  5253      From:
  5254        NamespaceSelector: foo in (bar1,bar2),id=app2,id2=app3
  5255      From:
  5256        IPBlock:
  5257          CIDR: 192.168.0.0/16
  5258          Except: 192.168.3.0/24, 192.168.4.0/24
  5259      ----------
  5260      To Port: <any> (traffic allowed to all ports)
  5261      From: <any> (traffic not restricted by source)
  5262    Not affecting egress traffic
  5263    Policy Types: Ingress
  5264  `
  5265  
  5266  	port80 := intstr.FromInt32(80)
  5267  	port82 := intstr.FromInt32(82)
  5268  	protoTCP := corev1.ProtocolTCP
  5269  
  5270  	versionedFake := fake.NewSimpleClientset(&networkingv1.NetworkPolicy{
  5271  		ObjectMeta: metav1.ObjectMeta{
  5272  			Name:              "network-policy-1",
  5273  			Namespace:         "default",
  5274  			CreationTimestamp: metav1.NewTime(expectedTime),
  5275  		},
  5276  		Spec: networkingv1.NetworkPolicySpec{
  5277  			PodSelector: metav1.LabelSelector{
  5278  				MatchLabels: map[string]string{
  5279  					"id1": "app1",
  5280  					"id2": "app2",
  5281  				},
  5282  				MatchExpressions: []metav1.LabelSelectorRequirement{
  5283  					{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5284  					{Key: "foo2", Operator: "NotIn", Values: []string{"bar1", "bar2"}},
  5285  				},
  5286  			},
  5287  			Ingress: []networkingv1.NetworkPolicyIngressRule{
  5288  				{
  5289  					Ports: []networkingv1.NetworkPolicyPort{
  5290  						{Port: &port80},
  5291  						{Port: &port82, Protocol: &protoTCP},
  5292  					},
  5293  					From: []networkingv1.NetworkPolicyPeer{
  5294  						{
  5295  							PodSelector: &metav1.LabelSelector{
  5296  								MatchLabels: map[string]string{
  5297  									"id":  "pod1",
  5298  									"id2": "pod2",
  5299  								},
  5300  							},
  5301  							NamespaceSelector: &metav1.LabelSelector{
  5302  								MatchLabels: map[string]string{
  5303  									"id":  "ns1",
  5304  									"id2": "ns2",
  5305  								},
  5306  							},
  5307  						},
  5308  						{
  5309  							PodSelector: &metav1.LabelSelector{
  5310  								MatchLabels: map[string]string{
  5311  									"id":  "app2",
  5312  									"id2": "app3",
  5313  								},
  5314  							},
  5315  						},
  5316  						{
  5317  							NamespaceSelector: &metav1.LabelSelector{
  5318  								MatchLabels: map[string]string{
  5319  									"id":  "app2",
  5320  									"id2": "app3",
  5321  								},
  5322  							},
  5323  						},
  5324  						{
  5325  							NamespaceSelector: &metav1.LabelSelector{
  5326  								MatchLabels: map[string]string{
  5327  									"id":  "app2",
  5328  									"id2": "app3",
  5329  								},
  5330  								MatchExpressions: []metav1.LabelSelectorRequirement{
  5331  									{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5332  								},
  5333  							},
  5334  						},
  5335  						{
  5336  							IPBlock: &networkingv1.IPBlock{
  5337  								CIDR:   "192.168.0.0/16",
  5338  								Except: []string{"192.168.3.0/24", "192.168.4.0/24"},
  5339  							},
  5340  						},
  5341  					},
  5342  				},
  5343  				{},
  5344  			},
  5345  			PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
  5346  		},
  5347  	})
  5348  	d := NetworkPolicyDescriber{versionedFake}
  5349  	out, err := d.Describe("default", "network-policy-1", DescriberSettings{})
  5350  	if err != nil {
  5351  		t.Errorf("unexpected error: %s", err)
  5352  	}
  5353  	if out != expectedOut {
  5354  		t.Errorf("want:\n%s\ngot:\n%s", expectedOut, out)
  5355  	}
  5356  }
  5357  
  5358  func TestDescribeIsolatedEgressNetworkPolicies(t *testing.T) {
  5359  	expectedTime, err := time.Parse("2006-01-02 15:04:05 Z0700 MST", "2017-06-04 21:45:56 -0700 PDT")
  5360  	if err != nil {
  5361  		t.Errorf("unable to parse time %q error: %s", "2017-06-04 21:45:56 -0700 PDT", err)
  5362  	}
  5363  	expectedOut := `Name:         network-policy-1
  5364  Namespace:    default
  5365  Created on:   2017-06-04 21:45:56 -0700 PDT
  5366  Labels:       <none>
  5367  Annotations:  <none>
  5368  Spec:
  5369    PodSelector:     foo in (bar1,bar2),foo2 notin (bar1,bar2),id1=app1,id2=app2
  5370    Allowing ingress traffic:
  5371      To Port: 80/TCP
  5372      To Port: 82/TCP
  5373      From:
  5374        NamespaceSelector: id=ns1,id2=ns2
  5375        PodSelector: id=pod1,id2=pod2
  5376      From:
  5377        PodSelector: id=app2,id2=app3
  5378      From:
  5379        NamespaceSelector: id=app2,id2=app3
  5380      From:
  5381        NamespaceSelector: foo in (bar1,bar2),id=app2,id2=app3
  5382      From:
  5383        IPBlock:
  5384          CIDR: 192.168.0.0/16
  5385          Except: 192.168.3.0/24, 192.168.4.0/24
  5386      ----------
  5387      To Port: <any> (traffic allowed to all ports)
  5388      From: <any> (traffic not restricted by source)
  5389    Allowing egress traffic:
  5390      <none> (Selected pods are isolated for egress connectivity)
  5391    Policy Types: Ingress, Egress
  5392  `
  5393  
  5394  	port80 := intstr.FromInt32(80)
  5395  	port82 := intstr.FromInt32(82)
  5396  	protoTCP := corev1.ProtocolTCP
  5397  
  5398  	versionedFake := fake.NewSimpleClientset(&networkingv1.NetworkPolicy{
  5399  		ObjectMeta: metav1.ObjectMeta{
  5400  			Name:              "network-policy-1",
  5401  			Namespace:         "default",
  5402  			CreationTimestamp: metav1.NewTime(expectedTime),
  5403  		},
  5404  		Spec: networkingv1.NetworkPolicySpec{
  5405  			PodSelector: metav1.LabelSelector{
  5406  				MatchLabels: map[string]string{
  5407  					"id1": "app1",
  5408  					"id2": "app2",
  5409  				},
  5410  				MatchExpressions: []metav1.LabelSelectorRequirement{
  5411  					{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5412  					{Key: "foo2", Operator: "NotIn", Values: []string{"bar1", "bar2"}},
  5413  				},
  5414  			},
  5415  			Ingress: []networkingv1.NetworkPolicyIngressRule{
  5416  				{
  5417  					Ports: []networkingv1.NetworkPolicyPort{
  5418  						{Port: &port80},
  5419  						{Port: &port82, Protocol: &protoTCP},
  5420  					},
  5421  					From: []networkingv1.NetworkPolicyPeer{
  5422  						{
  5423  							PodSelector: &metav1.LabelSelector{
  5424  								MatchLabels: map[string]string{
  5425  									"id":  "pod1",
  5426  									"id2": "pod2",
  5427  								},
  5428  							},
  5429  							NamespaceSelector: &metav1.LabelSelector{
  5430  								MatchLabels: map[string]string{
  5431  									"id":  "ns1",
  5432  									"id2": "ns2",
  5433  								},
  5434  							},
  5435  						},
  5436  						{
  5437  							PodSelector: &metav1.LabelSelector{
  5438  								MatchLabels: map[string]string{
  5439  									"id":  "app2",
  5440  									"id2": "app3",
  5441  								},
  5442  							},
  5443  						},
  5444  						{
  5445  							NamespaceSelector: &metav1.LabelSelector{
  5446  								MatchLabels: map[string]string{
  5447  									"id":  "app2",
  5448  									"id2": "app3",
  5449  								},
  5450  							},
  5451  						},
  5452  						{
  5453  							NamespaceSelector: &metav1.LabelSelector{
  5454  								MatchLabels: map[string]string{
  5455  									"id":  "app2",
  5456  									"id2": "app3",
  5457  								},
  5458  								MatchExpressions: []metav1.LabelSelectorRequirement{
  5459  									{Key: "foo", Operator: "In", Values: []string{"bar1", "bar2"}},
  5460  								},
  5461  							},
  5462  						},
  5463  						{
  5464  							IPBlock: &networkingv1.IPBlock{
  5465  								CIDR:   "192.168.0.0/16",
  5466  								Except: []string{"192.168.3.0/24", "192.168.4.0/24"},
  5467  							},
  5468  						},
  5469  					},
  5470  				},
  5471  				{},
  5472  			},
  5473  			PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress},
  5474  		},
  5475  	})
  5476  	d := NetworkPolicyDescriber{versionedFake}
  5477  	out, err := d.Describe("default", "network-policy-1", DescriberSettings{})
  5478  	if err != nil {
  5479  		t.Errorf("unexpected error: %s", err)
  5480  	}
  5481  	if out != expectedOut {
  5482  		t.Errorf("want:\n%s\ngot:\n%s", expectedOut, out)
  5483  	}
  5484  }
  5485  
  5486  func TestDescribeServiceAccount(t *testing.T) {
  5487  	fake := fake.NewSimpleClientset(&corev1.ServiceAccount{
  5488  		ObjectMeta: metav1.ObjectMeta{
  5489  			Name:      "bar",
  5490  			Namespace: "foo",
  5491  		},
  5492  		Secrets: []corev1.ObjectReference{
  5493  			{
  5494  				Name: "test-objectref",
  5495  			},
  5496  		},
  5497  		ImagePullSecrets: []corev1.LocalObjectReference{
  5498  			{
  5499  				Name: "test-local-ref",
  5500  			},
  5501  		},
  5502  	})
  5503  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
  5504  	d := ServiceAccountDescriber{c}
  5505  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  5506  	if err != nil {
  5507  		t.Errorf("unexpected error: %v", err)
  5508  	}
  5509  	expectedOut := `Name:                bar
  5510  Namespace:           foo
  5511  Labels:              <none>
  5512  Annotations:         <none>
  5513  Image pull secrets:  test-local-ref (not found)
  5514  Mountable secrets:   test-objectref (not found)
  5515  Tokens:              <none>
  5516  Events:              <none>` + "\n"
  5517  	if out != expectedOut {
  5518  		t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)
  5519  	}
  5520  
  5521  }
  5522  func getHugePageResourceList(pageSize, value string) corev1.ResourceList {
  5523  	res := corev1.ResourceList{}
  5524  	if pageSize != "" && value != "" {
  5525  		res[corev1.ResourceName(corev1.ResourceHugePagesPrefix+pageSize)] = resource.MustParse(value)
  5526  	}
  5527  	return res
  5528  }
  5529  
  5530  // mergeResourceLists will merge resoure lists. When two lists have the same resourece, the value from
  5531  // the last list will be present in the result
  5532  func mergeResourceLists(resourceLists ...corev1.ResourceList) corev1.ResourceList {
  5533  	result := corev1.ResourceList{}
  5534  	for _, rl := range resourceLists {
  5535  		for resource, quantity := range rl {
  5536  			result[resource] = quantity
  5537  		}
  5538  	}
  5539  	return result
  5540  }
  5541  
  5542  func TestDescribeNode(t *testing.T) {
  5543  	holderIdentity := "holder"
  5544  	nodeCapacity := mergeResourceLists(
  5545  		getHugePageResourceList("2Mi", "4Gi"),
  5546  		getResourceList("8", "24Gi"),
  5547  		getHugePageResourceList("1Gi", "0"),
  5548  	)
  5549  	nodeAllocatable := mergeResourceLists(
  5550  		getHugePageResourceList("2Mi", "2Gi"),
  5551  		getResourceList("4", "12Gi"),
  5552  		getHugePageResourceList("1Gi", "0"),
  5553  	)
  5554  
  5555  	fake := fake.NewSimpleClientset(
  5556  		&corev1.Node{
  5557  			ObjectMeta: metav1.ObjectMeta{
  5558  				Name: "bar",
  5559  				UID:  "uid",
  5560  			},
  5561  			Spec: corev1.NodeSpec{
  5562  				Unschedulable: true,
  5563  			},
  5564  			Status: corev1.NodeStatus{
  5565  				Capacity:    nodeCapacity,
  5566  				Allocatable: nodeAllocatable,
  5567  			},
  5568  		},
  5569  		&coordinationv1.Lease{
  5570  			ObjectMeta: metav1.ObjectMeta{
  5571  				Name:      "bar",
  5572  				Namespace: corev1.NamespaceNodeLease,
  5573  			},
  5574  			Spec: coordinationv1.LeaseSpec{
  5575  				HolderIdentity: &holderIdentity,
  5576  				AcquireTime:    &metav1.MicroTime{Time: time.Now().Add(-time.Hour)},
  5577  				RenewTime:      &metav1.MicroTime{Time: time.Now()},
  5578  			},
  5579  		},
  5580  		&corev1.Pod{
  5581  			ObjectMeta: metav1.ObjectMeta{
  5582  				Name:      "pod-with-resources",
  5583  				Namespace: "foo",
  5584  			},
  5585  			TypeMeta: metav1.TypeMeta{
  5586  				Kind: "Pod",
  5587  			},
  5588  			Spec: corev1.PodSpec{
  5589  				Containers: []corev1.Container{
  5590  					{
  5591  						Name:  "cpu-mem",
  5592  						Image: "image:latest",
  5593  						Resources: corev1.ResourceRequirements{
  5594  							Requests: getResourceList("1", "1Gi"),
  5595  							Limits:   getResourceList("2", "2Gi"),
  5596  						},
  5597  					},
  5598  					{
  5599  						Name:  "hugepages",
  5600  						Image: "image:latest",
  5601  						Resources: corev1.ResourceRequirements{
  5602  							Requests: getHugePageResourceList("2Mi", "512Mi"),
  5603  							Limits:   getHugePageResourceList("2Mi", "512Mi"),
  5604  						},
  5605  					},
  5606  				},
  5607  			},
  5608  			Status: corev1.PodStatus{
  5609  				Phase: corev1.PodRunning,
  5610  			},
  5611  		},
  5612  		&corev1.EventList{
  5613  			Items: []corev1.Event{
  5614  				{
  5615  					ObjectMeta: metav1.ObjectMeta{
  5616  						Name:      "event-1",
  5617  						Namespace: "default",
  5618  					},
  5619  					InvolvedObject: corev1.ObjectReference{
  5620  						Kind: "Node",
  5621  						Name: "bar",
  5622  						UID:  "bar",
  5623  					},
  5624  					Message:        "Node bar status is now: NodeHasNoDiskPressure",
  5625  					FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5626  					LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5627  					Count:          1,
  5628  					Type:           corev1.EventTypeNormal,
  5629  				},
  5630  				{
  5631  					ObjectMeta: metav1.ObjectMeta{
  5632  						Name:      "event-2",
  5633  						Namespace: "default",
  5634  					},
  5635  					InvolvedObject: corev1.ObjectReference{
  5636  						Kind: "Node",
  5637  						Name: "bar",
  5638  						UID:  "0ceac5fb-a393-49d7-b04f-9ea5f18de5e9",
  5639  					},
  5640  					Message:        "Node bar status is now: NodeReady",
  5641  					FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5642  					LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5643  					Count:          2,
  5644  					Type:           corev1.EventTypeNormal,
  5645  				},
  5646  			},
  5647  		},
  5648  	)
  5649  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
  5650  	d := NodeDescriber{c}
  5651  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  5652  	if err != nil {
  5653  		t.Errorf("unexpected error: %v", err)
  5654  	}
  5655  
  5656  	expectedOut := []string{"Unschedulable", "true", "holder",
  5657  		`Allocated resources:
  5658    (Total limits may be over 100 percent, i.e., overcommitted.)
  5659    Resource           Requests     Limits
  5660    --------           --------     ------
  5661    cpu                1 (25%)      2 (50%)
  5662    memory             1Gi (8%)     2Gi (16%)
  5663    ephemeral-storage  0 (0%)       0 (0%)
  5664    hugepages-1Gi      0 (0%)       0 (0%)
  5665    hugepages-2Mi      512Mi (25%)  512Mi (25%)`,
  5666  		`Node bar status is now: NodeHasNoDiskPressure`,
  5667  		`Node bar status is now: NodeReady`}
  5668  	for _, expected := range expectedOut {
  5669  		if !strings.Contains(out, expected) {
  5670  			t.Errorf("expected to find %q in output: %q", expected, out)
  5671  		}
  5672  	}
  5673  }
  5674  
  5675  func TestDescribeNodeWithSidecar(t *testing.T) {
  5676  	holderIdentity := "holder"
  5677  	nodeCapacity := mergeResourceLists(
  5678  		getHugePageResourceList("2Mi", "4Gi"),
  5679  		getResourceList("8", "24Gi"),
  5680  		getHugePageResourceList("1Gi", "0"),
  5681  	)
  5682  	nodeAllocatable := mergeResourceLists(
  5683  		getHugePageResourceList("2Mi", "2Gi"),
  5684  		getResourceList("4", "12Gi"),
  5685  		getHugePageResourceList("1Gi", "0"),
  5686  	)
  5687  
  5688  	restartPolicy := corev1.ContainerRestartPolicyAlways
  5689  	fake := fake.NewSimpleClientset(
  5690  		&corev1.Node{
  5691  			ObjectMeta: metav1.ObjectMeta{
  5692  				Name: "bar",
  5693  				UID:  "uid",
  5694  			},
  5695  			Spec: corev1.NodeSpec{
  5696  				Unschedulable: true,
  5697  			},
  5698  			Status: corev1.NodeStatus{
  5699  				Capacity:    nodeCapacity,
  5700  				Allocatable: nodeAllocatable,
  5701  			},
  5702  		},
  5703  		&coordinationv1.Lease{
  5704  			ObjectMeta: metav1.ObjectMeta{
  5705  				Name:      "bar",
  5706  				Namespace: corev1.NamespaceNodeLease,
  5707  			},
  5708  			Spec: coordinationv1.LeaseSpec{
  5709  				HolderIdentity: &holderIdentity,
  5710  				AcquireTime:    &metav1.MicroTime{Time: time.Now().Add(-time.Hour)},
  5711  				RenewTime:      &metav1.MicroTime{Time: time.Now()},
  5712  			},
  5713  		},
  5714  		&corev1.Pod{
  5715  			ObjectMeta: metav1.ObjectMeta{
  5716  				Name:      "pod-with-resources",
  5717  				Namespace: "foo",
  5718  			},
  5719  			TypeMeta: metav1.TypeMeta{
  5720  				Kind: "Pod",
  5721  			},
  5722  			Spec: corev1.PodSpec{
  5723  				InitContainers: []corev1.Container{
  5724  					// sidecar, should sum into the total resources
  5725  					{
  5726  						Name:          "init-container-1",
  5727  						RestartPolicy: &restartPolicy,
  5728  						Resources: corev1.ResourceRequirements{
  5729  							Requests: getResourceList("1", "1Gi"),
  5730  						},
  5731  					},
  5732  					// non-sidecar
  5733  					{
  5734  						Name: "init-container-2",
  5735  						Resources: corev1.ResourceRequirements{
  5736  							Requests: getResourceList("1", "1Gi"),
  5737  						},
  5738  					},
  5739  				},
  5740  				Containers: []corev1.Container{
  5741  					{
  5742  						Name:  "cpu-mem",
  5743  						Image: "image:latest",
  5744  						Resources: corev1.ResourceRequirements{
  5745  							Requests: getResourceList("1", "1Gi"),
  5746  							Limits:   getResourceList("2", "2Gi"),
  5747  						},
  5748  					},
  5749  					{
  5750  						Name:  "hugepages",
  5751  						Image: "image:latest",
  5752  						Resources: corev1.ResourceRequirements{
  5753  							Requests: getHugePageResourceList("2Mi", "512Mi"),
  5754  							Limits:   getHugePageResourceList("2Mi", "512Mi"),
  5755  						},
  5756  					},
  5757  				},
  5758  			},
  5759  			Status: corev1.PodStatus{
  5760  				Phase: corev1.PodRunning,
  5761  			},
  5762  		},
  5763  		&corev1.EventList{
  5764  			Items: []corev1.Event{
  5765  				{
  5766  					ObjectMeta: metav1.ObjectMeta{
  5767  						Name:      "event-1",
  5768  						Namespace: "default",
  5769  					},
  5770  					InvolvedObject: corev1.ObjectReference{
  5771  						Kind: "Node",
  5772  						Name: "bar",
  5773  						UID:  "bar",
  5774  					},
  5775  					Message:        "Node bar status is now: NodeHasNoDiskPressure",
  5776  					FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5777  					LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5778  					Count:          1,
  5779  					Type:           corev1.EventTypeNormal,
  5780  				},
  5781  				{
  5782  					ObjectMeta: metav1.ObjectMeta{
  5783  						Name:      "event-2",
  5784  						Namespace: "default",
  5785  					},
  5786  					InvolvedObject: corev1.ObjectReference{
  5787  						Kind: "Node",
  5788  						Name: "bar",
  5789  						UID:  "0ceac5fb-a393-49d7-b04f-9ea5f18de5e9",
  5790  					},
  5791  					Message:        "Node bar status is now: NodeReady",
  5792  					FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5793  					LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
  5794  					Count:          2,
  5795  					Type:           corev1.EventTypeNormal,
  5796  				},
  5797  			},
  5798  		},
  5799  	)
  5800  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
  5801  	d := NodeDescriber{c}
  5802  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  5803  	if err != nil {
  5804  		t.Errorf("unexpected error: %v", err)
  5805  	}
  5806  
  5807  	expectedOut := []string{"Unschedulable", "true", "holder",
  5808  		`Allocated resources:
  5809    (Total limits may be over 100 percent, i.e., overcommitted.)
  5810    Resource           Requests     Limits
  5811    --------           --------     ------
  5812    cpu                2 (50%)      2 (50%)
  5813    memory             2Gi (16%)    2Gi (16%)
  5814    ephemeral-storage  0 (0%)       0 (0%)
  5815    hugepages-1Gi      0 (0%)       0 (0%)
  5816    hugepages-2Mi      512Mi (25%)  512Mi (25%)`,
  5817  		`Node bar status is now: NodeHasNoDiskPressure`,
  5818  		`Node bar status is now: NodeReady`}
  5819  	for _, expected := range expectedOut {
  5820  		if !strings.Contains(out, expected) {
  5821  			t.Errorf("expected to find %s in output: %s", expected, out)
  5822  		}
  5823  	}
  5824  }
  5825  func TestDescribeStatefulSet(t *testing.T) {
  5826  	var partition int32 = 2672
  5827  	var replicas int32 = 1
  5828  	fake := fake.NewSimpleClientset(&appsv1.StatefulSet{
  5829  		ObjectMeta: metav1.ObjectMeta{
  5830  			Name:      "bar",
  5831  			Namespace: "foo",
  5832  		},
  5833  		Spec: appsv1.StatefulSetSpec{
  5834  			Replicas: &replicas,
  5835  			Selector: &metav1.LabelSelector{},
  5836  			Template: corev1.PodTemplateSpec{
  5837  				Spec: corev1.PodSpec{
  5838  					Containers: []corev1.Container{
  5839  						{Image: "mytest-image:latest"},
  5840  					},
  5841  				},
  5842  			},
  5843  			UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
  5844  				Type: appsv1.RollingUpdateStatefulSetStrategyType,
  5845  				RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
  5846  					Partition: &partition,
  5847  				},
  5848  			},
  5849  		},
  5850  	})
  5851  	d := StatefulSetDescriber{fake}
  5852  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
  5853  	if err != nil {
  5854  		t.Errorf("unexpected error: %v", err)
  5855  	}
  5856  	expectedOutputs := []string{
  5857  		"bar", "foo", "Containers:", "mytest-image:latest", "Update Strategy", "RollingUpdate", "Partition", "2672",
  5858  	}
  5859  	for _, o := range expectedOutputs {
  5860  		if !strings.Contains(out, o) {
  5861  			t.Errorf("unexpected out: %s", out)
  5862  			break
  5863  		}
  5864  	}
  5865  }
  5866  
  5867  func TestDescribeEndpointSlice(t *testing.T) {
  5868  	protocolTCP := corev1.ProtocolTCP
  5869  	port80 := int32(80)
  5870  
  5871  	testcases := map[string]struct {
  5872  		input  *fake.Clientset
  5873  		output string
  5874  	}{
  5875  		"EndpointSlices v1beta1": {
  5876  			input: fake.NewSimpleClientset(&discoveryv1beta1.EndpointSlice{
  5877  				ObjectMeta: metav1.ObjectMeta{
  5878  					Name:      "foo.123",
  5879  					Namespace: "bar",
  5880  				},
  5881  				AddressType: discoveryv1beta1.AddressTypeIPv4,
  5882  				Endpoints: []discoveryv1beta1.Endpoint{
  5883  					{
  5884  						Addresses:  []string{"1.2.3.4", "1.2.3.5"},
  5885  						Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
  5886  						TargetRef:  &corev1.ObjectReference{Kind: "Pod", Name: "test-123"},
  5887  						Topology: map[string]string{
  5888  							"topology.kubernetes.io/zone":   "us-central1-a",
  5889  							"topology.kubernetes.io/region": "us-central1",
  5890  						},
  5891  					}, {
  5892  						Addresses:  []string{"1.2.3.6", "1.2.3.7"},
  5893  						Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
  5894  						TargetRef:  &corev1.ObjectReference{Kind: "Pod", Name: "test-124"},
  5895  						Topology: map[string]string{
  5896  							"topology.kubernetes.io/zone":   "us-central1-b",
  5897  							"topology.kubernetes.io/region": "us-central1",
  5898  						},
  5899  					},
  5900  				},
  5901  				Ports: []discoveryv1beta1.EndpointPort{
  5902  					{
  5903  						Protocol: &protocolTCP,
  5904  						Port:     &port80,
  5905  					},
  5906  				},
  5907  			}),
  5908  
  5909  			output: `Name:         foo.123
  5910  Namespace:    bar
  5911  Labels:       <none>
  5912  Annotations:  <none>
  5913  AddressType:  IPv4
  5914  Ports:
  5915    Name     Port  Protocol
  5916    ----     ----  --------
  5917    <unset>  80    TCP
  5918  Endpoints:
  5919    - Addresses:  1.2.3.4,1.2.3.5
  5920      Conditions:
  5921        Ready:    true
  5922      Hostname:   <unset>
  5923      TargetRef:  Pod/test-123
  5924      Topology:   topology.kubernetes.io/region=us-central1
  5925                  topology.kubernetes.io/zone=us-central1-a
  5926    - Addresses:  1.2.3.6,1.2.3.7
  5927      Conditions:
  5928        Ready:    true
  5929      Hostname:   <unset>
  5930      TargetRef:  Pod/test-124
  5931      Topology:   topology.kubernetes.io/region=us-central1
  5932                  topology.kubernetes.io/zone=us-central1-b
  5933  Events:         <none>` + "\n",
  5934  		},
  5935  		"EndpointSlices v1": {
  5936  			input: fake.NewSimpleClientset(&discoveryv1.EndpointSlice{
  5937  				ObjectMeta: metav1.ObjectMeta{
  5938  					Name:      "foo.123",
  5939  					Namespace: "bar",
  5940  				},
  5941  				AddressType: discoveryv1.AddressTypeIPv4,
  5942  				Endpoints: []discoveryv1.Endpoint{
  5943  					{
  5944  						Addresses:  []string{"1.2.3.4", "1.2.3.5"},
  5945  						Conditions: discoveryv1.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
  5946  						TargetRef:  &corev1.ObjectReference{Kind: "Pod", Name: "test-123"},
  5947  						Zone:       utilpointer.StringPtr("us-central1-a"),
  5948  						NodeName:   utilpointer.StringPtr("node-1"),
  5949  					}, {
  5950  						Addresses:  []string{"1.2.3.6", "1.2.3.7"},
  5951  						Conditions: discoveryv1.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
  5952  						TargetRef:  &corev1.ObjectReference{Kind: "Pod", Name: "test-124"},
  5953  						NodeName:   utilpointer.StringPtr("node-2"),
  5954  					},
  5955  				},
  5956  				Ports: []discoveryv1.EndpointPort{
  5957  					{
  5958  						Protocol: &protocolTCP,
  5959  						Port:     &port80,
  5960  					},
  5961  				},
  5962  			}),
  5963  
  5964  			output: `Name:         foo.123
  5965  Namespace:    bar
  5966  Labels:       <none>
  5967  Annotations:  <none>
  5968  AddressType:  IPv4
  5969  Ports:
  5970    Name     Port  Protocol
  5971    ----     ----  --------
  5972    <unset>  80    TCP
  5973  Endpoints:
  5974    - Addresses:  1.2.3.4, 1.2.3.5
  5975      Conditions:
  5976        Ready:    true
  5977      Hostname:   <unset>
  5978      TargetRef:  Pod/test-123
  5979      NodeName:   node-1
  5980      Zone:       us-central1-a
  5981    - Addresses:  1.2.3.6, 1.2.3.7
  5982      Conditions:
  5983        Ready:    true
  5984      Hostname:   <unset>
  5985      TargetRef:  Pod/test-124
  5986      NodeName:   node-2
  5987      Zone:       <unset>
  5988  Events:         <none>` + "\n",
  5989  		},
  5990  	}
  5991  
  5992  	for name, tc := range testcases {
  5993  		t.Run(name, func(t *testing.T) {
  5994  			c := &describeClient{T: t, Namespace: "foo", Interface: tc.input}
  5995  			d := EndpointSliceDescriber{c}
  5996  			out, err := d.Describe("bar", "foo.123", DescriberSettings{ShowEvents: true})
  5997  			if err != nil {
  5998  				t.Errorf("unexpected error: %v", err)
  5999  			}
  6000  			if out != tc.output {
  6001  				t.Logf(out)
  6002  				t.Errorf("expected :\n%s\nbut got output:\n%s", tc.output, out)
  6003  			}
  6004  		})
  6005  	}
  6006  }
  6007  
  6008  func TestDescribeServiceCIDR(t *testing.T) {
  6009  
  6010  	testcases := map[string]struct {
  6011  		input  *fake.Clientset
  6012  		output string
  6013  	}{
  6014  		"ServiceCIDR v1alpha1": {
  6015  			input: fake.NewSimpleClientset(&networkingv1alpha1.ServiceCIDR{
  6016  				ObjectMeta: metav1.ObjectMeta{
  6017  					Name: "foo.123",
  6018  				},
  6019  				Spec: networkingv1alpha1.ServiceCIDRSpec{
  6020  					CIDRs: []string{"10.1.0.0/16", "fd00:1:1::/64"},
  6021  				},
  6022  			}),
  6023  
  6024  			output: `Name:         foo.123
  6025  Labels:       <none>
  6026  Annotations:  <none>
  6027  CIDRs:        10.1.0.0/16, fd00:1:1::/64
  6028  Events:       <none>` + "\n",
  6029  		},
  6030  		"ServiceCIDR v1alpha1 IPv4": {
  6031  			input: fake.NewSimpleClientset(&networkingv1alpha1.ServiceCIDR{
  6032  				ObjectMeta: metav1.ObjectMeta{
  6033  					Name: "foo.123",
  6034  				},
  6035  				Spec: networkingv1alpha1.ServiceCIDRSpec{
  6036  					CIDRs: []string{"10.1.0.0/16"},
  6037  				},
  6038  			}),
  6039  
  6040  			output: `Name:         foo.123
  6041  Labels:       <none>
  6042  Annotations:  <none>
  6043  CIDRs:        10.1.0.0/16
  6044  Events:       <none>` + "\n",
  6045  		},
  6046  		"ServiceCIDR v1alpha1 IPv6": {
  6047  			input: fake.NewSimpleClientset(&networkingv1alpha1.ServiceCIDR{
  6048  				ObjectMeta: metav1.ObjectMeta{
  6049  					Name: "foo.123",
  6050  				},
  6051  				Spec: networkingv1alpha1.ServiceCIDRSpec{
  6052  					CIDRs: []string{"fd00:1:1::/64"},
  6053  				},
  6054  			}),
  6055  
  6056  			output: `Name:         foo.123
  6057  Labels:       <none>
  6058  Annotations:  <none>
  6059  CIDRs:        fd00:1:1::/64
  6060  Events:       <none>` + "\n",
  6061  		},
  6062  	}
  6063  
  6064  	for name, tc := range testcases {
  6065  		t.Run(name, func(t *testing.T) {
  6066  			c := &describeClient{T: t, Namespace: "foo", Interface: tc.input}
  6067  			d := ServiceCIDRDescriber{c}
  6068  			out, err := d.Describe("bar", "foo.123", DescriberSettings{ShowEvents: true})
  6069  			if err != nil {
  6070  				t.Errorf("unexpected error: %v", err)
  6071  			}
  6072  			if out != tc.output {
  6073  				t.Errorf("expected :\n%s\nbut got output:\n%s diff:\n%s", tc.output, out, cmp.Diff(tc.output, out))
  6074  			}
  6075  		})
  6076  	}
  6077  }
  6078  
  6079  func TestDescribeIPAddress(t *testing.T) {
  6080  
  6081  	testcases := map[string]struct {
  6082  		input  *fake.Clientset
  6083  		output string
  6084  	}{
  6085  		"IPAddress v1alpha1": {
  6086  			input: fake.NewSimpleClientset(&networkingv1alpha1.IPAddress{
  6087  				ObjectMeta: metav1.ObjectMeta{
  6088  					Name: "foo.123",
  6089  				},
  6090  				Spec: networkingv1alpha1.IPAddressSpec{
  6091  					ParentRef: &networkingv1alpha1.ParentReference{
  6092  						Group:     "mygroup",
  6093  						Resource:  "myresource",
  6094  						Namespace: "mynamespace",
  6095  						Name:      "myname",
  6096  					},
  6097  				},
  6098  			}),
  6099  
  6100  			output: `Name:         foo.123
  6101  Labels:       <none>
  6102  Annotations:  <none>
  6103  Parent Reference:
  6104    Group:      mygroup
  6105    Resource:   myresource
  6106    Namespace:  mynamespace
  6107    Name:       myname
  6108  Events:       <none>` + "\n",
  6109  		},
  6110  	}
  6111  
  6112  	for name, tc := range testcases {
  6113  		t.Run(name, func(t *testing.T) {
  6114  			c := &describeClient{T: t, Namespace: "foo", Interface: tc.input}
  6115  			d := IPAddressDescriber{c}
  6116  			out, err := d.Describe("bar", "foo.123", DescriberSettings{ShowEvents: true})
  6117  			if err != nil {
  6118  				t.Errorf("unexpected error: %v", err)
  6119  			}
  6120  			if out != tc.output {
  6121  				t.Errorf("expected :\n%s\nbut got output:\n%s diff:\n%s", tc.output, out, cmp.Diff(tc.output, out))
  6122  			}
  6123  		})
  6124  	}
  6125  }
  6126  
  6127  func TestControllerRef(t *testing.T) {
  6128  	var replicas int32 = 1
  6129  	f := fake.NewSimpleClientset(
  6130  		&corev1.ReplicationController{
  6131  			ObjectMeta: metav1.ObjectMeta{
  6132  				Name:      "bar",
  6133  				Namespace: "foo",
  6134  				UID:       "123456",
  6135  			},
  6136  			TypeMeta: metav1.TypeMeta{
  6137  				Kind: "ReplicationController",
  6138  			},
  6139  			Spec: corev1.ReplicationControllerSpec{
  6140  				Replicas: &replicas,
  6141  				Selector: map[string]string{"abc": "xyz"},
  6142  				Template: &corev1.PodTemplateSpec{
  6143  					Spec: corev1.PodSpec{
  6144  						Containers: []corev1.Container{
  6145  							{Image: "mytest-image:latest"},
  6146  						},
  6147  					},
  6148  				},
  6149  			},
  6150  		},
  6151  		&corev1.Pod{
  6152  			ObjectMeta: metav1.ObjectMeta{
  6153  				Name:            "barpod",
  6154  				Namespace:       "foo",
  6155  				Labels:          map[string]string{"abc": "xyz"},
  6156  				OwnerReferences: []metav1.OwnerReference{{Name: "bar", UID: "123456", Controller: utilpointer.BoolPtr(true)}},
  6157  			},
  6158  			TypeMeta: metav1.TypeMeta{
  6159  				Kind: "Pod",
  6160  			},
  6161  			Spec: corev1.PodSpec{
  6162  				Containers: []corev1.Container{
  6163  					{Image: "mytest-image:latest"},
  6164  				},
  6165  			},
  6166  			Status: corev1.PodStatus{
  6167  				Phase: corev1.PodRunning,
  6168  			},
  6169  		},
  6170  		&corev1.Pod{
  6171  			ObjectMeta: metav1.ObjectMeta{
  6172  				Name:      "orphan",
  6173  				Namespace: "foo",
  6174  				Labels:    map[string]string{"abc": "xyz"},
  6175  			},
  6176  			TypeMeta: metav1.TypeMeta{
  6177  				Kind: "Pod",
  6178  			},
  6179  			Spec: corev1.PodSpec{
  6180  				Containers: []corev1.Container{
  6181  					{Image: "mytest-image:latest"},
  6182  				},
  6183  			},
  6184  			Status: corev1.PodStatus{
  6185  				Phase: corev1.PodRunning,
  6186  			},
  6187  		},
  6188  		&corev1.Pod{
  6189  			ObjectMeta: metav1.ObjectMeta{
  6190  				Name:            "buzpod",
  6191  				Namespace:       "foo",
  6192  				Labels:          map[string]string{"abc": "xyz"},
  6193  				OwnerReferences: []metav1.OwnerReference{{Name: "buz", UID: "654321", Controller: utilpointer.BoolPtr(true)}},
  6194  			},
  6195  			TypeMeta: metav1.TypeMeta{
  6196  				Kind: "Pod",
  6197  			},
  6198  			Spec: corev1.PodSpec{
  6199  				Containers: []corev1.Container{
  6200  					{Image: "mytest-image:latest"},
  6201  				},
  6202  			},
  6203  			Status: corev1.PodStatus{
  6204  				Phase: corev1.PodRunning,
  6205  			},
  6206  		})
  6207  	d := ReplicationControllerDescriber{f}
  6208  	out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: false})
  6209  	if err != nil {
  6210  		t.Errorf("unexpected error: %v", err)
  6211  	}
  6212  	if !strings.Contains(out, "1 Running") {
  6213  		t.Errorf("unexpected out: %s", out)
  6214  	}
  6215  }
  6216  
  6217  func TestDescribeTerminalEscape(t *testing.T) {
  6218  	fake := fake.NewSimpleClientset(&corev1.ConfigMap{
  6219  		ObjectMeta: metav1.ObjectMeta{
  6220  			Name:        "mycm",
  6221  			Namespace:   "foo",
  6222  			Annotations: map[string]string{"annotation1": "terminal escape: \x1b"},
  6223  		},
  6224  	})
  6225  	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
  6226  	d := ConfigMapDescriber{c}
  6227  	out, err := d.Describe("foo", "mycm", DescriberSettings{ShowEvents: true})
  6228  	if err != nil {
  6229  		t.Errorf("unexpected error: %v", err)
  6230  	}
  6231  	if strings.Contains(out, "\x1b") || !strings.Contains(out, "^[") {
  6232  		t.Errorf("unexpected out: %s", out)
  6233  	}
  6234  }
  6235  
  6236  func TestDescribeSeccompProfile(t *testing.T) {
  6237  	testLocalhostProfiles := []string{"lauseafoodpod", "tikkamasalaconatiner", "dropshotephemeral"}
  6238  
  6239  	testCases := []struct {
  6240  		name   string
  6241  		pod    *corev1.Pod
  6242  		expect []string
  6243  	}{
  6244  		{
  6245  			name: "podLocalhostSeccomp",
  6246  			pod: &corev1.Pod{
  6247  				Spec: corev1.PodSpec{
  6248  					SecurityContext: &corev1.PodSecurityContext{
  6249  						SeccompProfile: &corev1.SeccompProfile{
  6250  							Type:             corev1.SeccompProfileTypeLocalhost,
  6251  							LocalhostProfile: &testLocalhostProfiles[0],
  6252  						},
  6253  					},
  6254  				},
  6255  			},
  6256  			expect: []string{
  6257  				"SeccompProfile", "Localhost",
  6258  				"LocalhostProfile", testLocalhostProfiles[0],
  6259  			},
  6260  		},
  6261  		{
  6262  			name: "podOther",
  6263  			pod: &corev1.Pod{
  6264  				Spec: corev1.PodSpec{
  6265  					SecurityContext: &corev1.PodSecurityContext{
  6266  						SeccompProfile: &corev1.SeccompProfile{
  6267  							Type: corev1.SeccompProfileTypeRuntimeDefault,
  6268  						},
  6269  					},
  6270  				},
  6271  			},
  6272  			expect: []string{
  6273  				"SeccompProfile", "RuntimeDefault",
  6274  			},
  6275  		},
  6276  		{
  6277  			name: "containerLocalhostSeccomp",
  6278  			pod: &corev1.Pod{
  6279  				Spec: corev1.PodSpec{
  6280  					Containers: []corev1.Container{
  6281  						{
  6282  							SecurityContext: &corev1.SecurityContext{
  6283  								SeccompProfile: &corev1.SeccompProfile{
  6284  									Type:             corev1.SeccompProfileTypeLocalhost,
  6285  									LocalhostProfile: &testLocalhostProfiles[1],
  6286  								},
  6287  							},
  6288  						},
  6289  					},
  6290  				},
  6291  			},
  6292  			expect: []string{
  6293  				"SeccompProfile", "Localhost",
  6294  				"LocalhostProfile", testLocalhostProfiles[1],
  6295  			},
  6296  		},
  6297  		{
  6298  			name: "containerOther",
  6299  			pod: &corev1.Pod{
  6300  				Spec: corev1.PodSpec{
  6301  					Containers: []corev1.Container{
  6302  						{
  6303  							SecurityContext: &corev1.SecurityContext{
  6304  								SeccompProfile: &corev1.SeccompProfile{
  6305  									Type: corev1.SeccompProfileTypeUnconfined,
  6306  								},
  6307  							},
  6308  						},
  6309  					},
  6310  				},
  6311  			},
  6312  			expect: []string{
  6313  				"SeccompProfile", "Unconfined",
  6314  			},
  6315  		},
  6316  		{
  6317  			name: "ephemeralLocalhostSeccomp",
  6318  			pod: &corev1.Pod{
  6319  				Spec: corev1.PodSpec{
  6320  					EphemeralContainers: []corev1.EphemeralContainer{
  6321  						{
  6322  							EphemeralContainerCommon: corev1.EphemeralContainerCommon{
  6323  								SecurityContext: &corev1.SecurityContext{
  6324  									SeccompProfile: &corev1.SeccompProfile{
  6325  										Type:             corev1.SeccompProfileTypeLocalhost,
  6326  										LocalhostProfile: &testLocalhostProfiles[2],
  6327  									},
  6328  								},
  6329  							},
  6330  						},
  6331  					},
  6332  				},
  6333  			},
  6334  			expect: []string{
  6335  				"SeccompProfile", "Localhost",
  6336  				"LocalhostProfile", testLocalhostProfiles[2],
  6337  			},
  6338  		},
  6339  		{
  6340  			name: "ephemeralOther",
  6341  			pod: &corev1.Pod{
  6342  				Spec: corev1.PodSpec{
  6343  					Containers: []corev1.Container{
  6344  						{
  6345  							SecurityContext: &corev1.SecurityContext{
  6346  								SeccompProfile: &corev1.SeccompProfile{
  6347  									Type: corev1.SeccompProfileTypeUnconfined,
  6348  								},
  6349  							},
  6350  						},
  6351  					},
  6352  				},
  6353  			},
  6354  			expect: []string{
  6355  				"SeccompProfile", "Unconfined",
  6356  			},
  6357  		},
  6358  	}
  6359  	for _, testCase := range testCases {
  6360  		t.Run(testCase.name, func(t *testing.T) {
  6361  			fake := fake.NewSimpleClientset(testCase.pod)
  6362  			c := &describeClient{T: t, Interface: fake}
  6363  			d := PodDescriber{c}
  6364  			out, err := d.Describe("", "", DescriberSettings{ShowEvents: true})
  6365  			if err != nil {
  6366  				t.Errorf("unexpected error: %v", err)
  6367  			}
  6368  			for _, expected := range testCase.expect {
  6369  				if !strings.Contains(out, expected) {
  6370  					t.Errorf("expected to find %q in output: %q", expected, out)
  6371  				}
  6372  			}
  6373  		})
  6374  	}
  6375  }
  6376  

View as plain text