...

Source file src/sigs.k8s.io/cli-utils/pkg/object/unstructured_test.go

Documentation: sigs.k8s.io/cli-utils/pkg/object

     1  // Copyright 2021 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package object_test
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"k8s.io/apimachinery/pkg/api/meta"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/runtime/schema"
    14  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    15  	. "sigs.k8s.io/cli-utils/pkg/object"
    16  	"sigs.k8s.io/cli-utils/pkg/testutil"
    17  )
    18  
    19  var rbac = `
    20  apiVersion: rbac.authorization.k8s.io/v1
    21  kind: ClusterRole
    22  metadata:
    23    name: test-cluster-role
    24  `
    25  
    26  var testCRD = `
    27  apiVersion: apiextensions.k8s.io/v1
    28  kind: CustomResourceDefinition
    29  metadata:
    30    name: test-crd
    31  spec:
    32    group: example.com
    33    scope: Cluster
    34    names:
    35      kind: crontab
    36    versions:
    37    - name: v1
    38  `
    39  
    40  var testCRDv2 = `
    41  apiVersion: apiextensions.k8s.io/v1
    42  kind: CustomResourceDefinition
    43  metadata:
    44    name: test-crd
    45  spec:
    46    group: example.com
    47    scope: Cluster
    48    names:
    49      kind: crontab
    50    versions:
    51    - name: v2
    52  `
    53  
    54  var testCR = `
    55  apiVersion: example.com/v1
    56  kind: crontab
    57  metadata:
    58    name: test-cr
    59  `
    60  
    61  var testNamespace = `
    62  apiVersion: v1,
    63  kind: Namespace
    64  metadata:
    65    name: test-namespace
    66  `
    67  
    68  var testPod = `
    69  apiVersion: v1
    70  kind: Pod
    71  metadata:
    72    name: test-pod
    73    namespace: test-namespace
    74  `
    75  
    76  var defaultNamespacePod = `
    77  apiVersion: v1
    78  kind: Pod
    79  metadata:
    80    name: test-pod
    81    namespace: default
    82  `
    83  
    84  func TestUnstructuredToObjMeta(t *testing.T) {
    85  	tests := map[string]struct {
    86  		obj      *unstructured.Unstructured
    87  		expected ObjMetadata
    88  	}{
    89  		"test RBAC translation": {
    90  			obj: testutil.Unstructured(t, rbac),
    91  			expected: ObjMetadata{
    92  				Name: "test-cluster-role",
    93  				GroupKind: schema.GroupKind{
    94  					Group: "rbac.authorization.k8s.io",
    95  					Kind:  "ClusterRole",
    96  				},
    97  			},
    98  		},
    99  		"test CRD translation": {
   100  			obj: testutil.Unstructured(t, testCRD),
   101  			expected: ObjMetadata{
   102  				Name: "test-crd",
   103  				GroupKind: schema.GroupKind{
   104  					Group: "apiextensions.k8s.io",
   105  					Kind:  "CustomResourceDefinition",
   106  				},
   107  			},
   108  		},
   109  		"test pod translation": {
   110  			obj: testutil.Unstructured(t, testPod),
   111  			expected: ObjMetadata{
   112  				Name:      "test-pod",
   113  				Namespace: "test-namespace",
   114  				GroupKind: schema.GroupKind{
   115  					Group: "",
   116  					Kind:  "Pod",
   117  				},
   118  			},
   119  		},
   120  	}
   121  
   122  	for name, tc := range tests {
   123  		t.Run(name, func(t *testing.T) {
   124  			actual := UnstructuredToObjMetadata(tc.obj)
   125  			if tc.expected != actual {
   126  				t.Errorf("expected ObjMetadata (%s), got (%s)", tc.expected, actual)
   127  			}
   128  		})
   129  	}
   130  }
   131  
   132  func TestIsKindNamespace(t *testing.T) {
   133  	tests := map[string]struct {
   134  		obj             *unstructured.Unstructured
   135  		isKindNamespace bool
   136  	}{
   137  		"cluster-scoped RBAC is not a namespace": {
   138  			obj:             testutil.Unstructured(t, rbac),
   139  			isKindNamespace: false,
   140  		},
   141  		"test namespace is a namespace": {
   142  			obj:             testutil.Unstructured(t, testNamespace),
   143  			isKindNamespace: true,
   144  		},
   145  		"test pod is not a namespace": {
   146  			obj:             testutil.Unstructured(t, testPod),
   147  			isKindNamespace: false,
   148  		},
   149  		"default namespaced pod is not a namespace": {
   150  			obj:             testutil.Unstructured(t, defaultNamespacePod),
   151  			isKindNamespace: false,
   152  		},
   153  	}
   154  
   155  	for name, tc := range tests {
   156  		t.Run(name, func(t *testing.T) {
   157  			actual := IsKindNamespace(tc.obj)
   158  			if tc.isKindNamespace != actual {
   159  				t.Errorf("expected IsKindNamespace (%t), got (%t) for (%s)",
   160  					tc.isKindNamespace, actual, tc.obj)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestIsCRD(t *testing.T) {
   167  	tests := map[string]struct {
   168  		obj   *unstructured.Unstructured
   169  		isCRD bool
   170  	}{
   171  		"RBAC is not a CRD": {
   172  			obj:   testutil.Unstructured(t, rbac),
   173  			isCRD: false,
   174  		},
   175  		"test namespace is not a CRD": {
   176  			obj:   testutil.Unstructured(t, testNamespace),
   177  			isCRD: false,
   178  		},
   179  		"test CRD is a CRD": {
   180  			obj:   testutil.Unstructured(t, testCRD),
   181  			isCRD: true,
   182  		},
   183  		"test pod is not a CRD": {
   184  			obj:   testutil.Unstructured(t, testPod),
   185  			isCRD: false,
   186  		},
   187  	}
   188  
   189  	for name, tc := range tests {
   190  		t.Run(name, func(t *testing.T) {
   191  			actual := IsCRD(tc.obj)
   192  			if tc.isCRD != actual {
   193  				t.Errorf("expected IsCRD (%t), got (%t) for (%s)", tc.isCRD, actual, tc.obj)
   194  			}
   195  		})
   196  	}
   197  }
   198  
   199  func TestIsNamespaced(t *testing.T) {
   200  	tests := map[string]struct {
   201  		obj          *unstructured.Unstructured
   202  		isNamespaced bool
   203  	}{
   204  		"cluster-scoped RBAC is not namespaced": {
   205  			obj:          testutil.Unstructured(t, rbac),
   206  			isNamespaced: false,
   207  		},
   208  		"a CRD is cluster-scoped": {
   209  			obj:          testutil.Unstructured(t, testCRD),
   210  			isNamespaced: false,
   211  		},
   212  		"a namespace is cluster-scoped": {
   213  			obj:          testutil.Unstructured(t, testNamespace),
   214  			isNamespaced: false,
   215  		},
   216  		"pod is namespaced": {
   217  			obj:          testutil.Unstructured(t, testPod),
   218  			isNamespaced: true,
   219  		},
   220  		"default namespaced pod is namespaced": {
   221  			obj:          testutil.Unstructured(t, defaultNamespacePod),
   222  			isNamespaced: true,
   223  		},
   224  	}
   225  
   226  	for name, tc := range tests {
   227  		t.Run(name, func(t *testing.T) {
   228  			actual := IsNamespaced(tc.obj)
   229  			if tc.isNamespaced != actual {
   230  				t.Errorf("expected namespaced (%t), got (%t) for (%s)",
   231  					tc.isNamespaced, actual, tc.obj)
   232  			}
   233  		})
   234  	}
   235  }
   236  
   237  func TestGetCRDGroupKind(t *testing.T) {
   238  	tests := map[string]struct {
   239  		obj       *unstructured.Unstructured
   240  		isCRD     bool
   241  		groupKind string
   242  	}{
   243  		"RBAC is not a CRD": {
   244  			obj:       testutil.Unstructured(t, rbac),
   245  			isCRD:     false,
   246  			groupKind: "",
   247  		},
   248  		"pod is not a CRD": {
   249  			obj:       testutil.Unstructured(t, testPod),
   250  			isCRD:     false,
   251  			groupKind: "",
   252  		},
   253  		"testCRD has example.com/crontab GroupKind": {
   254  			obj:       testutil.Unstructured(t, testCRD),
   255  			isCRD:     true,
   256  			groupKind: "crontab.example.com",
   257  		},
   258  	}
   259  
   260  	for name, tc := range tests {
   261  		t.Run(name, func(t *testing.T) {
   262  			actualGroupKind, actualIsCRD := GetCRDGroupKind(tc.obj)
   263  			if tc.isCRD != actualIsCRD {
   264  				t.Errorf("expected IsCRD (%t), got (%t) for (%s)", tc.isCRD, actualIsCRD, tc.obj)
   265  			}
   266  			if tc.groupKind != actualGroupKind.String() {
   267  				t.Errorf("expected CRD GroupKind (%s), got (%s) for (%s)",
   268  					tc.groupKind, actualGroupKind, tc.obj)
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func TestLookupResourceScope(t *testing.T) {
   275  	testCases := map[string]struct {
   276  		resource      *unstructured.Unstructured
   277  		crds          []*unstructured.Unstructured
   278  		expectedScope meta.RESTScope
   279  		expectedErr   error
   280  	}{
   281  		"regular resource": {
   282  			resource:      testutil.Unstructured(t, testPod),
   283  			expectedScope: meta.RESTScopeNamespace,
   284  		},
   285  		"CR not found in the RESTMapper or the provided CRDs": {
   286  			resource: testutil.Unstructured(t, testCR),
   287  			expectedErr: &UnknownTypeError{
   288  				GroupVersionKind: schema.GroupVersionKind{
   289  					Group:   "example.com",
   290  					Version: "v1",
   291  					Kind:    "crontab",
   292  				},
   293  			},
   294  		},
   295  		"CR not found in the RESTMapper or the provided CRDs because version is missing": {
   296  			resource: testutil.Unstructured(t, testCR),
   297  			crds: []*unstructured.Unstructured{
   298  				testutil.Unstructured(t, testCRDv2),
   299  			},
   300  			expectedErr: &UnknownTypeError{
   301  				GroupVersionKind: schema.GroupVersionKind{
   302  					Group:   "example.com",
   303  					Version: "v1",
   304  					Kind:    "crontab",
   305  				},
   306  			},
   307  		},
   308  		"CR found in in the provided CRDs": {
   309  			resource: testutil.Unstructured(t, testCR),
   310  			crds: []*unstructured.Unstructured{
   311  				testutil.Unstructured(t, testCRD),
   312  			},
   313  			expectedScope: meta.RESTScopeRoot,
   314  		},
   315  	}
   316  
   317  	for tn, tc := range testCases {
   318  		t.Run(tn, func(t *testing.T) {
   319  			tf := cmdtesting.NewTestFactory().WithNamespace("test-ns")
   320  			defer tf.Cleanup()
   321  
   322  			mapper, err := tf.ToRESTMapper()
   323  			require.NoError(t, err)
   324  
   325  			scope, err := LookupResourceScope(tc.resource, tc.crds, mapper)
   326  
   327  			if tc.expectedErr != nil {
   328  				require.Equal(t, tc.expectedErr, err)
   329  				return
   330  			}
   331  
   332  			require.NoError(t, err)
   333  			assert.Equal(t, tc.expectedScope, scope)
   334  		})
   335  	}
   336  }
   337  

View as plain text