1
2
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