1
16
17 package integration
18
19 import (
20 "context"
21 "encoding/json"
22 "fmt"
23 "net/http"
24 "testing"
25
26 "sigs.k8s.io/yaml"
27
28 "k8s.io/apimachinery/pkg/api/errors"
29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30 "k8s.io/apimachinery/pkg/types"
31 "k8s.io/client-go/dynamic"
32
33 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
34 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
35 "k8s.io/apiextensions-apiserver/test/integration/fixtures"
36 )
37
38 func TestYAML(t *testing.T) {
39 tearDown, config, _, err := fixtures.StartDefaultServer(t)
40 if err != nil {
41 t.Fatal(err)
42 }
43 defer tearDown()
44
45 apiExtensionClient, err := clientset.NewForConfig(config)
46 if err != nil {
47 t.Fatal(err)
48 }
49 dynamicClient, err := dynamic.NewForConfig(config)
50 if err != nil {
51 t.Fatal(err)
52 }
53
54 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
55 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
56 if err != nil {
57 t.Fatal(err)
58 }
59
60 kind := noxuDefinition.Spec.Names.Kind
61 listKind := noxuDefinition.Spec.Names.ListKind
62 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
63
64 rest := apiExtensionClient.Discovery().RESTClient()
65
66
67 {
68 result, err := rest.Get().
69 SetHeader("Accept", "application/yaml").
70 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name).
71 DoRaw(context.TODO())
72 if err != nil {
73 t.Fatal(err, string(result))
74 }
75 obj, err := decodeYAML(result)
76 if err != nil {
77 t.Fatal(err)
78 }
79 if obj.GetAPIVersion() != "v1" || obj.GetKind() != "APIResourceList" {
80 t.Fatalf("unexpected discovery kind: %s", string(result))
81 }
82 if v, ok, err := unstructured.NestedString(obj.Object, "groupVersion"); v != apiVersion || !ok || err != nil {
83 t.Fatal(v, ok, err, string(result))
84 }
85 }
86
87
88 {
89 result, err := rest.Get().
90 SetHeader("Accept", "application/yaml").
91 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "missingname").
92 DoRaw(context.TODO())
93 if !errors.IsNotFound(err) {
94 t.Fatalf("expected not found, got %v", err)
95 }
96 obj, err := decodeYAML(result)
97 if err != nil {
98 t.Fatal(err)
99 }
100 if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
101 t.Fatalf("unexpected discovery kind: %s", string(result))
102 }
103 if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "NotFound" || !ok || err != nil {
104 t.Fatal(v, ok, err, string(result))
105 }
106 }
107
108 uid := types.UID("")
109 resourceVersion := ""
110
111
112 {
113 yamlBody := []byte(fmt.Sprintf(`
114 apiVersion: %s
115 kind: %s
116 metadata:
117 name: mytest
118 values:
119 numVal: 1
120 boolVal: true
121 stringVal: "1"`, apiVersion, kind))
122
123 result, err := rest.Post().
124 SetHeader("Accept", "application/yaml").
125 SetHeader("Content-Type", "application/yaml").
126 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
127 Body(yamlBody).
128 DoRaw(context.TODO())
129 if err != nil {
130 t.Fatal(err, string(result))
131 }
132 obj, err := decodeYAML(result)
133 if err != nil {
134 t.Fatal(err)
135 }
136 if obj.GetName() != "mytest" {
137 t.Fatalf("expected mytest, got %s", obj.GetName())
138 }
139 if obj.GetAPIVersion() != apiVersion {
140 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
141 }
142 if obj.GetKind() != kind {
143 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
144 }
145 if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
146 t.Fatal(v, ok, err, string(result))
147 }
148 if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
149 t.Fatal(v, ok, err, string(result))
150 }
151 if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
152 t.Fatal(v, ok, err, string(result))
153 }
154 uid = obj.GetUID()
155 resourceVersion = obj.GetResourceVersion()
156 }
157
158
159 {
160 result, err := rest.Get().
161 SetHeader("Accept", "application/yaml").
162 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
163 DoRaw(context.TODO())
164 if err != nil {
165 t.Fatal(err)
166 }
167 obj, err := decodeYAML(result)
168 if err != nil {
169 t.Fatal(err, string(result))
170 }
171 if obj.GetName() != "mytest" {
172 t.Fatalf("expected mytest, got %s", obj.GetName())
173 }
174 if obj.GetAPIVersion() != apiVersion {
175 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
176 }
177 if obj.GetKind() != kind {
178 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
179 }
180 if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
181 t.Fatal(v, ok, err, string(result))
182 }
183 if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
184 t.Fatal(v, ok, err, string(result))
185 }
186 if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
187 t.Fatal(v, ok, err, string(result))
188 }
189 }
190
191
192 {
193 result, err := rest.Get().
194 SetHeader("Accept", "application/yaml").
195 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
196 DoRaw(context.TODO())
197 if err != nil {
198 t.Fatal(err, string(result))
199 }
200 listObj, err := decodeYAML(result)
201 if err != nil {
202 t.Fatal(err)
203 }
204 if listObj.GetAPIVersion() != apiVersion {
205 t.Fatalf("expected %s, got %s", apiVersion, listObj.GetAPIVersion())
206 }
207 if listObj.GetKind() != listKind {
208 t.Fatalf("expected %s, got %s", kind, listObj.GetKind())
209 }
210 items, ok, err := unstructured.NestedSlice(listObj.Object, "items")
211 if !ok || err != nil || len(items) != 1 {
212 t.Fatalf("expected one item, got %v %v %v", items, ok, err)
213 }
214 obj := unstructured.Unstructured{Object: items[0].(map[string]interface{})}
215 if obj.GetName() != "mytest" {
216 t.Fatalf("expected mytest, got %s", obj.GetName())
217 }
218 if obj.GetAPIVersion() != apiVersion {
219 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
220 }
221 if obj.GetKind() != kind {
222 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
223 }
224 if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 1 || !ok || err != nil {
225 t.Fatal(v, ok, err, string(result))
226 }
227 if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != true || !ok || err != nil {
228 t.Fatal(v, ok, err, string(result))
229 }
230 if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "1" || !ok || err != nil {
231 t.Fatal(v, ok, err, string(result))
232 }
233 }
234
235
236 {
237 result, err := rest.Get().
238 SetHeader("Accept", "application/yaml").
239 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
240 Param("watch", "true").
241 DoRaw(context.TODO())
242 if !errors.IsNotAcceptable(err) {
243 t.Fatalf("expected not acceptable error, got %v (%s)", err, string(result))
244 }
245 obj, err := decodeYAML(result)
246 if err != nil {
247 t.Fatal(err)
248 }
249 if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
250 t.Fatalf("unexpected result: %s", string(result))
251 }
252 if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "NotAcceptable" || !ok || err != nil {
253 t.Fatal(v, ok, err, string(result))
254 }
255 if v, ok, err := unstructured.NestedFloat64(obj.Object, "code"); v != http.StatusNotAcceptable || !ok || err != nil {
256 t.Fatal(v, ok, err, string(result))
257 }
258 }
259
260
261 {
262 yamlBody := []byte(fmt.Sprintf(`
263 apiVersion: %s
264 kind: %s
265 metadata:
266 name: mytest
267 uid: %s
268 resourceVersion: "%s"
269 values:
270 numVal: 2
271 boolVal: false
272 stringVal: "2"`, apiVersion, kind, uid, resourceVersion))
273 result, err := rest.Put().
274 SetHeader("Accept", "application/yaml").
275 SetHeader("Content-Type", "application/yaml").
276 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
277 Body(yamlBody).
278 DoRaw(context.TODO())
279 if err != nil {
280 t.Fatal(err, string(result))
281 }
282 obj, err := decodeYAML(result)
283 if err != nil {
284 t.Fatal(err)
285 }
286 if obj.GetName() != "mytest" {
287 t.Fatalf("expected mytest, got %s", obj.GetName())
288 }
289 if obj.GetAPIVersion() != apiVersion {
290 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
291 }
292 if obj.GetKind() != kind {
293 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
294 }
295 if v, ok, err := unstructured.NestedFloat64(obj.Object, "values", "numVal"); v != 2 || !ok || err != nil {
296 t.Fatal(v, ok, err, string(result))
297 }
298 if v, ok, err := unstructured.NestedBool(obj.Object, "values", "boolVal"); v != false || !ok || err != nil {
299 t.Fatal(v, ok, err, string(result))
300 }
301 if v, ok, err := unstructured.NestedString(obj.Object, "values", "stringVal"); v != "2" || !ok || err != nil {
302 t.Fatal(v, ok, err, string(result))
303 }
304 if obj.GetUID() != uid {
305 t.Fatalf("uid changed: %v vs %v", uid, obj.GetUID())
306 }
307 }
308
309
310 {
311 yamlBody := []byte(fmt.Sprintf(`
312 values:
313 numVal: 3`))
314 result, err := rest.Patch(types.MergePatchType).
315 SetHeader("Accept", "application/yaml").
316 SetHeader("Content-Type", "application/yaml").
317 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
318 Body(yamlBody).
319 DoRaw(context.TODO())
320 if !errors.IsUnsupportedMediaType(err) {
321 t.Fatalf("Expected bad request, got %v\n%s", err, string(result))
322 }
323 obj, err := decodeYAML(result)
324 if err != nil {
325 t.Fatal(err)
326 }
327 if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
328 t.Fatalf("expected %s %s, got %s %s", "v1", "Status", obj.GetAPIVersion(), obj.GetKind())
329 }
330 if v, ok, err := unstructured.NestedString(obj.Object, "reason"); v != "UnsupportedMediaType" || !ok || err != nil {
331 t.Fatal(v, ok, err, string(result))
332 }
333 }
334
335
336 {
337 result, err := rest.Delete().
338 SetHeader("Accept", "application/yaml").
339 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest").
340 DoRaw(context.TODO())
341 if err != nil {
342 t.Fatal(err, string(result))
343 }
344 obj, err := decodeYAML(result)
345 if err != nil {
346 t.Fatal(err)
347 }
348 if obj.GetAPIVersion() != "v1" || obj.GetKind() != "Status" {
349 t.Fatalf("unexpected response: %s", string(result))
350 }
351 if v, ok, err := unstructured.NestedString(obj.Object, "status"); v != "Success" || !ok || err != nil {
352 t.Fatal(v, ok, err, string(result))
353 }
354 }
355 }
356
357 func TestYAMLSubresource(t *testing.T) {
358 tearDown, config, _, err := fixtures.StartDefaultServer(t)
359 if err != nil {
360 t.Fatal(err)
361 }
362 defer tearDown()
363
364 apiExtensionClient, err := clientset.NewForConfig(config)
365 if err != nil {
366 t.Fatal(err)
367 }
368 dynamicClient, err := dynamic.NewForConfig(config)
369 if err != nil {
370 t.Fatal(err)
371 }
372
373 noxuDefinition := NewNoxuSubresourcesCRDs(apiextensionsv1.ClusterScoped)[0]
374 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
375 if err != nil {
376 t.Fatal(err)
377 }
378
379 kind := noxuDefinition.Spec.Names.Kind
380 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
381
382 rest := apiExtensionClient.Discovery().RESTClient()
383
384 uid := types.UID("")
385 resourceVersion := ""
386
387
388 {
389 yamlBody := []byte(fmt.Sprintf(`
390 apiVersion: %s
391 kind: %s
392 metadata:
393 name: mytest
394 spec:
395 replicas: 3`, apiVersion, kind))
396
397 result, err := rest.Post().
398 SetHeader("Accept", "application/yaml").
399 SetHeader("Content-Type", "application/yaml").
400 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
401 Body(yamlBody).
402 DoRaw(context.TODO())
403 if err != nil {
404 t.Fatal(err, string(result))
405 }
406 obj, err := decodeYAML(result)
407 if err != nil {
408 t.Fatal(err)
409 }
410 if obj.GetName() != "mytest" {
411 t.Fatalf("expected mytest, got %s", obj.GetName())
412 }
413 if obj.GetAPIVersion() != apiVersion {
414 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
415 }
416 if obj.GetKind() != kind {
417 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
418 }
419 if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
420 t.Fatal(v, ok, err, string(result))
421 }
422 uid = obj.GetUID()
423 resourceVersion = obj.GetResourceVersion()
424 }
425
426
427 {
428 result, err := rest.Get().
429 SetHeader("Accept", "application/yaml").
430 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "status").
431 DoRaw(context.TODO())
432 if err != nil {
433 t.Fatal(err)
434 }
435 obj, err := decodeYAML(result)
436 if err != nil {
437 t.Fatal(err, string(result))
438 }
439 if obj.GetName() != "mytest" {
440 t.Fatalf("expected mytest, got %s", obj.GetName())
441 }
442 if obj.GetAPIVersion() != apiVersion {
443 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
444 }
445 if obj.GetKind() != kind {
446 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
447 }
448 if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
449 t.Fatal(v, ok, err, string(result))
450 }
451 }
452
453
454 {
455 yamlBody := []byte(fmt.Sprintf(`
456 apiVersion: %s
457 kind: %s
458 metadata:
459 name: mytest
460 uid: %s
461 resourceVersion: "%s"
462 spec:
463 replicas: 5
464 status:
465 replicas: 3`, apiVersion, kind, uid, resourceVersion))
466 result, err := rest.Put().
467 SetHeader("Accept", "application/yaml").
468 SetHeader("Content-Type", "application/yaml").
469 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "status").
470 Body(yamlBody).
471 DoRaw(context.TODO())
472 if err != nil {
473 t.Fatal(err, string(result))
474 }
475 obj, err := decodeYAML(result)
476 if err != nil {
477 t.Fatal(err)
478 }
479 if obj.GetName() != "mytest" {
480 t.Fatalf("expected mytest, got %s", obj.GetName())
481 }
482 if obj.GetAPIVersion() != apiVersion {
483 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
484 }
485 if obj.GetKind() != kind {
486 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
487 }
488 if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
489 t.Fatal(v, ok, err, string(result))
490 }
491 if v, ok, err := unstructured.NestedFloat64(obj.Object, "status", "replicas"); v != 3 || !ok || err != nil {
492 t.Fatal(v, ok, err, string(result))
493 }
494 if obj.GetUID() != uid {
495 t.Fatalf("uid changed: %v vs %v", uid, obj.GetUID())
496 }
497 }
498
499
500 {
501 result, err := rest.Get().
502 SetHeader("Accept", "application/yaml").
503 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural, "mytest", "scale").
504 DoRaw(context.TODO())
505 if err != nil {
506 t.Fatal(err)
507 }
508 obj, err := decodeYAML(result)
509 if err != nil {
510 t.Fatal(err, string(result))
511 }
512 if obj.GetName() != "mytest" {
513 t.Fatalf("expected mytest, got %s", obj.GetName())
514 }
515 if obj.GetAPIVersion() != "autoscaling/v1" {
516 t.Fatalf("expected %s, got %s", apiVersion, obj.GetAPIVersion())
517 }
518 if obj.GetKind() != "Scale" {
519 t.Fatalf("expected %s, got %s", kind, obj.GetKind())
520 }
521 if v, ok, err := unstructured.NestedFloat64(obj.Object, "spec", "replicas"); v != 3 || !ok || err != nil {
522 t.Fatal(v, ok, err, string(result))
523 }
524 if v, ok, err := unstructured.NestedFloat64(obj.Object, "status", "replicas"); v != 3 || !ok || err != nil {
525 t.Fatal(v, ok, err, string(result))
526 }
527 }
528 }
529
530 func decodeYAML(data []byte) (*unstructured.Unstructured, error) {
531 retval := &unstructured.Unstructured{Object: map[string]interface{}{}}
532
533 if json.Unmarshal(data, &retval.Object) == nil {
534 return nil, fmt.Errorf("data is JSON, not YAML: %s", string(data))
535 }
536
537 retval.Object = map[string]interface{}{}
538 if err := yaml.Unmarshal(data, &retval.Object); err != nil {
539 return nil, fmt.Errorf("error decoding YAML: %v\noriginal YAML: %s", err, string(data))
540 }
541 return retval, nil
542 }
543
View as plain text