...

Source file src/k8s.io/kubectl/pkg/cmd/get/humanreadable_flags_test.go

Documentation: k8s.io/kubectl/pkg/cmd/get

     1  /*
     2  Copyright 2018 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 get
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"regexp"
    23  	"strings"
    24  	"testing"
    25  
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/cli-runtime/pkg/genericclioptions"
    31  )
    32  
    33  func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
    34  	testTable := &metav1.Table{
    35  		ColumnDefinitions: []metav1.TableColumnDefinition{
    36  			{Name: "Name", Type: "string", Format: "name"},
    37  			{Name: "Ready", Type: "string", Format: ""},
    38  			{Name: "Status", Type: "string", Format: ""},
    39  			{Name: "Restarts", Type: "integer", Format: ""},
    40  			{Name: "Age", Type: "string", Format: ""},
    41  			{Name: "IP", Type: "string", Format: "", Priority: 1},
    42  			{Name: "Node", Type: "string", Format: "", Priority: 1},
    43  			{Name: "Nominated Node", Type: "string", Format: "", Priority: 1},
    44  			{Name: "Readiness Gates", Type: "string", Format: "", Priority: 1},
    45  		},
    46  		Rows: []metav1.TableRow{{
    47  			Object: runtime.RawExtension{
    48  				Object: &corev1.Pod{
    49  					TypeMeta:   metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
    50  					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foons", Labels: map[string]string{"l1": "value"}},
    51  				},
    52  			},
    53  			Cells: []interface{}{"foo", "0/0", "", int64(0), "<unknown>", "<none>", "<none>", "<none>", "<none>"},
    54  		}},
    55  	}
    56  	testPod := &corev1.Pod{
    57  		TypeMeta: metav1.TypeMeta{
    58  			APIVersion: "v1",
    59  			Kind:       "Pod",
    60  		},
    61  		ObjectMeta: metav1.ObjectMeta{
    62  			Name:      "foo",
    63  			Namespace: "foons",
    64  			Labels: map[string]string{
    65  				"l1": "value",
    66  			},
    67  		},
    68  	}
    69  
    70  	testCases := []struct {
    71  		name       string
    72  		testObject runtime.Object
    73  		showKind   bool
    74  		showLabels bool
    75  
    76  		// TODO(juanvallejo): test sorting once it's moved to the HumanReadablePrinter
    77  		sortBy       string
    78  		columnLabels []string
    79  
    80  		noHeaders     bool
    81  		withNamespace bool
    82  
    83  		outputFormat string
    84  
    85  		expectedError  string
    86  		expectedOutput string
    87  		expectNoMatch  bool
    88  	}{
    89  		{
    90  			name:           "empty output format matches a humanreadable printer",
    91  			testObject:     testPod.DeepCopy(),
    92  			expectedOutput: "NAME\\ +AGE\nfoo\\ +<unknown>\n",
    93  		},
    94  		{
    95  			name:           "empty output format matches a humanreadable printer",
    96  			testObject:     testTable.DeepCopy(),
    97  			expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoo\\ +0/0\\ +0\\ +<unknown>\n",
    98  		},
    99  		{
   100  			name:           "\"wide\" output format prints",
   101  			testObject:     testPod.DeepCopy(),
   102  			outputFormat:   "wide",
   103  			expectedOutput: "NAME\\ +AGE\nfoo\\ +<unknown>\n",
   104  		},
   105  		{
   106  			name:           "\"wide\" output format prints",
   107  			testObject:     testTable.DeepCopy(),
   108  			outputFormat:   "wide",
   109  			expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
   110  		},
   111  		{
   112  			name:           "no-headers prints output with no headers",
   113  			testObject:     testPod.DeepCopy(),
   114  			noHeaders:      true,
   115  			expectedOutput: "foo\\ +<unknown>\n",
   116  		},
   117  		{
   118  			name:           "no-headers prints output with no headers",
   119  			testObject:     testTable.DeepCopy(),
   120  			noHeaders:      true,
   121  			expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\n",
   122  		},
   123  		{
   124  			name:           "no-headers and a \"wide\" output format prints output with no headers and additional columns",
   125  			testObject:     testPod.DeepCopy(),
   126  			outputFormat:   "wide",
   127  			noHeaders:      true,
   128  			expectedOutput: "foo\\ +<unknown>\n",
   129  		},
   130  		{
   131  			name:           "no-headers and a \"wide\" output format prints output with no headers and additional columns",
   132  			testObject:     testTable.DeepCopy(),
   133  			outputFormat:   "wide",
   134  			noHeaders:      true,
   135  			expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
   136  		},
   137  		{
   138  			name:           "show-kind displays the resource's kind, even when printing a single type of resource",
   139  			testObject:     testPod.DeepCopy(),
   140  			showKind:       true,
   141  			expectedOutput: "NAME\\ +AGE\npod/foo\\ +<unknown>\n",
   142  		},
   143  		{
   144  			name:           "show-kind displays the resource's kind, even when printing a single type of resource",
   145  			testObject:     testTable.DeepCopy(),
   146  			showKind:       true,
   147  			expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
   148  		},
   149  		{
   150  			name:           "label-columns prints specified label values in new column",
   151  			testObject:     testPod.DeepCopy(),
   152  			columnLabels:   []string{"l1"},
   153  			expectedOutput: "NAME\\ +AGE\\ +L1\nfoo\\ +<unknown>\\ +value\n",
   154  		},
   155  		{
   156  			name:           "label-columns prints specified label values in new column",
   157  			testObject:     testTable.DeepCopy(),
   158  			columnLabels:   []string{"l1"},
   159  			expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
   160  		},
   161  		{
   162  			name:           "withNamespace displays an additional NAMESPACE column",
   163  			testObject:     testPod.DeepCopy(),
   164  			withNamespace:  true,
   165  			expectedOutput: "NAMESPACE\\ +NAME\\ +AGE\nfoons\\ +foo\\ +<unknown>\n",
   166  		},
   167  		{
   168  			name:           "withNamespace displays an additional NAMESPACE column",
   169  			testObject:     testTable.DeepCopy(),
   170  			withNamespace:  true,
   171  			expectedOutput: "NAMESPACE\\ +NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\nfoons\\ +foo\\ +0/0\\ +0\\ +<unknown>\n",
   172  		},
   173  		{
   174  			name:          "no printer is matched on an invalid outputFormat",
   175  			testObject:    testPod.DeepCopy(),
   176  			outputFormat:  "invalid",
   177  			expectNoMatch: true,
   178  		},
   179  		{
   180  			name:          "printer should not match on any other format supported by another printer",
   181  			testObject:    testPod.DeepCopy(),
   182  			outputFormat:  "go-template",
   183  			expectNoMatch: true,
   184  		},
   185  	}
   186  
   187  	for _, tc := range testCases {
   188  		t.Run(fmt.Sprintf("%s %T", tc.name, tc.testObject), func(t *testing.T) {
   189  			printFlags := HumanPrintFlags{
   190  				ShowKind:     &tc.showKind,
   191  				ShowLabels:   &tc.showLabels,
   192  				SortBy:       &tc.sortBy,
   193  				ColumnLabels: &tc.columnLabels,
   194  
   195  				NoHeaders:     tc.noHeaders,
   196  				WithNamespace: tc.withNamespace,
   197  			}
   198  
   199  			if tc.showKind {
   200  				printFlags.Kind = schema.GroupKind{Kind: "pod"}
   201  			}
   202  
   203  			p, err := printFlags.ToPrinter(tc.outputFormat)
   204  			if tc.expectNoMatch {
   205  				if !genericclioptions.IsNoCompatiblePrinterError(err) {
   206  					t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
   207  				}
   208  				return
   209  			}
   210  			if genericclioptions.IsNoCompatiblePrinterError(err) {
   211  				t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
   212  			}
   213  
   214  			if len(tc.expectedError) > 0 {
   215  				if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
   216  					t.Errorf("expecting error %q, got %v", tc.expectedError, err)
   217  				}
   218  				return
   219  			}
   220  			if err != nil {
   221  				t.Fatalf("unexpected error: %v", err)
   222  			}
   223  
   224  			out := bytes.NewBuffer([]byte{})
   225  			err = p.PrintObj(tc.testObject, out)
   226  			if err != nil {
   227  				t.Errorf("unexpected error: %v", err)
   228  			}
   229  
   230  			match, err := regexp.Match(tc.expectedOutput, out.Bytes())
   231  			if err != nil {
   232  				t.Errorf("unexpected error: %v", err)
   233  			}
   234  			if !match {
   235  				t.Errorf("unexpected output: expecting\n%s\ngot\n%s", tc.expectedOutput, out.String())
   236  			}
   237  		})
   238  	}
   239  }
   240  

View as plain text