...

Source file src/k8s.io/kubernetes/pkg/printers/internalversion/printers_test.go

Documentation: k8s.io/kubernetes/pkg/printers/internalversion

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

View as plain text