k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/printers/internalversion/printers_test.go (about)

     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 internalversion
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"reflect"
    23  	"runtime"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  
    29  	apiv1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/resource"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/util/intstr"
    34  	"k8s.io/client-go/util/certificate/csr"
    35  	"k8s.io/kubernetes/pkg/apis/admissionregistration"
    36  	"k8s.io/kubernetes/pkg/apis/apiserverinternal"
    37  	"k8s.io/kubernetes/pkg/apis/apps"
    38  	"k8s.io/kubernetes/pkg/apis/autoscaling"
    39  	"k8s.io/kubernetes/pkg/apis/batch"
    40  	"k8s.io/kubernetes/pkg/apis/certificates"
    41  	"k8s.io/kubernetes/pkg/apis/coordination"
    42  	api "k8s.io/kubernetes/pkg/apis/core"
    43  	"k8s.io/kubernetes/pkg/apis/discovery"
    44  	"k8s.io/kubernetes/pkg/apis/flowcontrol"
    45  	"k8s.io/kubernetes/pkg/apis/networking"
    46  	nodeapi "k8s.io/kubernetes/pkg/apis/node"
    47  	"k8s.io/kubernetes/pkg/apis/policy"
    48  	"k8s.io/kubernetes/pkg/apis/rbac"
    49  	"k8s.io/kubernetes/pkg/apis/scheduling"
    50  	"k8s.io/kubernetes/pkg/apis/storage"
    51  	"k8s.io/kubernetes/pkg/apis/storagemigration"
    52  	"k8s.io/kubernetes/pkg/printers"
    53  	utilpointer "k8s.io/utils/pointer"
    54  	"k8s.io/utils/ptr"
    55  )
    56  
    57  var containerRestartPolicyAlways = api.ContainerRestartPolicyAlways
    58  
    59  func TestFormatResourceName(t *testing.T) {
    60  	tests := []struct {
    61  		kind schema.GroupKind
    62  		name string
    63  		want string
    64  	}{
    65  		{schema.GroupKind{}, "", ""},
    66  		{schema.GroupKind{}, "name", "name"},
    67  		{schema.GroupKind{Kind: "Kind"}, "", "kind/"}, // should not happen in practice
    68  		{schema.GroupKind{Kind: "Kind"}, "name", "kind/name"},
    69  		{schema.GroupKind{Group: "group", Kind: "Kind"}, "name", "kind.group/name"},
    70  	}
    71  	for _, tt := range tests {
    72  		if got := formatResourceName(tt.kind, tt.name, true); got != tt.want {
    73  			t.Errorf("formatResourceName(%q, %q) = %q, want %q", tt.kind, tt.name, got, tt.want)
    74  		}
    75  	}
    76  }
    77  
    78  type TestPrintHandler struct {
    79  	numCalls int
    80  }
    81  
    82  func (t *TestPrintHandler) TableHandler(columnDefinitions []metav1.TableColumnDefinition, printFunc interface{}) error {
    83  	t.numCalls++
    84  	return nil
    85  }
    86  
    87  func (t *TestPrintHandler) getNumCalls() int {
    88  	return t.numCalls
    89  }
    90  
    91  func TestAllHandlers(t *testing.T) {
    92  	h := &TestPrintHandler{numCalls: 0}
    93  	AddHandlers(h)
    94  	if h.getNumCalls() == 0 {
    95  		t.Error("TableHandler not called in AddHandlers")
    96  	}
    97  }
    98  
    99  func TestPrintEvent(t *testing.T) {
   100  	tests := []struct {
   101  		event    api.Event
   102  		options  printers.GenerateOptions
   103  		expected []metav1.TableRow
   104  	}{
   105  		// Basic event; no generate options
   106  		{
   107  			event: api.Event{
   108  				Source: api.EventSource{Component: "kubelet"},
   109  				InvolvedObject: api.ObjectReference{
   110  					Kind:      "Pod",
   111  					Name:      "Pod Name",
   112  					FieldPath: "spec.containers{foo}",
   113  				},
   114  				Reason:         "Event Reason",
   115  				Message:        "Message Data",
   116  				FirstTimestamp: metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -3)},
   117  				LastTimestamp:  metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)},
   118  				Count:          6,
   119  				Type:           api.EventTypeNormal,
   120  				ObjectMeta:     metav1.ObjectMeta{Name: "event1"},
   121  			},
   122  			options: printers.GenerateOptions{},
   123  			// Columns: Last Seen, Type, Reason, Object, Message
   124  			expected: []metav1.TableRow{{Cells: []interface{}{"2d", "Normal", "Event Reason", "pod/Pod Name", "Message Data"}}},
   125  		},
   126  		// Basic event; generate options=Wide
   127  		{
   128  			event: api.Event{
   129  				Source: api.EventSource{
   130  					Component: "kubelet",
   131  					Host:      "Node1",
   132  				},
   133  				InvolvedObject: api.ObjectReference{
   134  					Kind:      "Deployment",
   135  					Name:      "Deployment Name",
   136  					FieldPath: "spec.containers{foo}",
   137  				},
   138  				Reason:         "Event Reason",
   139  				Message:        "Message Data",
   140  				FirstTimestamp: metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -3)},
   141  				LastTimestamp:  metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)},
   142  				Count:          6,
   143  				Type:           api.EventTypeWarning,
   144  				ObjectMeta:     metav1.ObjectMeta{Name: "event2"},
   145  			},
   146  			options: printers.GenerateOptions{Wide: true},
   147  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   148  			expected: []metav1.TableRow{{Cells: []interface{}{"2d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(6), "event2"}}},
   149  		},
   150  		// Basic event, w/o FirstTimestamp set
   151  		{
   152  			event: api.Event{
   153  				Source: api.EventSource{
   154  					Component: "kubelet",
   155  					Host:      "Node1",
   156  				},
   157  				InvolvedObject: api.ObjectReference{
   158  					Kind:      "Deployment",
   159  					Name:      "Deployment Name",
   160  					FieldPath: "spec.containers{foo}",
   161  				},
   162  				Reason:        "Event Reason",
   163  				Message:       "Message Data",
   164  				EventTime:     metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -3)},
   165  				LastTimestamp: metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -3)},
   166  				Count:         1,
   167  				Type:          api.EventTypeWarning,
   168  				ObjectMeta:    metav1.ObjectMeta{Name: "event3"},
   169  			},
   170  			options: printers.GenerateOptions{Wide: true},
   171  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   172  			expected: []metav1.TableRow{{Cells: []interface{}{"3d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(1), "event3"}}},
   173  		},
   174  		// Basic event, w/o LastTimestamp set
   175  		{
   176  			event: api.Event{
   177  				Source: api.EventSource{
   178  					Component: "kubelet",
   179  					Host:      "Node1",
   180  				},
   181  				InvolvedObject: api.ObjectReference{
   182  					Kind:      "Deployment",
   183  					Name:      "Deployment Name",
   184  					FieldPath: "spec.containers{foo}",
   185  				},
   186  				Reason:         "Event Reason",
   187  				Message:        "Message Data",
   188  				EventTime:      metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -3)},
   189  				FirstTimestamp: metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -3)},
   190  				Count:          1,
   191  				Type:           api.EventTypeWarning,
   192  				ObjectMeta:     metav1.ObjectMeta{Name: "event4"},
   193  			},
   194  			options: printers.GenerateOptions{Wide: true},
   195  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   196  			expected: []metav1.TableRow{{Cells: []interface{}{"3d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(1), "event4"}}},
   197  		},
   198  		// Basic event, w/o FirstTimestamp and LastTimestamp set
   199  		{
   200  			event: api.Event{
   201  				Source: api.EventSource{
   202  					Component: "kubelet",
   203  					Host:      "Node1",
   204  				},
   205  				InvolvedObject: api.ObjectReference{
   206  					Kind:      "Deployment",
   207  					Name:      "Deployment Name",
   208  					FieldPath: "spec.containers{foo}",
   209  				},
   210  				Reason:     "Event Reason",
   211  				Message:    "Message Data",
   212  				EventTime:  metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -3)},
   213  				Count:      1,
   214  				Type:       api.EventTypeWarning,
   215  				ObjectMeta: metav1.ObjectMeta{Name: "event5"},
   216  			},
   217  			options: printers.GenerateOptions{Wide: true},
   218  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   219  			expected: []metav1.TableRow{{Cells: []interface{}{"3d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(1), "event5"}}},
   220  		},
   221  		// Basic event serie, w/o FirstTimestamp, LastTimestamp and Count set
   222  		{
   223  			event: api.Event{
   224  				Source: api.EventSource{
   225  					Component: "kubelet",
   226  					Host:      "Node1",
   227  				},
   228  				InvolvedObject: api.ObjectReference{
   229  					Kind:      "Deployment",
   230  					Name:      "Deployment Name",
   231  					FieldPath: "spec.containers{foo}",
   232  				},
   233  				Series: &api.EventSeries{
   234  					Count:            2,
   235  					LastObservedTime: metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -2)},
   236  				},
   237  				Reason:     "Event Reason",
   238  				Message:    "Message Data",
   239  				EventTime:  metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -3)},
   240  				Type:       api.EventTypeWarning,
   241  				ObjectMeta: metav1.ObjectMeta{Name: "event6"},
   242  			},
   243  			options: printers.GenerateOptions{Wide: true},
   244  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   245  			expected: []metav1.TableRow{{Cells: []interface{}{"2d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(2), "event6"}}},
   246  		},
   247  		// Singleton event, w/o FirstTimestamp, LastTimestamp and Count set
   248  		{
   249  			event: api.Event{
   250  				Source: api.EventSource{
   251  					Component: "kubelet",
   252  					Host:      "Node1",
   253  				},
   254  				InvolvedObject: api.ObjectReference{
   255  					Kind:      "Deployment",
   256  					Name:      "Deployment Name",
   257  					FieldPath: "spec.containers{foo}",
   258  				},
   259  				Reason:     "Event Reason",
   260  				Message:    "Message Data",
   261  				EventTime:  metav1.MicroTime{Time: time.Now().UTC().AddDate(0, 0, -3)},
   262  				Type:       api.EventTypeWarning,
   263  				ObjectMeta: metav1.ObjectMeta{Name: "event7"},
   264  			},
   265  			options: printers.GenerateOptions{Wide: true},
   266  			// Columns: Last Seen, Type, Reason, Object, Subobject, Message, First Seen, Count, Name
   267  			expected: []metav1.TableRow{{Cells: []interface{}{"3d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, Node1", "Message Data", "3d", int64(1), "event7"}}},
   268  		},
   269  		// Basic event, with empty Source; generate options=Wide
   270  		{
   271  			event: api.Event{
   272  				ReportingController: "kubelet",
   273  				ReportingInstance:   "test",
   274  				InvolvedObject: api.ObjectReference{
   275  					Kind:      "Deployment",
   276  					Name:      "Deployment Name",
   277  					FieldPath: "spec.containers{foo}",
   278  				},
   279  				Reason:         "Event Reason",
   280  				Message:        "Message Data",
   281  				FirstTimestamp: metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -3)},
   282  				LastTimestamp:  metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)},
   283  				Count:          6,
   284  				Type:           api.EventTypeWarning,
   285  				ObjectMeta:     metav1.ObjectMeta{Name: "event2"},
   286  			},
   287  			options: printers.GenerateOptions{Wide: true},
   288  			// Columns: Last Seen, Type, Reason, Object, Subobject, Source, Message, First Seen, Count, Name
   289  			expected: []metav1.TableRow{{Cells: []interface{}{"2d", "Warning", "Event Reason", "deployment/Deployment Name", "spec.containers{foo}", "kubelet, test", "Message Data", "3d", int64(6), "event2"}}},
   290  		},
   291  	}
   292  
   293  	for i, test := range tests {
   294  		rows, err := printEvent(&test.event, test.options)
   295  		if err != nil {
   296  			t.Fatal(err)
   297  		}
   298  		for i := range rows {
   299  			rows[i].Object.Object = nil
   300  		}
   301  		if !reflect.DeepEqual(test.expected, rows) {
   302  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   303  		}
   304  	}
   305  }
   306  
   307  func TestPrintEventsResultSorted(t *testing.T) {
   308  
   309  	eventList := api.EventList{
   310  		Items: []api.Event{
   311  			{
   312  				Source:         api.EventSource{Component: "kubelet"},
   313  				Message:        "Item 1",
   314  				FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
   315  				LastTimestamp:  metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
   316  				Count:          1,
   317  				Type:           api.EventTypeNormal,
   318  			},
   319  			{
   320  				Source:         api.EventSource{Component: "scheduler"},
   321  				Message:        "Item 2",
   322  				FirstTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
   323  				LastTimestamp:  metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
   324  				Count:          1,
   325  				Type:           api.EventTypeNormal,
   326  			},
   327  			{
   328  				Source:         api.EventSource{Component: "kubelet"},
   329  				Message:        "Item 3",
   330  				FirstTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
   331  				LastTimestamp:  metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
   332  				Count:          1,
   333  				Type:           api.EventTypeNormal,
   334  			},
   335  		},
   336  	}
   337  
   338  	rows, err := printEventList(&eventList, printers.GenerateOptions{})
   339  	if err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	if len(rows) != 3 {
   343  		t.Errorf("Generate Event List: Wrong number of table rows returned. Expected 3, got (%d)", len(rows))
   344  	}
   345  	// Verify the watch event dates are in order.
   346  	firstRow := rows[0]
   347  	message1 := firstRow.Cells[4]
   348  	if message1.(string) != "Item 1" {
   349  		t.Errorf("Wrong event ordering: expecting (Item 1), got (%s)", message1)
   350  	}
   351  	secondRow := rows[1]
   352  	message2 := secondRow.Cells[4]
   353  	if message2 != "Item 2" {
   354  		t.Errorf("Wrong event ordering: expecting (Item 2), got (%s)", message2)
   355  	}
   356  	thirdRow := rows[2]
   357  	message3 := thirdRow.Cells[4]
   358  	if message3 != "Item 3" {
   359  		t.Errorf("Wrong event ordering: expecting (Item 3), got (%s)", message3)
   360  	}
   361  }
   362  
   363  func TestPrintNamespace(t *testing.T) {
   364  	tests := []struct {
   365  		namespace api.Namespace
   366  		expected  []metav1.TableRow
   367  	}{
   368  		// Basic namespace with status and age.
   369  		{
   370  			namespace: api.Namespace{
   371  				ObjectMeta: metav1.ObjectMeta{
   372  					Name:              "namespace1",
   373  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
   374  				},
   375  				Status: api.NamespaceStatus{
   376  					Phase: "FooStatus",
   377  				},
   378  			},
   379  			// Columns: Name, Status, Age
   380  			expected: []metav1.TableRow{{Cells: []interface{}{"namespace1", "FooStatus", "0s"}}},
   381  		},
   382  		// Basic namespace without status or age.
   383  		{
   384  			namespace: api.Namespace{
   385  				ObjectMeta: metav1.ObjectMeta{
   386  					Name: "namespace2",
   387  				},
   388  			},
   389  			// Columns: Name, Status, Age
   390  			expected: []metav1.TableRow{{Cells: []interface{}{"namespace2", "", "<unknown>"}}},
   391  		},
   392  	}
   393  
   394  	for i, test := range tests {
   395  		rows, err := printNamespace(&test.namespace, printers.GenerateOptions{})
   396  		if err != nil {
   397  			t.Fatal(err)
   398  		}
   399  		for i := range rows {
   400  			rows[i].Object.Object = nil
   401  		}
   402  		if !reflect.DeepEqual(test.expected, rows) {
   403  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   404  		}
   405  	}
   406  }
   407  
   408  func TestPrintSecret(t *testing.T) {
   409  	tests := []struct {
   410  		secret   api.Secret
   411  		expected []metav1.TableRow
   412  	}{
   413  		// Basic namespace with type, data, and age.
   414  		{
   415  			secret: api.Secret{
   416  				ObjectMeta: metav1.ObjectMeta{
   417  					Name:              "secret1",
   418  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
   419  				},
   420  				Type: "kubernetes.io/service-account-token",
   421  				Data: map[string][]byte{
   422  					"token": []byte("secret data"),
   423  				},
   424  			},
   425  			// Columns: Name, Type, Data, Age
   426  			expected: []metav1.TableRow{{Cells: []interface{}{"secret1", "kubernetes.io/service-account-token", int64(1), "0s"}}},
   427  		},
   428  		// Basic namespace with type and age; no data.
   429  		{
   430  			secret: api.Secret{
   431  				ObjectMeta: metav1.ObjectMeta{
   432  					Name:              "secret1",
   433  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
   434  				},
   435  				Type: "kubernetes.io/service-account-token",
   436  			},
   437  			// Columns: Name, Type, Data, Age
   438  			expected: []metav1.TableRow{{Cells: []interface{}{"secret1", "kubernetes.io/service-account-token", int64(0), "0s"}}},
   439  		},
   440  	}
   441  
   442  	for i, test := range tests {
   443  		rows, err := printSecret(&test.secret, printers.GenerateOptions{})
   444  		if err != nil {
   445  			t.Fatal(err)
   446  		}
   447  		for i := range rows {
   448  			rows[i].Object.Object = nil
   449  		}
   450  		if !reflect.DeepEqual(test.expected, rows) {
   451  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   452  		}
   453  	}
   454  }
   455  
   456  func TestPrintServiceAccount(t *testing.T) {
   457  	tests := []struct {
   458  		serviceAccount api.ServiceAccount
   459  		expected       []metav1.TableRow
   460  	}{
   461  		// Basic service account without secrets
   462  		{
   463  			serviceAccount: api.ServiceAccount{
   464  				ObjectMeta: metav1.ObjectMeta{
   465  					Name:              "sa1",
   466  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
   467  				},
   468  				Secrets: []api.ObjectReference{},
   469  			},
   470  			// Columns: Name, (Num) Secrets, Age
   471  			expected: []metav1.TableRow{{Cells: []interface{}{"sa1", int64(0), "0s"}}},
   472  		},
   473  		// Basic service account with two secrets.
   474  		{
   475  			serviceAccount: api.ServiceAccount{
   476  				ObjectMeta: metav1.ObjectMeta{
   477  					Name:              "sa1",
   478  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
   479  				},
   480  				Secrets: []api.ObjectReference{
   481  					{Name: "Secret1"},
   482  					{Name: "Secret2"},
   483  				},
   484  			},
   485  			// Columns: Name, (Num) Secrets, Age
   486  			expected: []metav1.TableRow{{Cells: []interface{}{"sa1", int64(2), "0s"}}},
   487  		},
   488  	}
   489  
   490  	for i, test := range tests {
   491  		rows, err := printServiceAccount(&test.serviceAccount, printers.GenerateOptions{})
   492  		if err != nil {
   493  			t.Fatal(err)
   494  		}
   495  		for i := range rows {
   496  			rows[i].Object.Object = nil
   497  		}
   498  		if !reflect.DeepEqual(test.expected, rows) {
   499  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   500  		}
   501  	}
   502  }
   503  
   504  func TestPrintNodeStatus(t *testing.T) {
   505  
   506  	table := []struct {
   507  		node     api.Node
   508  		expected []metav1.TableRow
   509  	}{
   510  		{
   511  			node: api.Node{
   512  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   513  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}},
   514  			},
   515  			// Columns: Name, Status, Roles, Age, KubeletVersion
   516  			expected: []metav1.TableRow{{Cells: []interface{}{"foo1", "Ready", "<none>", "<unknown>", ""}}},
   517  		},
   518  		{
   519  			node: api.Node{
   520  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   521  				Spec:       api.NodeSpec{Unschedulable: true},
   522  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}},
   523  			},
   524  			// Columns: Name, Status, Roles, Age, KubeletVersion
   525  			expected: []metav1.TableRow{{Cells: []interface{}{"foo2", "Ready,SchedulingDisabled", "<none>", "<unknown>", ""}}},
   526  		},
   527  		{
   528  			node: api.Node{
   529  				ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
   530  				Status: api.NodeStatus{Conditions: []api.NodeCondition{
   531  					{Type: api.NodeReady, Status: api.ConditionTrue},
   532  					{Type: api.NodeReady, Status: api.ConditionTrue}}},
   533  			},
   534  			// Columns: Name, Status, Roles, Age, KubeletVersion
   535  			expected: []metav1.TableRow{{Cells: []interface{}{"foo3", "Ready", "<none>", "<unknown>", ""}}},
   536  		},
   537  		{
   538  			node: api.Node{
   539  				ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
   540  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}},
   541  			},
   542  			// Columns: Name, Status, Roles, Age, KubeletVersion
   543  			expected: []metav1.TableRow{{Cells: []interface{}{"foo4", "NotReady", "<none>", "<unknown>", ""}}},
   544  		},
   545  		{
   546  			node: api.Node{
   547  				ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
   548  				Spec:       api.NodeSpec{Unschedulable: true},
   549  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}},
   550  			},
   551  			// Columns: Name, Status, Roles, Age, KubeletVersion
   552  			expected: []metav1.TableRow{{Cells: []interface{}{"foo5", "NotReady,SchedulingDisabled", "<none>", "<unknown>", ""}}},
   553  		},
   554  		{
   555  			node: api.Node{
   556  				ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
   557  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}},
   558  			},
   559  			// Columns: Name, Status, Roles, Age, KubeletVersion
   560  			expected: []metav1.TableRow{{Cells: []interface{}{"foo6", "Unknown", "<none>", "<unknown>", ""}}},
   561  		},
   562  		{
   563  			node: api.Node{
   564  				ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
   565  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{}}},
   566  			},
   567  			// Columns: Name, Status, Roles, Age, KubeletVersion
   568  			expected: []metav1.TableRow{{Cells: []interface{}{"foo7", "Unknown", "<none>", "<unknown>", ""}}},
   569  		},
   570  		{
   571  			node: api.Node{
   572  				ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
   573  				Spec:       api.NodeSpec{Unschedulable: true},
   574  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}},
   575  			},
   576  			// Columns: Name, Status, Roles, Age, KubeletVersion
   577  			expected: []metav1.TableRow{{Cells: []interface{}{"foo8", "Unknown,SchedulingDisabled", "<none>", "<unknown>", ""}}},
   578  		},
   579  		{
   580  			node: api.Node{
   581  				ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
   582  				Spec:       api.NodeSpec{Unschedulable: true},
   583  				Status:     api.NodeStatus{Conditions: []api.NodeCondition{{}}},
   584  			},
   585  			// Columns: Name, Status, Roles, Age, KubeletVersion
   586  			expected: []metav1.TableRow{{Cells: []interface{}{"foo9", "Unknown,SchedulingDisabled", "<none>", "<unknown>", ""}}},
   587  		},
   588  	}
   589  
   590  	for i, test := range table {
   591  		rows, err := printNode(&test.node, printers.GenerateOptions{})
   592  		if err != nil {
   593  			t.Fatalf("Error generating table rows for Node: %#v", err)
   594  		}
   595  		for i := range rows {
   596  			rows[i].Object.Object = nil
   597  		}
   598  		if !reflect.DeepEqual(test.expected, rows) {
   599  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   600  		}
   601  	}
   602  }
   603  
   604  func TestPrintNodeRole(t *testing.T) {
   605  
   606  	table := []struct {
   607  		node     api.Node
   608  		expected []metav1.TableRow
   609  	}{
   610  		{
   611  			node: api.Node{
   612  				ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
   613  			},
   614  			// Columns: Name, Status, Roles, Age, KubeletVersion
   615  			expected: []metav1.TableRow{{Cells: []interface{}{"foo9", "Unknown", "<none>", "<unknown>", ""}}},
   616  		},
   617  		{
   618  			node: api.Node{
   619  				ObjectMeta: metav1.ObjectMeta{
   620  					Name:   "foo10",
   621  					Labels: map[string]string{"node-role.kubernetes.io/master": "", "node-role.kubernetes.io/control-plane": "", "node-role.kubernetes.io/proxy": "", "kubernetes.io/role": "node"},
   622  				},
   623  			},
   624  			// Columns: Name, Status, Roles, Age, KubeletVersion
   625  			expected: []metav1.TableRow{{Cells: []interface{}{"foo10", "Unknown", "control-plane,master,node,proxy", "<unknown>", ""}}},
   626  		},
   627  		{
   628  			node: api.Node{
   629  				ObjectMeta: metav1.ObjectMeta{
   630  					Name:   "foo11",
   631  					Labels: map[string]string{"kubernetes.io/role": "node"},
   632  				},
   633  			},
   634  			// Columns: Name, Status, Roles, Age, KubeletVersion
   635  			expected: []metav1.TableRow{{Cells: []interface{}{"foo11", "Unknown", "node", "<unknown>", ""}}},
   636  		},
   637  	}
   638  
   639  	for i, test := range table {
   640  		rows, err := printNode(&test.node, printers.GenerateOptions{})
   641  		if err != nil {
   642  			t.Fatalf("An error occurred generating table rows for Node: %#v", err)
   643  		}
   644  		for i := range rows {
   645  			rows[i].Object.Object = nil
   646  		}
   647  		if !reflect.DeepEqual(test.expected, rows) {
   648  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   649  		}
   650  	}
   651  }
   652  
   653  func TestPrintNodeOSImage(t *testing.T) {
   654  
   655  	table := []struct {
   656  		node     api.Node
   657  		expected []metav1.TableRow
   658  	}{
   659  		{
   660  			node: api.Node{
   661  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   662  				Status: api.NodeStatus{
   663  					NodeInfo:  api.NodeSystemInfo{OSImage: "fake-os-image"},
   664  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   665  				},
   666  			},
   667  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   668  			expected: []metav1.TableRow{
   669  				{
   670  					Cells: []interface{}{"foo1", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "fake-os-image", "<unknown>", "<unknown>"},
   671  				},
   672  			},
   673  		},
   674  		{
   675  			node: api.Node{
   676  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   677  				Status: api.NodeStatus{
   678  					NodeInfo:  api.NodeSystemInfo{KernelVersion: "fake-kernel-version"},
   679  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   680  				},
   681  			},
   682  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   683  			expected: []metav1.TableRow{
   684  				{
   685  					Cells: []interface{}{"foo2", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "fake-kernel-version", "<unknown>"},
   686  				},
   687  			},
   688  		},
   689  	}
   690  
   691  	for i, test := range table {
   692  		rows, err := printNode(&test.node, printers.GenerateOptions{Wide: true})
   693  		if err != nil {
   694  			t.Fatalf("An error occurred generating table for Node: %#v", err)
   695  		}
   696  		for i := range rows {
   697  			rows[i].Object.Object = nil
   698  		}
   699  		if !reflect.DeepEqual(test.expected, rows) {
   700  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   701  		}
   702  	}
   703  }
   704  
   705  func TestPrintNodeKernelVersion(t *testing.T) {
   706  
   707  	table := []struct {
   708  		node     api.Node
   709  		expected []metav1.TableRow
   710  	}{
   711  		{
   712  			node: api.Node{
   713  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   714  				Status: api.NodeStatus{
   715  					NodeInfo:  api.NodeSystemInfo{KernelVersion: "fake-kernel-version"},
   716  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   717  				},
   718  			},
   719  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   720  			expected: []metav1.TableRow{
   721  				{
   722  					Cells: []interface{}{"foo1", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "fake-kernel-version", "<unknown>"},
   723  				},
   724  			},
   725  		},
   726  		{
   727  			node: api.Node{
   728  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   729  				Status: api.NodeStatus{
   730  					NodeInfo:  api.NodeSystemInfo{OSImage: "fake-os-image"},
   731  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   732  				},
   733  			},
   734  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   735  			expected: []metav1.TableRow{
   736  				{
   737  					Cells: []interface{}{"foo2", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "fake-os-image", "<unknown>", "<unknown>"},
   738  				},
   739  			},
   740  		},
   741  	}
   742  
   743  	for i, test := range table {
   744  		rows, err := printNode(&test.node, printers.GenerateOptions{Wide: true})
   745  		if err != nil {
   746  			t.Fatalf("An error occurred generating table rows Node: %#v", err)
   747  		}
   748  		for i := range rows {
   749  			rows[i].Object.Object = nil
   750  		}
   751  		if !reflect.DeepEqual(test.expected, rows) {
   752  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   753  		}
   754  	}
   755  }
   756  
   757  func TestPrintNodeContainerRuntimeVersion(t *testing.T) {
   758  
   759  	table := []struct {
   760  		node     api.Node
   761  		expected []metav1.TableRow
   762  	}{
   763  		{
   764  			node: api.Node{
   765  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   766  				Status: api.NodeStatus{
   767  					NodeInfo:  api.NodeSystemInfo{ContainerRuntimeVersion: "foo://1.2.3"},
   768  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   769  				},
   770  			},
   771  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   772  			expected: []metav1.TableRow{
   773  				{
   774  					Cells: []interface{}{"foo1", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "<unknown>", "foo://1.2.3"},
   775  				},
   776  			},
   777  		},
   778  		{
   779  			node: api.Node{
   780  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   781  				Status: api.NodeStatus{
   782  					NodeInfo:  api.NodeSystemInfo{},
   783  					Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}},
   784  				},
   785  			},
   786  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   787  			expected: []metav1.TableRow{
   788  				{
   789  					Cells: []interface{}{"foo2", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "<unknown>", "<unknown>"},
   790  				},
   791  			},
   792  		},
   793  	}
   794  
   795  	for i, test := range table {
   796  		rows, err := printNode(&test.node, printers.GenerateOptions{Wide: true})
   797  		if err != nil {
   798  			t.Fatalf("An error occurred generating table rows Node: %#v", err)
   799  		}
   800  		for i := range rows {
   801  			rows[i].Object.Object = nil
   802  		}
   803  		if !reflect.DeepEqual(test.expected, rows) {
   804  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   805  		}
   806  	}
   807  }
   808  
   809  func TestPrintNodeName(t *testing.T) {
   810  
   811  	table := []struct {
   812  		node     api.Node
   813  		expected []metav1.TableRow
   814  	}{
   815  		{
   816  			node: api.Node{
   817  				ObjectMeta: metav1.ObjectMeta{Name: "127.0.0.1"},
   818  				Status:     api.NodeStatus{},
   819  			},
   820  			// Columns: Name, Status, Roles, Age, KubeletVersion
   821  			expected: []metav1.TableRow{{Cells: []interface{}{"127.0.0.1", "Unknown", "<none>", "<unknown>", ""}}}},
   822  		{
   823  			node: api.Node{
   824  				ObjectMeta: metav1.ObjectMeta{Name: ""},
   825  				Status:     api.NodeStatus{},
   826  			},
   827  			// Columns: Name, Status, Roles, Age, KubeletVersion
   828  			expected: []metav1.TableRow{{Cells: []interface{}{"", "Unknown", "<none>", "<unknown>", ""}}},
   829  		},
   830  	}
   831  
   832  	for i, test := range table {
   833  		rows, err := printNode(&test.node, printers.GenerateOptions{})
   834  		if err != nil {
   835  			t.Fatalf("An error occurred generating table rows Node: %#v", err)
   836  		}
   837  		for i := range rows {
   838  			rows[i].Object.Object = nil
   839  		}
   840  		if !reflect.DeepEqual(test.expected, rows) {
   841  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   842  		}
   843  	}
   844  }
   845  
   846  func TestPrintNodeExternalIP(t *testing.T) {
   847  
   848  	table := []struct {
   849  		node     api.Node
   850  		expected []metav1.TableRow
   851  	}{
   852  		{
   853  			node: api.Node{
   854  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   855  				Status:     api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}}},
   856  			},
   857  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   858  			expected: []metav1.TableRow{
   859  				{
   860  					Cells: []interface{}{"foo1", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "<unknown>", "<unknown>"},
   861  				},
   862  			},
   863  		},
   864  		{
   865  			node: api.Node{
   866  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   867  				Status:     api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeInternalIP, Address: "1.1.1.1"}}},
   868  			},
   869  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   870  			expected: []metav1.TableRow{
   871  				{
   872  					Cells: []interface{}{"foo2", "Unknown", "<none>", "<unknown>", "", "1.1.1.1", "<none>", "<unknown>", "<unknown>", "<unknown>"},
   873  				},
   874  			},
   875  		},
   876  		{
   877  			node: api.Node{
   878  				ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
   879  				Status: api.NodeStatus{Addresses: []api.NodeAddress{
   880  					{Type: api.NodeExternalIP, Address: "2.2.2.2"},
   881  					{Type: api.NodeInternalIP, Address: "3.3.3.3"},
   882  					{Type: api.NodeExternalIP, Address: "4.4.4.4"},
   883  				}},
   884  			},
   885  			expected: []metav1.TableRow{
   886  				{
   887  					Cells: []interface{}{"foo3", "Unknown", "<none>", "<unknown>", "", "3.3.3.3", "2.2.2.2", "<unknown>", "<unknown>", "<unknown>"},
   888  				},
   889  			},
   890  		},
   891  	}
   892  
   893  	for i, test := range table {
   894  		rows, err := printNode(&test.node, printers.GenerateOptions{Wide: true})
   895  		if err != nil {
   896  			t.Fatalf("An error occurred generating table rows Node: %#v", err)
   897  		}
   898  		for i := range rows {
   899  			rows[i].Object.Object = nil
   900  		}
   901  		if !reflect.DeepEqual(test.expected, rows) {
   902  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   903  		}
   904  	}
   905  }
   906  
   907  func TestPrintNodeInternalIP(t *testing.T) {
   908  
   909  	table := []struct {
   910  		node     api.Node
   911  		expected []metav1.TableRow
   912  	}{
   913  		{
   914  			node: api.Node{
   915  				ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   916  				Status:     api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeInternalIP, Address: "1.1.1.1"}}},
   917  			},
   918  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   919  			expected: []metav1.TableRow{
   920  				{
   921  					Cells: []interface{}{"foo1", "Unknown", "<none>", "<unknown>", "", "1.1.1.1", "<none>", "<unknown>", "<unknown>", "<unknown>"},
   922  				},
   923  			},
   924  		},
   925  		{
   926  			node: api.Node{
   927  				ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   928  				Status:     api.NodeStatus{Addresses: []api.NodeAddress{{Type: api.NodeExternalIP, Address: "1.1.1.1"}}},
   929  			},
   930  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   931  			expected: []metav1.TableRow{
   932  				{
   933  					Cells: []interface{}{"foo2", "Unknown", "<none>", "<unknown>", "", "<none>", "1.1.1.1", "<unknown>", "<unknown>", "<unknown>"},
   934  				},
   935  			},
   936  		},
   937  		{
   938  			node: api.Node{
   939  				ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
   940  				Status: api.NodeStatus{Addresses: []api.NodeAddress{
   941  					{Type: api.NodeInternalIP, Address: "2.2.2.2"},
   942  					{Type: api.NodeExternalIP, Address: "3.3.3.3"},
   943  					{Type: api.NodeInternalIP, Address: "4.4.4.4"},
   944  				}},
   945  			},
   946  			// Columns: Name, Status, Roles, Age, KubeletVersion, NodeInternalIP, NodeExternalIP, OSImage, KernelVersion, ContainerRuntimeVersion
   947  			expected: []metav1.TableRow{
   948  				{
   949  					Cells: []interface{}{"foo3", "Unknown", "<none>", "<unknown>", "", "2.2.2.2", "3.3.3.3", "<unknown>", "<unknown>", "<unknown>"},
   950  				},
   951  			},
   952  		},
   953  	}
   954  
   955  	for i, test := range table {
   956  		rows, err := printNode(&test.node, printers.GenerateOptions{Wide: true})
   957  		if err != nil {
   958  			t.Fatalf("An error occurred generating table rows Node: %#v", err)
   959  		}
   960  		for i := range rows {
   961  			rows[i].Object.Object = nil
   962  		}
   963  		if !reflect.DeepEqual(test.expected, rows) {
   964  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
   965  		}
   966  	}
   967  }
   968  
   969  func TestPrintIngress(t *testing.T) {
   970  	ingress := networking.Ingress{
   971  		ObjectMeta: metav1.ObjectMeta{
   972  			Name:              "test1",
   973  			CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
   974  		},
   975  		Spec: networking.IngressSpec{
   976  			IngressClassName: utilpointer.StringPtr("foo"),
   977  			DefaultBackend: &networking.IngressBackend{
   978  				Service: &networking.IngressServiceBackend{
   979  					Name: "default-backend",
   980  					Port: networking.ServiceBackendPort{
   981  						Name:   "default-backend",
   982  						Number: 80,
   983  					},
   984  				},
   985  			},
   986  		},
   987  		Status: networking.IngressStatus{
   988  			LoadBalancer: networking.IngressLoadBalancerStatus{
   989  				Ingress: []networking.IngressLoadBalancerIngress{
   990  					{
   991  						IP:       "2.3.4.5",
   992  						Hostname: "localhost.localdomain",
   993  					},
   994  				},
   995  			},
   996  		},
   997  	}
   998  	// Columns: Name, Hosts, Address, Ports, Age
   999  	expected := []metav1.TableRow{{Cells: []interface{}{"test1", "foo", "*", "2.3.4.5", "80", "10y"}}}
  1000  
  1001  	rows, err := printIngress(&ingress, printers.GenerateOptions{})
  1002  	if err != nil {
  1003  		t.Fatalf("Error generating table rows for Ingress: %#v", err)
  1004  	}
  1005  	rows[0].Object.Object = nil
  1006  	if !reflect.DeepEqual(expected, rows) {
  1007  		t.Errorf("mismatch: %s", cmp.Diff(expected, rows))
  1008  	}
  1009  }
  1010  
  1011  func TestPrintIngressClass(t *testing.T) {
  1012  	testCases := []struct {
  1013  		name         string
  1014  		ingressClass *networking.IngressClass
  1015  		expected     []metav1.TableRow
  1016  	}{{
  1017  		name: "example with params",
  1018  		ingressClass: &networking.IngressClass{
  1019  			ObjectMeta: metav1.ObjectMeta{
  1020  				Name:              "test1",
  1021  				CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  1022  			},
  1023  			Spec: networking.IngressClassSpec{
  1024  				Controller: "example.com/controller",
  1025  				Parameters: &networking.IngressClassParametersReference{Kind: "customgroup", Name: "example"},
  1026  			},
  1027  		},
  1028  		expected: []metav1.TableRow{{Cells: []interface{}{"test1", "example.com/controller", "customgroup/example", "10y"}}},
  1029  	}, {
  1030  		name: "example with params + API Group",
  1031  		ingressClass: &networking.IngressClass{
  1032  			ObjectMeta: metav1.ObjectMeta{
  1033  				Name:              "test1",
  1034  				CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  1035  			},
  1036  			Spec: networking.IngressClassSpec{
  1037  				Controller: "example.com/controller",
  1038  				Parameters: &networking.IngressClassParametersReference{
  1039  					APIGroup: utilpointer.StringPtr("example.com"),
  1040  					Kind:     "customgroup",
  1041  					Name:     "example",
  1042  				},
  1043  			},
  1044  		},
  1045  		expected: []metav1.TableRow{{Cells: []interface{}{"test1", "example.com/controller", "customgroup.example.com/example", "10y"}}},
  1046  	}, {
  1047  		name: "example without params",
  1048  		ingressClass: &networking.IngressClass{
  1049  			ObjectMeta: metav1.ObjectMeta{
  1050  				Name:              "test2",
  1051  				CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-11, 0, 0)},
  1052  			},
  1053  			Spec: networking.IngressClassSpec{
  1054  				Controller: "example.com/controller2",
  1055  			},
  1056  		},
  1057  		expected: []metav1.TableRow{{Cells: []interface{}{"test2", "example.com/controller2", "<none>", "11y"}}},
  1058  	}}
  1059  
  1060  	for _, testCase := range testCases {
  1061  		t.Run(testCase.name, func(t *testing.T) {
  1062  			rows, err := printIngressClass(testCase.ingressClass, printers.GenerateOptions{})
  1063  			if err != nil {
  1064  				t.Fatalf("Error generating table rows for Ingress: %#v", err)
  1065  			}
  1066  			for i := range rows {
  1067  				rows[i].Object.Object = nil
  1068  			}
  1069  			if !reflect.DeepEqual(testCase.expected, rows) {
  1070  				t.Errorf("mismatch: %s", cmp.Diff(testCase.expected, rows))
  1071  			}
  1072  		})
  1073  	}
  1074  }
  1075  
  1076  func TestPrintServiceLoadBalancer(t *testing.T) {
  1077  	tests := []struct {
  1078  		service  api.Service
  1079  		options  printers.GenerateOptions
  1080  		expected []metav1.TableRow
  1081  	}{
  1082  		// Test load balancer service with multiple external IP's
  1083  		{
  1084  			service: api.Service{
  1085  				ObjectMeta: metav1.ObjectMeta{Name: "service1"},
  1086  				Spec: api.ServiceSpec{
  1087  					ClusterIPs: []string{"1.2.3.4"},
  1088  					Type:       "LoadBalancer",
  1089  					Ports:      []api.ServicePort{{Port: 80, Protocol: "TCP"}},
  1090  				},
  1091  				Status: api.ServiceStatus{
  1092  					LoadBalancer: api.LoadBalancerStatus{
  1093  						Ingress: []api.LoadBalancerIngress{{IP: "2.3.4.5"}, {IP: "3.4.5.6"}}},
  1094  				},
  1095  			},
  1096  			options: printers.GenerateOptions{},
  1097  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  1098  			expected: []metav1.TableRow{{Cells: []interface{}{"service1", "LoadBalancer", "1.2.3.4", "2.3.4.5,3.4.5.6", "80/TCP", "<unknown>"}}},
  1099  		},
  1100  		// Test load balancer service with pending external IP.
  1101  		{
  1102  			service: api.Service{
  1103  				ObjectMeta: metav1.ObjectMeta{Name: "service2"},
  1104  				Spec: api.ServiceSpec{
  1105  					ClusterIPs: []string{"1.3.4.5"},
  1106  					Type:       "LoadBalancer",
  1107  					Ports:      []api.ServicePort{{Port: 80, Protocol: "TCP"}, {Port: 8090, Protocol: "UDP"}, {Port: 8000, Protocol: "TCP"}, {Port: 7777, Protocol: "SCTP"}},
  1108  				},
  1109  			},
  1110  			options: printers.GenerateOptions{},
  1111  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  1112  			expected: []metav1.TableRow{{Cells: []interface{}{"service2", "LoadBalancer", "1.3.4.5", "<pending>", "80/TCP,8090/UDP,8000/TCP,7777/SCTP", "<unknown>"}}},
  1113  		},
  1114  		// Test load balancer service with multiple ports.
  1115  		{
  1116  			service: api.Service{
  1117  				ObjectMeta: metav1.ObjectMeta{Name: "service3"},
  1118  				Spec: api.ServiceSpec{
  1119  					ClusterIPs: []string{"1.4.5.6"},
  1120  					Type:       "LoadBalancer",
  1121  					Ports:      []api.ServicePort{{Port: 80, Protocol: "TCP"}, {Port: 8090, Protocol: "UDP"}, {Port: 8000, Protocol: "TCP"}},
  1122  				},
  1123  				Status: api.ServiceStatus{
  1124  					LoadBalancer: api.LoadBalancerStatus{
  1125  						Ingress: []api.LoadBalancerIngress{{IP: "2.3.4.5"}}},
  1126  				},
  1127  			},
  1128  			options: printers.GenerateOptions{},
  1129  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  1130  			expected: []metav1.TableRow{{Cells: []interface{}{"service3", "LoadBalancer", "1.4.5.6", "2.3.4.5", "80/TCP,8090/UDP,8000/TCP", "<unknown>"}}},
  1131  		},
  1132  		// Long external IP's list gets elided.
  1133  		{
  1134  			service: api.Service{
  1135  				ObjectMeta: metav1.ObjectMeta{Name: "service4"},
  1136  				Spec: api.ServiceSpec{
  1137  					ClusterIPs: []string{"1.5.6.7"},
  1138  					Type:       "LoadBalancer",
  1139  					Ports:      []api.ServicePort{{Port: 80, Protocol: "TCP"}, {Port: 8090, Protocol: "UDP"}, {Port: 8000, Protocol: "TCP"}},
  1140  				},
  1141  				Status: api.ServiceStatus{
  1142  					LoadBalancer: api.LoadBalancerStatus{
  1143  						Ingress: []api.LoadBalancerIngress{{IP: "2.3.4.5"}, {IP: "3.4.5.6"}, {IP: "5.6.7.8", Hostname: "host5678"}}},
  1144  				},
  1145  			},
  1146  			options: printers.GenerateOptions{},
  1147  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  1148  			expected: []metav1.TableRow{{Cells: []interface{}{"service4", "LoadBalancer", "1.5.6.7", "2.3.4.5,3.4.5...", "80/TCP,8090/UDP,8000/TCP", "<unknown>"}}},
  1149  		},
  1150  		// Generate options: Wide, includes selectors.
  1151  		{
  1152  			service: api.Service{
  1153  				ObjectMeta: metav1.ObjectMeta{Name: "service4"},
  1154  				Spec: api.ServiceSpec{
  1155  					ClusterIPs: []string{"1.5.6.7"},
  1156  					Type:       "LoadBalancer",
  1157  					Ports:      []api.ServicePort{{Port: 80, Protocol: "TCP"}, {Port: 8090, Protocol: "UDP"}, {Port: 8000, Protocol: "TCP"}},
  1158  					Selector:   map[string]string{"foo": "bar"},
  1159  				},
  1160  				Status: api.ServiceStatus{
  1161  					LoadBalancer: api.LoadBalancerStatus{
  1162  						Ingress: []api.LoadBalancerIngress{{IP: "2.3.4.5"}, {IP: "3.4.5.6"}, {IP: "5.6.7.8", Hostname: "host5678"}}},
  1163  				},
  1164  			},
  1165  			options: printers.GenerateOptions{Wide: true},
  1166  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  1167  			expected: []metav1.TableRow{{Cells: []interface{}{"service4", "LoadBalancer", "1.5.6.7", "2.3.4.5,3.4.5.6,5.6.7.8", "80/TCP,8090/UDP,8000/TCP", "<unknown>", "foo=bar"}}},
  1168  		},
  1169  	}
  1170  
  1171  	for i, test := range tests {
  1172  		rows, err := printService(&test.service, test.options)
  1173  		if err != nil {
  1174  			t.Fatalf("Error printing table rows for Service: %#v", err)
  1175  		}
  1176  		for i := range rows {
  1177  			rows[i].Object.Object = nil
  1178  		}
  1179  		if !reflect.DeepEqual(test.expected, rows) {
  1180  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  1181  		}
  1182  	}
  1183  }
  1184  
  1185  func TestPrintPod(t *testing.T) {
  1186  	tests := []struct {
  1187  		pod    api.Pod
  1188  		expect []metav1.TableRow
  1189  	}{
  1190  		{
  1191  			// Test name, num of containers, restarts, container ready status
  1192  			api.Pod{
  1193  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1194  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1195  				Status: api.PodStatus{
  1196  					Phase: "podPhase",
  1197  					ContainerStatuses: []api.ContainerStatus{
  1198  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1199  						{RestartCount: 3},
  1200  					},
  1201  				},
  1202  			},
  1203  			[]metav1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", "6", "<unknown>"}}},
  1204  		},
  1205  		{
  1206  			// Test container error overwrites pod phase
  1207  			api.Pod{
  1208  				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1209  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1210  				Status: api.PodStatus{
  1211  					Phase: "podPhase",
  1212  					ContainerStatuses: []api.ContainerStatus{
  1213  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1214  						{State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1215  					},
  1216  				},
  1217  			},
  1218  			[]metav1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", "6", "<unknown>"}}},
  1219  		},
  1220  		{
  1221  			// Test the same as the above but with Terminated state and the first container overwrites the rest
  1222  			api.Pod{
  1223  				ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  1224  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1225  				Status: api.PodStatus{
  1226  					Phase: "podPhase",
  1227  					ContainerStatuses: []api.ContainerStatus{
  1228  						{State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1229  						{State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "ContainerTerminatedReason"}}, RestartCount: 3},
  1230  					},
  1231  				},
  1232  			},
  1233  			[]metav1.TableRow{{Cells: []interface{}{"test3", "0/2", "ContainerWaitingReason", "6", "<unknown>"}}},
  1234  		},
  1235  		{
  1236  			// Test ready is not enough for reporting running
  1237  			api.Pod{
  1238  				ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  1239  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1240  				Status: api.PodStatus{
  1241  					Phase: "podPhase",
  1242  					ContainerStatuses: []api.ContainerStatus{
  1243  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1244  						{Ready: true, RestartCount: 3},
  1245  					},
  1246  				},
  1247  			},
  1248  			[]metav1.TableRow{{Cells: []interface{}{"test4", "1/2", "podPhase", "6", "<unknown>"}}},
  1249  		},
  1250  		{
  1251  			// Test ready is not enough for reporting running
  1252  			api.Pod{
  1253  				ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  1254  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1255  				Status: api.PodStatus{
  1256  					Reason: "podReason",
  1257  					Phase:  "podPhase",
  1258  					ContainerStatuses: []api.ContainerStatus{
  1259  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1260  						{Ready: true, RestartCount: 3},
  1261  					},
  1262  				},
  1263  			},
  1264  			[]metav1.TableRow{{Cells: []interface{}{"test5", "1/2", "podReason", "6", "<unknown>"}}},
  1265  		},
  1266  		{
  1267  			// Test pod has 2 containers, one is running and the other is completed, w/o ready condition
  1268  			api.Pod{
  1269  				ObjectMeta: metav1.ObjectMeta{Name: "test6"},
  1270  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1271  				Status: api.PodStatus{
  1272  					Phase:  "Running",
  1273  					Reason: "",
  1274  					ContainerStatuses: []api.ContainerStatus{
  1275  						{Ready: true, RestartCount: 3, State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}}},
  1276  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1277  					},
  1278  				},
  1279  			},
  1280  			[]metav1.TableRow{{Cells: []interface{}{"test6", "1/2", "NotReady", "6", "<unknown>"}}},
  1281  		},
  1282  		{
  1283  			// Test pod has 2 containers, one is running and the other is completed, with ready condition
  1284  			api.Pod{
  1285  				ObjectMeta: metav1.ObjectMeta{Name: "test6"},
  1286  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1287  				Status: api.PodStatus{
  1288  					Phase:  "Running",
  1289  					Reason: "",
  1290  					ContainerStatuses: []api.ContainerStatus{
  1291  						{Ready: true, RestartCount: 3, State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}}},
  1292  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1293  					},
  1294  					Conditions: []api.PodCondition{
  1295  						{Type: api.PodReady, Status: api.ConditionTrue, LastProbeTime: metav1.Time{Time: time.Now()}},
  1296  					},
  1297  				},
  1298  			},
  1299  			[]metav1.TableRow{{Cells: []interface{}{"test6", "1/2", "Running", "6", "<unknown>"}}},
  1300  		},
  1301  		{
  1302  			// Test pod has 1 init container restarting and 1 container not running
  1303  			api.Pod{
  1304  				ObjectMeta: metav1.ObjectMeta{Name: "test7"},
  1305  				Spec:       api.PodSpec{InitContainers: make([]api.Container, 1), Containers: make([]api.Container, 1)},
  1306  				Status: api.PodStatus{
  1307  					Phase: "podPhase",
  1308  					InitContainerStatuses: []api.ContainerStatus{
  1309  						{
  1310  							Ready:                false,
  1311  							RestartCount:         3,
  1312  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1313  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1314  						},
  1315  					},
  1316  					ContainerStatuses: []api.ContainerStatus{
  1317  						{
  1318  							Ready:        false,
  1319  							RestartCount: 0,
  1320  							State:        api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1321  						},
  1322  					},
  1323  				},
  1324  			},
  1325  			[]metav1.TableRow{{Cells: []interface{}{"test7", "0/1", "Init:0/1", "3 (10s ago)", "<unknown>"}}},
  1326  		},
  1327  		{
  1328  			// Test pod has 2 init containers, one restarting and the other not running, and 1 container not running
  1329  			api.Pod{
  1330  				ObjectMeta: metav1.ObjectMeta{Name: "test8"},
  1331  				Spec:       api.PodSpec{InitContainers: make([]api.Container, 2), Containers: make([]api.Container, 1)},
  1332  				Status: api.PodStatus{
  1333  					Phase: "podPhase",
  1334  					InitContainerStatuses: []api.ContainerStatus{
  1335  						{
  1336  							Ready:                false,
  1337  							RestartCount:         3,
  1338  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1339  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1340  						},
  1341  						{
  1342  							Ready: false,
  1343  							State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1344  						},
  1345  					},
  1346  					ContainerStatuses: []api.ContainerStatus{
  1347  						{
  1348  							Ready: false,
  1349  							State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1350  						},
  1351  					},
  1352  				},
  1353  			},
  1354  			[]metav1.TableRow{{Cells: []interface{}{"test8", "0/1", "Init:0/2", "3 (10s ago)", "<unknown>"}}},
  1355  		},
  1356  		{
  1357  			// Test pod has 2 init containers, one completed without restarts and the other restarting, and 1 container not running
  1358  			api.Pod{
  1359  				ObjectMeta: metav1.ObjectMeta{Name: "test9"},
  1360  				Spec:       api.PodSpec{InitContainers: make([]api.Container, 2), Containers: make([]api.Container, 1)},
  1361  				Status: api.PodStatus{
  1362  					Phase: "podPhase",
  1363  					InitContainerStatuses: []api.ContainerStatus{
  1364  						{
  1365  							Ready: false,
  1366  							State: api.ContainerState{Terminated: &api.ContainerStateTerminated{}},
  1367  						},
  1368  						{
  1369  							Ready:                false,
  1370  							RestartCount:         3,
  1371  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1372  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1373  						},
  1374  					},
  1375  					ContainerStatuses: []api.ContainerStatus{
  1376  						{
  1377  							Ready: false,
  1378  							State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1379  						},
  1380  					},
  1381  				},
  1382  			},
  1383  			[]metav1.TableRow{{Cells: []interface{}{"test9", "0/1", "Init:1/2", "3 (10s ago)", "<unknown>"}}},
  1384  		},
  1385  		{
  1386  			// Test pod has 2 init containers, one completed with restarts and the other restarting, and 1 container not running
  1387  			api.Pod{
  1388  				ObjectMeta: metav1.ObjectMeta{Name: "test10"},
  1389  				Spec:       api.PodSpec{InitContainers: make([]api.Container, 2), Containers: make([]api.Container, 1)},
  1390  				Status: api.PodStatus{
  1391  					Phase: "podPhase",
  1392  					InitContainerStatuses: []api.ContainerStatus{
  1393  						{
  1394  							Ready:                false,
  1395  							RestartCount:         2,
  1396  							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{}},
  1397  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-2 * time.Minute))}},
  1398  						},
  1399  						{
  1400  							Ready:                false,
  1401  							RestartCount:         3,
  1402  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1403  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1404  						},
  1405  					},
  1406  					ContainerStatuses: []api.ContainerStatus{
  1407  						{
  1408  							Ready: false,
  1409  							State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1410  						},
  1411  					},
  1412  				},
  1413  			},
  1414  			[]metav1.TableRow{{Cells: []interface{}{"test10", "0/1", "Init:1/2", "5 (10s ago)", "<unknown>"}}},
  1415  		},
  1416  		{
  1417  			// Test pod has 1 init container completed with restarts and one container restarting
  1418  			api.Pod{
  1419  				ObjectMeta: metav1.ObjectMeta{Name: "test11"},
  1420  				Spec:       api.PodSpec{InitContainers: make([]api.Container, 1), Containers: make([]api.Container, 1)},
  1421  				Status: api.PodStatus{
  1422  					Phase: "Running",
  1423  					InitContainerStatuses: []api.ContainerStatus{
  1424  						{
  1425  							Ready:                false,
  1426  							RestartCount:         2,
  1427  							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{}},
  1428  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-2 * time.Minute))}},
  1429  						},
  1430  					},
  1431  					ContainerStatuses: []api.ContainerStatus{
  1432  						{
  1433  							Ready:                false,
  1434  							RestartCount:         4,
  1435  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1436  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * time.Second))}},
  1437  						},
  1438  					},
  1439  				},
  1440  			},
  1441  			[]metav1.TableRow{{Cells: []interface{}{"test11", "0/1", "Running", "4 (20s ago)", "<unknown>"}}},
  1442  		},
  1443  		{
  1444  			// Test pod has 1 container that restarted 5d ago
  1445  			api.Pod{
  1446  				ObjectMeta: metav1.ObjectMeta{Name: "test12"},
  1447  				Spec:       api.PodSpec{Containers: make([]api.Container, 1)},
  1448  				Status: api.PodStatus{
  1449  					Phase: "Running",
  1450  					ContainerStatuses: []api.ContainerStatus{
  1451  						{
  1452  							Ready:                true,
  1453  							RestartCount:         3,
  1454  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1455  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-5 * 24 * time.Hour))}},
  1456  						},
  1457  					},
  1458  				},
  1459  			},
  1460  			[]metav1.TableRow{{Cells: []interface{}{"test12", "1/1", "Running", "3 (5d ago)", "<unknown>"}}},
  1461  		},
  1462  		{
  1463  			// Test pod has 2 containers, one has never restarted and the other has restarted 10d ago
  1464  			api.Pod{
  1465  				ObjectMeta: metav1.ObjectMeta{Name: "test13"},
  1466  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1467  				Status: api.PodStatus{
  1468  					Phase: "Running",
  1469  					ContainerStatuses: []api.ContainerStatus{
  1470  						{
  1471  							Ready:        true,
  1472  							RestartCount: 0,
  1473  							State:        api.ContainerState{Running: &api.ContainerStateRunning{}},
  1474  						},
  1475  						{
  1476  							Ready:                true,
  1477  							RestartCount:         3,
  1478  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1479  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * 24 * time.Hour))}},
  1480  						},
  1481  					},
  1482  				},
  1483  			},
  1484  			[]metav1.TableRow{{Cells: []interface{}{"test13", "2/2", "Running", "3 (10d ago)", "<unknown>"}}},
  1485  		},
  1486  		{
  1487  			// Test pod has 2 containers, one restarted 5d ago and the other restarted 20d ago
  1488  			api.Pod{
  1489  				ObjectMeta: metav1.ObjectMeta{Name: "test14"},
  1490  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1491  				Status: api.PodStatus{
  1492  					Phase: "Running",
  1493  					ContainerStatuses: []api.ContainerStatus{
  1494  						{
  1495  							Ready:                true,
  1496  							RestartCount:         6,
  1497  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1498  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-5 * 24 * time.Hour))}},
  1499  						},
  1500  						{
  1501  							Ready:                true,
  1502  							RestartCount:         3,
  1503  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1504  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * 24 * time.Hour))}},
  1505  						},
  1506  					},
  1507  				},
  1508  			},
  1509  			[]metav1.TableRow{{Cells: []interface{}{"test14", "2/2", "Running", "9 (5d ago)", "<unknown>"}}},
  1510  		},
  1511  		{
  1512  			// Test PodScheduled condition with reason SchedulingGated
  1513  			api.Pod{
  1514  				ObjectMeta: metav1.ObjectMeta{Name: "test15"},
  1515  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1516  				Status: api.PodStatus{
  1517  					Phase: "podPhase",
  1518  					Conditions: []api.PodCondition{
  1519  						{
  1520  							Type:   api.PodScheduled,
  1521  							Status: api.ConditionFalse,
  1522  							Reason: apiv1.PodReasonSchedulingGated,
  1523  						},
  1524  					},
  1525  				},
  1526  			},
  1527  			[]metav1.TableRow{{Cells: []interface{}{"test15", "0/2", apiv1.PodReasonSchedulingGated, "0", "<unknown>"}}},
  1528  		},
  1529  	}
  1530  
  1531  	for i, test := range tests {
  1532  		rows, err := printPod(&test.pod, printers.GenerateOptions{})
  1533  		if err != nil {
  1534  			t.Fatal(err)
  1535  		}
  1536  		for i := range rows {
  1537  			rows[i].Object.Object = nil
  1538  		}
  1539  		if !reflect.DeepEqual(test.expect, rows) {
  1540  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
  1541  		}
  1542  	}
  1543  }
  1544  
  1545  func TestPrintPodWithRestartableInitContainer(t *testing.T) {
  1546  	tests := []struct {
  1547  		pod    api.Pod
  1548  		expect []metav1.TableRow
  1549  	}{
  1550  		{
  1551  			// Test pod has 2 restartable init containers, the first one running but not started.
  1552  			api.Pod{
  1553  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1554  				Spec: api.PodSpec{
  1555  					InitContainers: []api.Container{
  1556  						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
  1557  						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
  1558  					}, Containers: make([]api.Container, 1)},
  1559  				Status: api.PodStatus{
  1560  					Phase: "Pending",
  1561  					InitContainerStatuses: []api.ContainerStatus{
  1562  						{
  1563  							Name:                 "restartable-init-1",
  1564  							Ready:                false,
  1565  							RestartCount:         3,
  1566  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1567  							Started:              utilpointer.Bool(false),
  1568  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1569  						},
  1570  						{
  1571  							Name:    "restartable-init-2",
  1572  							Ready:   false,
  1573  							State:   api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1574  							Started: utilpointer.Bool(false),
  1575  						},
  1576  					},
  1577  					ContainerStatuses: []api.ContainerStatus{
  1578  						{
  1579  							Ready:        false,
  1580  							RestartCount: 0,
  1581  							State:        api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1582  						},
  1583  					},
  1584  					Conditions: []api.PodCondition{
  1585  						{Type: api.PodInitialized, Status: api.ConditionFalse},
  1586  					},
  1587  				},
  1588  			},
  1589  			[]metav1.TableRow{{Cells: []interface{}{"test1", "0/3", "Init:0/2", "3 (10s ago)", "<unknown>"}}},
  1590  		},
  1591  		{
  1592  			// Test pod has 2 restartable init containers, the first one started and the second one running but not started.
  1593  			api.Pod{
  1594  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1595  				Spec: api.PodSpec{
  1596  					InitContainers: []api.Container{
  1597  						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
  1598  						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
  1599  					}, Containers: make([]api.Container, 1)},
  1600  				Status: api.PodStatus{
  1601  					Phase: "Pending",
  1602  					InitContainerStatuses: []api.ContainerStatus{
  1603  						{
  1604  							Name:                 "restartable-init-1",
  1605  							Ready:                false,
  1606  							RestartCount:         3,
  1607  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1608  							Started:              utilpointer.Bool(true),
  1609  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1610  						},
  1611  						{
  1612  							Name:    "restartable-init-2",
  1613  							Ready:   false,
  1614  							State:   api.ContainerState{Running: &api.ContainerStateRunning{}},
  1615  							Started: utilpointer.Bool(false),
  1616  						},
  1617  					},
  1618  					ContainerStatuses: []api.ContainerStatus{
  1619  						{
  1620  							Ready:        false,
  1621  							RestartCount: 0,
  1622  							State:        api.ContainerState{Waiting: &api.ContainerStateWaiting{}},
  1623  						},
  1624  					},
  1625  					Conditions: []api.PodCondition{
  1626  						{Type: api.PodInitialized, Status: api.ConditionFalse},
  1627  					},
  1628  				},
  1629  			},
  1630  			[]metav1.TableRow{{Cells: []interface{}{"test1", "0/3", "Init:1/2", "3 (10s ago)", "<unknown>"}}},
  1631  		},
  1632  		{
  1633  			// Test pod has 2 restartable init containers started and 1 container running
  1634  			api.Pod{
  1635  				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1636  				Spec: api.PodSpec{
  1637  					InitContainers: []api.Container{
  1638  						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
  1639  						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
  1640  					}, Containers: make([]api.Container, 1)},
  1641  				Status: api.PodStatus{
  1642  					Phase: "Running",
  1643  					InitContainerStatuses: []api.ContainerStatus{
  1644  						{
  1645  							Name:                 "restartable-init-1",
  1646  							Ready:                false,
  1647  							RestartCount:         3,
  1648  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1649  							Started:              utilpointer.Bool(true),
  1650  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1651  						},
  1652  						{
  1653  							Name:    "restartable-init-2",
  1654  							Ready:   false,
  1655  							State:   api.ContainerState{Running: &api.ContainerStateRunning{}},
  1656  							Started: utilpointer.Bool(true),
  1657  						},
  1658  					},
  1659  					ContainerStatuses: []api.ContainerStatus{
  1660  						{
  1661  							Ready:                true,
  1662  							RestartCount:         4,
  1663  							State:                api.ContainerState{Running: &api.ContainerStateRunning{}},
  1664  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * time.Second))}},
  1665  						},
  1666  					},
  1667  					Conditions: []api.PodCondition{
  1668  						{Type: api.PodInitialized, Status: api.ConditionTrue},
  1669  					},
  1670  				},
  1671  			},
  1672  			[]metav1.TableRow{{Cells: []interface{}{"test2", "1/3", "Running", "7 (10s ago)", "<unknown>"}}},
  1673  		},
  1674  		{
  1675  			// Test pod has 2 restartable init containers completed with non-zero and 1 container completed
  1676  			api.Pod{
  1677  				ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  1678  				Spec: api.PodSpec{
  1679  					InitContainers: []api.Container{
  1680  						{Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways},
  1681  						{Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways},
  1682  					}, Containers: make([]api.Container, 1)},
  1683  				Status: api.PodStatus{
  1684  					Phase: "Succeeded",
  1685  					InitContainerStatuses: []api.ContainerStatus{
  1686  						{
  1687  							Name:                 "restartable-init-1",
  1688  							Ready:                false,
  1689  							RestartCount:         3,
  1690  							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Error", ExitCode: 137}},
  1691  							Started:              utilpointer.Bool(false),
  1692  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-10 * time.Second))}},
  1693  						},
  1694  						{
  1695  							Name:    "restartable-init-2",
  1696  							Ready:   false,
  1697  							State:   api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Error", ExitCode: 137}},
  1698  							Started: utilpointer.Bool(false),
  1699  						},
  1700  					},
  1701  					ContainerStatuses: []api.ContainerStatus{
  1702  						{
  1703  							Ready:                false,
  1704  							RestartCount:         4,
  1705  							State:                api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "Completed", ExitCode: 0}},
  1706  							LastTerminationState: api.ContainerState{Terminated: &api.ContainerStateTerminated{FinishedAt: metav1.NewTime(time.Now().Add(-20 * time.Second))}},
  1707  						},
  1708  					},
  1709  					Conditions: []api.PodCondition{
  1710  						{Type: api.PodInitialized, Status: api.ConditionTrue},
  1711  					},
  1712  				},
  1713  			},
  1714  			[]metav1.TableRow{
  1715  				{
  1716  					Cells: []interface{}{"test3", "0/3", "Completed", "7 (10s ago)", "<unknown>"},
  1717  					Conditions: []metav1.TableRowCondition{
  1718  						{Type: metav1.RowCompleted, Status: metav1.ConditionTrue, Reason: "Succeeded", Message: "The pod has completed successfully."},
  1719  					},
  1720  				},
  1721  			},
  1722  		},
  1723  		{
  1724  			// Test pod has container statuses for non-existent initContainers and containers
  1725  			api.Pod{
  1726  				ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  1727  				Spec: api.PodSpec{
  1728  					InitContainers: []api.Container{
  1729  						{Name: "init1", Image: "initimage"},
  1730  						{Name: "sidecar1", Image: "sidecarimage", RestartPolicy: ptr.To(api.ContainerRestartPolicyAlways)},
  1731  					},
  1732  					Containers: []api.Container{{Name: "container1", Image: "containerimage"}},
  1733  				},
  1734  				Status: api.PodStatus{
  1735  					Phase: "Running",
  1736  					InitContainerStatuses: []api.ContainerStatus{
  1737  						{Name: "initinvalid"},
  1738  						{Name: "init1"},
  1739  						{Name: "sidecar1"},
  1740  					},
  1741  					ContainerStatuses: []api.ContainerStatus{
  1742  						{Name: "containerinvalid"},
  1743  						{Name: "container1"},
  1744  					},
  1745  				},
  1746  			},
  1747  			[]metav1.TableRow{{Cells: []interface{}{"test4", "0/2", "Init:0/2", "0", "<unknown>"}}},
  1748  		},
  1749  	}
  1750  
  1751  	for i, test := range tests {
  1752  		rows, err := printPod(&test.pod, printers.GenerateOptions{})
  1753  		if err != nil {
  1754  			t.Fatal(err)
  1755  		}
  1756  		for i := range rows {
  1757  			rows[i].Object.Object = nil
  1758  		}
  1759  		if !reflect.DeepEqual(test.expect, rows) {
  1760  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
  1761  		}
  1762  	}
  1763  }
  1764  
  1765  func TestPrintPodwide(t *testing.T) {
  1766  	condition1 := "condition1"
  1767  	condition2 := "condition2"
  1768  	condition3 := "condition3"
  1769  	tests := []struct {
  1770  		pod    api.Pod
  1771  		expect []metav1.TableRow
  1772  	}{
  1773  		{
  1774  			// Test when the NodeName and PodIP are not none
  1775  			api.Pod{
  1776  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1777  				Spec: api.PodSpec{
  1778  					Containers: make([]api.Container, 2),
  1779  					NodeName:   "test1",
  1780  					ReadinessGates: []api.PodReadinessGate{
  1781  						{
  1782  							ConditionType: api.PodConditionType(condition1),
  1783  						},
  1784  						{
  1785  							ConditionType: api.PodConditionType(condition2),
  1786  						},
  1787  						{
  1788  							ConditionType: api.PodConditionType(condition3),
  1789  						},
  1790  					},
  1791  				},
  1792  				Status: api.PodStatus{
  1793  					Conditions: []api.PodCondition{
  1794  						{
  1795  							Type:   api.PodConditionType(condition1),
  1796  							Status: api.ConditionFalse,
  1797  						},
  1798  						{
  1799  							Type:   api.PodConditionType(condition2),
  1800  							Status: api.ConditionTrue,
  1801  						},
  1802  					},
  1803  					Phase:  "podPhase",
  1804  					PodIPs: []api.PodIP{{IP: "1.1.1.1"}},
  1805  					ContainerStatuses: []api.ContainerStatus{
  1806  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1807  						{RestartCount: 3},
  1808  					},
  1809  					NominatedNodeName: "node1",
  1810  				},
  1811  			},
  1812  			[]metav1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", "6", "<unknown>", "1.1.1.1", "test1", "node1", "1/3"}}},
  1813  		},
  1814  		{
  1815  			// Test when the NodeName and PodIP are not none
  1816  			api.Pod{
  1817  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1818  				Spec: api.PodSpec{
  1819  					Containers: make([]api.Container, 2),
  1820  					NodeName:   "test1",
  1821  					ReadinessGates: []api.PodReadinessGate{
  1822  						{
  1823  							ConditionType: api.PodConditionType(condition1),
  1824  						},
  1825  						{
  1826  							ConditionType: api.PodConditionType(condition2),
  1827  						},
  1828  						{
  1829  							ConditionType: api.PodConditionType(condition3),
  1830  						},
  1831  					},
  1832  				},
  1833  				Status: api.PodStatus{
  1834  					Conditions: []api.PodCondition{
  1835  						{
  1836  							Type:   api.PodConditionType(condition1),
  1837  							Status: api.ConditionFalse,
  1838  						},
  1839  						{
  1840  							Type:   api.PodConditionType(condition2),
  1841  							Status: api.ConditionTrue,
  1842  						},
  1843  					},
  1844  					Phase:  "podPhase",
  1845  					PodIPs: []api.PodIP{{IP: "1.1.1.1"}, {IP: "2001:db8::"}},
  1846  					ContainerStatuses: []api.ContainerStatus{
  1847  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1848  						{RestartCount: 3},
  1849  					},
  1850  					NominatedNodeName: "node1",
  1851  				},
  1852  			},
  1853  			[]metav1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", "6", "<unknown>", "1.1.1.1", "test1", "node1", "1/3"}}},
  1854  		},
  1855  		{
  1856  			// Test when the NodeName and PodIP are none
  1857  			api.Pod{
  1858  				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1859  				Spec: api.PodSpec{
  1860  					Containers: make([]api.Container, 2),
  1861  					NodeName:   "",
  1862  				},
  1863  				Status: api.PodStatus{
  1864  					Phase: "podPhase",
  1865  					ContainerStatuses: []api.ContainerStatus{
  1866  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1867  						{State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3},
  1868  					},
  1869  				},
  1870  			},
  1871  			[]metav1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", "6", "<unknown>", "<none>", "<none>", "<none>", "<none>"}}},
  1872  		},
  1873  	}
  1874  
  1875  	for i, test := range tests {
  1876  		rows, err := printPod(&test.pod, printers.GenerateOptions{Wide: true})
  1877  		if err != nil {
  1878  			t.Fatal(err)
  1879  		}
  1880  		for i := range rows {
  1881  			rows[i].Object.Object = nil
  1882  		}
  1883  		if !reflect.DeepEqual(test.expect, rows) {
  1884  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
  1885  		}
  1886  	}
  1887  }
  1888  
  1889  func TestPrintPodConditions(t *testing.T) {
  1890  	runningPod := &api.Pod{
  1891  		ObjectMeta: metav1.ObjectMeta{Name: "test1", Labels: map[string]string{"a": "1", "b": "2"}},
  1892  		Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1893  		Status: api.PodStatus{
  1894  			Phase: "Running",
  1895  			ContainerStatuses: []api.ContainerStatus{
  1896  				{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1897  				{RestartCount: 3},
  1898  			},
  1899  		},
  1900  	}
  1901  	succeededPod := &api.Pod{
  1902  		ObjectMeta: metav1.ObjectMeta{Name: "test1", Labels: map[string]string{"a": "1", "b": "2"}},
  1903  		Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1904  		Status: api.PodStatus{
  1905  			Phase: "Succeeded",
  1906  			ContainerStatuses: []api.ContainerStatus{
  1907  				{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1908  				{RestartCount: 3},
  1909  			},
  1910  		},
  1911  	}
  1912  	failedPod := &api.Pod{
  1913  		ObjectMeta: metav1.ObjectMeta{Name: "test2", Labels: map[string]string{"b": "2"}},
  1914  		Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1915  		Status: api.PodStatus{
  1916  			Phase: "Failed",
  1917  			ContainerStatuses: []api.ContainerStatus{
  1918  				{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1919  				{RestartCount: 3},
  1920  			},
  1921  		},
  1922  	}
  1923  	tests := []struct {
  1924  		pod    *api.Pod
  1925  		expect []metav1.TableRow
  1926  	}{
  1927  		// Should not have TableRowCondition
  1928  		{
  1929  			pod: runningPod,
  1930  			// Columns: Name, Ready, Reason, Restarts, Age
  1931  			expect: []metav1.TableRow{{Cells: []interface{}{"test1", "1/2", "Running", "6", "<unknown>"}}},
  1932  		},
  1933  		// Should have TableRowCondition: podSuccessConditions
  1934  		{
  1935  			pod: succeededPod,
  1936  			expect: []metav1.TableRow{
  1937  				{
  1938  					// Columns: Name, Ready, Reason, Restarts, Age
  1939  					Cells:      []interface{}{"test1", "1/2", "Succeeded", "6", "<unknown>"},
  1940  					Conditions: podSuccessConditions,
  1941  				},
  1942  			},
  1943  		},
  1944  		// Should have TableRowCondition: podFailedCondition
  1945  		{
  1946  			pod: failedPod,
  1947  			expect: []metav1.TableRow{
  1948  				{
  1949  					// Columns: Name, Ready, Reason, Restarts, Age
  1950  					Cells:      []interface{}{"test2", "1/2", "Failed", "6", "<unknown>"},
  1951  					Conditions: podFailedConditions,
  1952  				},
  1953  			},
  1954  		},
  1955  	}
  1956  
  1957  	for i, test := range tests {
  1958  		rows, err := printPod(test.pod, printers.GenerateOptions{})
  1959  		if err != nil {
  1960  			t.Fatal(err)
  1961  		}
  1962  		for i := range rows {
  1963  			rows[i].Object.Object = nil
  1964  		}
  1965  		if !reflect.DeepEqual(test.expect, rows) {
  1966  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
  1967  		}
  1968  	}
  1969  }
  1970  
  1971  func TestPrintPodList(t *testing.T) {
  1972  	tests := []struct {
  1973  		pods   api.PodList
  1974  		expect []metav1.TableRow
  1975  	}{
  1976  		// Test podList's pod: name, num of containers, restarts, container ready status
  1977  		{
  1978  			api.PodList{
  1979  				Items: []api.Pod{
  1980  					{
  1981  						ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  1982  						Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  1983  						Status: api.PodStatus{
  1984  							Phase: "podPhase",
  1985  							ContainerStatuses: []api.ContainerStatus{
  1986  								{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1987  								{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1988  							},
  1989  						},
  1990  					},
  1991  					{
  1992  						ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  1993  						Spec:       api.PodSpec{Containers: make([]api.Container, 1)},
  1994  						Status: api.PodStatus{
  1995  							Phase: "podPhase",
  1996  							ContainerStatuses: []api.ContainerStatus{
  1997  								{Ready: true, RestartCount: 1, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  1998  							},
  1999  						},
  2000  					},
  2001  				},
  2002  			},
  2003  			[]metav1.TableRow{{Cells: []interface{}{"test1", "2/2", "podPhase", "6", "<unknown>"}}, {Cells: []interface{}{"test2", "1/1", "podPhase", "1", "<unknown>"}}},
  2004  		},
  2005  	}
  2006  
  2007  	for _, test := range tests {
  2008  		rows, err := printPodList(&test.pods, printers.GenerateOptions{})
  2009  
  2010  		if err != nil {
  2011  			t.Fatal(err)
  2012  		}
  2013  		for i := range rows {
  2014  			rows[i].Object.Object = nil
  2015  		}
  2016  		if !reflect.DeepEqual(test.expect, rows) {
  2017  			t.Errorf("mismatch: %s", cmp.Diff(test.expect, rows))
  2018  		}
  2019  	}
  2020  }
  2021  
  2022  func TestPrintNonTerminatedPod(t *testing.T) {
  2023  	tests := []struct {
  2024  		pod    api.Pod
  2025  		expect []metav1.TableRow
  2026  	}{
  2027  		{
  2028  			// Test pod phase Running should be printed
  2029  			api.Pod{
  2030  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  2031  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  2032  				Status: api.PodStatus{
  2033  					Phase: api.PodRunning,
  2034  					ContainerStatuses: []api.ContainerStatus{
  2035  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2036  						{RestartCount: 3},
  2037  					},
  2038  				},
  2039  			},
  2040  			// Columns: Name, Ready, Reason, Restarts, Age
  2041  			[]metav1.TableRow{{Cells: []interface{}{"test1", "1/2", "Running", "6", "<unknown>"}}},
  2042  		},
  2043  		{
  2044  			// Test pod phase Pending should be printed
  2045  			api.Pod{
  2046  				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  2047  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  2048  				Status: api.PodStatus{
  2049  					Phase: api.PodPending,
  2050  					ContainerStatuses: []api.ContainerStatus{
  2051  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2052  						{RestartCount: 3},
  2053  					},
  2054  				},
  2055  			},
  2056  			// Columns: Name, Ready, Reason, Restarts, Age
  2057  			[]metav1.TableRow{{Cells: []interface{}{"test2", "1/2", "Pending", "6", "<unknown>"}}},
  2058  		},
  2059  		{
  2060  			// Test pod phase Unknown should be printed
  2061  			api.Pod{
  2062  				ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  2063  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  2064  				Status: api.PodStatus{
  2065  					Phase: api.PodUnknown,
  2066  					ContainerStatuses: []api.ContainerStatus{
  2067  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2068  						{RestartCount: 3},
  2069  					},
  2070  				},
  2071  			},
  2072  			// Columns: Name, Ready, Reason, Restarts, Age
  2073  			[]metav1.TableRow{{Cells: []interface{}{"test3", "1/2", "Unknown", "6", "<unknown>"}}},
  2074  		},
  2075  		{
  2076  			// Test pod phase Succeeded should be printed
  2077  			api.Pod{
  2078  				ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  2079  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  2080  				Status: api.PodStatus{
  2081  					Phase: api.PodSucceeded,
  2082  					ContainerStatuses: []api.ContainerStatus{
  2083  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2084  						{RestartCount: 3},
  2085  					},
  2086  				},
  2087  			},
  2088  			// Columns: Name, Ready, Reason, Restarts, Age
  2089  			[]metav1.TableRow{
  2090  				{
  2091  					Cells:      []interface{}{"test4", "1/2", "Succeeded", "6", "<unknown>"},
  2092  					Conditions: podSuccessConditions,
  2093  				},
  2094  			},
  2095  		},
  2096  		{
  2097  			// Test pod phase Failed shouldn't be printed
  2098  			api.Pod{
  2099  				ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  2100  				Spec:       api.PodSpec{Containers: make([]api.Container, 2)},
  2101  				Status: api.PodStatus{
  2102  					Phase: api.PodFailed,
  2103  					ContainerStatuses: []api.ContainerStatus{
  2104  						{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  2105  						{Ready: true, RestartCount: 3},
  2106  					},
  2107  				},
  2108  			},
  2109  			// Columns: Name, Ready, Reason, Restarts, Age
  2110  			[]metav1.TableRow{
  2111  				{
  2112  					Cells:      []interface{}{"test5", "1/2", "Failed", "6", "<unknown>"},
  2113  					Conditions: podFailedConditions,
  2114  				},
  2115  			},
  2116  		},
  2117  	}
  2118  
  2119  	for i, test := range tests {
  2120  		rows, err := printPod(&test.pod, printers.GenerateOptions{})
  2121  		if err != nil {
  2122  			t.Fatal(err)
  2123  		}
  2124  		for i := range rows {
  2125  			rows[i].Object.Object = nil
  2126  		}
  2127  		if !reflect.DeepEqual(test.expect, rows) {
  2128  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expect, rows))
  2129  		}
  2130  	}
  2131  }
  2132  
  2133  func TestPrintPodTemplate(t *testing.T) {
  2134  	tests := []struct {
  2135  		podTemplate api.PodTemplate
  2136  		options     printers.GenerateOptions
  2137  		expected    []metav1.TableRow
  2138  	}{
  2139  		// Test basic pod template with no containers.
  2140  		{
  2141  			podTemplate: api.PodTemplate{
  2142  				ObjectMeta: metav1.ObjectMeta{Name: "pod-template-1"},
  2143  				Template: api.PodTemplateSpec{
  2144  					ObjectMeta: metav1.ObjectMeta{Name: "pod-template-1"},
  2145  					Spec: api.PodSpec{
  2146  						Containers: []api.Container{},
  2147  					},
  2148  				},
  2149  			},
  2150  
  2151  			options: printers.GenerateOptions{},
  2152  			// Columns: Name, Containers, Images, Pod Labels
  2153  			expected: []metav1.TableRow{{Cells: []interface{}{"pod-template-1", "", "", "<none>"}}},
  2154  		},
  2155  		// Test basic pod template with two containers.
  2156  		{
  2157  			podTemplate: api.PodTemplate{
  2158  				ObjectMeta: metav1.ObjectMeta{Name: "pod-template-2"},
  2159  				Template: api.PodTemplateSpec{
  2160  					ObjectMeta: metav1.ObjectMeta{Name: "pod-template-2"},
  2161  					Spec: api.PodSpec{
  2162  						Containers: []api.Container{
  2163  							{
  2164  								Name:  "fake-container1",
  2165  								Image: "fake-image1",
  2166  							},
  2167  							{
  2168  								Name:  "fake-container2",
  2169  								Image: "fake-image2",
  2170  							},
  2171  						},
  2172  					},
  2173  				},
  2174  			},
  2175  
  2176  			options: printers.GenerateOptions{},
  2177  			// Columns: Name, Containers, Images, Pod Labels
  2178  			expected: []metav1.TableRow{{Cells: []interface{}{"pod-template-2", "fake-container1,fake-container2", "fake-image1,fake-image2", "<none>"}}},
  2179  		},
  2180  		// Test basic pod template with pod labels
  2181  		{
  2182  			podTemplate: api.PodTemplate{
  2183  				ObjectMeta: metav1.ObjectMeta{Name: "pod-template-3"},
  2184  				Template: api.PodTemplateSpec{
  2185  					ObjectMeta: metav1.ObjectMeta{
  2186  						Name:   "pod-template-3",
  2187  						Labels: map[string]string{"foo": "bar"},
  2188  					},
  2189  					Spec: api.PodSpec{
  2190  						Containers: []api.Container{},
  2191  					},
  2192  				},
  2193  			},
  2194  
  2195  			options: printers.GenerateOptions{},
  2196  			// Columns: Name, Containers, Images, Pod Labels
  2197  			expected: []metav1.TableRow{{Cells: []interface{}{"pod-template-3", "", "", "foo=bar"}}},
  2198  		},
  2199  	}
  2200  
  2201  	for i, test := range tests {
  2202  		rows, err := printPodTemplate(&test.podTemplate, test.options)
  2203  		if err != nil {
  2204  			t.Fatal(err)
  2205  		}
  2206  		for i := range rows {
  2207  			rows[i].Object.Object = nil
  2208  		}
  2209  		if !reflect.DeepEqual(test.expected, rows) {
  2210  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  2211  		}
  2212  	}
  2213  }
  2214  
  2215  func TestPrintPodTemplateList(t *testing.T) {
  2216  
  2217  	templateList := api.PodTemplateList{
  2218  		Items: []api.PodTemplate{
  2219  			{
  2220  				ObjectMeta: metav1.ObjectMeta{Name: "pod-template-1"},
  2221  				Template: api.PodTemplateSpec{
  2222  					ObjectMeta: metav1.ObjectMeta{
  2223  						Name:   "pod-template-2",
  2224  						Labels: map[string]string{"foo": "bar"},
  2225  					},
  2226  					Spec: api.PodSpec{
  2227  						Containers: []api.Container{},
  2228  					},
  2229  				},
  2230  			},
  2231  			{
  2232  				ObjectMeta: metav1.ObjectMeta{Name: "pod-template-2"},
  2233  				Template: api.PodTemplateSpec{
  2234  					ObjectMeta: metav1.ObjectMeta{
  2235  						Name:   "pod-template-2",
  2236  						Labels: map[string]string{"a": "b"},
  2237  					},
  2238  					Spec: api.PodSpec{
  2239  						Containers: []api.Container{},
  2240  					},
  2241  				},
  2242  			},
  2243  		},
  2244  	}
  2245  
  2246  	// Columns: Name, Containers, Images, Pod Labels
  2247  	expectedRows := []metav1.TableRow{
  2248  		{Cells: []interface{}{"pod-template-1", "", "", "foo=bar"}},
  2249  		{Cells: []interface{}{"pod-template-2", "", "", "a=b"}},
  2250  	}
  2251  
  2252  	rows, err := printPodTemplateList(&templateList, printers.GenerateOptions{})
  2253  	if err != nil {
  2254  		t.Fatalf("Error printing pod template list: %#v", err)
  2255  	}
  2256  	for i := range rows {
  2257  		rows[i].Object.Object = nil
  2258  	}
  2259  	if !reflect.DeepEqual(expectedRows, rows) {
  2260  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  2261  	}
  2262  }
  2263  
  2264  type stringTestList []struct {
  2265  	name, got, exp string
  2266  }
  2267  
  2268  func TestTranslateTimestampSince(t *testing.T) {
  2269  	tl := stringTestList{
  2270  		{"a while from now", translateTimestampSince(metav1.Time{Time: time.Now().Add(2.1e9)}), "<invalid>"},
  2271  		{"almost now", translateTimestampSince(metav1.Time{Time: time.Now().Add(1.9e9)}), "0s"},
  2272  		{"now", translateTimestampSince(metav1.Time{Time: time.Now()}), "0s"},
  2273  		{"unknown", translateTimestampSince(metav1.Time{}), "<unknown>"},
  2274  		{"30 seconds ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"},
  2275  		{"5 minutes ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"},
  2276  		{"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "100m"},
  2277  		{"2 days ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"},
  2278  		{"months ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"},
  2279  		{"10 years ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"},
  2280  	}
  2281  	for _, test := range tl {
  2282  		if test.got != test.exp {
  2283  			t.Errorf("On %v, expected '%v', but got '%v'",
  2284  				test.name, test.exp, test.got)
  2285  		}
  2286  	}
  2287  }
  2288  
  2289  func TestTranslateTimestampUntil(t *testing.T) {
  2290  	// Since this method compares the time with time.Now() internally,
  2291  	// small buffers of 0.1 seconds are added on comparing times to consider method call overhead.
  2292  	// Otherwise, the output strings become shorter than expected.
  2293  	const buf = 1e8
  2294  	tl := stringTestList{
  2295  		{"a while ago", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-2.1e9)}), "<invalid>"},
  2296  		{"almost now", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-1.9e9)}), "0s"},
  2297  		{"now", translateTimestampUntil(metav1.Time{Time: time.Now()}), "0s"},
  2298  		{"unknown", translateTimestampUntil(metav1.Time{}), "<unknown>"},
  2299  		{"in 30 seconds", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e10 + buf)}), "30s"},
  2300  		{"in 5 minutes", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e11 + buf)}), "5m"},
  2301  		{"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "100m"},
  2302  		{"in 2 days", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 2).Add(buf)}), "2d"},
  2303  		{"in months", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 90).Add(buf)}), "90d"},
  2304  		{"in 10 years", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0).Add(buf)}), "10y"},
  2305  	}
  2306  	for _, test := range tl {
  2307  		if test.got != test.exp {
  2308  			t.Errorf("On %v, expected '%v', but got '%v'",
  2309  				test.name, test.exp, test.got)
  2310  		}
  2311  	}
  2312  }
  2313  
  2314  func TestPrintDeployment(t *testing.T) {
  2315  
  2316  	testDeployment := apps.Deployment{
  2317  		ObjectMeta: metav1.ObjectMeta{
  2318  			Name:              "test1",
  2319  			CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2320  		},
  2321  		Spec: apps.DeploymentSpec{
  2322  			Replicas: 5,
  2323  			Template: api.PodTemplateSpec{
  2324  				Spec: api.PodSpec{
  2325  					Containers: []api.Container{
  2326  						{
  2327  							Name:  "fake-container1",
  2328  							Image: "fake-image1",
  2329  						},
  2330  						{
  2331  							Name:  "fake-container2",
  2332  							Image: "fake-image2",
  2333  						},
  2334  					},
  2335  				},
  2336  			},
  2337  			Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  2338  		},
  2339  		Status: apps.DeploymentStatus{
  2340  			Replicas:            10,
  2341  			UpdatedReplicas:     2,
  2342  			AvailableReplicas:   1,
  2343  			UnavailableReplicas: 4,
  2344  		},
  2345  	}
  2346  
  2347  	tests := []struct {
  2348  		deployment apps.Deployment
  2349  		options    printers.GenerateOptions
  2350  		expected   []metav1.TableRow
  2351  	}{
  2352  		// Test Deployment with no generate options.
  2353  		{
  2354  			deployment: testDeployment,
  2355  			options:    printers.GenerateOptions{},
  2356  			// Columns: Name, ReadyReplicas, UpdatedReplicas, AvailableReplicas, Age
  2357  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "0/5", int64(2), int64(1), "0s"}}},
  2358  		},
  2359  		// Test generate options: Wide.
  2360  		{
  2361  			deployment: testDeployment,
  2362  			options:    printers.GenerateOptions{Wide: true},
  2363  			// Columns: Name, ReadyReplicas, UpdatedReplicas, AvailableReplicas, Age, Containers, Images, Selectors
  2364  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "0/5", int64(2), int64(1), "0s", "fake-container1,fake-container2", "fake-image1,fake-image2", "foo=bar"}}},
  2365  		},
  2366  	}
  2367  
  2368  	for i, test := range tests {
  2369  		rows, err := printDeployment(&test.deployment, test.options)
  2370  		if err != nil {
  2371  			t.Fatal(err)
  2372  		}
  2373  		for i := range rows {
  2374  			rows[i].Object.Object = nil
  2375  		}
  2376  		if !reflect.DeepEqual(test.expected, rows) {
  2377  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  2378  		}
  2379  	}
  2380  }
  2381  
  2382  func TestPrintDaemonSet(t *testing.T) {
  2383  
  2384  	testDaemonSet := apps.DaemonSet{
  2385  		ObjectMeta: metav1.ObjectMeta{
  2386  			Name:              "test1",
  2387  			CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2388  		},
  2389  		Spec: apps.DaemonSetSpec{
  2390  			Template: api.PodTemplateSpec{
  2391  				Spec: api.PodSpec{
  2392  					Containers: []api.Container{
  2393  						{
  2394  							Name:  "fake-container1",
  2395  							Image: "fake-image1",
  2396  						},
  2397  						{
  2398  							Name:  "fake-container2",
  2399  							Image: "fake-image2",
  2400  						},
  2401  					},
  2402  				},
  2403  			},
  2404  			Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  2405  		},
  2406  		Status: apps.DaemonSetStatus{
  2407  			CurrentNumberScheduled: 2,
  2408  			DesiredNumberScheduled: 3,
  2409  			NumberReady:            1,
  2410  			UpdatedNumberScheduled: 2,
  2411  			NumberAvailable:        0,
  2412  		},
  2413  	}
  2414  
  2415  	tests := []struct {
  2416  		daemonSet apps.DaemonSet
  2417  		options   printers.GenerateOptions
  2418  		expected  []metav1.TableRow
  2419  	}{
  2420  		// Test generate daemon set with no generate options.
  2421  		{
  2422  			daemonSet: testDaemonSet,
  2423  			options:   printers.GenerateOptions{},
  2424  			// Columns: Name, Num Desired, Num Current, Num Ready, Num Updated, Num Available, Selectors, Age
  2425  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", int64(3), int64(2), int64(1), int64(2), int64(0), "<none>", "0s"}}},
  2426  		},
  2427  		// Test generate daemon set with "Wide" generate options.
  2428  		{
  2429  			daemonSet: testDaemonSet,
  2430  			options:   printers.GenerateOptions{Wide: true},
  2431  			// Columns: Name, Num Desired, Num Current, Num Ready, Num Updated, Num Available, Node Selectors, Age, Containers, Images, Labels
  2432  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", int64(3), int64(2), int64(1), int64(2), int64(0), "<none>", "0s", "fake-container1,fake-container2", "fake-image1,fake-image2", "foo=bar"}}},
  2433  		},
  2434  	}
  2435  
  2436  	for i, test := range tests {
  2437  		rows, err := printDaemonSet(&test.daemonSet, test.options)
  2438  		if err != nil {
  2439  			t.Fatal(err)
  2440  		}
  2441  		for i := range rows {
  2442  			rows[i].Object.Object = nil
  2443  		}
  2444  		if !reflect.DeepEqual(test.expected, rows) {
  2445  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  2446  		}
  2447  	}
  2448  }
  2449  
  2450  func TestPrintDaemonSetList(t *testing.T) {
  2451  
  2452  	daemonSetList := apps.DaemonSetList{
  2453  		Items: []apps.DaemonSet{
  2454  			{
  2455  				ObjectMeta: metav1.ObjectMeta{
  2456  					Name:              "daemonset1",
  2457  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2458  				},
  2459  				Spec: apps.DaemonSetSpec{
  2460  					Template: api.PodTemplateSpec{
  2461  						Spec: api.PodSpec{
  2462  							Containers: []api.Container{
  2463  								{
  2464  									Name:  "fake-container1",
  2465  									Image: "fake-image1",
  2466  								},
  2467  								{
  2468  									Name:  "fake-container2",
  2469  									Image: "fake-image2",
  2470  								},
  2471  							},
  2472  						},
  2473  					},
  2474  				},
  2475  				Status: apps.DaemonSetStatus{
  2476  					CurrentNumberScheduled: 2,
  2477  					DesiredNumberScheduled: 3,
  2478  					NumberReady:            1,
  2479  					UpdatedNumberScheduled: 2,
  2480  					NumberAvailable:        0,
  2481  				},
  2482  			},
  2483  			{
  2484  				ObjectMeta: metav1.ObjectMeta{
  2485  					Name:              "daemonset2",
  2486  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2487  				},
  2488  				Spec: apps.DaemonSetSpec{
  2489  					Template: api.PodTemplateSpec{},
  2490  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  2491  				},
  2492  				Status: apps.DaemonSetStatus{
  2493  					CurrentNumberScheduled: 4,
  2494  					DesiredNumberScheduled: 2,
  2495  					NumberReady:            9,
  2496  					UpdatedNumberScheduled: 3,
  2497  					NumberAvailable:        3,
  2498  				},
  2499  			},
  2500  		},
  2501  	}
  2502  
  2503  	// Columns: Name, Num Desired, Num Current, Num Ready, Num Updated, Num Available, Selectors, Age
  2504  	expectedRows := []metav1.TableRow{
  2505  		{Cells: []interface{}{"daemonset1", int64(3), int64(2), int64(1), int64(2), int64(0), "<none>", "0s"}},
  2506  		{Cells: []interface{}{"daemonset2", int64(2), int64(4), int64(9), int64(3), int64(3), "<none>", "0s"}},
  2507  	}
  2508  
  2509  	rows, err := printDaemonSetList(&daemonSetList, printers.GenerateOptions{})
  2510  	if err != nil {
  2511  		t.Fatalf("Error printing daemon set list: %#v", err)
  2512  	}
  2513  	for i := range rows {
  2514  		rows[i].Object.Object = nil
  2515  	}
  2516  	if !reflect.DeepEqual(expectedRows, rows) {
  2517  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  2518  	}
  2519  }
  2520  
  2521  func TestPrintJob(t *testing.T) {
  2522  	now := time.Now()
  2523  	completions := int32(2)
  2524  	tests := []struct {
  2525  		job      batch.Job
  2526  		options  printers.GenerateOptions
  2527  		expected []metav1.TableRow
  2528  	}{
  2529  		{
  2530  			// Generate table rows for Job with no generate options.
  2531  			job: batch.Job{
  2532  				ObjectMeta: metav1.ObjectMeta{
  2533  					Name:              "job1",
  2534  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2535  				},
  2536  				Spec: batch.JobSpec{
  2537  					Completions: &completions,
  2538  					Template: api.PodTemplateSpec{
  2539  						Spec: api.PodSpec{
  2540  							Containers: []api.Container{
  2541  								{
  2542  									Name:  "fake-job-container1",
  2543  									Image: "fake-job-image1",
  2544  								},
  2545  								{
  2546  									Name:  "fake-job-container2",
  2547  									Image: "fake-job-image2",
  2548  								},
  2549  							},
  2550  						},
  2551  					},
  2552  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"job-label": "job-lable-value"}},
  2553  				},
  2554  				Status: batch.JobStatus{
  2555  					Succeeded: 1,
  2556  				},
  2557  			},
  2558  			options: printers.GenerateOptions{},
  2559  			// Columns: Name, Status, Completions, Duration, Age
  2560  			expected: []metav1.TableRow{{Cells: []interface{}{"job1", "Running", "1/2", "", "0s"}}},
  2561  		},
  2562  		// Generate table rows for Job with generate options "Wide".
  2563  		{
  2564  			job: batch.Job{
  2565  				ObjectMeta: metav1.ObjectMeta{
  2566  					Name:              "job1",
  2567  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2568  				},
  2569  				Spec: batch.JobSpec{
  2570  					Completions: &completions,
  2571  					Template: api.PodTemplateSpec{
  2572  						Spec: api.PodSpec{
  2573  							Containers: []api.Container{
  2574  								{
  2575  									Name:  "fake-job-container1",
  2576  									Image: "fake-job-image1",
  2577  								},
  2578  								{
  2579  									Name:  "fake-job-container2",
  2580  									Image: "fake-job-image2",
  2581  								},
  2582  							},
  2583  						},
  2584  					},
  2585  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"job-label": "job-label-value"}},
  2586  				},
  2587  				Status: batch.JobStatus{
  2588  					Succeeded: 1,
  2589  				},
  2590  			},
  2591  			options: printers.GenerateOptions{Wide: true},
  2592  			// Columns: Name, Status, Completions, Duration, Age, Containers, Images, Selectors
  2593  			expected: []metav1.TableRow{
  2594  				{
  2595  					Cells: []interface{}{"job1", "Running", "1/2", "", "0s", "fake-job-container1,fake-job-container2", "fake-job-image1,fake-job-image2", "job-label=job-label-value"},
  2596  				},
  2597  			},
  2598  		},
  2599  		// Job with ten-year age.
  2600  		{
  2601  			job: batch.Job{
  2602  				ObjectMeta: metav1.ObjectMeta{
  2603  					Name:              "job2",
  2604  					CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2605  				},
  2606  				Spec: batch.JobSpec{
  2607  					Completions: nil,
  2608  				},
  2609  				Status: batch.JobStatus{
  2610  					Succeeded: 0,
  2611  				},
  2612  			},
  2613  			options: printers.GenerateOptions{},
  2614  			// Columns: Name, Status, Completions, Duration, Age
  2615  			expected: []metav1.TableRow{{Cells: []interface{}{"job2", "Running", "0/1", "", "10y"}}},
  2616  		},
  2617  		// Job with duration.
  2618  		{
  2619  			job: batch.Job{
  2620  				ObjectMeta: metav1.ObjectMeta{
  2621  					Name:              "job3",
  2622  					CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2623  				},
  2624  				Spec: batch.JobSpec{
  2625  					Completions: nil,
  2626  				},
  2627  				Status: batch.JobStatus{
  2628  					Succeeded:      0,
  2629  					StartTime:      &metav1.Time{Time: now.Add(time.Minute)},
  2630  					CompletionTime: &metav1.Time{Time: now.Add(31 * time.Minute)},
  2631  				},
  2632  			},
  2633  			options: printers.GenerateOptions{},
  2634  			// Columns: Name, Status, Completions, Duration, Age
  2635  			expected: []metav1.TableRow{{Cells: []interface{}{"job3", "Running", "0/1", "30m", "10y"}}},
  2636  		},
  2637  		{
  2638  			job: batch.Job{
  2639  				ObjectMeta: metav1.ObjectMeta{
  2640  					Name:              "job4",
  2641  					CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  2642  				},
  2643  				Spec: batch.JobSpec{
  2644  					Completions: nil,
  2645  				},
  2646  				Status: batch.JobStatus{
  2647  					Succeeded: 0,
  2648  					StartTime: &metav1.Time{Time: time.Now().Add(-20 * time.Minute)},
  2649  				},
  2650  			},
  2651  			options: printers.GenerateOptions{},
  2652  			// Columns: Name, Status, Completions, Duration, Age
  2653  			expected: []metav1.TableRow{{Cells: []interface{}{"job4", "Running", "0/1", "20m", "10y"}}},
  2654  		},
  2655  		{
  2656  			job: batch.Job{
  2657  				ObjectMeta: metav1.ObjectMeta{
  2658  					Name:              "job5",
  2659  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2660  				},
  2661  				Spec: batch.JobSpec{
  2662  					Completions: nil,
  2663  				},
  2664  				Status: batch.JobStatus{
  2665  					Succeeded: 0,
  2666  					Conditions: []batch.JobCondition{
  2667  						{
  2668  							Type:   batch.JobComplete,
  2669  							Status: api.ConditionTrue,
  2670  						},
  2671  					},
  2672  				},
  2673  			},
  2674  			options: printers.GenerateOptions{},
  2675  			// Columns: Name, Status, Completions, Duration, Age
  2676  			expected: []metav1.TableRow{{Cells: []interface{}{"job5", "Complete", "0/1", "", "0s"}}},
  2677  		},
  2678  		{
  2679  			job: batch.Job{
  2680  				ObjectMeta: metav1.ObjectMeta{
  2681  					Name:              "job6",
  2682  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2683  				},
  2684  				Spec: batch.JobSpec{
  2685  					Completions: nil,
  2686  				},
  2687  				Status: batch.JobStatus{
  2688  					Succeeded: 0,
  2689  					Conditions: []batch.JobCondition{
  2690  						{
  2691  							Type:   batch.JobFailed,
  2692  							Status: api.ConditionTrue,
  2693  						},
  2694  					},
  2695  				},
  2696  			},
  2697  			options: printers.GenerateOptions{},
  2698  			// Columns: Name, Status, Completions, Duration, Age
  2699  			expected: []metav1.TableRow{{Cells: []interface{}{"job6", "Failed", "0/1", "", "0s"}}},
  2700  		},
  2701  		{
  2702  			job: batch.Job{
  2703  				ObjectMeta: metav1.ObjectMeta{
  2704  					Name:              "job7",
  2705  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2706  				},
  2707  				Spec: batch.JobSpec{
  2708  					Completions: nil,
  2709  				},
  2710  				Status: batch.JobStatus{
  2711  					Succeeded: 0,
  2712  					Conditions: []batch.JobCondition{
  2713  						{
  2714  							Type:   batch.JobSuspended,
  2715  							Status: api.ConditionTrue,
  2716  						},
  2717  					},
  2718  				},
  2719  			},
  2720  			options: printers.GenerateOptions{},
  2721  			// Columns: Name, Status, Completions, Duration, Age
  2722  			expected: []metav1.TableRow{{Cells: []interface{}{"job7", "Suspended", "0/1", "", "0s"}}},
  2723  		},
  2724  		{
  2725  			job: batch.Job{
  2726  				ObjectMeta: metav1.ObjectMeta{
  2727  					Name:              "job8",
  2728  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2729  				},
  2730  				Spec: batch.JobSpec{
  2731  					Completions: nil,
  2732  				},
  2733  				Status: batch.JobStatus{
  2734  					Succeeded: 0,
  2735  					Conditions: []batch.JobCondition{
  2736  						{
  2737  							Type:   batch.JobFailureTarget,
  2738  							Status: api.ConditionTrue,
  2739  						},
  2740  					},
  2741  				},
  2742  			},
  2743  			options: printers.GenerateOptions{},
  2744  			// Columns: Name, Status, Completions, Duration, Age
  2745  			expected: []metav1.TableRow{{Cells: []interface{}{"job8", "FailureTarget", "0/1", "", "0s"}}},
  2746  		},
  2747  		{
  2748  			job: batch.Job{
  2749  				ObjectMeta: metav1.ObjectMeta{
  2750  					Name:              "job9",
  2751  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2752  					DeletionTimestamp: &metav1.Time{Time: time.Now().Add(1.9e9)},
  2753  				},
  2754  				Spec: batch.JobSpec{
  2755  					Completions: nil,
  2756  				},
  2757  			},
  2758  			options: printers.GenerateOptions{},
  2759  			// Columns: Name, Status, Completions, Duration, Age
  2760  			expected: []metav1.TableRow{{Cells: []interface{}{"job9", "Terminating", "0/1", "", "0s"}}},
  2761  		},
  2762  	}
  2763  
  2764  	for i, test := range tests {
  2765  		rows, err := printJob(&test.job, test.options)
  2766  		if err != nil {
  2767  			t.Fatal(err)
  2768  		}
  2769  		for i := range rows {
  2770  			rows[i].Object.Object = nil
  2771  		}
  2772  		if !reflect.DeepEqual(test.expected, rows) {
  2773  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  2774  		}
  2775  	}
  2776  }
  2777  
  2778  func TestPrintJobList(t *testing.T) {
  2779  	completions := int32(2)
  2780  	jobList := batch.JobList{
  2781  		Items: []batch.Job{
  2782  			{
  2783  				ObjectMeta: metav1.ObjectMeta{
  2784  					Name:              "job1",
  2785  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2786  				},
  2787  				Spec: batch.JobSpec{
  2788  					Completions: &completions,
  2789  					Template: api.PodTemplateSpec{
  2790  						Spec: api.PodSpec{
  2791  							Containers: []api.Container{
  2792  								{
  2793  									Name:  "fake-job-container1",
  2794  									Image: "fake-job-image1",
  2795  								},
  2796  								{
  2797  									Name:  "fake-job-container2",
  2798  									Image: "fake-job-image2",
  2799  								},
  2800  							},
  2801  						},
  2802  					},
  2803  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"job-label": "job-lable-value"}},
  2804  				},
  2805  				Status: batch.JobStatus{
  2806  					Succeeded: 1,
  2807  				},
  2808  			},
  2809  			{
  2810  				ObjectMeta: metav1.ObjectMeta{
  2811  					Name:              "job2",
  2812  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  2813  				},
  2814  				Spec: batch.JobSpec{
  2815  					Completions: &completions,
  2816  					Template: api.PodTemplateSpec{
  2817  						Spec: api.PodSpec{
  2818  							Containers: []api.Container{
  2819  								{
  2820  									Name:  "fake-job-container1",
  2821  									Image: "fake-job-image1",
  2822  								},
  2823  								{
  2824  									Name:  "fake-job-container2",
  2825  									Image: "fake-job-image2",
  2826  								},
  2827  							},
  2828  						},
  2829  					},
  2830  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"job-label": "job-lable-value"}},
  2831  				},
  2832  				Status: batch.JobStatus{
  2833  					Succeeded: 2,
  2834  					StartTime: &metav1.Time{Time: time.Now().Add(-20 * time.Minute)},
  2835  				},
  2836  			},
  2837  		},
  2838  	}
  2839  
  2840  	// Columns: Name, Status, Completions, Duration, Age
  2841  	expectedRows := []metav1.TableRow{
  2842  		{Cells: []interface{}{"job1", "Running", "1/2", "", "0s"}},
  2843  		{Cells: []interface{}{"job2", "Running", "2/2", "20m", "0s"}},
  2844  	}
  2845  
  2846  	rows, err := printJobList(&jobList, printers.GenerateOptions{})
  2847  	if err != nil {
  2848  		t.Fatalf("Error printing job list: %#v", err)
  2849  	}
  2850  	for i := range rows {
  2851  		rows[i].Object.Object = nil
  2852  	}
  2853  	if !reflect.DeepEqual(expectedRows, rows) {
  2854  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  2855  	}
  2856  }
  2857  
  2858  func TestPrintHPA(t *testing.T) {
  2859  	minReplicasVal := int32(2)
  2860  	targetUtilizationVal := int32(80)
  2861  	currentUtilizationVal := int32(50)
  2862  	metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
  2863  	if err != nil {
  2864  		t.Errorf("unable to parse label selector: %v", err)
  2865  	}
  2866  	tests := []struct {
  2867  		hpa      autoscaling.HorizontalPodAutoscaler
  2868  		expected []metav1.TableRow
  2869  	}{
  2870  		// minReplicas unset
  2871  		{
  2872  			hpa: autoscaling.HorizontalPodAutoscaler{
  2873  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2874  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2875  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2876  						Name: "some-rc",
  2877  						Kind: "ReplicationController",
  2878  					},
  2879  					MaxReplicas: 10,
  2880  				},
  2881  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  2882  					CurrentReplicas: 4,
  2883  					DesiredReplicas: 5,
  2884  				},
  2885  			},
  2886  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  2887  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<none>", "<unset>", int64(10), int64(4), "<unknown>"}}},
  2888  		},
  2889  		// external source type, target average value (no current)
  2890  		{
  2891  			hpa: autoscaling.HorizontalPodAutoscaler{
  2892  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2893  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2894  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2895  						Name: "some-rc",
  2896  						Kind: "ReplicationController",
  2897  					},
  2898  					MinReplicas: &minReplicasVal,
  2899  					MaxReplicas: 10,
  2900  					Metrics: []autoscaling.MetricSpec{
  2901  						{
  2902  							Type: autoscaling.ExternalMetricSourceType,
  2903  							External: &autoscaling.ExternalMetricSource{
  2904  								Metric: autoscaling.MetricIdentifier{
  2905  									Name:     "some-external-metric",
  2906  									Selector: metricLabelSelector,
  2907  								},
  2908  								Target: autoscaling.MetricTarget{
  2909  									Type:         autoscaling.AverageValueMetricType,
  2910  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2911  								},
  2912  							},
  2913  						},
  2914  					},
  2915  				},
  2916  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  2917  					CurrentReplicas: 4,
  2918  					DesiredReplicas: 5,
  2919  				},
  2920  			},
  2921  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  2922  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/100m (avg)", "2", int64(10), int64(4), "<unknown>"}}},
  2923  		},
  2924  		// external source type, target average value
  2925  		{
  2926  			hpa: autoscaling.HorizontalPodAutoscaler{
  2927  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2928  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2929  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2930  						Name: "some-rc",
  2931  						Kind: "ReplicationController",
  2932  					},
  2933  					MinReplicas: &minReplicasVal,
  2934  					MaxReplicas: 10,
  2935  					Metrics: []autoscaling.MetricSpec{
  2936  						{
  2937  							Type: autoscaling.ExternalMetricSourceType,
  2938  							External: &autoscaling.ExternalMetricSource{
  2939  								Metric: autoscaling.MetricIdentifier{
  2940  									Name:     "some-external-metric",
  2941  									Selector: metricLabelSelector,
  2942  								},
  2943  								Target: autoscaling.MetricTarget{
  2944  									Type:         autoscaling.AverageValueMetricType,
  2945  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  2946  								},
  2947  							},
  2948  						},
  2949  					},
  2950  				},
  2951  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  2952  					CurrentReplicas: 4,
  2953  					DesiredReplicas: 5,
  2954  					CurrentMetrics: []autoscaling.MetricStatus{
  2955  						{
  2956  							Type: autoscaling.ExternalMetricSourceType,
  2957  							External: &autoscaling.ExternalMetricStatus{
  2958  								Metric: autoscaling.MetricIdentifier{
  2959  									Name:     "some-external-metric",
  2960  									Selector: metricLabelSelector,
  2961  								},
  2962  								Current: autoscaling.MetricValueStatus{
  2963  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  2964  								},
  2965  							},
  2966  						},
  2967  					},
  2968  				},
  2969  			},
  2970  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  2971  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m (avg)", "2", int64(10), int64(4), "<unknown>"}}},
  2972  		},
  2973  		// external source type, target value (no current)
  2974  		{
  2975  			hpa: autoscaling.HorizontalPodAutoscaler{
  2976  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  2977  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  2978  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  2979  						Name: "some-rc",
  2980  						Kind: "ReplicationController",
  2981  					},
  2982  					MinReplicas: &minReplicasVal,
  2983  					MaxReplicas: 10,
  2984  					Metrics: []autoscaling.MetricSpec{
  2985  						{
  2986  							Type: autoscaling.ExternalMetricSourceType,
  2987  							External: &autoscaling.ExternalMetricSource{
  2988  								Metric: autoscaling.MetricIdentifier{
  2989  									Name:     "some-service-metric",
  2990  									Selector: metricLabelSelector,
  2991  								},
  2992  								Target: autoscaling.MetricTarget{
  2993  									Type:  autoscaling.ValueMetricType,
  2994  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  2995  								},
  2996  							},
  2997  						},
  2998  					},
  2999  				},
  3000  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3001  					CurrentReplicas: 4,
  3002  					DesiredReplicas: 5,
  3003  				},
  3004  			},
  3005  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3006  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3007  		},
  3008  		// external source type, target value
  3009  		{
  3010  			hpa: autoscaling.HorizontalPodAutoscaler{
  3011  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3012  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3013  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3014  						Name: "some-rc",
  3015  						Kind: "ReplicationController",
  3016  					},
  3017  					MinReplicas: &minReplicasVal,
  3018  					MaxReplicas: 10,
  3019  					Metrics: []autoscaling.MetricSpec{
  3020  						{
  3021  							Type: autoscaling.ExternalMetricSourceType,
  3022  							External: &autoscaling.ExternalMetricSource{
  3023  								Metric: autoscaling.MetricIdentifier{
  3024  									Name:     "some-external-metric",
  3025  									Selector: metricLabelSelector,
  3026  								},
  3027  								Target: autoscaling.MetricTarget{
  3028  									Type:  autoscaling.ValueMetricType,
  3029  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3030  								},
  3031  							},
  3032  						},
  3033  					},
  3034  				},
  3035  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3036  					CurrentReplicas: 4,
  3037  					DesiredReplicas: 5,
  3038  					CurrentMetrics: []autoscaling.MetricStatus{
  3039  						{
  3040  							Type: autoscaling.ExternalMetricSourceType,
  3041  							External: &autoscaling.ExternalMetricStatus{
  3042  								Metric: autoscaling.MetricIdentifier{
  3043  									Name: "some-external-metric",
  3044  								},
  3045  								Current: autoscaling.MetricValueStatus{
  3046  									Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  3047  								},
  3048  							},
  3049  						},
  3050  					},
  3051  				},
  3052  			},
  3053  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3054  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3055  		},
  3056  		// pods source type (no current)
  3057  		{
  3058  			hpa: autoscaling.HorizontalPodAutoscaler{
  3059  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3060  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3061  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3062  						Name: "some-rc",
  3063  						Kind: "ReplicationController",
  3064  					},
  3065  					MinReplicas: &minReplicasVal,
  3066  					MaxReplicas: 10,
  3067  					Metrics: []autoscaling.MetricSpec{
  3068  						{
  3069  							Type: autoscaling.PodsMetricSourceType,
  3070  							Pods: &autoscaling.PodsMetricSource{
  3071  								Metric: autoscaling.MetricIdentifier{
  3072  									Name: "some-pods-metric",
  3073  								},
  3074  								Target: autoscaling.MetricTarget{
  3075  									Type:         autoscaling.AverageValueMetricType,
  3076  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3077  								},
  3078  							},
  3079  						},
  3080  					},
  3081  				},
  3082  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3083  					CurrentReplicas: 4,
  3084  					DesiredReplicas: 5,
  3085  				},
  3086  			},
  3087  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3088  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3089  		},
  3090  		// pods source type
  3091  		{
  3092  			hpa: autoscaling.HorizontalPodAutoscaler{
  3093  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3094  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3095  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3096  						Name: "some-rc",
  3097  						Kind: "ReplicationController",
  3098  					},
  3099  					MinReplicas: &minReplicasVal,
  3100  					MaxReplicas: 10,
  3101  					Metrics: []autoscaling.MetricSpec{
  3102  						{
  3103  							Type: autoscaling.PodsMetricSourceType,
  3104  							Pods: &autoscaling.PodsMetricSource{
  3105  								Metric: autoscaling.MetricIdentifier{
  3106  									Name: "some-pods-metric",
  3107  								},
  3108  								Target: autoscaling.MetricTarget{
  3109  									Type:         autoscaling.AverageValueMetricType,
  3110  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3111  								},
  3112  							},
  3113  						},
  3114  					},
  3115  				},
  3116  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3117  					CurrentReplicas: 4,
  3118  					DesiredReplicas: 5,
  3119  					CurrentMetrics: []autoscaling.MetricStatus{
  3120  						{
  3121  							Type: autoscaling.PodsMetricSourceType,
  3122  							Pods: &autoscaling.PodsMetricStatus{
  3123  								Metric: autoscaling.MetricIdentifier{
  3124  									Name: "some-pods-metric",
  3125  								},
  3126  								Current: autoscaling.MetricValueStatus{
  3127  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3128  								},
  3129  							},
  3130  						},
  3131  					},
  3132  				},
  3133  			},
  3134  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3135  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3136  		},
  3137  		// object source type (no current)
  3138  		{
  3139  			hpa: autoscaling.HorizontalPodAutoscaler{
  3140  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3141  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3142  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3143  						Name: "some-rc",
  3144  						Kind: "ReplicationController",
  3145  					},
  3146  					MinReplicas: &minReplicasVal,
  3147  					MaxReplicas: 10,
  3148  					Metrics: []autoscaling.MetricSpec{
  3149  						{
  3150  							Type: autoscaling.ObjectMetricSourceType,
  3151  							Object: &autoscaling.ObjectMetricSource{
  3152  								DescribedObject: autoscaling.CrossVersionObjectReference{
  3153  									Name: "some-service",
  3154  									Kind: "Service",
  3155  								},
  3156  								Metric: autoscaling.MetricIdentifier{
  3157  									Name: "some-service-metric",
  3158  								},
  3159  								Target: autoscaling.MetricTarget{
  3160  									Type:  autoscaling.ValueMetricType,
  3161  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3162  								},
  3163  							},
  3164  						},
  3165  					},
  3166  				},
  3167  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3168  					CurrentReplicas: 4,
  3169  					DesiredReplicas: 5,
  3170  				},
  3171  			},
  3172  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3173  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3174  		},
  3175  		// object source type
  3176  		{
  3177  			hpa: autoscaling.HorizontalPodAutoscaler{
  3178  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3179  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3180  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3181  						Name: "some-rc",
  3182  						Kind: "ReplicationController",
  3183  					},
  3184  					MinReplicas: &minReplicasVal,
  3185  					MaxReplicas: 10,
  3186  					Metrics: []autoscaling.MetricSpec{
  3187  						{
  3188  							Type: autoscaling.ObjectMetricSourceType,
  3189  							Object: &autoscaling.ObjectMetricSource{
  3190  								DescribedObject: autoscaling.CrossVersionObjectReference{
  3191  									Name: "some-service",
  3192  									Kind: "Service",
  3193  								},
  3194  								Metric: autoscaling.MetricIdentifier{
  3195  									Name: "some-service-metric",
  3196  								},
  3197  								Target: autoscaling.MetricTarget{
  3198  									Type:  autoscaling.ValueMetricType,
  3199  									Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  3200  								},
  3201  							},
  3202  						},
  3203  					},
  3204  				},
  3205  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3206  					CurrentReplicas: 4,
  3207  					DesiredReplicas: 5,
  3208  					CurrentMetrics: []autoscaling.MetricStatus{
  3209  						{
  3210  							Type: autoscaling.ObjectMetricSourceType,
  3211  							Object: &autoscaling.ObjectMetricStatus{
  3212  								DescribedObject: autoscaling.CrossVersionObjectReference{
  3213  									Name: "some-service",
  3214  									Kind: "Service",
  3215  								},
  3216  								Metric: autoscaling.MetricIdentifier{
  3217  									Name: "some-service-metric",
  3218  								},
  3219  								Current: autoscaling.MetricValueStatus{
  3220  									Value: resource.NewMilliQuantity(50, resource.DecimalSI),
  3221  								},
  3222  							},
  3223  						},
  3224  					},
  3225  				},
  3226  			},
  3227  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3228  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3229  		},
  3230  		// resource source type, targetVal (no current)
  3231  		{
  3232  			hpa: autoscaling.HorizontalPodAutoscaler{
  3233  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3234  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3235  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3236  						Name: "some-rc",
  3237  						Kind: "ReplicationController",
  3238  					},
  3239  					MinReplicas: &minReplicasVal,
  3240  					MaxReplicas: 10,
  3241  					Metrics: []autoscaling.MetricSpec{
  3242  						{
  3243  							Type: autoscaling.ResourceMetricSourceType,
  3244  							Resource: &autoscaling.ResourceMetricSource{
  3245  								Name: api.ResourceCPU,
  3246  								Target: autoscaling.MetricTarget{
  3247  									Type:         autoscaling.AverageValueMetricType,
  3248  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3249  								},
  3250  							},
  3251  						},
  3252  					},
  3253  				},
  3254  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3255  					CurrentReplicas: 4,
  3256  					DesiredReplicas: 5,
  3257  				},
  3258  			},
  3259  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3260  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: <unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3261  		},
  3262  		// resource source type, targetVal
  3263  		{
  3264  			hpa: autoscaling.HorizontalPodAutoscaler{
  3265  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3266  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3267  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3268  						Name: "some-rc",
  3269  						Kind: "ReplicationController",
  3270  					},
  3271  					MinReplicas: &minReplicasVal,
  3272  					MaxReplicas: 10,
  3273  					Metrics: []autoscaling.MetricSpec{
  3274  						{
  3275  							Type: autoscaling.ResourceMetricSourceType,
  3276  							Resource: &autoscaling.ResourceMetricSource{
  3277  								Name: api.ResourceCPU,
  3278  								Target: autoscaling.MetricTarget{
  3279  									Type:         autoscaling.AverageValueMetricType,
  3280  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3281  								},
  3282  							},
  3283  						},
  3284  					},
  3285  				},
  3286  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3287  					CurrentReplicas: 4,
  3288  					DesiredReplicas: 5,
  3289  					CurrentMetrics: []autoscaling.MetricStatus{
  3290  						{
  3291  							Type: autoscaling.ResourceMetricSourceType,
  3292  							Resource: &autoscaling.ResourceMetricStatus{
  3293  								Name: api.ResourceCPU,
  3294  								Current: autoscaling.MetricValueStatus{
  3295  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3296  								},
  3297  							},
  3298  						},
  3299  					},
  3300  				},
  3301  			},
  3302  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3303  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: 50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3304  		},
  3305  		// resource source type, targetUtil (no current)
  3306  		{
  3307  			hpa: autoscaling.HorizontalPodAutoscaler{
  3308  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3309  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3310  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3311  						Name: "some-rc",
  3312  						Kind: "ReplicationController",
  3313  					},
  3314  					MinReplicas: &minReplicasVal,
  3315  					MaxReplicas: 10,
  3316  					Metrics: []autoscaling.MetricSpec{
  3317  						{
  3318  							Type: autoscaling.ResourceMetricSourceType,
  3319  							Resource: &autoscaling.ResourceMetricSource{
  3320  								Name: api.ResourceCPU,
  3321  								Target: autoscaling.MetricTarget{
  3322  									Type:               autoscaling.UtilizationMetricType,
  3323  									AverageUtilization: &targetUtilizationVal,
  3324  								},
  3325  							},
  3326  						},
  3327  					},
  3328  				},
  3329  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3330  					CurrentReplicas: 4,
  3331  					DesiredReplicas: 5,
  3332  				},
  3333  			},
  3334  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3335  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: <unknown>/80%", "2", int64(10), int64(4), "<unknown>"}}},
  3336  		},
  3337  		// resource source type, targetUtil
  3338  		{
  3339  			hpa: autoscaling.HorizontalPodAutoscaler{
  3340  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3341  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3342  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3343  						Name: "some-rc",
  3344  						Kind: "ReplicationController",
  3345  					},
  3346  					MinReplicas: &minReplicasVal,
  3347  					MaxReplicas: 10,
  3348  					Metrics: []autoscaling.MetricSpec{
  3349  						{
  3350  							Type: autoscaling.ResourceMetricSourceType,
  3351  							Resource: &autoscaling.ResourceMetricSource{
  3352  								Name: api.ResourceCPU,
  3353  								Target: autoscaling.MetricTarget{
  3354  									Type:               autoscaling.UtilizationMetricType,
  3355  									AverageUtilization: &targetUtilizationVal,
  3356  								},
  3357  							},
  3358  						},
  3359  					},
  3360  				},
  3361  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3362  					CurrentReplicas: 4,
  3363  					DesiredReplicas: 5,
  3364  					CurrentMetrics: []autoscaling.MetricStatus{
  3365  						{
  3366  							Type: autoscaling.ResourceMetricSourceType,
  3367  							Resource: &autoscaling.ResourceMetricStatus{
  3368  								Name: api.ResourceCPU,
  3369  								Current: autoscaling.MetricValueStatus{
  3370  									AverageUtilization: &currentUtilizationVal,
  3371  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  3372  								},
  3373  							},
  3374  						},
  3375  					},
  3376  				},
  3377  			},
  3378  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3379  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: 50%/80%", "2", int64(10), int64(4), "<unknown>"}}},
  3380  		},
  3381  		// container resource source type, targetVal (no current)
  3382  		{
  3383  			hpa: autoscaling.HorizontalPodAutoscaler{
  3384  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3385  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3386  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3387  						Name: "some-rc",
  3388  						Kind: "ReplicationController",
  3389  					},
  3390  					MinReplicas: &minReplicasVal,
  3391  					MaxReplicas: 10,
  3392  					Metrics: []autoscaling.MetricSpec{
  3393  						{
  3394  							Type: autoscaling.ContainerResourceMetricSourceType,
  3395  							ContainerResource: &autoscaling.ContainerResourceMetricSource{
  3396  								Name:      api.ResourceCPU,
  3397  								Container: "application",
  3398  								Target: autoscaling.MetricTarget{
  3399  									Type:         autoscaling.AverageValueMetricType,
  3400  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3401  								},
  3402  							},
  3403  						},
  3404  					},
  3405  				},
  3406  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3407  					CurrentReplicas: 4,
  3408  					DesiredReplicas: 5,
  3409  				},
  3410  			},
  3411  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3412  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: <unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3413  		},
  3414  		// container resource source type, targetVal
  3415  		{
  3416  			hpa: autoscaling.HorizontalPodAutoscaler{
  3417  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3418  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3419  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3420  						Name: "some-rc",
  3421  						Kind: "ReplicationController",
  3422  					},
  3423  					MinReplicas: &minReplicasVal,
  3424  					MaxReplicas: 10,
  3425  					Metrics: []autoscaling.MetricSpec{
  3426  						{
  3427  							Type: autoscaling.ContainerResourceMetricSourceType,
  3428  							ContainerResource: &autoscaling.ContainerResourceMetricSource{
  3429  								Name: api.ResourceCPU,
  3430  								Target: autoscaling.MetricTarget{
  3431  									Type:         autoscaling.AverageValueMetricType,
  3432  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3433  								},
  3434  							},
  3435  						},
  3436  					},
  3437  				},
  3438  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3439  					CurrentReplicas: 4,
  3440  					DesiredReplicas: 5,
  3441  					CurrentMetrics: []autoscaling.MetricStatus{
  3442  						{
  3443  							Type: autoscaling.ContainerResourceMetricSourceType,
  3444  							ContainerResource: &autoscaling.ContainerResourceMetricStatus{
  3445  								Name:      api.ResourceCPU,
  3446  								Container: "application",
  3447  								Current: autoscaling.MetricValueStatus{
  3448  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3449  								},
  3450  							},
  3451  						},
  3452  					},
  3453  				},
  3454  			},
  3455  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3456  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: 50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
  3457  		},
  3458  		// container resource source type, targetUtil (no current)
  3459  		{
  3460  			hpa: autoscaling.HorizontalPodAutoscaler{
  3461  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3462  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3463  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3464  						Name: "some-rc",
  3465  						Kind: "ReplicationController",
  3466  					},
  3467  					MinReplicas: &minReplicasVal,
  3468  					MaxReplicas: 10,
  3469  					Metrics: []autoscaling.MetricSpec{
  3470  						{
  3471  							Type: autoscaling.ContainerResourceMetricSourceType,
  3472  							ContainerResource: &autoscaling.ContainerResourceMetricSource{
  3473  								Name:      api.ResourceCPU,
  3474  								Container: "application",
  3475  								Target: autoscaling.MetricTarget{
  3476  									Type:               autoscaling.UtilizationMetricType,
  3477  									AverageUtilization: &targetUtilizationVal,
  3478  								},
  3479  							},
  3480  						},
  3481  					},
  3482  				},
  3483  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3484  					CurrentReplicas: 4,
  3485  					DesiredReplicas: 5,
  3486  				},
  3487  			},
  3488  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3489  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: <unknown>/80%", "2", int64(10), int64(4), "<unknown>"}}},
  3490  		},
  3491  		// container resource source type, targetUtil
  3492  		{
  3493  			hpa: autoscaling.HorizontalPodAutoscaler{
  3494  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3495  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3496  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3497  						Name: "some-rc",
  3498  						Kind: "ReplicationController",
  3499  					},
  3500  					MinReplicas: &minReplicasVal,
  3501  					MaxReplicas: 10,
  3502  					Metrics: []autoscaling.MetricSpec{
  3503  						{
  3504  							Type: autoscaling.ContainerResourceMetricSourceType,
  3505  							ContainerResource: &autoscaling.ContainerResourceMetricSource{
  3506  								Name: api.ResourceCPU,
  3507  								Target: autoscaling.MetricTarget{
  3508  									Type:               autoscaling.UtilizationMetricType,
  3509  									AverageUtilization: &targetUtilizationVal,
  3510  								},
  3511  							},
  3512  						},
  3513  					},
  3514  				},
  3515  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3516  					CurrentReplicas: 4,
  3517  					DesiredReplicas: 5,
  3518  					CurrentMetrics: []autoscaling.MetricStatus{
  3519  						{
  3520  							Type: autoscaling.ContainerResourceMetricSourceType,
  3521  							ContainerResource: &autoscaling.ContainerResourceMetricStatus{
  3522  								Name:      api.ResourceCPU,
  3523  								Container: "application",
  3524  								Current: autoscaling.MetricValueStatus{
  3525  									AverageUtilization: &currentUtilizationVal,
  3526  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  3527  								},
  3528  							},
  3529  						},
  3530  					},
  3531  				},
  3532  			},
  3533  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3534  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "cpu: 50%/80%", "2", int64(10), int64(4), "<unknown>"}}},
  3535  		},
  3536  		// multiple specs
  3537  		{
  3538  			hpa: autoscaling.HorizontalPodAutoscaler{
  3539  				ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
  3540  				Spec: autoscaling.HorizontalPodAutoscalerSpec{
  3541  					ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  3542  						Name: "some-rc",
  3543  						Kind: "ReplicationController",
  3544  					},
  3545  					MinReplicas: &minReplicasVal,
  3546  					MaxReplicas: 10,
  3547  					Metrics: []autoscaling.MetricSpec{
  3548  						{
  3549  							Type: autoscaling.PodsMetricSourceType,
  3550  							Pods: &autoscaling.PodsMetricSource{
  3551  								Metric: autoscaling.MetricIdentifier{
  3552  									Name: "some-pods-metric",
  3553  								},
  3554  								Target: autoscaling.MetricTarget{
  3555  									Type:         autoscaling.AverageValueMetricType,
  3556  									AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  3557  								},
  3558  							},
  3559  						},
  3560  						{
  3561  							Type: autoscaling.ResourceMetricSourceType,
  3562  							Resource: &autoscaling.ResourceMetricSource{
  3563  								Name: api.ResourceCPU,
  3564  								Target: autoscaling.MetricTarget{
  3565  									Type:               autoscaling.UtilizationMetricType,
  3566  									AverageUtilization: &targetUtilizationVal,
  3567  								},
  3568  							},
  3569  						},
  3570  						{
  3571  							Type: autoscaling.PodsMetricSourceType,
  3572  							Pods: &autoscaling.PodsMetricSource{
  3573  								Metric: autoscaling.MetricIdentifier{
  3574  									Name: "other-pods-metric",
  3575  								},
  3576  								Target: autoscaling.MetricTarget{
  3577  									Type:         autoscaling.AverageValueMetricType,
  3578  									AverageValue: resource.NewMilliQuantity(400, resource.DecimalSI),
  3579  								},
  3580  							},
  3581  						},
  3582  					},
  3583  				},
  3584  				Status: autoscaling.HorizontalPodAutoscalerStatus{
  3585  					CurrentReplicas: 4,
  3586  					DesiredReplicas: 5,
  3587  					CurrentMetrics: []autoscaling.MetricStatus{
  3588  						{
  3589  							Type: autoscaling.PodsMetricSourceType,
  3590  							Pods: &autoscaling.PodsMetricStatus{
  3591  								Metric: autoscaling.MetricIdentifier{
  3592  									Name: "some-pods-metric",
  3593  								},
  3594  								Current: autoscaling.MetricValueStatus{
  3595  									AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
  3596  								},
  3597  							},
  3598  						},
  3599  						{
  3600  							Type: autoscaling.ResourceMetricSourceType,
  3601  							Resource: &autoscaling.ResourceMetricStatus{
  3602  								Name: api.ResourceCPU,
  3603  								Current: autoscaling.MetricValueStatus{
  3604  									AverageUtilization: &currentUtilizationVal,
  3605  									AverageValue:       resource.NewMilliQuantity(40, resource.DecimalSI),
  3606  								},
  3607  							},
  3608  						},
  3609  					},
  3610  				},
  3611  			},
  3612  			// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
  3613  			expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m, cpu: 50%/80% + 1 more...", "2", int64(10), int64(4), "<unknown>"}}},
  3614  		},
  3615  	}
  3616  
  3617  	for i, test := range tests {
  3618  		rows, err := printHorizontalPodAutoscaler(&test.hpa, printers.GenerateOptions{})
  3619  		if err != nil {
  3620  			t.Fatal(err)
  3621  		}
  3622  		for i := range rows {
  3623  			rows[i].Object.Object = nil
  3624  		}
  3625  		if !reflect.DeepEqual(test.expected, rows) {
  3626  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  3627  		}
  3628  	}
  3629  }
  3630  
  3631  func TestPrintService(t *testing.T) {
  3632  	singleExternalIP := []string{"80.11.12.10"}
  3633  	mulExternalIP := []string{"80.11.12.10", "80.11.12.11"}
  3634  	tests := []struct {
  3635  		service  api.Service
  3636  		options  printers.GenerateOptions
  3637  		expected []metav1.TableRow
  3638  	}{
  3639  		{
  3640  			// Test name, cluster ip, port with protocol
  3641  			service: api.Service{
  3642  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  3643  				Spec: api.ServiceSpec{
  3644  					Type: api.ServiceTypeClusterIP,
  3645  					Ports: []api.ServicePort{
  3646  						{
  3647  							Protocol: "tcp",
  3648  							Port:     2233,
  3649  						},
  3650  					},
  3651  					ClusterIPs: []string{"10.9.8.7"},
  3652  					Selector:   map[string]string{"foo": "bar"}, // Does NOT get printed.
  3653  				},
  3654  			},
  3655  			options: printers.GenerateOptions{},
  3656  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3657  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "ClusterIP", "10.9.8.7", "<none>", "2233/tcp", "<unknown>"}}},
  3658  		},
  3659  		{
  3660  			// Test generate options: Wide includes selectors.
  3661  			service: api.Service{
  3662  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  3663  				Spec: api.ServiceSpec{
  3664  					Type: api.ServiceTypeClusterIP,
  3665  					Ports: []api.ServicePort{
  3666  						{
  3667  							Protocol: "tcp",
  3668  							Port:     2233,
  3669  						},
  3670  					},
  3671  					ClusterIPs: []string{"10.9.8.7"},
  3672  					Selector:   map[string]string{"foo": "bar"},
  3673  				},
  3674  			},
  3675  			options: printers.GenerateOptions{Wide: true},
  3676  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age, Selector
  3677  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "ClusterIP", "10.9.8.7", "<none>", "2233/tcp", "<unknown>", "foo=bar"}}},
  3678  		},
  3679  		{
  3680  			// Test NodePort service
  3681  			service: api.Service{
  3682  				ObjectMeta: metav1.ObjectMeta{Name: "test2"},
  3683  				Spec: api.ServiceSpec{
  3684  					Type: api.ServiceTypeNodePort,
  3685  					Ports: []api.ServicePort{
  3686  						{
  3687  							Protocol: "tcp",
  3688  							Port:     8888,
  3689  							NodePort: 9999,
  3690  						},
  3691  					},
  3692  					ClusterIPs: []string{"10.9.8.7"},
  3693  				},
  3694  			},
  3695  			options: printers.GenerateOptions{},
  3696  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3697  			expected: []metav1.TableRow{{Cells: []interface{}{"test2", "NodePort", "10.9.8.7", "<none>", "8888:9999/tcp", "<unknown>"}}},
  3698  		},
  3699  		{
  3700  			// Test LoadBalancer service
  3701  			service: api.Service{
  3702  				ObjectMeta: metav1.ObjectMeta{Name: "test3"},
  3703  				Spec: api.ServiceSpec{
  3704  					Type: api.ServiceTypeLoadBalancer,
  3705  					Ports: []api.ServicePort{
  3706  						{
  3707  							Protocol: "tcp",
  3708  							Port:     8888,
  3709  						},
  3710  					},
  3711  					ClusterIPs: []string{"10.9.8.7"},
  3712  				},
  3713  			},
  3714  			options: printers.GenerateOptions{},
  3715  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3716  			expected: []metav1.TableRow{{Cells: []interface{}{"test3", "LoadBalancer", "10.9.8.7", "<pending>", "8888/tcp", "<unknown>"}}},
  3717  		},
  3718  		{
  3719  			// Test LoadBalancer service with single ExternalIP and no LoadBalancerStatus
  3720  			service: api.Service{
  3721  				ObjectMeta: metav1.ObjectMeta{Name: "test4"},
  3722  				Spec: api.ServiceSpec{
  3723  					Type: api.ServiceTypeLoadBalancer,
  3724  					Ports: []api.ServicePort{
  3725  						{
  3726  							Protocol: "tcp",
  3727  							Port:     8888,
  3728  						},
  3729  					},
  3730  					ClusterIPs:  []string{"10.9.8.7"},
  3731  					ExternalIPs: singleExternalIP,
  3732  				},
  3733  			},
  3734  			options: printers.GenerateOptions{},
  3735  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3736  			expected: []metav1.TableRow{{Cells: []interface{}{"test4", "LoadBalancer", "10.9.8.7", "80.11.12.10", "8888/tcp", "<unknown>"}}},
  3737  		},
  3738  		{
  3739  			// Test LoadBalancer service with single ExternalIP
  3740  			service: api.Service{
  3741  				ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  3742  				Spec: api.ServiceSpec{
  3743  					Type: api.ServiceTypeLoadBalancer,
  3744  					Ports: []api.ServicePort{
  3745  						{
  3746  							Protocol: "tcp",
  3747  							Port:     8888,
  3748  						},
  3749  					},
  3750  					ClusterIPs:  []string{"10.9.8.7"},
  3751  					ExternalIPs: singleExternalIP,
  3752  				},
  3753  				Status: api.ServiceStatus{
  3754  					LoadBalancer: api.LoadBalancerStatus{
  3755  						Ingress: []api.LoadBalancerIngress{
  3756  							{
  3757  								IP:       "3.4.5.6",
  3758  								Hostname: "test.cluster.com",
  3759  							},
  3760  						},
  3761  					},
  3762  				},
  3763  			},
  3764  			options: printers.GenerateOptions{},
  3765  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3766  			expected: []metav1.TableRow{{Cells: []interface{}{"test5", "LoadBalancer", "10.9.8.7", "3.4.5.6,80.11.12.10", "8888/tcp", "<unknown>"}}},
  3767  		},
  3768  		{
  3769  			// Test LoadBalancer service with mul ExternalIPs
  3770  			service: api.Service{
  3771  				ObjectMeta: metav1.ObjectMeta{Name: "test6"},
  3772  				Spec: api.ServiceSpec{
  3773  					Type: api.ServiceTypeLoadBalancer,
  3774  					Ports: []api.ServicePort{
  3775  						{
  3776  							Protocol: "tcp",
  3777  							Port:     8888,
  3778  						},
  3779  					},
  3780  					ClusterIPs:  []string{"10.9.8.7"},
  3781  					ExternalIPs: mulExternalIP,
  3782  				},
  3783  				Status: api.ServiceStatus{
  3784  					LoadBalancer: api.LoadBalancerStatus{
  3785  						Ingress: []api.LoadBalancerIngress{
  3786  							{
  3787  								IP:       "2.3.4.5",
  3788  								Hostname: "test.cluster.local",
  3789  							},
  3790  							{
  3791  								IP:       "3.4.5.6",
  3792  								Hostname: "test.cluster.com",
  3793  							},
  3794  						},
  3795  					},
  3796  				},
  3797  			},
  3798  			options: printers.GenerateOptions{},
  3799  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3800  			expected: []metav1.TableRow{{Cells: []interface{}{"test6", "LoadBalancer", "10.9.8.7", "2.3.4.5,3.4.5.6,80.11.12.10,80.11.12.11", "8888/tcp", "<unknown>"}}},
  3801  		},
  3802  		{
  3803  			// Test ExternalName service
  3804  			service: api.Service{
  3805  				ObjectMeta: metav1.ObjectMeta{Name: "test7"},
  3806  				Spec: api.ServiceSpec{
  3807  					Type:         api.ServiceTypeExternalName,
  3808  					ExternalName: "my.database.example.com",
  3809  				},
  3810  			},
  3811  			options: printers.GenerateOptions{},
  3812  			// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3813  			expected: []metav1.TableRow{{Cells: []interface{}{"test7", "ExternalName", "<none>", "my.database.example.com", "<none>", "<unknown>"}}},
  3814  		},
  3815  	}
  3816  
  3817  	for i, test := range tests {
  3818  		rows, err := printService(&test.service, test.options)
  3819  		if err != nil {
  3820  			t.Fatal(err)
  3821  		}
  3822  		for i := range rows {
  3823  			rows[i].Object.Object = nil
  3824  		}
  3825  		if !reflect.DeepEqual(test.expected, rows) {
  3826  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  3827  		}
  3828  	}
  3829  }
  3830  
  3831  func TestPrintServiceList(t *testing.T) {
  3832  	serviceList := api.ServiceList{
  3833  		Items: []api.Service{
  3834  			{
  3835  				ObjectMeta: metav1.ObjectMeta{Name: "service1"},
  3836  				Spec: api.ServiceSpec{
  3837  					Type: api.ServiceTypeClusterIP,
  3838  					Ports: []api.ServicePort{
  3839  						{
  3840  							Protocol: "tcp",
  3841  							Port:     2233,
  3842  						},
  3843  					},
  3844  					ClusterIPs: []string{"10.9.8.7"},
  3845  				},
  3846  			},
  3847  			{
  3848  				ObjectMeta: metav1.ObjectMeta{Name: "service2"},
  3849  				Spec: api.ServiceSpec{
  3850  					Type: api.ServiceTypeNodePort,
  3851  					Ports: []api.ServicePort{
  3852  						{
  3853  							Protocol: "udp",
  3854  							Port:     5566,
  3855  						},
  3856  					},
  3857  					ClusterIPs: []string{"1.2.3.4"},
  3858  				},
  3859  			},
  3860  		},
  3861  	}
  3862  
  3863  	// Columns: Name, Type, Cluster-IP, External-IP, Port(s), Age
  3864  	expectedRows := []metav1.TableRow{
  3865  		{Cells: []interface{}{"service1", "ClusterIP", "10.9.8.7", "<none>", "2233/tcp", "<unknown>"}},
  3866  		{Cells: []interface{}{"service2", "NodePort", "1.2.3.4", "<none>", "5566/udp", "<unknown>"}},
  3867  	}
  3868  
  3869  	rows, err := printServiceList(&serviceList, printers.GenerateOptions{})
  3870  	if err != nil {
  3871  		t.Fatalf("Error printing service list: %#v", err)
  3872  	}
  3873  	for i := range rows {
  3874  		rows[i].Object.Object = nil
  3875  	}
  3876  	if !reflect.DeepEqual(expectedRows, rows) {
  3877  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  3878  	}
  3879  }
  3880  
  3881  func TestPrintPodDisruptionBudget(t *testing.T) {
  3882  	minAvailable := intstr.FromInt32(22)
  3883  	maxUnavailable := intstr.FromInt32(11)
  3884  	tests := []struct {
  3885  		pdb      policy.PodDisruptionBudget
  3886  		expected []metav1.TableRow
  3887  	}{
  3888  		// Min Available set, no Max Available.
  3889  		{
  3890  			pdb: policy.PodDisruptionBudget{
  3891  				ObjectMeta: metav1.ObjectMeta{
  3892  					Namespace:         "ns1",
  3893  					Name:              "pdb1",
  3894  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3895  				},
  3896  				Spec: policy.PodDisruptionBudgetSpec{
  3897  					MinAvailable: &minAvailable,
  3898  				},
  3899  				Status: policy.PodDisruptionBudgetStatus{
  3900  					DisruptionsAllowed: 5,
  3901  				},
  3902  			},
  3903  			// Columns: Name, Min Available, Max Available, Allowed Disruptions, Age
  3904  			expected: []metav1.TableRow{{Cells: []interface{}{"pdb1", "22", "N/A", int64(5), "0s"}}},
  3905  		},
  3906  		// Max Available set, no Min Available.
  3907  		{
  3908  			pdb: policy.PodDisruptionBudget{
  3909  				ObjectMeta: metav1.ObjectMeta{
  3910  					Namespace:         "ns2",
  3911  					Name:              "pdb2",
  3912  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3913  				},
  3914  				Spec: policy.PodDisruptionBudgetSpec{
  3915  					MaxUnavailable: &maxUnavailable,
  3916  				},
  3917  				Status: policy.PodDisruptionBudgetStatus{
  3918  					DisruptionsAllowed: 5,
  3919  				},
  3920  			},
  3921  			// Columns: Name, Min Available, Max Available, Allowed Disruptions, Age
  3922  			expected: []metav1.TableRow{{Cells: []interface{}{"pdb2", "N/A", "11", int64(5), "0s"}}},
  3923  		}}
  3924  
  3925  	for i, test := range tests {
  3926  		rows, err := printPodDisruptionBudget(&test.pdb, printers.GenerateOptions{})
  3927  		if err != nil {
  3928  			t.Fatal(err)
  3929  		}
  3930  		for i := range rows {
  3931  			rows[i].Object.Object = nil
  3932  		}
  3933  		if !reflect.DeepEqual(test.expected, rows) {
  3934  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  3935  		}
  3936  	}
  3937  }
  3938  
  3939  func TestPrintPodDisruptionBudgetList(t *testing.T) {
  3940  	minAvailable := intstr.FromInt32(22)
  3941  	maxUnavailable := intstr.FromInt32(11)
  3942  
  3943  	pdbList := policy.PodDisruptionBudgetList{
  3944  		Items: []policy.PodDisruptionBudget{
  3945  			{
  3946  				ObjectMeta: metav1.ObjectMeta{
  3947  					Namespace:         "ns1",
  3948  					Name:              "pdb1",
  3949  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3950  				},
  3951  				Spec: policy.PodDisruptionBudgetSpec{
  3952  					MaxUnavailable: &maxUnavailable,
  3953  				},
  3954  				Status: policy.PodDisruptionBudgetStatus{
  3955  					DisruptionsAllowed: 5,
  3956  				},
  3957  			},
  3958  			{
  3959  				ObjectMeta: metav1.ObjectMeta{
  3960  					Namespace:         "ns2",
  3961  					Name:              "pdb2",
  3962  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  3963  				},
  3964  				Spec: policy.PodDisruptionBudgetSpec{
  3965  					MinAvailable: &minAvailable,
  3966  				},
  3967  				Status: policy.PodDisruptionBudgetStatus{
  3968  					DisruptionsAllowed: 3,
  3969  				},
  3970  			},
  3971  		},
  3972  	}
  3973  
  3974  	// Columns: Name, Min Available, Max Available, Allowed Disruptions, Age
  3975  	expectedRows := []metav1.TableRow{
  3976  		{Cells: []interface{}{"pdb1", "N/A", "11", int64(5), "0s"}},
  3977  		{Cells: []interface{}{"pdb2", "22", "N/A", int64(3), "0s"}},
  3978  	}
  3979  
  3980  	rows, err := printPodDisruptionBudgetList(&pdbList, printers.GenerateOptions{})
  3981  	if err != nil {
  3982  		t.Fatalf("Error printing pod template list: %#v", err)
  3983  	}
  3984  	for i := range rows {
  3985  		rows[i].Object.Object = nil
  3986  	}
  3987  	if !reflect.DeepEqual(expectedRows, rows) {
  3988  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  3989  	}
  3990  }
  3991  
  3992  func TestPrintControllerRevision(t *testing.T) {
  3993  	tests := []struct {
  3994  		history  apps.ControllerRevision
  3995  		expected []metav1.TableRow
  3996  	}{
  3997  		{
  3998  			history: apps.ControllerRevision{
  3999  				ObjectMeta: metav1.ObjectMeta{
  4000  					Name:              "test1",
  4001  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4002  					OwnerReferences: []metav1.OwnerReference{
  4003  						{
  4004  							Controller: boolP(true),
  4005  							APIVersion: "apps/v1",
  4006  							Kind:       "DaemonSet",
  4007  							Name:       "foo",
  4008  						},
  4009  					},
  4010  				},
  4011  				Revision: 1,
  4012  			},
  4013  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "daemonset.apps/foo", int64(1), "0s"}}},
  4014  		},
  4015  		{
  4016  			history: apps.ControllerRevision{
  4017  				ObjectMeta: metav1.ObjectMeta{
  4018  					Name:              "test2",
  4019  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4020  					OwnerReferences: []metav1.OwnerReference{
  4021  						{
  4022  							Controller: boolP(false),
  4023  							Kind:       "ABC",
  4024  							Name:       "foo",
  4025  						},
  4026  					},
  4027  				},
  4028  				Revision: 2,
  4029  			},
  4030  			expected: []metav1.TableRow{{Cells: []interface{}{"test2", "<none>", int64(2), "0s"}}},
  4031  		},
  4032  		{
  4033  			history: apps.ControllerRevision{
  4034  				ObjectMeta: metav1.ObjectMeta{
  4035  					Name:              "test3",
  4036  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4037  					OwnerReferences:   []metav1.OwnerReference{},
  4038  				},
  4039  				Revision: 3,
  4040  			},
  4041  			expected: []metav1.TableRow{{Cells: []interface{}{"test3", "<none>", int64(3), "0s"}}},
  4042  		},
  4043  		{
  4044  			history: apps.ControllerRevision{
  4045  				ObjectMeta: metav1.ObjectMeta{
  4046  					Name:              "test4",
  4047  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4048  					OwnerReferences:   nil,
  4049  				},
  4050  				Revision: 4,
  4051  			},
  4052  			expected: []metav1.TableRow{{Cells: []interface{}{"test4", "<none>", int64(4), "0s"}}},
  4053  		},
  4054  	}
  4055  
  4056  	for i, test := range tests {
  4057  		rows, err := printControllerRevision(&test.history, printers.GenerateOptions{})
  4058  		if err != nil {
  4059  			t.Fatal(err)
  4060  		}
  4061  		for i := range rows {
  4062  			rows[i].Object.Object = nil
  4063  		}
  4064  		if !reflect.DeepEqual(test.expected, rows) {
  4065  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4066  		}
  4067  	}
  4068  }
  4069  
  4070  func boolP(b bool) *bool {
  4071  	return &b
  4072  }
  4073  
  4074  func TestPrintConfigMap(t *testing.T) {
  4075  	tests := []struct {
  4076  		configMap api.ConfigMap
  4077  		expected  []metav1.TableRow
  4078  	}{
  4079  		// Basic config map with no data.
  4080  		{
  4081  			configMap: api.ConfigMap{
  4082  				ObjectMeta: metav1.ObjectMeta{
  4083  					Name:              "configmap1",
  4084  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4085  				},
  4086  			},
  4087  			// Columns: Name, Data, Age
  4088  			expected: []metav1.TableRow{{Cells: []interface{}{"configmap1", int64(0), "0s"}}},
  4089  		},
  4090  		// Basic config map with one data entry
  4091  		{
  4092  			configMap: api.ConfigMap{
  4093  				ObjectMeta: metav1.ObjectMeta{
  4094  					Name:              "configmap2",
  4095  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4096  				},
  4097  				Data: map[string]string{
  4098  					"foo": "bar",
  4099  				},
  4100  			},
  4101  			// Columns: Name, (Num) Data, Age
  4102  			expected: []metav1.TableRow{{Cells: []interface{}{"configmap2", int64(1), "0s"}}},
  4103  		},
  4104  		// Basic config map with one data and one binary data entry.
  4105  		{
  4106  			configMap: api.ConfigMap{
  4107  				ObjectMeta: metav1.ObjectMeta{
  4108  					Name:              "configmap3",
  4109  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4110  				},
  4111  				Data: map[string]string{
  4112  					"foo": "bar",
  4113  				},
  4114  				BinaryData: map[string][]byte{
  4115  					"bin": []byte("binary data"),
  4116  				},
  4117  			},
  4118  			// Columns: Name, (Num) Data, Age
  4119  			expected: []metav1.TableRow{{Cells: []interface{}{"configmap3", int64(2), "0s"}}},
  4120  		},
  4121  	}
  4122  
  4123  	for i, test := range tests {
  4124  		rows, err := printConfigMap(&test.configMap, printers.GenerateOptions{})
  4125  		if err != nil {
  4126  			t.Fatal(err)
  4127  		}
  4128  		for i := range rows {
  4129  			rows[i].Object.Object = nil
  4130  		}
  4131  		if !reflect.DeepEqual(test.expected, rows) {
  4132  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4133  		}
  4134  	}
  4135  }
  4136  
  4137  func TestPrintNetworkPolicy(t *testing.T) {
  4138  	tests := []struct {
  4139  		policy   networking.NetworkPolicy
  4140  		expected []metav1.TableRow
  4141  	}{
  4142  		// Basic network policy with empty spec.
  4143  		{
  4144  			policy: networking.NetworkPolicy{
  4145  				ObjectMeta: metav1.ObjectMeta{
  4146  					Name:              "policy1",
  4147  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4148  				},
  4149  				Spec: networking.NetworkPolicySpec{},
  4150  			},
  4151  			// Columns: Name, Pod-Selector, Age
  4152  			expected: []metav1.TableRow{{Cells: []interface{}{"policy1", "<none>", "0s"}}},
  4153  		},
  4154  		// Basic network policy with pod selector.
  4155  		{
  4156  			policy: networking.NetworkPolicy{
  4157  				ObjectMeta: metav1.ObjectMeta{
  4158  					Name:              "policy2",
  4159  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4160  				},
  4161  				Spec: networking.NetworkPolicySpec{
  4162  					PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  4163  				},
  4164  			},
  4165  			// Columns: Name, Pod-Selector, Age
  4166  			expected: []metav1.TableRow{{Cells: []interface{}{"policy2", "foo=bar", "0s"}}},
  4167  		},
  4168  	}
  4169  
  4170  	for i, test := range tests {
  4171  		rows, err := printNetworkPolicy(&test.policy, printers.GenerateOptions{})
  4172  		if err != nil {
  4173  			t.Fatal(err)
  4174  		}
  4175  		for i := range rows {
  4176  			rows[i].Object.Object = nil
  4177  		}
  4178  		if !reflect.DeepEqual(test.expected, rows) {
  4179  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4180  		}
  4181  	}
  4182  }
  4183  
  4184  func TestPrintRoleBinding(t *testing.T) {
  4185  	tests := []struct {
  4186  		binding  rbac.RoleBinding
  4187  		options  printers.GenerateOptions
  4188  		expected []metav1.TableRow
  4189  	}{
  4190  		// Basic role binding
  4191  		{
  4192  			binding: rbac.RoleBinding{
  4193  				ObjectMeta: metav1.ObjectMeta{
  4194  					Name:              "binding1",
  4195  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4196  				},
  4197  				Subjects: []rbac.Subject{
  4198  					{
  4199  						Kind: "User",
  4200  						Name: "system:kube-controller-manager",
  4201  					},
  4202  				},
  4203  				RoleRef: rbac.RoleRef{
  4204  					Kind: "Role",
  4205  					Name: "extension-apiserver-authentication-reader",
  4206  				},
  4207  			},
  4208  			options: printers.GenerateOptions{},
  4209  			// Columns: Name, Age
  4210  			expected: []metav1.TableRow{{Cells: []interface{}{"binding1", "Role/extension-apiserver-authentication-reader", "0s"}}},
  4211  		},
  4212  		// Generate options=Wide; print subject and roles.
  4213  		{
  4214  			binding: rbac.RoleBinding{
  4215  				ObjectMeta: metav1.ObjectMeta{
  4216  					Name:              "binding2",
  4217  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4218  				},
  4219  				Subjects: []rbac.Subject{
  4220  					{
  4221  						Kind: "User",
  4222  						Name: "user-name",
  4223  					},
  4224  					{
  4225  						Kind: "Group",
  4226  						Name: "group-name",
  4227  					},
  4228  					{
  4229  						Kind:      "ServiceAccount",
  4230  						Name:      "service-account-name",
  4231  						Namespace: "service-account-namespace",
  4232  					},
  4233  				},
  4234  				RoleRef: rbac.RoleRef{
  4235  					Kind: "Role",
  4236  					Name: "role-name",
  4237  				},
  4238  			},
  4239  			options: printers.GenerateOptions{Wide: true},
  4240  			// Columns: Name, Age, Role, Users, Groups, ServiceAccounts
  4241  			expected: []metav1.TableRow{{Cells: []interface{}{"binding2", "Role/role-name", "0s", "user-name", "group-name", "service-account-namespace/service-account-name"}}},
  4242  		},
  4243  	}
  4244  
  4245  	for i, test := range tests {
  4246  		rows, err := printRoleBinding(&test.binding, test.options)
  4247  		if err != nil {
  4248  			t.Fatal(err)
  4249  		}
  4250  		for i := range rows {
  4251  			rows[i].Object.Object = nil
  4252  		}
  4253  		if !reflect.DeepEqual(test.expected, rows) {
  4254  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4255  		}
  4256  	}
  4257  }
  4258  
  4259  func TestPrintClusterRoleBinding(t *testing.T) {
  4260  	tests := []struct {
  4261  		binding  rbac.ClusterRoleBinding
  4262  		options  printers.GenerateOptions
  4263  		expected []metav1.TableRow
  4264  	}{
  4265  		// Basic cluster role binding
  4266  		{
  4267  			binding: rbac.ClusterRoleBinding{
  4268  				ObjectMeta: metav1.ObjectMeta{
  4269  					Name:              "binding1",
  4270  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4271  				},
  4272  				Subjects: []rbac.Subject{
  4273  					{
  4274  						Kind: "User",
  4275  						Name: "system:kube-controller-manager",
  4276  					},
  4277  				},
  4278  				RoleRef: rbac.RoleRef{
  4279  					Kind: "Role",
  4280  					Name: "extension-apiserver-authentication-reader",
  4281  				},
  4282  			},
  4283  			options: printers.GenerateOptions{},
  4284  			// Columns: Name, Age
  4285  			expected: []metav1.TableRow{{Cells: []interface{}{"binding1", "Role/extension-apiserver-authentication-reader", "0s"}}},
  4286  		},
  4287  		// Generate options=Wide; print subject and roles.
  4288  		{
  4289  			binding: rbac.ClusterRoleBinding{
  4290  				ObjectMeta: metav1.ObjectMeta{
  4291  					Name:              "binding2",
  4292  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4293  				},
  4294  				Subjects: []rbac.Subject{
  4295  					{
  4296  						Kind: "User",
  4297  						Name: "user-name",
  4298  					},
  4299  					{
  4300  						Kind: "Group",
  4301  						Name: "group-name",
  4302  					},
  4303  					{
  4304  						Kind:      "ServiceAccount",
  4305  						Name:      "service-account-name",
  4306  						Namespace: "service-account-namespace",
  4307  					},
  4308  				},
  4309  				RoleRef: rbac.RoleRef{
  4310  					Kind: "Role",
  4311  					Name: "role-name",
  4312  				},
  4313  			},
  4314  			options: printers.GenerateOptions{Wide: true},
  4315  			// Columns: Name, Age, Role, Users, Groups, ServiceAccounts
  4316  			expected: []metav1.TableRow{{Cells: []interface{}{"binding2", "Role/role-name", "0s", "user-name", "group-name", "service-account-namespace/service-account-name"}}},
  4317  		},
  4318  	}
  4319  
  4320  	for i, test := range tests {
  4321  		rows, err := printClusterRoleBinding(&test.binding, test.options)
  4322  		if err != nil {
  4323  			t.Fatal(err)
  4324  		}
  4325  		for i := range rows {
  4326  			rows[i].Object.Object = nil
  4327  		}
  4328  		if !reflect.DeepEqual(test.expected, rows) {
  4329  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4330  		}
  4331  	}
  4332  }
  4333  func TestPrintCertificateSigningRequest(t *testing.T) {
  4334  	tests := []struct {
  4335  		csr      certificates.CertificateSigningRequest
  4336  		expected []metav1.TableRow
  4337  	}{
  4338  		// Basic CSR with no spec or status; defaults to status: Pending.
  4339  		{
  4340  			csr: certificates.CertificateSigningRequest{
  4341  				ObjectMeta: metav1.ObjectMeta{
  4342  					Name:              "csr1",
  4343  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4344  				},
  4345  				Spec:   certificates.CertificateSigningRequestSpec{},
  4346  				Status: certificates.CertificateSigningRequestStatus{},
  4347  			},
  4348  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4349  			expected: []metav1.TableRow{{Cells: []interface{}{"csr1", "0s", "<none>", "", "<none>", "Pending"}}},
  4350  		},
  4351  		// Basic CSR with Spec and Status=Approved.
  4352  		{
  4353  			csr: certificates.CertificateSigningRequest{
  4354  				ObjectMeta: metav1.ObjectMeta{
  4355  					Name:              "csr2",
  4356  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4357  				},
  4358  				Spec: certificates.CertificateSigningRequestSpec{
  4359  					Username: "CSR Requestor",
  4360  				},
  4361  				Status: certificates.CertificateSigningRequestStatus{
  4362  					Conditions: []certificates.CertificateSigningRequestCondition{
  4363  						{
  4364  							Type: certificates.CertificateApproved,
  4365  						},
  4366  					},
  4367  				},
  4368  			},
  4369  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4370  			expected: []metav1.TableRow{{Cells: []interface{}{"csr2", "0s", "<none>", "CSR Requestor", "<none>", "Approved"}}},
  4371  		},
  4372  		// Basic CSR with Spec and SignerName set
  4373  		{
  4374  			csr: certificates.CertificateSigningRequest{
  4375  				ObjectMeta: metav1.ObjectMeta{
  4376  					Name:              "csr2",
  4377  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4378  				},
  4379  				Spec: certificates.CertificateSigningRequestSpec{
  4380  					Username:   "CSR Requestor",
  4381  					SignerName: "example.com/test-signer",
  4382  				},
  4383  				Status: certificates.CertificateSigningRequestStatus{
  4384  					Conditions: []certificates.CertificateSigningRequestCondition{
  4385  						{
  4386  							Type: certificates.CertificateApproved,
  4387  						},
  4388  					},
  4389  				},
  4390  			},
  4391  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4392  			expected: []metav1.TableRow{{Cells: []interface{}{"csr2", "0s", "example.com/test-signer", "CSR Requestor", "<none>", "Approved"}}},
  4393  		},
  4394  		// Basic CSR with Spec, SignerName and ExpirationSeconds set
  4395  		{
  4396  			csr: certificates.CertificateSigningRequest{
  4397  				ObjectMeta: metav1.ObjectMeta{
  4398  					Name:              "csr2",
  4399  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4400  				},
  4401  				Spec: certificates.CertificateSigningRequestSpec{
  4402  					Username:          "CSR Requestor",
  4403  					SignerName:        "example.com/test-signer",
  4404  					ExpirationSeconds: csr.DurationToExpirationSeconds(7*24*time.Hour + time.Hour), // a little bit more than a week
  4405  				},
  4406  				Status: certificates.CertificateSigningRequestStatus{
  4407  					Conditions: []certificates.CertificateSigningRequestCondition{
  4408  						{
  4409  							Type: certificates.CertificateApproved,
  4410  						},
  4411  					},
  4412  				},
  4413  			},
  4414  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4415  			expected: []metav1.TableRow{{Cells: []interface{}{"csr2", "0s", "example.com/test-signer", "CSR Requestor", "7d1h", "Approved"}}},
  4416  		},
  4417  		// Basic CSR with Spec and Status=Approved; certificate issued.
  4418  		{
  4419  			csr: certificates.CertificateSigningRequest{
  4420  				ObjectMeta: metav1.ObjectMeta{
  4421  					Name:              "csr2",
  4422  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4423  				},
  4424  				Spec: certificates.CertificateSigningRequestSpec{
  4425  					Username: "CSR Requestor",
  4426  				},
  4427  				Status: certificates.CertificateSigningRequestStatus{
  4428  					Conditions: []certificates.CertificateSigningRequestCondition{
  4429  						{
  4430  							Type: certificates.CertificateApproved,
  4431  						},
  4432  					},
  4433  					Certificate: []byte("cert data"),
  4434  				},
  4435  			},
  4436  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4437  			expected: []metav1.TableRow{{Cells: []interface{}{"csr2", "0s", "<none>", "CSR Requestor", "<none>", "Approved,Issued"}}},
  4438  		},
  4439  		// Basic CSR with Spec and Status=Denied.
  4440  		{
  4441  			csr: certificates.CertificateSigningRequest{
  4442  				ObjectMeta: metav1.ObjectMeta{
  4443  					Name:              "csr3",
  4444  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4445  				},
  4446  				Spec: certificates.CertificateSigningRequestSpec{
  4447  					Username: "CSR Requestor",
  4448  				},
  4449  				Status: certificates.CertificateSigningRequestStatus{
  4450  					Conditions: []certificates.CertificateSigningRequestCondition{
  4451  						{
  4452  							Type: certificates.CertificateDenied,
  4453  						},
  4454  					},
  4455  				},
  4456  			},
  4457  			// Columns: Name, Age, SignerName, Requestor, RequestedDuration, Condition
  4458  			expected: []metav1.TableRow{{Cells: []interface{}{"csr3", "0s", "<none>", "CSR Requestor", "<none>", "Denied"}}},
  4459  		},
  4460  	}
  4461  
  4462  	for i, test := range tests {
  4463  		rows, err := printCertificateSigningRequest(&test.csr, printers.GenerateOptions{})
  4464  		if err != nil {
  4465  			t.Fatal(err)
  4466  		}
  4467  		for i := range rows {
  4468  			rows[i].Object.Object = nil
  4469  		}
  4470  		if !reflect.DeepEqual(test.expected, rows) {
  4471  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4472  		}
  4473  	}
  4474  }
  4475  
  4476  func TestPrintReplicationController(t *testing.T) {
  4477  	tests := []struct {
  4478  		rc       api.ReplicationController
  4479  		options  printers.GenerateOptions
  4480  		expected []metav1.TableRow
  4481  	}{
  4482  		// Basic print replication controller without replicas or status.
  4483  		{
  4484  			rc: api.ReplicationController{
  4485  				ObjectMeta: metav1.ObjectMeta{
  4486  					Name:      "rc1",
  4487  					Namespace: "test-namespace",
  4488  				},
  4489  				Spec: api.ReplicationControllerSpec{
  4490  					Selector: map[string]string{"a": "b"},
  4491  					Template: &api.PodTemplateSpec{
  4492  						ObjectMeta: metav1.ObjectMeta{
  4493  							Labels: map[string]string{"a": "b"},
  4494  						},
  4495  						Spec: api.PodSpec{
  4496  							Containers: []api.Container{
  4497  								{
  4498  									Name:                     "test",
  4499  									Image:                    "test_image",
  4500  									ImagePullPolicy:          api.PullIfNotPresent,
  4501  									TerminationMessagePolicy: api.TerminationMessageReadFile,
  4502  								},
  4503  							},
  4504  							RestartPolicy: api.RestartPolicyAlways,
  4505  							DNSPolicy:     api.DNSClusterFirst,
  4506  						},
  4507  					},
  4508  				},
  4509  			},
  4510  			options: printers.GenerateOptions{},
  4511  			// Columns: Name, Desired, Current, Ready, Age
  4512  			expected: []metav1.TableRow{{Cells: []interface{}{"rc1", int64(0), int64(0), int64(0), "<unknown>"}}},
  4513  		},
  4514  		// Basic print replication controller with replicas; does not print containers or labels
  4515  		{
  4516  			rc: api.ReplicationController{
  4517  				ObjectMeta: metav1.ObjectMeta{
  4518  					Name:      "rc1",
  4519  					Namespace: "test-namespace",
  4520  				},
  4521  				Spec: api.ReplicationControllerSpec{
  4522  					Replicas: 5,
  4523  					Selector: map[string]string{"a": "b"},
  4524  					Template: &api.PodTemplateSpec{
  4525  						ObjectMeta: metav1.ObjectMeta{
  4526  							Labels: map[string]string{"a": "b"},
  4527  						},
  4528  						Spec: api.PodSpec{
  4529  							Containers: []api.Container{
  4530  								{
  4531  									Name:                     "test",
  4532  									Image:                    "test_image",
  4533  									ImagePullPolicy:          api.PullIfNotPresent,
  4534  									TerminationMessagePolicy: api.TerminationMessageReadFile,
  4535  								},
  4536  							},
  4537  							RestartPolicy: api.RestartPolicyAlways,
  4538  							DNSPolicy:     api.DNSClusterFirst,
  4539  						},
  4540  					},
  4541  				},
  4542  				Status: api.ReplicationControllerStatus{
  4543  					Replicas:      3,
  4544  					ReadyReplicas: 1,
  4545  				},
  4546  			},
  4547  			options: printers.GenerateOptions{},
  4548  			// Columns: Name, Desired, Current, Ready, Age
  4549  			expected: []metav1.TableRow{{Cells: []interface{}{"rc1", int64(5), int64(3), int64(1), "<unknown>"}}},
  4550  		},
  4551  		// Generate options: Wide; print containers and labels.
  4552  		{
  4553  			rc: api.ReplicationController{
  4554  				ObjectMeta: metav1.ObjectMeta{
  4555  					Name: "rc1",
  4556  				},
  4557  				Spec: api.ReplicationControllerSpec{
  4558  					Replicas: 5,
  4559  					Selector: map[string]string{"a": "b"},
  4560  					Template: &api.PodTemplateSpec{
  4561  						ObjectMeta: metav1.ObjectMeta{
  4562  							Labels: map[string]string{"a": "b"},
  4563  						},
  4564  						Spec: api.PodSpec{
  4565  							Containers: []api.Container{
  4566  								{
  4567  									Name:                     "test",
  4568  									Image:                    "test_image",
  4569  									ImagePullPolicy:          api.PullIfNotPresent,
  4570  									TerminationMessagePolicy: api.TerminationMessageReadFile,
  4571  								},
  4572  							},
  4573  							RestartPolicy: api.RestartPolicyAlways,
  4574  							DNSPolicy:     api.DNSClusterFirst,
  4575  						},
  4576  					},
  4577  				},
  4578  				Status: api.ReplicationControllerStatus{
  4579  					Replicas:      3,
  4580  					ReadyReplicas: 1,
  4581  				},
  4582  			},
  4583  			options: printers.GenerateOptions{Wide: true},
  4584  			// Columns: Name, Desired, Current, Ready, Age, Containers, Images, Selector
  4585  			expected: []metav1.TableRow{{Cells: []interface{}{"rc1", int64(5), int64(3), int64(1), "<unknown>", "test", "test_image", "a=b"}}},
  4586  		},
  4587  	}
  4588  
  4589  	for i, test := range tests {
  4590  		rows, err := printReplicationController(&test.rc, test.options)
  4591  		if err != nil {
  4592  			t.Fatal(err)
  4593  		}
  4594  		for i := range rows {
  4595  			rows[i].Object.Object = nil
  4596  		}
  4597  		if !reflect.DeepEqual(test.expected, rows) {
  4598  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4599  		}
  4600  	}
  4601  }
  4602  
  4603  func TestPrintReplicaSet(t *testing.T) {
  4604  	tests := []struct {
  4605  		replicaSet apps.ReplicaSet
  4606  		options    printers.GenerateOptions
  4607  		expected   []metav1.TableRow
  4608  	}{
  4609  		// Generate options empty
  4610  		{
  4611  			replicaSet: apps.ReplicaSet{
  4612  				ObjectMeta: metav1.ObjectMeta{
  4613  					Name:              "test1",
  4614  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4615  				},
  4616  				Spec: apps.ReplicaSetSpec{
  4617  					Replicas: 5,
  4618  					Template: api.PodTemplateSpec{
  4619  						Spec: api.PodSpec{
  4620  							Containers: []api.Container{
  4621  								{
  4622  									Name:  "fake-container1",
  4623  									Image: "fake-image1",
  4624  								},
  4625  								{
  4626  									Name:  "fake-container2",
  4627  									Image: "fake-image2",
  4628  								},
  4629  							},
  4630  						},
  4631  					},
  4632  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  4633  				},
  4634  				Status: apps.ReplicaSetStatus{
  4635  					Replicas:      5,
  4636  					ReadyReplicas: 2,
  4637  				},
  4638  			},
  4639  			options: printers.GenerateOptions{},
  4640  			// Columns: Name, Desired, Current, Ready, Age
  4641  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", int64(5), int64(5), int64(2), "0s"}}},
  4642  		},
  4643  		// Generate options "Wide"
  4644  		{
  4645  			replicaSet: apps.ReplicaSet{
  4646  				ObjectMeta: metav1.ObjectMeta{
  4647  					Name:              "test1",
  4648  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4649  				},
  4650  				Spec: apps.ReplicaSetSpec{
  4651  					Replicas: 5,
  4652  					Template: api.PodTemplateSpec{
  4653  						Spec: api.PodSpec{
  4654  							Containers: []api.Container{
  4655  								{
  4656  									Name:  "fake-container1",
  4657  									Image: "fake-image1",
  4658  								},
  4659  								{
  4660  									Name:  "fake-container2",
  4661  									Image: "fake-image2",
  4662  								},
  4663  							},
  4664  						},
  4665  					},
  4666  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  4667  				},
  4668  				Status: apps.ReplicaSetStatus{
  4669  					Replicas:      5,
  4670  					ReadyReplicas: 2,
  4671  				},
  4672  			},
  4673  			options: printers.GenerateOptions{Wide: true},
  4674  			// Columns: Name, Desired, Current, Ready, Age, Containers, Images, Selector
  4675  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", int64(5), int64(5), int64(2), "0s", "fake-container1,fake-container2", "fake-image1,fake-image2", "foo=bar"}}},
  4676  		},
  4677  	}
  4678  
  4679  	for i, test := range tests {
  4680  		rows, err := printReplicaSet(&test.replicaSet, test.options)
  4681  		if err != nil {
  4682  			t.Fatal(err)
  4683  		}
  4684  		for i := range rows {
  4685  			rows[i].Object.Object = nil
  4686  		}
  4687  		if !reflect.DeepEqual(test.expected, rows) {
  4688  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4689  		}
  4690  	}
  4691  }
  4692  
  4693  func TestPrintReplicaSetList(t *testing.T) {
  4694  
  4695  	replicaSetList := apps.ReplicaSetList{
  4696  		Items: []apps.ReplicaSet{
  4697  			{
  4698  				ObjectMeta: metav1.ObjectMeta{
  4699  					Name:              "replicaset1",
  4700  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4701  				},
  4702  				Spec: apps.ReplicaSetSpec{
  4703  					Replicas: 5,
  4704  					Template: api.PodTemplateSpec{
  4705  						Spec: api.PodSpec{
  4706  							Containers: []api.Container{
  4707  								{
  4708  									Name:  "fake-container1",
  4709  									Image: "fake-image1",
  4710  								},
  4711  								{
  4712  									Name:  "fake-container2",
  4713  									Image: "fake-image2",
  4714  								},
  4715  							},
  4716  						},
  4717  					},
  4718  				},
  4719  				Status: apps.ReplicaSetStatus{
  4720  					Replicas:      5,
  4721  					ReadyReplicas: 2,
  4722  				},
  4723  			},
  4724  			{
  4725  				ObjectMeta: metav1.ObjectMeta{
  4726  					Name:              "replicaset2",
  4727  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4728  				},
  4729  				Spec: apps.ReplicaSetSpec{
  4730  					Replicas: 4,
  4731  					Template: api.PodTemplateSpec{},
  4732  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  4733  				},
  4734  				Status: apps.ReplicaSetStatus{
  4735  					Replicas:      3,
  4736  					ReadyReplicas: 1,
  4737  				},
  4738  			},
  4739  		},
  4740  	}
  4741  
  4742  	// Columns: Name, Desired, Current, Ready, Age
  4743  	expectedRows := []metav1.TableRow{
  4744  		{Cells: []interface{}{"replicaset1", int64(5), int64(5), int64(2), "0s"}},
  4745  		{Cells: []interface{}{"replicaset2", int64(4), int64(3), int64(1), "0s"}},
  4746  	}
  4747  
  4748  	rows, err := printReplicaSetList(&replicaSetList, printers.GenerateOptions{})
  4749  	if err != nil {
  4750  		t.Fatalf("Error printing replica set list: %#v", err)
  4751  	}
  4752  	for i := range rows {
  4753  		rows[i].Object.Object = nil
  4754  	}
  4755  	if !reflect.DeepEqual(expectedRows, rows) {
  4756  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  4757  	}
  4758  }
  4759  
  4760  func TestPrintStatefulSet(t *testing.T) {
  4761  	tests := []struct {
  4762  		statefulSet apps.StatefulSet
  4763  		options     printers.GenerateOptions
  4764  		expected    []metav1.TableRow
  4765  	}{
  4766  		// Basic stateful set; no generate options.
  4767  		{
  4768  			statefulSet: apps.StatefulSet{
  4769  				ObjectMeta: metav1.ObjectMeta{
  4770  					Name:              "test1",
  4771  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4772  				},
  4773  				Spec: apps.StatefulSetSpec{
  4774  					Replicas: 5,
  4775  					Template: api.PodTemplateSpec{
  4776  						Spec: api.PodSpec{
  4777  							Containers: []api.Container{
  4778  								{
  4779  									Name:  "fake-container1",
  4780  									Image: "fake-image1",
  4781  								},
  4782  								{
  4783  									Name:  "fake-container2",
  4784  									Image: "fake-image2",
  4785  								},
  4786  							},
  4787  						},
  4788  					},
  4789  				},
  4790  				Status: apps.StatefulSetStatus{
  4791  					Replicas:      5,
  4792  					ReadyReplicas: 2,
  4793  				},
  4794  			},
  4795  			options: printers.GenerateOptions{},
  4796  			// Columns: Name, Ready, Age
  4797  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "2/5", "0s"}}},
  4798  		},
  4799  		// Generate options "Wide"; includes containers and images.
  4800  		{
  4801  			statefulSet: apps.StatefulSet{
  4802  				ObjectMeta: metav1.ObjectMeta{
  4803  					Name:              "test1",
  4804  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  4805  				},
  4806  				Spec: apps.StatefulSetSpec{
  4807  					Replicas: 5,
  4808  					Template: api.PodTemplateSpec{
  4809  						Spec: api.PodSpec{
  4810  							Containers: []api.Container{
  4811  								{
  4812  									Name:  "fake-container1",
  4813  									Image: "fake-image1",
  4814  								},
  4815  								{
  4816  									Name:  "fake-container2",
  4817  									Image: "fake-image2",
  4818  								},
  4819  							},
  4820  						},
  4821  					},
  4822  				},
  4823  				Status: apps.StatefulSetStatus{
  4824  					Replicas:      5,
  4825  					ReadyReplicas: 2,
  4826  				},
  4827  			},
  4828  			options: printers.GenerateOptions{Wide: true},
  4829  			// Columns: Name, Ready, Age, Containers, Images
  4830  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "2/5", "0s", "fake-container1,fake-container2", "fake-image1,fake-image2"}}},
  4831  		},
  4832  	}
  4833  
  4834  	for i, test := range tests {
  4835  		rows, err := printStatefulSet(&test.statefulSet, test.options)
  4836  		if err != nil {
  4837  			t.Fatal(err)
  4838  		}
  4839  		for i := range rows {
  4840  			rows[i].Object.Object = nil
  4841  		}
  4842  		if !reflect.DeepEqual(test.expected, rows) {
  4843  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  4844  		}
  4845  	}
  4846  }
  4847  
  4848  func TestPrintPersistentVolume(t *testing.T) {
  4849  	myScn := "my-scn"
  4850  	myVacn := "my-vacn"
  4851  
  4852  	claimRef := api.ObjectReference{
  4853  		Name:      "test",
  4854  		Namespace: "default",
  4855  	}
  4856  	tests := []struct {
  4857  		pv       api.PersistentVolume
  4858  		expected []metav1.TableRow
  4859  	}{
  4860  		{
  4861  			// Test bound
  4862  			pv: api.PersistentVolume{
  4863  				ObjectMeta: metav1.ObjectMeta{
  4864  					Name: "test1",
  4865  				},
  4866  				Spec: api.PersistentVolumeSpec{
  4867  					ClaimRef:    &claimRef,
  4868  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  4869  					Capacity: map[api.ResourceName]resource.Quantity{
  4870  						api.ResourceStorage: resource.MustParse("4Gi"),
  4871  					},
  4872  				},
  4873  				Status: api.PersistentVolumeStatus{
  4874  					Phase: api.VolumeBound,
  4875  				},
  4876  			},
  4877  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "4Gi", "ROX", "", "Bound", "default/test", "", "<unset>", "", "<unknown>", "<unset>"}}},
  4878  		},
  4879  		{
  4880  			// Test failed
  4881  			pv: api.PersistentVolume{
  4882  				ObjectMeta: metav1.ObjectMeta{
  4883  					Name: "test2",
  4884  				},
  4885  				Spec: api.PersistentVolumeSpec{
  4886  					ClaimRef:    &claimRef,
  4887  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  4888  					Capacity: map[api.ResourceName]resource.Quantity{
  4889  						api.ResourceStorage: resource.MustParse("4Gi"),
  4890  					},
  4891  				},
  4892  				Status: api.PersistentVolumeStatus{
  4893  					Phase: api.VolumeFailed,
  4894  				},
  4895  			},
  4896  			expected: []metav1.TableRow{{Cells: []interface{}{"test2", "4Gi", "ROX", "", "Failed", "default/test", "", "<unset>", "", "<unknown>", "<unset>"}}},
  4897  		},
  4898  		{
  4899  			// Test pending
  4900  			pv: api.PersistentVolume{
  4901  				ObjectMeta: metav1.ObjectMeta{
  4902  					Name: "test3",
  4903  				},
  4904  				Spec: api.PersistentVolumeSpec{
  4905  					ClaimRef:    &claimRef,
  4906  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteMany},
  4907  					Capacity: map[api.ResourceName]resource.Quantity{
  4908  						api.ResourceStorage: resource.MustParse("10Gi"),
  4909  					},
  4910  				},
  4911  				Status: api.PersistentVolumeStatus{
  4912  					Phase: api.VolumePending,
  4913  				},
  4914  			},
  4915  			expected: []metav1.TableRow{{Cells: []interface{}{"test3", "10Gi", "RWX", "", "Pending", "default/test", "", "<unset>", "", "<unknown>", "<unset>"}}},
  4916  		},
  4917  		{
  4918  			// Test pending, storageClass
  4919  			pv: api.PersistentVolume{
  4920  				ObjectMeta: metav1.ObjectMeta{
  4921  					Name: "test4",
  4922  				},
  4923  				Spec: api.PersistentVolumeSpec{
  4924  					ClaimRef:         &claimRef,
  4925  					StorageClassName: myScn,
  4926  					AccessModes:      []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  4927  					Capacity: map[api.ResourceName]resource.Quantity{
  4928  						api.ResourceStorage: resource.MustParse("10Gi"),
  4929  					},
  4930  				},
  4931  				Status: api.PersistentVolumeStatus{
  4932  					Phase: api.VolumePending,
  4933  				},
  4934  			},
  4935  			expected: []metav1.TableRow{{Cells: []interface{}{"test4", "10Gi", "RWO", "", "Pending", "default/test", "my-scn", "<unset>", "", "<unknown>", "<unset>"}}},
  4936  		},
  4937  		{
  4938  			// Test pending, storageClass, volumeAttributesClass
  4939  			pv: api.PersistentVolume{
  4940  				ObjectMeta: metav1.ObjectMeta{
  4941  					Name: "test4",
  4942  				},
  4943  				Spec: api.PersistentVolumeSpec{
  4944  					ClaimRef:                  &claimRef,
  4945  					StorageClassName:          myScn,
  4946  					VolumeAttributesClassName: &myVacn,
  4947  					AccessModes:               []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  4948  					Capacity: map[api.ResourceName]resource.Quantity{
  4949  						api.ResourceStorage: resource.MustParse("10Gi"),
  4950  					},
  4951  				},
  4952  				Status: api.PersistentVolumeStatus{
  4953  					Phase: api.VolumePending,
  4954  				},
  4955  			},
  4956  			expected: []metav1.TableRow{{Cells: []interface{}{"test4", "10Gi", "RWO", "", "Pending", "default/test", "my-scn", "my-vacn", "", "<unknown>", "<unset>"}}},
  4957  		},
  4958  		{
  4959  			// Test available
  4960  			pv: api.PersistentVolume{
  4961  				ObjectMeta: metav1.ObjectMeta{
  4962  					Name: "test5",
  4963  				},
  4964  				Spec: api.PersistentVolumeSpec{
  4965  					ClaimRef:         &claimRef,
  4966  					StorageClassName: myScn,
  4967  					AccessModes:      []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  4968  					Capacity: map[api.ResourceName]resource.Quantity{
  4969  						api.ResourceStorage: resource.MustParse("10Gi"),
  4970  					},
  4971  				},
  4972  				Status: api.PersistentVolumeStatus{
  4973  					Phase: api.VolumeAvailable,
  4974  				},
  4975  			},
  4976  			expected: []metav1.TableRow{{Cells: []interface{}{"test5", "10Gi", "RWO", "", "Available", "default/test", "my-scn", "<unset>", "", "<unknown>", "<unset>"}}},
  4977  		},
  4978  		{
  4979  			// Test released
  4980  			pv: api.PersistentVolume{
  4981  				ObjectMeta: metav1.ObjectMeta{
  4982  					Name: "test6",
  4983  				},
  4984  				Spec: api.PersistentVolumeSpec{
  4985  					ClaimRef:         &claimRef,
  4986  					StorageClassName: myScn,
  4987  					AccessModes:      []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  4988  					Capacity: map[api.ResourceName]resource.Quantity{
  4989  						api.ResourceStorage: resource.MustParse("10Gi"),
  4990  					},
  4991  				},
  4992  				Status: api.PersistentVolumeStatus{
  4993  					Phase: api.VolumeReleased,
  4994  				},
  4995  			},
  4996  			expected: []metav1.TableRow{{Cells: []interface{}{"test6", "10Gi", "RWO", "", "Released", "default/test", "my-scn", "<unset>", "", "<unknown>", "<unset>"}}},
  4997  		},
  4998  	}
  4999  
  5000  	for i, test := range tests {
  5001  		rows, err := printPersistentVolume(&test.pv, printers.GenerateOptions{})
  5002  		if err != nil {
  5003  			t.Fatal(err)
  5004  		}
  5005  		for i := range rows {
  5006  			rows[i].Object.Object = nil
  5007  		}
  5008  		if !reflect.DeepEqual(test.expected, rows) {
  5009  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5010  		}
  5011  	}
  5012  }
  5013  
  5014  func TestPrintPersistentVolumeClaim(t *testing.T) {
  5015  	volumeMode := api.PersistentVolumeFilesystem
  5016  	myVacn := "my-vacn"
  5017  	myScn := "my-scn"
  5018  	tests := []struct {
  5019  		pvc      api.PersistentVolumeClaim
  5020  		expected []metav1.TableRow
  5021  	}{
  5022  		{
  5023  			// Test name, num of containers, restarts, container ready status
  5024  			pvc: api.PersistentVolumeClaim{
  5025  				ObjectMeta: metav1.ObjectMeta{
  5026  					Name: "test1",
  5027  				},
  5028  				Spec: api.PersistentVolumeClaimSpec{
  5029  					VolumeName: "my-volume",
  5030  					VolumeMode: &volumeMode,
  5031  				},
  5032  				Status: api.PersistentVolumeClaimStatus{
  5033  					Phase:       api.ClaimBound,
  5034  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  5035  					Capacity: map[api.ResourceName]resource.Quantity{
  5036  						api.ResourceStorage: resource.MustParse("4Gi"),
  5037  					},
  5038  				},
  5039  			},
  5040  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", "Bound", "my-volume", "4Gi", "ROX", "", "<unset>", "<unknown>", "Filesystem"}}},
  5041  		},
  5042  		{
  5043  			// Test name, num of containers, restarts, container ready status
  5044  			pvc: api.PersistentVolumeClaim{
  5045  				ObjectMeta: metav1.ObjectMeta{
  5046  					Name: "test2",
  5047  				},
  5048  				Spec: api.PersistentVolumeClaimSpec{
  5049  					VolumeMode: &volumeMode,
  5050  				},
  5051  				Status: api.PersistentVolumeClaimStatus{
  5052  					Phase:       api.ClaimLost,
  5053  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
  5054  					Capacity: map[api.ResourceName]resource.Quantity{
  5055  						api.ResourceStorage: resource.MustParse("4Gi"),
  5056  					},
  5057  				},
  5058  			},
  5059  			expected: []metav1.TableRow{{Cells: []interface{}{"test2", "Lost", "", "", "", "", "<unset>", "<unknown>", "Filesystem"}}},
  5060  		},
  5061  		{
  5062  			// Test name, num of containers, restarts, container ready status
  5063  			pvc: api.PersistentVolumeClaim{
  5064  				ObjectMeta: metav1.ObjectMeta{
  5065  					Name: "test3",
  5066  				},
  5067  				Spec: api.PersistentVolumeClaimSpec{
  5068  					VolumeName: "my-volume",
  5069  					VolumeMode: &volumeMode,
  5070  				},
  5071  				Status: api.PersistentVolumeClaimStatus{
  5072  					Phase:       api.ClaimPending,
  5073  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteMany},
  5074  					Capacity: map[api.ResourceName]resource.Quantity{
  5075  						api.ResourceStorage: resource.MustParse("10Gi"),
  5076  					},
  5077  				},
  5078  			},
  5079  			expected: []metav1.TableRow{{Cells: []interface{}{"test3", "Pending", "my-volume", "10Gi", "RWX", "", "<unset>", "<unknown>", "Filesystem"}}},
  5080  		},
  5081  		{
  5082  			// Test name, num of containers, restarts, container ready status
  5083  			pvc: api.PersistentVolumeClaim{
  5084  				ObjectMeta: metav1.ObjectMeta{
  5085  					Name: "test4",
  5086  				},
  5087  				Spec: api.PersistentVolumeClaimSpec{
  5088  					VolumeName:       "my-volume",
  5089  					StorageClassName: &myScn,
  5090  					VolumeMode:       &volumeMode,
  5091  				},
  5092  				Status: api.PersistentVolumeClaimStatus{
  5093  					Phase:       api.ClaimPending,
  5094  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  5095  					Capacity: map[api.ResourceName]resource.Quantity{
  5096  						api.ResourceStorage: resource.MustParse("10Gi"),
  5097  					},
  5098  				},
  5099  			},
  5100  			expected: []metav1.TableRow{{Cells: []interface{}{"test4", "Pending", "my-volume", "10Gi", "RWO", "my-scn", "<unset>", "<unknown>", "Filesystem"}}},
  5101  		},
  5102  		{
  5103  			// Test name, num of containers, restarts, container ready status
  5104  			pvc: api.PersistentVolumeClaim{
  5105  				ObjectMeta: metav1.ObjectMeta{
  5106  					Name: "test5",
  5107  				},
  5108  				Spec: api.PersistentVolumeClaimSpec{
  5109  					VolumeName:       "my-volume",
  5110  					StorageClassName: &myScn,
  5111  				},
  5112  				Status: api.PersistentVolumeClaimStatus{
  5113  					Phase:       api.ClaimPending,
  5114  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  5115  					Capacity: map[api.ResourceName]resource.Quantity{
  5116  						api.ResourceStorage: resource.MustParse("10Gi"),
  5117  					},
  5118  				},
  5119  			},
  5120  			expected: []metav1.TableRow{{Cells: []interface{}{"test5", "Pending", "my-volume", "10Gi", "RWO", "my-scn", "<unset>", "<unknown>", "<unset>"}}},
  5121  		},
  5122  		{
  5123  			// Test name, num of containers, restarts, container ready status
  5124  			pvc: api.PersistentVolumeClaim{
  5125  				ObjectMeta: metav1.ObjectMeta{
  5126  					Name: "test5",
  5127  				},
  5128  				Spec: api.PersistentVolumeClaimSpec{
  5129  					VolumeName:                "my-volume",
  5130  					StorageClassName:          &myScn,
  5131  					VolumeAttributesClassName: &myVacn,
  5132  				},
  5133  				Status: api.PersistentVolumeClaimStatus{
  5134  					Phase:       api.ClaimPending,
  5135  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  5136  					Capacity: map[api.ResourceName]resource.Quantity{
  5137  						api.ResourceStorage: resource.MustParse("10Gi"),
  5138  					},
  5139  				},
  5140  			},
  5141  			expected: []metav1.TableRow{{Cells: []interface{}{"test5", "Pending", "my-volume", "10Gi", "RWO", "my-scn", "my-vacn", "<unknown>", "<unset>"}}},
  5142  		},
  5143  	}
  5144  
  5145  	for i, test := range tests {
  5146  		rows, err := printPersistentVolumeClaim(&test.pvc, printers.GenerateOptions{})
  5147  		if err != nil {
  5148  			t.Fatal(err)
  5149  		}
  5150  		for i := range rows {
  5151  			rows[i].Object.Object = nil
  5152  		}
  5153  		if !reflect.DeepEqual(test.expected, rows) {
  5154  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5155  		}
  5156  	}
  5157  }
  5158  
  5159  func TestPrintComponentStatus(t *testing.T) {
  5160  	tests := []struct {
  5161  		componentStatus api.ComponentStatus
  5162  		expected        []metav1.TableRow
  5163  	}{
  5164  		// Basic component status without conditions
  5165  		{
  5166  			componentStatus: api.ComponentStatus{
  5167  				ObjectMeta: metav1.ObjectMeta{
  5168  					Name: "cs1",
  5169  				},
  5170  				Conditions: []api.ComponentCondition{},
  5171  			},
  5172  			// Columns: Name, Status, Message, Error
  5173  			expected: []metav1.TableRow{{Cells: []interface{}{"cs1", "Unknown", "", ""}}},
  5174  		},
  5175  		// Basic component status with healthy condition.
  5176  		{
  5177  			componentStatus: api.ComponentStatus{
  5178  				ObjectMeta: metav1.ObjectMeta{
  5179  					Name: "cs2",
  5180  				},
  5181  				Conditions: []api.ComponentCondition{
  5182  					{
  5183  						Type:    "Healthy",
  5184  						Status:  api.ConditionTrue,
  5185  						Message: "test message",
  5186  						Error:   "test error",
  5187  					},
  5188  				},
  5189  			},
  5190  			// Columns: Name, Status, Message, Error
  5191  			expected: []metav1.TableRow{{Cells: []interface{}{"cs2", "Healthy", "test message", "test error"}}},
  5192  		},
  5193  		// Basic component status with healthy condition.
  5194  		{
  5195  			componentStatus: api.ComponentStatus{
  5196  				ObjectMeta: metav1.ObjectMeta{
  5197  					Name: "cs3",
  5198  				},
  5199  				Conditions: []api.ComponentCondition{
  5200  					{
  5201  						Type:    "Healthy",
  5202  						Status:  api.ConditionFalse,
  5203  						Message: "test message",
  5204  						Error:   "test error",
  5205  					},
  5206  				},
  5207  			},
  5208  			// Columns: Name, Status, Message, Error
  5209  			expected: []metav1.TableRow{{Cells: []interface{}{"cs3", "Unhealthy", "test message", "test error"}}},
  5210  		},
  5211  	}
  5212  
  5213  	for i, test := range tests {
  5214  		rows, err := printComponentStatus(&test.componentStatus, printers.GenerateOptions{})
  5215  		if err != nil {
  5216  			t.Fatal(err)
  5217  		}
  5218  		for i := range rows {
  5219  			rows[i].Object.Object = nil
  5220  		}
  5221  		if !reflect.DeepEqual(test.expected, rows) {
  5222  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5223  		}
  5224  	}
  5225  }
  5226  
  5227  func TestPrintCronJob(t *testing.T) {
  5228  	timeZone := "LOCAL"
  5229  	completions := int32(2)
  5230  	suspend := false
  5231  	tests := []struct {
  5232  		cronjob  batch.CronJob
  5233  		options  printers.GenerateOptions
  5234  		expected []metav1.TableRow
  5235  	}{
  5236  		// Basic cron job; does not print containers, images, or labels.
  5237  		{
  5238  			cronjob: batch.CronJob{
  5239  				ObjectMeta: metav1.ObjectMeta{
  5240  					Name:              "cronjob1",
  5241  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5242  				},
  5243  				Spec: batch.CronJobSpec{
  5244  					Schedule: "0/5 * * * ?",
  5245  					Suspend:  &suspend,
  5246  					JobTemplate: batch.JobTemplateSpec{
  5247  						Spec: batch.JobSpec{
  5248  							Completions: &completions,
  5249  							Template: api.PodTemplateSpec{
  5250  								Spec: api.PodSpec{
  5251  									Containers: []api.Container{
  5252  										{
  5253  											Name:  "fake-job-container1",
  5254  											Image: "fake-job-image1",
  5255  										},
  5256  										{
  5257  											Name:  "fake-job-container2",
  5258  											Image: "fake-job-image2",
  5259  										},
  5260  									},
  5261  								},
  5262  							},
  5263  							Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
  5264  						},
  5265  					},
  5266  				},
  5267  				Status: batch.CronJobStatus{
  5268  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(1.9e9)},
  5269  				},
  5270  			},
  5271  			options: printers.GenerateOptions{},
  5272  			// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5273  			expected: []metav1.TableRow{{Cells: []interface{}{"cronjob1", "0/5 * * * ?", "<none>", "False", int64(0), "0s", "0s"}}},
  5274  		},
  5275  		// Basic cron job; does not print containers, images, or labels.
  5276  		{
  5277  			cronjob: batch.CronJob{
  5278  				ObjectMeta: metav1.ObjectMeta{
  5279  					Name:              "cronjob1",
  5280  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5281  				},
  5282  				Spec: batch.CronJobSpec{
  5283  					Schedule: "0/5 * * * ?",
  5284  					TimeZone: &timeZone,
  5285  					Suspend:  &suspend,
  5286  					JobTemplate: batch.JobTemplateSpec{
  5287  						Spec: batch.JobSpec{
  5288  							Completions: &completions,
  5289  							Template: api.PodTemplateSpec{
  5290  								Spec: api.PodSpec{
  5291  									Containers: []api.Container{
  5292  										{
  5293  											Name:  "fake-job-container1",
  5294  											Image: "fake-job-image1",
  5295  										},
  5296  										{
  5297  											Name:  "fake-job-container2",
  5298  											Image: "fake-job-image2",
  5299  										},
  5300  									},
  5301  								},
  5302  							},
  5303  							Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
  5304  						},
  5305  					},
  5306  				},
  5307  				Status: batch.CronJobStatus{
  5308  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(1.9e9)},
  5309  				},
  5310  			},
  5311  			options: printers.GenerateOptions{},
  5312  			// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5313  			expected: []metav1.TableRow{{Cells: []interface{}{"cronjob1", "0/5 * * * ?", "LOCAL", "False", int64(0), "0s", "0s"}}},
  5314  		},
  5315  		// Generate options: Wide; prints containers, images, and labels.
  5316  		{
  5317  			cronjob: batch.CronJob{
  5318  				ObjectMeta: metav1.ObjectMeta{
  5319  					Name:              "cronjob1",
  5320  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5321  				},
  5322  				Spec: batch.CronJobSpec{
  5323  					Schedule: "0/5 * * * ?",
  5324  					Suspend:  &suspend,
  5325  					JobTemplate: batch.JobTemplateSpec{
  5326  						Spec: batch.JobSpec{
  5327  							Completions: &completions,
  5328  							Template: api.PodTemplateSpec{
  5329  								Spec: api.PodSpec{
  5330  									Containers: []api.Container{
  5331  										{
  5332  											Name:  "fake-job-container1",
  5333  											Image: "fake-job-image1",
  5334  										},
  5335  										{
  5336  											Name:  "fake-job-container2",
  5337  											Image: "fake-job-image2",
  5338  										},
  5339  									},
  5340  								},
  5341  							},
  5342  							Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
  5343  						},
  5344  					},
  5345  				},
  5346  				Status: batch.CronJobStatus{
  5347  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(1.9e9)},
  5348  				},
  5349  			},
  5350  			options: printers.GenerateOptions{Wide: true},
  5351  			// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5352  			expected: []metav1.TableRow{{Cells: []interface{}{"cronjob1", "0/5 * * * ?", "<none>", "False", int64(0), "0s", "0s", "fake-job-container1,fake-job-container2", "fake-job-image1,fake-job-image2", "a=b"}}},
  5353  		},
  5354  		// CronJob with Last Schedule and Age
  5355  		{
  5356  			cronjob: batch.CronJob{
  5357  				ObjectMeta: metav1.ObjectMeta{
  5358  					Name:              "cronjob2",
  5359  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5360  				},
  5361  				Spec: batch.CronJobSpec{
  5362  					Schedule: "0/5 * * * ?",
  5363  					Suspend:  &suspend,
  5364  				},
  5365  				Status: batch.CronJobStatus{
  5366  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(-3e10)},
  5367  				},
  5368  			},
  5369  			options: printers.GenerateOptions{},
  5370  			// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5371  			expected: []metav1.TableRow{{Cells: []interface{}{"cronjob2", "0/5 * * * ?", "<none>", "False", int64(0), "30s", "5m"}}},
  5372  		},
  5373  		// CronJob without Last Schedule
  5374  		{
  5375  			cronjob: batch.CronJob{
  5376  				ObjectMeta: metav1.ObjectMeta{
  5377  					Name:              "cronjob3",
  5378  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5379  				},
  5380  				Spec: batch.CronJobSpec{
  5381  					Schedule: "0/5 * * * ?",
  5382  					Suspend:  &suspend,
  5383  				},
  5384  				Status: batch.CronJobStatus{},
  5385  			},
  5386  			options: printers.GenerateOptions{},
  5387  			// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5388  			expected: []metav1.TableRow{{Cells: []interface{}{"cronjob3", "0/5 * * * ?", "<none>", "False", int64(0), "<none>", "5m"}}},
  5389  		},
  5390  	}
  5391  
  5392  	for i, test := range tests {
  5393  		rows, err := printCronJob(&test.cronjob, test.options)
  5394  		if err != nil {
  5395  			t.Fatal(err)
  5396  		}
  5397  		for i := range rows {
  5398  			rows[i].Object.Object = nil
  5399  		}
  5400  		if !reflect.DeepEqual(test.expected, rows) {
  5401  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5402  		}
  5403  	}
  5404  }
  5405  
  5406  func TestPrintCronJobList(t *testing.T) {
  5407  	timeZone := "LOCAL"
  5408  	completions := int32(2)
  5409  	suspend := false
  5410  
  5411  	cronJobList := batch.CronJobList{
  5412  		Items: []batch.CronJob{
  5413  			{
  5414  				ObjectMeta: metav1.ObjectMeta{
  5415  					Name:              "cronjob1",
  5416  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5417  				},
  5418  				Spec: batch.CronJobSpec{
  5419  					Schedule: "0/5 * * * ?",
  5420  					TimeZone: &timeZone,
  5421  					Suspend:  &suspend,
  5422  					JobTemplate: batch.JobTemplateSpec{
  5423  						Spec: batch.JobSpec{
  5424  							Completions: &completions,
  5425  							Selector:    &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
  5426  						},
  5427  					},
  5428  				},
  5429  				Status: batch.CronJobStatus{
  5430  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(1.9e9)},
  5431  				},
  5432  			},
  5433  			{
  5434  				ObjectMeta: metav1.ObjectMeta{
  5435  					Name:              "cronjob2",
  5436  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5437  				},
  5438  				Spec: batch.CronJobSpec{
  5439  					Schedule: "4/5 1 1 1 ?",
  5440  					Suspend:  &suspend,
  5441  					JobTemplate: batch.JobTemplateSpec{
  5442  						Spec: batch.JobSpec{
  5443  							Completions: &completions,
  5444  							Selector:    &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
  5445  						},
  5446  					},
  5447  				},
  5448  				Status: batch.CronJobStatus{
  5449  					LastScheduleTime: &metav1.Time{Time: time.Now().Add(-20 * time.Minute)},
  5450  				},
  5451  			},
  5452  		},
  5453  	}
  5454  
  5455  	// Columns: Name, Schedule, Suspend, Active, Last Schedule, Age
  5456  	expectedRows := []metav1.TableRow{
  5457  		{Cells: []interface{}{"cronjob1", "0/5 * * * ?", "LOCAL", "False", int64(0), "0s", "0s"}},
  5458  		{Cells: []interface{}{"cronjob2", "4/5 1 1 1 ?", "<none>", "False", int64(0), "20m", "0s"}},
  5459  	}
  5460  
  5461  	rows, err := printCronJobList(&cronJobList, printers.GenerateOptions{})
  5462  	if err != nil {
  5463  		t.Fatalf("Error printing job list: %#v", err)
  5464  	}
  5465  	for i := range rows {
  5466  		rows[i].Object.Object = nil
  5467  	}
  5468  	if !reflect.DeepEqual(expectedRows, rows) {
  5469  		t.Errorf("mismatch: %s", cmp.Diff(expectedRows, rows))
  5470  	}
  5471  }
  5472  
  5473  func TestPrintStorageClass(t *testing.T) {
  5474  	policyDelte := api.PersistentVolumeReclaimDelete
  5475  	policyRetain := api.PersistentVolumeReclaimRetain
  5476  	bindModeImmediate := storage.VolumeBindingImmediate
  5477  	bindModeWait := storage.VolumeBindingWaitForFirstConsumer
  5478  	tests := []struct {
  5479  		sc       storage.StorageClass
  5480  		expected []metav1.TableRow
  5481  	}{
  5482  		{
  5483  			sc: storage.StorageClass{
  5484  				ObjectMeta: metav1.ObjectMeta{
  5485  					Name:              "sc1",
  5486  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5487  				},
  5488  				Provisioner: "kubernetes.io/glusterfs",
  5489  			},
  5490  			expected: []metav1.TableRow{{Cells: []interface{}{"sc1", "kubernetes.io/glusterfs", "Delete",
  5491  				"Immediate", false, "0s"}}},
  5492  		},
  5493  		{
  5494  			sc: storage.StorageClass{
  5495  				ObjectMeta: metav1.ObjectMeta{
  5496  					Name:              "sc2",
  5497  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5498  				},
  5499  				Provisioner: "kubernetes.io/nfs",
  5500  			},
  5501  			expected: []metav1.TableRow{{Cells: []interface{}{"sc2", "kubernetes.io/nfs", "Delete",
  5502  				"Immediate", false, "5m"}}},
  5503  		},
  5504  		{
  5505  			sc: storage.StorageClass{
  5506  				ObjectMeta: metav1.ObjectMeta{
  5507  					Name:              "sc3",
  5508  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5509  				},
  5510  				Provisioner:   "kubernetes.io/nfs",
  5511  				ReclaimPolicy: &policyDelte,
  5512  			},
  5513  			expected: []metav1.TableRow{{Cells: []interface{}{"sc3", "kubernetes.io/nfs", "Delete",
  5514  				"Immediate", false, "5m"}}},
  5515  		},
  5516  		{
  5517  			sc: storage.StorageClass{
  5518  				ObjectMeta: metav1.ObjectMeta{
  5519  					Name:              "sc4",
  5520  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5521  				},
  5522  				Provisioner:       "kubernetes.io/nfs",
  5523  				ReclaimPolicy:     &policyRetain,
  5524  				VolumeBindingMode: &bindModeImmediate,
  5525  			},
  5526  			expected: []metav1.TableRow{{Cells: []interface{}{"sc4", "kubernetes.io/nfs", "Retain",
  5527  				"Immediate", false, "5m"}}},
  5528  		},
  5529  		{
  5530  			sc: storage.StorageClass{
  5531  				ObjectMeta: metav1.ObjectMeta{
  5532  					Name:              "sc5",
  5533  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5534  				},
  5535  				Provisioner:       "kubernetes.io/nfs",
  5536  				ReclaimPolicy:     &policyRetain,
  5537  				VolumeBindingMode: &bindModeWait,
  5538  			},
  5539  			expected: []metav1.TableRow{{Cells: []interface{}{"sc5", "kubernetes.io/nfs", "Retain",
  5540  				"WaitForFirstConsumer", false, "5m"}}},
  5541  		},
  5542  		{
  5543  			sc: storage.StorageClass{
  5544  				ObjectMeta: metav1.ObjectMeta{
  5545  					Name:              "sc6",
  5546  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5547  				},
  5548  				Provisioner:          "kubernetes.io/nfs",
  5549  				ReclaimPolicy:        &policyRetain,
  5550  				AllowVolumeExpansion: boolP(true),
  5551  				VolumeBindingMode:    &bindModeWait,
  5552  			},
  5553  			expected: []metav1.TableRow{{Cells: []interface{}{"sc6", "kubernetes.io/nfs", "Retain",
  5554  				"WaitForFirstConsumer", true, "5m"}}},
  5555  		},
  5556  	}
  5557  
  5558  	for i, test := range tests {
  5559  		rows, err := printStorageClass(&test.sc, printers.GenerateOptions{})
  5560  		if err != nil {
  5561  			t.Fatal(err)
  5562  		}
  5563  		for i := range rows {
  5564  			rows[i].Object.Object = nil
  5565  		}
  5566  		if !reflect.DeepEqual(test.expected, rows) {
  5567  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5568  		}
  5569  	}
  5570  }
  5571  
  5572  func TestPrintVolumeAttributesClass(t *testing.T) {
  5573  	tests := []struct {
  5574  		vac      storage.VolumeAttributesClass
  5575  		expected []metav1.TableRow
  5576  	}{
  5577  		{
  5578  			vac: storage.VolumeAttributesClass{
  5579  				ObjectMeta: metav1.ObjectMeta{
  5580  					Name:              "vac1",
  5581  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5582  				},
  5583  				DriverName: "fake",
  5584  			},
  5585  			expected: []metav1.TableRow{{Cells: []interface{}{"vac1", "fake", "0s"}}},
  5586  		},
  5587  		{
  5588  			vac: storage.VolumeAttributesClass{
  5589  				ObjectMeta: metav1.ObjectMeta{
  5590  					Name:              "vac2",
  5591  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5592  				},
  5593  				DriverName: "fake",
  5594  				Parameters: map[string]string{
  5595  					"iops":       "500",
  5596  					"throughput": "50MiB/s",
  5597  				},
  5598  			},
  5599  			expected: []metav1.TableRow{{Cells: []interface{}{"vac2", "fake", "5m"}}},
  5600  		},
  5601  	}
  5602  
  5603  	for i, test := range tests {
  5604  		rows, err := printVolumeAttributesClass(&test.vac, printers.GenerateOptions{})
  5605  		if err != nil {
  5606  			t.Fatal(err)
  5607  		}
  5608  		for i := range rows {
  5609  			rows[i].Object.Object = nil
  5610  		}
  5611  		if !reflect.DeepEqual(test.expected, rows) {
  5612  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5613  		}
  5614  	}
  5615  }
  5616  
  5617  func TestPrintLease(t *testing.T) {
  5618  	holder1 := "holder1"
  5619  	holder2 := "holder2"
  5620  	tests := []struct {
  5621  		lease    coordination.Lease
  5622  		expected []metav1.TableRow
  5623  	}{
  5624  		{
  5625  			lease: coordination.Lease{
  5626  				ObjectMeta: metav1.ObjectMeta{
  5627  					Name:              "lease1",
  5628  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5629  				},
  5630  				Spec: coordination.LeaseSpec{
  5631  					HolderIdentity: &holder1,
  5632  				},
  5633  			},
  5634  			expected: []metav1.TableRow{{Cells: []interface{}{"lease1", "holder1", "0s"}}},
  5635  		},
  5636  		{
  5637  			lease: coordination.Lease{
  5638  				ObjectMeta: metav1.ObjectMeta{
  5639  					Name:              "lease2",
  5640  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5641  				},
  5642  				Spec: coordination.LeaseSpec{
  5643  					HolderIdentity: &holder2,
  5644  				},
  5645  			},
  5646  			expected: []metav1.TableRow{{Cells: []interface{}{"lease2", "holder2", "5m"}}},
  5647  		},
  5648  	}
  5649  
  5650  	for i, test := range tests {
  5651  		rows, err := printLease(&test.lease, printers.GenerateOptions{})
  5652  		if err != nil {
  5653  			t.Fatal(err)
  5654  		}
  5655  		for i := range rows {
  5656  			rows[i].Object.Object = nil
  5657  		}
  5658  		if !reflect.DeepEqual(test.expected, rows) {
  5659  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5660  		}
  5661  	}
  5662  }
  5663  
  5664  func TestPrintPriorityClass(t *testing.T) {
  5665  	tests := []struct {
  5666  		pc       scheduling.PriorityClass
  5667  		expected []metav1.TableRow
  5668  	}{
  5669  		{
  5670  			pc: scheduling.PriorityClass{
  5671  				ObjectMeta: metav1.ObjectMeta{
  5672  					Name:              "pc1",
  5673  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5674  				},
  5675  				Value: 1,
  5676  			},
  5677  			expected: []metav1.TableRow{{Cells: []interface{}{"pc1", int64(1), bool(false), "0s"}}},
  5678  		},
  5679  		{
  5680  			pc: scheduling.PriorityClass{
  5681  				ObjectMeta: metav1.ObjectMeta{
  5682  					Name:              "pc2",
  5683  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5684  				},
  5685  				Value:         1000000000,
  5686  				GlobalDefault: true,
  5687  			},
  5688  			expected: []metav1.TableRow{{Cells: []interface{}{"pc2", int64(1000000000), bool(true), "5m"}}},
  5689  		},
  5690  	}
  5691  
  5692  	for i, test := range tests {
  5693  		rows, err := printPriorityClass(&test.pc, printers.GenerateOptions{})
  5694  		if err != nil {
  5695  			t.Fatal(err)
  5696  		}
  5697  		for i := range rows {
  5698  			rows[i].Object.Object = nil
  5699  		}
  5700  		if !reflect.DeepEqual(test.expected, rows) {
  5701  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5702  		}
  5703  	}
  5704  }
  5705  
  5706  func TestPrintRuntimeClass(t *testing.T) {
  5707  	tests := []struct {
  5708  		rc       nodeapi.RuntimeClass
  5709  		expected []metav1.TableRow
  5710  	}{
  5711  		{
  5712  			rc: nodeapi.RuntimeClass{
  5713  				ObjectMeta: metav1.ObjectMeta{
  5714  					Name:              "rc1",
  5715  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5716  				},
  5717  				Handler: "h1",
  5718  			},
  5719  			expected: []metav1.TableRow{{Cells: []interface{}{"rc1", "h1", "0s"}}},
  5720  		},
  5721  		{
  5722  			rc: nodeapi.RuntimeClass{
  5723  				ObjectMeta: metav1.ObjectMeta{
  5724  					Name:              "rc2",
  5725  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5726  				},
  5727  				Handler: "h2",
  5728  			},
  5729  			expected: []metav1.TableRow{{Cells: []interface{}{"rc2", "h2", "5m"}}},
  5730  		},
  5731  	}
  5732  
  5733  	for i, test := range tests {
  5734  		rows, err := printRuntimeClass(&test.rc, printers.GenerateOptions{})
  5735  		if err != nil {
  5736  			t.Fatal(err)
  5737  		}
  5738  		for i := range rows {
  5739  			rows[i].Object.Object = nil
  5740  		}
  5741  		if !reflect.DeepEqual(test.expected, rows) {
  5742  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5743  		}
  5744  	}
  5745  }
  5746  
  5747  func TestPrintEndpoint(t *testing.T) {
  5748  
  5749  	tests := []struct {
  5750  		endpoint api.Endpoints
  5751  		expected []metav1.TableRow
  5752  	}{
  5753  		// Basic endpoint with no IP's
  5754  		{
  5755  			endpoint: api.Endpoints{
  5756  				ObjectMeta: metav1.ObjectMeta{
  5757  					Name:              "endpoint1",
  5758  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5759  				},
  5760  			},
  5761  			// Columns: Name, Endpoints, Age
  5762  			expected: []metav1.TableRow{{Cells: []interface{}{"endpoint1", "<none>", "0s"}}},
  5763  		},
  5764  		// Endpoint with no ports
  5765  		{
  5766  			endpoint: api.Endpoints{
  5767  				ObjectMeta: metav1.ObjectMeta{
  5768  					Name:              "endpoint3",
  5769  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5770  				},
  5771  				Subsets: []api.EndpointSubset{
  5772  					{
  5773  						Addresses: []api.EndpointAddress{
  5774  							{
  5775  								IP: "1.2.3.4",
  5776  							},
  5777  							{
  5778  								IP: "5.6.7.8",
  5779  							},
  5780  						},
  5781  					},
  5782  				},
  5783  			},
  5784  			// Columns: Name, Endpoints, Age
  5785  			expected: []metav1.TableRow{{Cells: []interface{}{"endpoint3", "1.2.3.4,5.6.7.8", "5m"}}},
  5786  		},
  5787  		// Basic endpoint with two IP's and one port
  5788  		{
  5789  			endpoint: api.Endpoints{
  5790  				ObjectMeta: metav1.ObjectMeta{
  5791  					Name:              "endpoint2",
  5792  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5793  				},
  5794  				Subsets: []api.EndpointSubset{
  5795  					{
  5796  						Addresses: []api.EndpointAddress{
  5797  							{
  5798  								IP: "1.2.3.4",
  5799  							},
  5800  							{
  5801  								IP: "5.6.7.8",
  5802  							},
  5803  						},
  5804  						Ports: []api.EndpointPort{
  5805  							{
  5806  								Port:     8001,
  5807  								Protocol: "tcp",
  5808  							},
  5809  						},
  5810  					},
  5811  				},
  5812  			},
  5813  			// Columns: Name, Endpoints, Age
  5814  			expected: []metav1.TableRow{{Cells: []interface{}{"endpoint2", "1.2.3.4:8001,5.6.7.8:8001", "0s"}}},
  5815  		},
  5816  		// Basic endpoint with greater than three IP's triggering "more" string
  5817  		{
  5818  			endpoint: api.Endpoints{
  5819  				ObjectMeta: metav1.ObjectMeta{
  5820  					Name:              "endpoint2",
  5821  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5822  				},
  5823  				Subsets: []api.EndpointSubset{
  5824  					{
  5825  						Addresses: []api.EndpointAddress{
  5826  							{
  5827  								IP: "1.2.3.4",
  5828  							},
  5829  							{
  5830  								IP: "5.6.7.8",
  5831  							},
  5832  							{
  5833  								IP: "9.8.7.6",
  5834  							},
  5835  							{
  5836  								IP: "6.6.6.6",
  5837  							},
  5838  						},
  5839  						Ports: []api.EndpointPort{
  5840  							{
  5841  								Port:     8001,
  5842  								Protocol: "tcp",
  5843  							},
  5844  						},
  5845  					},
  5846  				},
  5847  			},
  5848  			// Columns: Name, Endpoints, Age
  5849  			expected: []metav1.TableRow{{Cells: []interface{}{"endpoint2", "1.2.3.4:8001,5.6.7.8:8001,9.8.7.6:8001 + 1 more...", "0s"}}},
  5850  		},
  5851  	}
  5852  
  5853  	for i, test := range tests {
  5854  		rows, err := printEndpoints(&test.endpoint, printers.GenerateOptions{})
  5855  		if err != nil {
  5856  			t.Fatal(err)
  5857  		}
  5858  		for i := range rows {
  5859  			rows[i].Object.Object = nil
  5860  		}
  5861  		if !reflect.DeepEqual(test.expected, rows) {
  5862  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5863  		}
  5864  	}
  5865  
  5866  }
  5867  
  5868  func TestPrintEndpointSlice(t *testing.T) {
  5869  	tcpProtocol := api.ProtocolTCP
  5870  
  5871  	tests := []struct {
  5872  		endpointSlice discovery.EndpointSlice
  5873  		expected      []metav1.TableRow
  5874  	}{
  5875  		{
  5876  			endpointSlice: discovery.EndpointSlice{
  5877  				ObjectMeta: metav1.ObjectMeta{
  5878  					Name:              "abcslice.123",
  5879  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5880  				},
  5881  				AddressType: discovery.AddressTypeIPv4,
  5882  				Ports: []discovery.EndpointPort{{
  5883  					Name:     utilpointer.StringPtr("http"),
  5884  					Port:     utilpointer.Int32Ptr(80),
  5885  					Protocol: &tcpProtocol,
  5886  				}},
  5887  				Endpoints: []discovery.Endpoint{{
  5888  					Addresses: []string{"10.1.2.3", "2001:db8::1234:5678"},
  5889  				}},
  5890  			},
  5891  			// Columns: Name, AddressType, Ports, Endpoints, Age
  5892  			expected: []metav1.TableRow{{Cells: []interface{}{"abcslice.123", "IPv4", "80", "10.1.2.3,2001:db8::1234:5678", "0s"}}},
  5893  		}, {
  5894  			endpointSlice: discovery.EndpointSlice{
  5895  				ObjectMeta: metav1.ObjectMeta{
  5896  					Name:              "longerslicename.123",
  5897  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5898  				},
  5899  				AddressType: discovery.AddressTypeIPv6,
  5900  				Ports: []discovery.EndpointPort{{
  5901  					Name:     utilpointer.StringPtr("http"),
  5902  					Port:     utilpointer.Int32Ptr(80),
  5903  					Protocol: &tcpProtocol,
  5904  				}, {
  5905  					Name:     utilpointer.StringPtr("https"),
  5906  					Port:     utilpointer.Int32Ptr(443),
  5907  					Protocol: &tcpProtocol,
  5908  				}},
  5909  				Endpoints: []discovery.Endpoint{{
  5910  					Addresses: []string{"10.1.2.3", "2001:db8::1234:5678"},
  5911  				}, {
  5912  					Addresses: []string{"10.2.3.4", "2001:db8::2345:6789"},
  5913  				}},
  5914  			},
  5915  			// Columns: Name, AddressType, Ports, Endpoints, Age
  5916  			expected: []metav1.TableRow{{Cells: []interface{}{"longerslicename.123", "IPv6", "80,443", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
  5917  		}, {
  5918  			endpointSlice: discovery.EndpointSlice{
  5919  				ObjectMeta: metav1.ObjectMeta{
  5920  					Name:              "multiportslice.123",
  5921  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  5922  				},
  5923  				AddressType: discovery.AddressTypeIPv4,
  5924  				Ports: []discovery.EndpointPort{{
  5925  					Name:     utilpointer.StringPtr("http"),
  5926  					Port:     utilpointer.Int32Ptr(80),
  5927  					Protocol: &tcpProtocol,
  5928  				}, {
  5929  					Name:     utilpointer.StringPtr("https"),
  5930  					Port:     utilpointer.Int32Ptr(443),
  5931  					Protocol: &tcpProtocol,
  5932  				}, {
  5933  					Name:     utilpointer.StringPtr("extra1"),
  5934  					Port:     utilpointer.Int32Ptr(3000),
  5935  					Protocol: &tcpProtocol,
  5936  				}, {
  5937  					Name:     utilpointer.StringPtr("extra2"),
  5938  					Port:     utilpointer.Int32Ptr(3001),
  5939  					Protocol: &tcpProtocol,
  5940  				}},
  5941  				Endpoints: []discovery.Endpoint{{
  5942  					Addresses: []string{"10.1.2.3", "2001:db8::1234:5678"},
  5943  				}, {
  5944  					Addresses: []string{"10.2.3.4", "2001:db8::2345:6789"},
  5945  				}},
  5946  			},
  5947  			// Columns: Name, AddressType, Ports, Endpoints, Age
  5948  			expected: []metav1.TableRow{{Cells: []interface{}{"multiportslice.123", "IPv4", "80,443,3000 + 1 more...", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
  5949  		},
  5950  	}
  5951  
  5952  	for i, test := range tests {
  5953  		rows, err := printEndpointSlice(&test.endpointSlice, printers.GenerateOptions{})
  5954  		if err != nil {
  5955  			t.Fatal(err)
  5956  		}
  5957  		for i := range rows {
  5958  			rows[i].Object.Object = nil
  5959  		}
  5960  		if !reflect.DeepEqual(test.expected, rows) {
  5961  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  5962  		}
  5963  	}
  5964  }
  5965  
  5966  func TestPrintFlowSchema(t *testing.T) {
  5967  	all := []string{"*"}
  5968  
  5969  	tests := []struct {
  5970  		fs       flowcontrol.FlowSchema
  5971  		expected []metav1.TableRow
  5972  	}{
  5973  		{
  5974  			fs: flowcontrol.FlowSchema{
  5975  				ObjectMeta: metav1.ObjectMeta{
  5976  					Name:              "all-matcher",
  5977  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  5978  				},
  5979  				Spec: flowcontrol.FlowSchemaSpec{
  5980  					PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{Name: "allee"},
  5981  					MatchingPrecedence:         math.MaxInt32,
  5982  					DistinguisherMethod:        &flowcontrol.FlowDistinguisherMethod{Type: flowcontrol.FlowDistinguisherMethodByUserType},
  5983  					Rules: []flowcontrol.PolicyRulesWithSubjects{{
  5984  						Subjects: []flowcontrol.Subject{{
  5985  							Kind:  flowcontrol.SubjectKindGroup,
  5986  							Group: &flowcontrol.GroupSubject{Name: "system:authenticated"},
  5987  						}},
  5988  						ResourceRules: []flowcontrol.ResourcePolicyRule{{
  5989  							Verbs:        all,
  5990  							APIGroups:    all,
  5991  							Resources:    all,
  5992  							ClusterScope: true,
  5993  							Namespaces:   all,
  5994  						}},
  5995  					}, {
  5996  						Subjects: []flowcontrol.Subject{{
  5997  							Kind:  flowcontrol.SubjectKindGroup,
  5998  							Group: &flowcontrol.GroupSubject{Name: "system:unauthenticated"},
  5999  						}},
  6000  						ResourceRules: []flowcontrol.ResourcePolicyRule{{
  6001  							Verbs:        all,
  6002  							APIGroups:    all,
  6003  							Resources:    all,
  6004  							ClusterScope: true,
  6005  							Namespaces:   all,
  6006  						}},
  6007  					}, {
  6008  						Subjects: []flowcontrol.Subject{{
  6009  							Kind:  flowcontrol.SubjectKindGroup,
  6010  							Group: &flowcontrol.GroupSubject{Name: "system:authenticated"},
  6011  						}, {
  6012  							Kind:  flowcontrol.SubjectKindGroup,
  6013  							Group: &flowcontrol.GroupSubject{Name: "system:unauthenticated"},
  6014  						}},
  6015  						NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
  6016  							Verbs:           all,
  6017  							NonResourceURLs: all,
  6018  						}},
  6019  					}},
  6020  				},
  6021  			},
  6022  			// Columns: Name, PriorityLevelName, MatchingPrecedence, DistinguisherMethod, Age, MissingPL
  6023  			expected: []metav1.TableRow{{Cells: []interface{}{"all-matcher", "allee", int64(math.MaxInt32), "ByUser", "0s", "?"}}},
  6024  		}, {
  6025  			fs: flowcontrol.FlowSchema{
  6026  				ObjectMeta: metav1.ObjectMeta{
  6027  					Name:              "some-matcher",
  6028  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  6029  				},
  6030  				Spec: flowcontrol.FlowSchemaSpec{
  6031  					PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{Name: "allee"},
  6032  					MatchingPrecedence:         0,
  6033  					DistinguisherMethod:        &flowcontrol.FlowDistinguisherMethod{Type: flowcontrol.FlowDistinguisherMethodByNamespaceType},
  6034  					Rules: []flowcontrol.PolicyRulesWithSubjects{{
  6035  						Subjects: []flowcontrol.Subject{{
  6036  							Kind:  flowcontrol.SubjectKindGroup,
  6037  							Group: &flowcontrol.GroupSubject{Name: "system:unauthenticated"},
  6038  						}},
  6039  						ResourceRules: []flowcontrol.ResourcePolicyRule{{
  6040  							Verbs:        all,
  6041  							APIGroups:    all,
  6042  							Resources:    all,
  6043  							ClusterScope: true,
  6044  							Namespaces:   all,
  6045  						}},
  6046  					}, {
  6047  						Subjects: []flowcontrol.Subject{{
  6048  							Kind:  flowcontrol.SubjectKindGroup,
  6049  							Group: &flowcontrol.GroupSubject{Name: "system:authenticated"},
  6050  						}, {
  6051  							Kind:  flowcontrol.SubjectKindGroup,
  6052  							Group: &flowcontrol.GroupSubject{Name: "system:unauthenticated"},
  6053  						}},
  6054  						NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
  6055  							Verbs:           all,
  6056  							NonResourceURLs: all,
  6057  						}},
  6058  					}},
  6059  				},
  6060  				Status: flowcontrol.FlowSchemaStatus{
  6061  					Conditions: []flowcontrol.FlowSchemaCondition{{
  6062  						Type:               flowcontrol.FlowSchemaConditionDangling,
  6063  						Status:             "True",
  6064  						LastTransitionTime: metav1.Time{Time: time.Now().Add(-time.Hour)},
  6065  					}},
  6066  				},
  6067  			},
  6068  			// Columns: Name, PriorityLevelName, MatchingPrecedence, DistinguisherMethod, Age, MissingPL
  6069  			expected: []metav1.TableRow{{Cells: []interface{}{"some-matcher", "allee", int64(0), "ByNamespace", "5m", "True"}}},
  6070  		}, {
  6071  			fs: flowcontrol.FlowSchema{
  6072  				ObjectMeta: metav1.ObjectMeta{
  6073  					Name:              "exempt",
  6074  					CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
  6075  				},
  6076  				Spec: flowcontrol.FlowSchemaSpec{
  6077  					PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{Name: "allee"},
  6078  					MatchingPrecedence:         0,
  6079  					DistinguisherMethod:        nil,
  6080  					Rules: []flowcontrol.PolicyRulesWithSubjects{{
  6081  						Subjects: []flowcontrol.Subject{{
  6082  							Kind:  flowcontrol.SubjectKindGroup,
  6083  							Group: &flowcontrol.GroupSubject{Name: "system:masters"},
  6084  						}},
  6085  						ResourceRules: []flowcontrol.ResourcePolicyRule{{
  6086  							Verbs:        all,
  6087  							APIGroups:    all,
  6088  							Resources:    all,
  6089  							ClusterScope: true,
  6090  							Namespaces:   all,
  6091  						}},
  6092  					}},
  6093  				},
  6094  			},
  6095  			// Columns: Name, PriorityLevelName, MatchingPrecedence, DistinguisherMethod, Age, MissingPL
  6096  			expected: []metav1.TableRow{{Cells: []interface{}{"exempt", "allee", int64(0), "<none>", "5m", "?"}}},
  6097  		},
  6098  	}
  6099  
  6100  	for i, test := range tests {
  6101  		rows, err := printFlowSchema(&test.fs, printers.GenerateOptions{})
  6102  		if err != nil {
  6103  			t.Fatal(err)
  6104  		}
  6105  		for i := range rows {
  6106  			rows[i].Object.Object = nil
  6107  		}
  6108  		if !reflect.DeepEqual(test.expected, rows) {
  6109  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  6110  		}
  6111  	}
  6112  }
  6113  
  6114  func TestPrintPriorityLevelConfiguration(t *testing.T) {
  6115  	tests := []struct {
  6116  		pl       flowcontrol.PriorityLevelConfiguration
  6117  		expected []metav1.TableRow
  6118  	}{
  6119  		{
  6120  			pl: flowcontrol.PriorityLevelConfiguration{
  6121  				ObjectMeta: metav1.ObjectMeta{
  6122  					Name:              "unlimited",
  6123  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6124  				},
  6125  				Spec: flowcontrol.PriorityLevelConfigurationSpec{
  6126  					Type: flowcontrol.PriorityLevelEnablementExempt,
  6127  				},
  6128  			},
  6129  			// Columns: Name, Type, NominalConcurrencyShares, Queues, HandSize, QueueLengthLimit, Age
  6130  			expected: []metav1.TableRow{{Cells: []interface{}{"unlimited", "Exempt", "<none>", "<none>", "<none>", "<none>", "0s"}}},
  6131  		},
  6132  		{
  6133  			pl: flowcontrol.PriorityLevelConfiguration{
  6134  				ObjectMeta: metav1.ObjectMeta{
  6135  					Name:              "unqueued",
  6136  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6137  				},
  6138  				Spec: flowcontrol.PriorityLevelConfigurationSpec{
  6139  					Type: flowcontrol.PriorityLevelEnablementLimited,
  6140  					Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
  6141  						NominalConcurrencyShares: 47,
  6142  						LimitResponse: flowcontrol.LimitResponse{
  6143  							Type: flowcontrol.LimitResponseTypeReject,
  6144  						},
  6145  					},
  6146  				},
  6147  			},
  6148  			// Columns: Name, Type, NominalConcurrencyShares, Queues, HandSize, QueueLengthLimit, Age
  6149  			expected: []metav1.TableRow{{Cells: []interface{}{"unqueued", "Limited", int32(47), "<none>", "<none>", "<none>", "0s"}}},
  6150  		},
  6151  		{
  6152  			pl: flowcontrol.PriorityLevelConfiguration{
  6153  				ObjectMeta: metav1.ObjectMeta{
  6154  					Name:              "queued",
  6155  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6156  				},
  6157  				Spec: flowcontrol.PriorityLevelConfigurationSpec{
  6158  					Type: flowcontrol.PriorityLevelEnablementLimited,
  6159  					Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
  6160  						NominalConcurrencyShares: 42,
  6161  						LimitResponse: flowcontrol.LimitResponse{
  6162  							Type: flowcontrol.LimitResponseTypeQueue,
  6163  							Queuing: &flowcontrol.QueuingConfiguration{
  6164  								Queues:           8,
  6165  								HandSize:         3,
  6166  								QueueLengthLimit: 4,
  6167  							},
  6168  						},
  6169  					},
  6170  				},
  6171  			},
  6172  			// Columns: Name, Type, NominalConcurrencyShares, Queues, HandSize, QueueLengthLimit, Age
  6173  			expected: []metav1.TableRow{{Cells: []interface{}{"queued", "Limited", int32(42), int32(8), int32(3), int32(4), "0s"}}},
  6174  		},
  6175  	}
  6176  
  6177  	for i, test := range tests {
  6178  		rows, err := printPriorityLevelConfiguration(&test.pl, printers.GenerateOptions{})
  6179  		if err != nil {
  6180  			t.Fatal(err)
  6181  		}
  6182  		for i := range rows {
  6183  			rows[i].Object.Object = nil
  6184  		}
  6185  		if !reflect.DeepEqual(test.expected, rows) {
  6186  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  6187  		}
  6188  	}
  6189  }
  6190  
  6191  func TestPrintStorageVersion(t *testing.T) {
  6192  	commonEncodingVersion := "v1"
  6193  	tests := []struct {
  6194  		sv       apiserverinternal.StorageVersion
  6195  		expected []metav1.TableRow
  6196  	}{
  6197  		{
  6198  			sv: apiserverinternal.StorageVersion{
  6199  				ObjectMeta: metav1.ObjectMeta{
  6200  					Name:              "empty",
  6201  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6202  				},
  6203  				Status: apiserverinternal.StorageVersionStatus{},
  6204  			},
  6205  			// Columns: Name, CommonEncodingVersion, StorageVersions, Age
  6206  			expected: []metav1.TableRow{{Cells: []interface{}{"empty", "<unset>", "<unset>", "0s"}}},
  6207  		},
  6208  		{
  6209  			sv: apiserverinternal.StorageVersion{
  6210  				ObjectMeta: metav1.ObjectMeta{
  6211  					Name:              "valid",
  6212  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6213  				},
  6214  				Status: apiserverinternal.StorageVersionStatus{
  6215  					StorageVersions: []apiserverinternal.ServerStorageVersion{
  6216  						{
  6217  							APIServerID:       "1",
  6218  							EncodingVersion:   "v1",
  6219  							DecodableVersions: []string{"v1"},
  6220  						},
  6221  						{
  6222  							APIServerID:       "2",
  6223  							EncodingVersion:   "v1",
  6224  							DecodableVersions: []string{"v1", "v2"},
  6225  						},
  6226  					},
  6227  					CommonEncodingVersion: &commonEncodingVersion,
  6228  				},
  6229  			},
  6230  			// Columns: Name, CommonEncodingVersion, StorageVersions, Age
  6231  			expected: []metav1.TableRow{{Cells: []interface{}{"valid", "v1", "1=v1,2=v1", "0s"}}},
  6232  		},
  6233  		{
  6234  			sv: apiserverinternal.StorageVersion{
  6235  				ObjectMeta: metav1.ObjectMeta{
  6236  					Name:              "disagree",
  6237  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6238  				},
  6239  				Status: apiserverinternal.StorageVersionStatus{
  6240  					StorageVersions: []apiserverinternal.ServerStorageVersion{
  6241  						{
  6242  							APIServerID:       "1",
  6243  							EncodingVersion:   "v1",
  6244  							DecodableVersions: []string{"v1"},
  6245  						},
  6246  						{
  6247  							APIServerID:       "2",
  6248  							EncodingVersion:   "v1",
  6249  							DecodableVersions: []string{"v1", "v2"},
  6250  						},
  6251  						{
  6252  							APIServerID:       "3",
  6253  							EncodingVersion:   "v2",
  6254  							DecodableVersions: []string{"v2"},
  6255  						},
  6256  					},
  6257  				},
  6258  			},
  6259  			// Columns: Name, CommonEncodingVersion, StorageVersions, Age
  6260  			expected: []metav1.TableRow{{Cells: []interface{}{"disagree", "<unset>", "1=v1,2=v1,3=v2", "0s"}}},
  6261  		},
  6262  		{
  6263  			sv: apiserverinternal.StorageVersion{
  6264  				ObjectMeta: metav1.ObjectMeta{
  6265  					Name:              "agreeWithMore",
  6266  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6267  				},
  6268  				Status: apiserverinternal.StorageVersionStatus{
  6269  					StorageVersions: []apiserverinternal.ServerStorageVersion{
  6270  						{
  6271  							APIServerID:       "1",
  6272  							EncodingVersion:   "v1",
  6273  							DecodableVersions: []string{"v1"},
  6274  						},
  6275  						{
  6276  							APIServerID:       "2",
  6277  							EncodingVersion:   "v1",
  6278  							DecodableVersions: []string{"v1", "v2"},
  6279  						},
  6280  						{
  6281  							APIServerID:       "3",
  6282  							EncodingVersion:   "v1",
  6283  							DecodableVersions: []string{"v1", "v2"},
  6284  						},
  6285  						{
  6286  							APIServerID:       "4",
  6287  							EncodingVersion:   "v1",
  6288  							DecodableVersions: []string{"v1", "v2", "v3alpha1"},
  6289  						},
  6290  					},
  6291  					CommonEncodingVersion: &commonEncodingVersion,
  6292  				},
  6293  			},
  6294  			// Columns: Name, CommonEncodingVersion, StorageVersions, Age
  6295  			expected: []metav1.TableRow{{Cells: []interface{}{"agreeWithMore", "v1", "1=v1,2=v1,3=v1 + 1 more...", "0s"}}},
  6296  		},
  6297  	}
  6298  
  6299  	for i, test := range tests {
  6300  		rows, err := printStorageVersion(&test.sv, printers.GenerateOptions{})
  6301  		if err != nil {
  6302  			t.Fatal(err)
  6303  		}
  6304  		for i := range rows {
  6305  			rows[i].Object.Object = nil
  6306  		}
  6307  		if !reflect.DeepEqual(test.expected, rows) {
  6308  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  6309  		}
  6310  	}
  6311  }
  6312  
  6313  func TestPrintScale(t *testing.T) {
  6314  	tests := []struct {
  6315  		scale    autoscaling.Scale
  6316  		options  printers.GenerateOptions
  6317  		expected []metav1.TableRow
  6318  	}{
  6319  		{
  6320  			scale: autoscaling.Scale{
  6321  				ObjectMeta: metav1.ObjectMeta{
  6322  					Name:              "test-autoscaling",
  6323  					CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
  6324  				},
  6325  				Spec:   autoscaling.ScaleSpec{Replicas: 2},
  6326  				Status: autoscaling.ScaleStatus{Replicas: 1},
  6327  			},
  6328  			expected: []metav1.TableRow{
  6329  				{
  6330  					Cells: []interface{}{"test-autoscaling", int64(2), int64(1), string("0s")},
  6331  				},
  6332  			},
  6333  		},
  6334  	}
  6335  
  6336  	for i, test := range tests {
  6337  		rows, err := printScale(&test.scale, test.options)
  6338  		if err != nil {
  6339  			t.Fatal(err)
  6340  		}
  6341  		for i := range rows {
  6342  			rows[i].Object.Object = nil
  6343  		}
  6344  		if !reflect.DeepEqual(test.expected, rows) {
  6345  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  6346  		}
  6347  	}
  6348  }
  6349  
  6350  func TestTableRowDeepCopyShouldNotPanic(t *testing.T) {
  6351  	tests := []struct {
  6352  		name    string
  6353  		printer func() ([]metav1.TableRow, error)
  6354  	}{
  6355  		{
  6356  			name: "Pod",
  6357  			printer: func() ([]metav1.TableRow, error) {
  6358  				return printPod(&api.Pod{}, printers.GenerateOptions{})
  6359  			},
  6360  		},
  6361  		{
  6362  			name: "PodTemplate",
  6363  			printer: func() ([]metav1.TableRow, error) {
  6364  				return printPodTemplate(&api.PodTemplate{}, printers.GenerateOptions{})
  6365  			},
  6366  		},
  6367  		{
  6368  			name: "PodDisruptionBudget",
  6369  			printer: func() ([]metav1.TableRow, error) {
  6370  				return printPodDisruptionBudget(&policy.PodDisruptionBudget{}, printers.GenerateOptions{})
  6371  			},
  6372  		},
  6373  		{
  6374  			name: "ReplicationController",
  6375  			printer: func() ([]metav1.TableRow, error) {
  6376  				return printReplicationController(&api.ReplicationController{}, printers.GenerateOptions{})
  6377  			},
  6378  		},
  6379  		{
  6380  			name: "ReplicaSet",
  6381  			printer: func() ([]metav1.TableRow, error) {
  6382  				return printReplicaSet(&apps.ReplicaSet{}, printers.GenerateOptions{})
  6383  			},
  6384  		},
  6385  		{
  6386  			name: "Job",
  6387  			printer: func() ([]metav1.TableRow, error) {
  6388  				return printJob(&batch.Job{}, printers.GenerateOptions{})
  6389  			},
  6390  		},
  6391  		{
  6392  			name: "CronJob",
  6393  			printer: func() ([]metav1.TableRow, error) {
  6394  				return printCronJob(&batch.CronJob{}, printers.GenerateOptions{})
  6395  			},
  6396  		},
  6397  		{
  6398  			name: "Service",
  6399  			printer: func() ([]metav1.TableRow, error) {
  6400  				return printService(&api.Service{}, printers.GenerateOptions{})
  6401  			},
  6402  		},
  6403  		{
  6404  			name: "Ingress",
  6405  			printer: func() ([]metav1.TableRow, error) {
  6406  				return printIngress(&networking.Ingress{}, printers.GenerateOptions{})
  6407  			},
  6408  		},
  6409  		{
  6410  			name: "IngressClass",
  6411  			printer: func() ([]metav1.TableRow, error) {
  6412  				return printIngressClass(&networking.IngressClass{}, printers.GenerateOptions{})
  6413  			},
  6414  		},
  6415  		{
  6416  			name: "StatefulSet",
  6417  			printer: func() ([]metav1.TableRow, error) {
  6418  				return printStatefulSet(&apps.StatefulSet{}, printers.GenerateOptions{})
  6419  			},
  6420  		},
  6421  		{
  6422  			name: "DaemonSet",
  6423  			printer: func() ([]metav1.TableRow, error) {
  6424  				return printDaemonSet(&apps.DaemonSet{}, printers.GenerateOptions{})
  6425  			},
  6426  		},
  6427  		{
  6428  			name: "Endpoints",
  6429  			printer: func() ([]metav1.TableRow, error) {
  6430  				return printEndpoints(&api.Endpoints{}, printers.GenerateOptions{})
  6431  			},
  6432  		},
  6433  		{
  6434  			name: "EndpointSlice",
  6435  			printer: func() ([]metav1.TableRow, error) {
  6436  				return printEndpointSlice(&discovery.EndpointSlice{}, printers.GenerateOptions{})
  6437  			},
  6438  		},
  6439  		{
  6440  			name: "CSINode",
  6441  			printer: func() ([]metav1.TableRow, error) {
  6442  				return printCSINode(&storage.CSINode{}, printers.GenerateOptions{})
  6443  			},
  6444  		},
  6445  		{
  6446  			name: "CSIDriver",
  6447  			printer: func() ([]metav1.TableRow, error) {
  6448  				return printCSIDriver(&storage.CSIDriver{}, printers.GenerateOptions{})
  6449  			},
  6450  		},
  6451  		{
  6452  			name: "CSIStorageCapacity",
  6453  			printer: func() ([]metav1.TableRow, error) {
  6454  				return printCSIStorageCapacity(&storage.CSIStorageCapacity{}, printers.GenerateOptions{})
  6455  			},
  6456  		},
  6457  		{
  6458  			name: "MutatingWebhookConfiguration",
  6459  			printer: func() ([]metav1.TableRow, error) {
  6460  				return printMutatingWebhook(&admissionregistration.MutatingWebhookConfiguration{}, printers.GenerateOptions{})
  6461  			},
  6462  		},
  6463  		{
  6464  			name: "ValidatingWebhookConfiguration",
  6465  			printer: func() ([]metav1.TableRow, error) {
  6466  				return printValidatingWebhook(&admissionregistration.ValidatingWebhookConfiguration{}, printers.GenerateOptions{})
  6467  			},
  6468  		},
  6469  		{
  6470  			name: "ValidatingAdmissionPolicy",
  6471  			printer: func() ([]metav1.TableRow, error) {
  6472  				return printValidatingAdmissionPolicy(&admissionregistration.ValidatingAdmissionPolicy{}, printers.GenerateOptions{})
  6473  			},
  6474  		},
  6475  		{
  6476  			name: "ValidatingAdmissionPolicyBinding",
  6477  			printer: func() ([]metav1.TableRow, error) {
  6478  				return printValidatingAdmissionPolicyBinding(&admissionregistration.ValidatingAdmissionPolicyBinding{}, printers.GenerateOptions{})
  6479  			},
  6480  		},
  6481  		{
  6482  			name: "Namespace",
  6483  			printer: func() ([]metav1.TableRow, error) {
  6484  				return printNamespace(&api.Namespace{}, printers.GenerateOptions{})
  6485  			},
  6486  		},
  6487  		{
  6488  			name: "Secret",
  6489  			printer: func() ([]metav1.TableRow, error) {
  6490  				return printSecret(&api.Secret{}, printers.GenerateOptions{})
  6491  			},
  6492  		},
  6493  		{
  6494  			name: "ServiceAccount",
  6495  			printer: func() ([]metav1.TableRow, error) {
  6496  				return printServiceAccount(&api.ServiceAccount{}, printers.GenerateOptions{})
  6497  			},
  6498  		},
  6499  		{
  6500  			name: "Node",
  6501  			printer: func() ([]metav1.TableRow, error) {
  6502  				return printNode(&api.Node{}, printers.GenerateOptions{})
  6503  			},
  6504  		},
  6505  		{
  6506  			name: "PersistentVolume",
  6507  			printer: func() ([]metav1.TableRow, error) {
  6508  				return printPersistentVolume(&api.PersistentVolume{}, printers.GenerateOptions{})
  6509  			},
  6510  		},
  6511  		{
  6512  			name: "PersistentVolumeClaim",
  6513  			printer: func() ([]metav1.TableRow, error) {
  6514  				return printPersistentVolumeClaim(&api.PersistentVolumeClaim{}, printers.GenerateOptions{})
  6515  			},
  6516  		},
  6517  		{
  6518  			name: "Event",
  6519  			printer: func() ([]metav1.TableRow, error) {
  6520  				return printEvent(&api.Event{}, printers.GenerateOptions{})
  6521  			},
  6522  		},
  6523  		{
  6524  			name: "RoleBinding",
  6525  			printer: func() ([]metav1.TableRow, error) {
  6526  				return printRoleBinding(&rbac.RoleBinding{}, printers.GenerateOptions{})
  6527  			},
  6528  		},
  6529  		{
  6530  			name: "ClusterRoleBinding",
  6531  			printer: func() ([]metav1.TableRow, error) {
  6532  				return printClusterRoleBinding(&rbac.ClusterRoleBinding{}, printers.GenerateOptions{})
  6533  			},
  6534  		},
  6535  		{
  6536  			name: "CertificateSigningRequest",
  6537  			printer: func() ([]metav1.TableRow, error) {
  6538  				return printCertificateSigningRequest(&certificates.CertificateSigningRequest{}, printers.GenerateOptions{})
  6539  			},
  6540  		},
  6541  		{
  6542  			name: "ComponentStatus",
  6543  			printer: func() ([]metav1.TableRow, error) {
  6544  				return printComponentStatus(&api.ComponentStatus{}, printers.GenerateOptions{})
  6545  			},
  6546  		},
  6547  		{
  6548  			name: "Deployment",
  6549  			printer: func() ([]metav1.TableRow, error) {
  6550  				return printDeployment(&apps.Deployment{}, printers.GenerateOptions{})
  6551  			},
  6552  		},
  6553  		{
  6554  			name: "HorizontalPodAutoscaler",
  6555  			printer: func() ([]metav1.TableRow, error) {
  6556  				return printHorizontalPodAutoscaler(&autoscaling.HorizontalPodAutoscaler{}, printers.GenerateOptions{})
  6557  			},
  6558  		},
  6559  		{
  6560  			name: "ConfigMap",
  6561  			printer: func() ([]metav1.TableRow, error) {
  6562  				return printConfigMap(&api.ConfigMap{}, printers.GenerateOptions{})
  6563  			},
  6564  		},
  6565  		{
  6566  			name: "NetworkPolicy",
  6567  			printer: func() ([]metav1.TableRow, error) {
  6568  				return printNetworkPolicy(&networking.NetworkPolicy{}, printers.GenerateOptions{})
  6569  			},
  6570  		},
  6571  		{
  6572  			name: "StorageClass",
  6573  			printer: func() ([]metav1.TableRow, error) {
  6574  				return printStorageClass(&storage.StorageClass{}, printers.GenerateOptions{})
  6575  			},
  6576  		},
  6577  		{
  6578  			name: "Lease",
  6579  			printer: func() ([]metav1.TableRow, error) {
  6580  				return printLease(&coordination.Lease{}, printers.GenerateOptions{})
  6581  			},
  6582  		},
  6583  		{
  6584  			name: "ControllerRevision",
  6585  			printer: func() ([]metav1.TableRow, error) {
  6586  				return printControllerRevision(&apps.ControllerRevision{}, printers.GenerateOptions{})
  6587  			},
  6588  		},
  6589  		{
  6590  			name: "ResourceQuota",
  6591  			printer: func() ([]metav1.TableRow, error) {
  6592  				return printResourceQuota(&api.ResourceQuota{}, printers.GenerateOptions{})
  6593  			},
  6594  		},
  6595  		{
  6596  			name: "PriorityClass",
  6597  			printer: func() ([]metav1.TableRow, error) {
  6598  				return printPriorityClass(&scheduling.PriorityClass{}, printers.GenerateOptions{})
  6599  			},
  6600  		},
  6601  		{
  6602  			name: "RuntimeClass",
  6603  			printer: func() ([]metav1.TableRow, error) {
  6604  				return printRuntimeClass(&nodeapi.RuntimeClass{}, printers.GenerateOptions{})
  6605  			},
  6606  		},
  6607  		{
  6608  			name: "VolumeAttachment",
  6609  			printer: func() ([]metav1.TableRow, error) {
  6610  				return printVolumeAttachment(&storage.VolumeAttachment{}, printers.GenerateOptions{})
  6611  			},
  6612  		},
  6613  		{
  6614  			name: "FlowSchema",
  6615  			printer: func() ([]metav1.TableRow, error) {
  6616  				return printFlowSchema(&flowcontrol.FlowSchema{}, printers.GenerateOptions{})
  6617  			},
  6618  		},
  6619  		{
  6620  			name: "StorageVersion",
  6621  			printer: func() ([]metav1.TableRow, error) {
  6622  				return printStorageVersion(&apiserverinternal.StorageVersion{}, printers.GenerateOptions{})
  6623  			},
  6624  		},
  6625  		{
  6626  			name: "PriorityLevelConfiguration",
  6627  			printer: func() ([]metav1.TableRow, error) {
  6628  				return printPriorityLevelConfiguration(&flowcontrol.PriorityLevelConfiguration{}, printers.GenerateOptions{})
  6629  			},
  6630  		},
  6631  		{
  6632  			name: "Scale",
  6633  			printer: func() ([]metav1.TableRow, error) {
  6634  				return printScale(&autoscaling.Scale{}, printers.GenerateOptions{})
  6635  			},
  6636  		},
  6637  		{
  6638  			name: "Status",
  6639  			printer: func() ([]metav1.TableRow, error) {
  6640  				return printStatus(&metav1.Status{}, printers.GenerateOptions{})
  6641  			},
  6642  		},
  6643  	}
  6644  
  6645  	for _, test := range tests {
  6646  		t.Run(test.name, func(t *testing.T) {
  6647  			rows, err := test.printer()
  6648  			if err != nil {
  6649  				t.Fatalf("expected no error, but got: %#v", err)
  6650  			}
  6651  			if len(rows) <= 0 {
  6652  				t.Fatalf("expected to have at least one TableRow, but got: %d", len(rows))
  6653  			}
  6654  
  6655  			func() {
  6656  				defer func() {
  6657  					if err := recover(); err != nil {
  6658  						// Same as stdlib http server code. Manually allocate stack
  6659  						// trace buffer size to prevent excessively large logs
  6660  						const size = 64 << 10
  6661  						buf := make([]byte, size)
  6662  						buf = buf[:runtime.Stack(buf, false)]
  6663  						err = fmt.Errorf("%q stack:\n%s", err, buf)
  6664  
  6665  						t.Errorf("Expected no panic, but got: %v", err)
  6666  					}
  6667  				}()
  6668  
  6669  				// should not panic
  6670  				rows[0].DeepCopy()
  6671  			}()
  6672  
  6673  		})
  6674  	}
  6675  }
  6676  
  6677  func TestPrintIPAddress(t *testing.T) {
  6678  	ip := networking.IPAddress{
  6679  		ObjectMeta: metav1.ObjectMeta{
  6680  			Name:              "192.168.2.2",
  6681  			CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
  6682  		},
  6683  		Spec: networking.IPAddressSpec{
  6684  			ParentRef: &networking.ParentReference{
  6685  				Group:     "mygroup",
  6686  				Resource:  "myresource",
  6687  				Namespace: "mynamespace",
  6688  				Name:      "myname",
  6689  			},
  6690  		},
  6691  	}
  6692  	// Columns: Name, ParentRef, Age
  6693  	expected := []metav1.TableRow{{Cells: []interface{}{"192.168.2.2", "myresource.mygroup/mynamespace/myname", "10y"}}}
  6694  
  6695  	rows, err := printIPAddress(&ip, printers.GenerateOptions{})
  6696  	if err != nil {
  6697  		t.Fatalf("Error generating table rows for IPAddress: %#v", err)
  6698  	}
  6699  	rows[0].Object.Object = nil
  6700  	if !reflect.DeepEqual(expected, rows) {
  6701  		t.Errorf("mismatch: %s", cmp.Diff(expected, rows))
  6702  	}
  6703  }
  6704  
  6705  func TestPrintIPAddressList(t *testing.T) {
  6706  	ipList := networking.IPAddressList{
  6707  		Items: []networking.IPAddress{
  6708  			{
  6709  				ObjectMeta: metav1.ObjectMeta{
  6710  					Name:              "192.168.2.2",
  6711  					CreationTimestamp: metav1.Time{},
  6712  				},
  6713  				Spec: networking.IPAddressSpec{
  6714  					ParentRef: &networking.ParentReference{
  6715  						Group:     "mygroup",
  6716  						Resource:  "myresource",
  6717  						Namespace: "mynamespace",
  6718  						Name:      "myname",
  6719  					},
  6720  				},
  6721  			}, {
  6722  				ObjectMeta: metav1.ObjectMeta{
  6723  					Name:              "2001:db8::2",
  6724  					CreationTimestamp: metav1.Time{},
  6725  				},
  6726  				Spec: networking.IPAddressSpec{
  6727  					ParentRef: &networking.ParentReference{
  6728  						Group:     "mygroup2",
  6729  						Resource:  "myresource2",
  6730  						Namespace: "mynamespace2",
  6731  						Name:      "myname2",
  6732  					},
  6733  				},
  6734  			},
  6735  		},
  6736  	}
  6737  	// Columns: Name, ParentRef, Age
  6738  	expected := []metav1.TableRow{
  6739  		{Cells: []interface{}{"192.168.2.2", "myresource.mygroup/mynamespace/myname", "<unknown>"}},
  6740  		{Cells: []interface{}{"2001:db8::2", "myresource2.mygroup2/mynamespace2/myname2", "<unknown>"}},
  6741  	}
  6742  
  6743  	rows, err := printIPAddressList(&ipList, printers.GenerateOptions{})
  6744  	if err != nil {
  6745  		t.Fatalf("Error generating table rows for IPAddress: %#v", err)
  6746  	}
  6747  	for i := range rows {
  6748  		rows[i].Object.Object = nil
  6749  
  6750  	}
  6751  	if !reflect.DeepEqual(expected, rows) {
  6752  		t.Errorf("mismatch: %s", cmp.Diff(expected, rows))
  6753  	}
  6754  
  6755  }
  6756  
  6757  func TestPrintServiceCIDR(t *testing.T) {
  6758  	ipv4CIDR := "10.1.0.0/16"
  6759  	ipv6CIDR := "fd00:1:1::/64"
  6760  
  6761  	tests := []struct {
  6762  		ccc      networking.ServiceCIDR
  6763  		options  printers.GenerateOptions
  6764  		expected []metav1.TableRow
  6765  	}{
  6766  		{
  6767  			// Test name, IPv4 only.
  6768  			ccc: networking.ServiceCIDR{
  6769  				ObjectMeta: metav1.ObjectMeta{Name: "test1"},
  6770  				Spec: networking.ServiceCIDRSpec{
  6771  					CIDRs: []string{ipv4CIDR},
  6772  				},
  6773  			},
  6774  			options: printers.GenerateOptions{},
  6775  			// Columns: Name, IPv4, IPv6, Age.
  6776  			expected: []metav1.TableRow{{Cells: []interface{}{"test1", ipv4CIDR, "<unknown>"}}},
  6777  		},
  6778  		{
  6779  			// Test name, IPv6 only.
  6780  			ccc: networking.ServiceCIDR{
  6781  				ObjectMeta: metav1.ObjectMeta{Name: "test5"},
  6782  				Spec: networking.ServiceCIDRSpec{
  6783  					CIDRs: []string{ipv6CIDR},
  6784  				},
  6785  			},
  6786  			options: printers.GenerateOptions{},
  6787  			// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age
  6788  			expected: []metav1.TableRow{{Cells: []interface{}{"test5", ipv6CIDR, "<unknown>"}}},
  6789  		},
  6790  		{
  6791  			// Test name, DualStack.
  6792  			ccc: networking.ServiceCIDR{
  6793  				ObjectMeta: metav1.ObjectMeta{Name: "test9"},
  6794  				Spec: networking.ServiceCIDRSpec{
  6795  					CIDRs: []string{ipv4CIDR, ipv6CIDR},
  6796  				},
  6797  			},
  6798  			options: printers.GenerateOptions{},
  6799  			// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
  6800  			expected: []metav1.TableRow{{Cells: []interface{}{"test9", ipv4CIDR + "," + ipv6CIDR, "<unknown>"}}},
  6801  		},
  6802  	}
  6803  
  6804  	for i, test := range tests {
  6805  		rows, err := printServiceCIDR(&test.ccc, test.options)
  6806  		if err != nil {
  6807  			t.Fatal(err)
  6808  		}
  6809  		for i := range rows {
  6810  			rows[i].Object.Object = nil
  6811  		}
  6812  		if !reflect.DeepEqual(test.expected, rows) {
  6813  			t.Errorf("%d mismatch: %s", i, cmp.Diff(test.expected, rows))
  6814  		}
  6815  	}
  6816  }
  6817  
  6818  func TestPrintServiceCIDRList(t *testing.T) {
  6819  	cccList := networking.ServiceCIDRList{
  6820  		Items: []networking.ServiceCIDR{
  6821  			{
  6822  				ObjectMeta: metav1.ObjectMeta{Name: "ccc1"},
  6823  				Spec: networking.ServiceCIDRSpec{
  6824  					CIDRs: []string{"10.1.0.0/16", "fd00:1:1::/64"},
  6825  				},
  6826  			},
  6827  			{
  6828  				ObjectMeta: metav1.ObjectMeta{Name: "ccc2"},
  6829  				Spec: networking.ServiceCIDRSpec{
  6830  					CIDRs: []string{"10.2.0.0/16", "fd00:2:1::/64"},
  6831  				},
  6832  			},
  6833  		},
  6834  	}
  6835  
  6836  	tests := []struct {
  6837  		options  printers.GenerateOptions
  6838  		expected []metav1.TableRow
  6839  	}{
  6840  		{
  6841  			// Test name, DualStack with node selector, wide.
  6842  			options: printers.GenerateOptions{Wide: false},
  6843  			expected: []metav1.TableRow{
  6844  				// Columns: Name, IPv4, IPv6, Age.
  6845  				{Cells: []interface{}{"ccc1", "10.1.0.0/16,fd00:1:1::/64", "<unknown>"}},
  6846  				{Cells: []interface{}{"ccc2", "10.2.0.0/16,fd00:2:1::/64", "<unknown>"}},
  6847  			},
  6848  		},
  6849  		{
  6850  			// Test name, DualStack with node selector, wide.
  6851  			options: printers.GenerateOptions{Wide: true},
  6852  			expected: []metav1.TableRow{
  6853  				// Columns: Name, CIDRs, Age.
  6854  				{Cells: []interface{}{"ccc1", "10.1.0.0/16,fd00:1:1::/64", "<unknown>"}},
  6855  				{Cells: []interface{}{"ccc2", "10.2.0.0/16,fd00:2:1::/64", "<unknown>"}},
  6856  			},
  6857  		},
  6858  	}
  6859  
  6860  	for _, test := range tests {
  6861  		rows, err := printServiceCIDRList(&cccList, test.options)
  6862  		if err != nil {
  6863  			t.Fatalf("Error printing service list: %#v", err)
  6864  		}
  6865  		for i := range rows {
  6866  			rows[i].Object.Object = nil
  6867  		}
  6868  		if !reflect.DeepEqual(test.expected, rows) {
  6869  			t.Errorf("mismatch: %s", cmp.Diff(test.expected, rows))
  6870  		}
  6871  	}
  6872  }
  6873  
  6874  func TestPrintStorageVersionMigration(t *testing.T) {
  6875  	storageVersionMigration := storagemigration.StorageVersionMigration{
  6876  		TypeMeta: metav1.TypeMeta{
  6877  			Kind:       "StorageVersionMigration",
  6878  			APIVersion: "storagemigration.k8s.io/v1alpha1",
  6879  		},
  6880  		ObjectMeta: metav1.ObjectMeta{
  6881  			Name: "print-test",
  6882  		},
  6883  		Spec: storagemigration.StorageVersionMigrationSpec{
  6884  			Resource: storagemigration.GroupVersionResource{
  6885  				Group:    "test-group",
  6886  				Version:  "test-version",
  6887  				Resource: "test-resource",
  6888  			},
  6889  		},
  6890  	}
  6891  
  6892  	// Columns: Name, GVRTOMIGRATE
  6893  	expected := []metav1.TableRow{{Cells: []interface{}{"print-test", "test-resource.test-version.test-group"}}}
  6894  
  6895  	rows, err := printStorageVersionMigration(&storageVersionMigration, printers.GenerateOptions{})
  6896  	if err != nil {
  6897  		t.Fatalf("Error generating table rows for StorageVersionMigration: %#v", err)
  6898  	}
  6899  	rows[0].Object.Object = nil
  6900  	if !reflect.DeepEqual(expected, rows) {
  6901  		t.Errorf("mismatch: %s", cmp.Diff(expected, rows))
  6902  	}
  6903  }
  6904  
  6905  func TestPrintStorageVersionMigrationList(t *testing.T) {
  6906  	storageVersionMigrationList := storagemigration.StorageVersionMigrationList{
  6907  		Items: []storagemigration.StorageVersionMigration{
  6908  			{
  6909  				TypeMeta: metav1.TypeMeta{
  6910  					Kind:       "StorageVersionMigration",
  6911  					APIVersion: "storagemigration.k8s.io/v1alpha1",
  6912  				},
  6913  				ObjectMeta: metav1.ObjectMeta{
  6914  					Name: "print-test",
  6915  				},
  6916  				Spec: storagemigration.StorageVersionMigrationSpec{
  6917  					Resource: storagemigration.GroupVersionResource{
  6918  						Group:    "test-group",
  6919  						Version:  "test-version",
  6920  						Resource: "test-resource",
  6921  					},
  6922  				},
  6923  			},
  6924  			{
  6925  				TypeMeta: metav1.TypeMeta{
  6926  					Kind:       "StorageVersionMigration",
  6927  					APIVersion: "storagemigration.k8s.io/v1alpha1",
  6928  				},
  6929  				ObjectMeta: metav1.ObjectMeta{
  6930  					Name: "print-test2",
  6931  				},
  6932  				Spec: storagemigration.StorageVersionMigrationSpec{
  6933  					Resource: storagemigration.GroupVersionResource{
  6934  						Group:    "test-group2",
  6935  						Version:  "test-version2",
  6936  						Resource: "test-resource2",
  6937  					},
  6938  				},
  6939  			},
  6940  		},
  6941  	}
  6942  
  6943  	// Columns: Name, GVRTOMIGRATE
  6944  	expected := []metav1.TableRow{
  6945  		{Cells: []interface{}{"print-test", "test-resource.test-version.test-group"}},
  6946  		{Cells: []interface{}{"print-test2", "test-resource2.test-version2.test-group2"}},
  6947  	}
  6948  
  6949  	rows, err := printStorageVersionMigrationList(&storageVersionMigrationList, printers.GenerateOptions{})
  6950  	if err != nil {
  6951  		t.Fatalf("Error generating table rows for StorageVersionMigration: %#v", err)
  6952  	}
  6953  
  6954  	for i := range rows {
  6955  		rows[i].Object.Object = nil
  6956  	}
  6957  
  6958  	if !reflect.DeepEqual(expected, rows) {
  6959  		t.Errorf("mismatch: %s", cmp.Diff(expected, rows))
  6960  	}
  6961  }