1
16
17 package apiserver
18
19 import (
20 "context"
21 "encoding/json"
22 "fmt"
23 "path"
24 "reflect"
25 "testing"
26 "time"
27
28 "k8s.io/apimachinery/pkg/util/wait"
29
30 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
31
32 "go.etcd.io/etcd/client/pkg/v3/transport"
33 clientv3 "go.etcd.io/etcd/client/v3"
34 "google.golang.org/grpc"
35
36 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
37 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
38 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
39 "k8s.io/apiextensions-apiserver/test/integration/fixtures"
40 apierrors "k8s.io/apimachinery/pkg/api/errors"
41 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
42 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
43 "k8s.io/apimachinery/pkg/types"
44 "k8s.io/client-go/dynamic"
45 apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
46 "k8s.io/kubernetes/test/integration/framework"
47 )
48
49
50
51 func TestApplyCRDStructuralSchema(t *testing.T) {
52 server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd())
53 if err != nil {
54 t.Fatal(err)
55 }
56 defer server.TearDownFn()
57 config := server.ClientConfig
58
59 apiExtensionClient, err := clientset.NewForConfig(config)
60 if err != nil {
61 t.Fatal(err)
62 }
63 dynamicClient, err := dynamic.NewForConfig(config)
64 if err != nil {
65 t.Fatal(err)
66 }
67
68 noxuDefinition := fixtures.NewMultipleVersionNoxuCRD(apiextensionsv1.ClusterScoped)
69
70 var c apiextensionsv1.CustomResourceValidation
71 err = json.Unmarshal([]byte(`{
72 "openAPIV3Schema": {
73 "type": "object",
74 "properties": {
75 "spec": {
76 "type": "object",
77 "x-kubernetes-preserve-unknown-fields": true,
78 "properties": {
79 "cronSpec": {
80 "type": "string",
81 "pattern": "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$"
82 },
83 "ports": {
84 "type": "array",
85 "x-kubernetes-list-map-keys": [
86 "containerPort",
87 "protocol"
88 ],
89 "x-kubernetes-list-type": "map",
90 "items": {
91 "properties": {
92 "containerPort": {
93 "format": "int32",
94 "type": "integer"
95 },
96 "hostIP": {
97 "type": "string"
98 },
99 "hostPort": {
100 "format": "int32",
101 "type": "integer"
102 },
103 "name": {
104 "type": "string"
105 },
106 "protocol": {
107 "type": "string"
108 }
109 },
110 "required": [
111 "containerPort",
112 "protocol"
113 ],
114 "type": "object"
115 }
116 }
117 }
118 }
119 }
120 }
121 }`), &c)
122 if err != nil {
123 t.Fatal(err)
124 }
125 for i := range noxuDefinition.Spec.Versions {
126 noxuDefinition.Spec.Versions[i].Schema = &c
127 }
128
129 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
130 if err != nil {
131 t.Fatal(err)
132 }
133
134 kind := noxuDefinition.Spec.Names.Kind
135 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
136 name := "mytest"
137
138 rest := apiExtensionClient.Discovery().RESTClient()
139 yamlBody := []byte(fmt.Sprintf(`
140 apiVersion: %s
141 kind: %s
142 metadata:
143 name: %s
144 finalizers:
145 - test-finalizer
146 spec:
147 cronSpec: "* * * * */5"
148 replicas: 1
149 ports:
150 - name: x
151 containerPort: 80
152 protocol: TCP`, apiVersion, kind, name))
153 result, err := rest.Patch(types.ApplyPatchType).
154 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
155 Name(name).
156 Param("fieldManager", "apply_test").
157 Body(yamlBody).
158 DoRaw(context.TODO())
159 if err != nil {
160 t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
161 }
162 verifyNumFinalizers(t, result, 1)
163 verifyFinalizersIncludes(t, result, "test-finalizer")
164 verifyReplicas(t, result, 1)
165 verifyNumPorts(t, result, 1)
166
167
168 result, err = rest.Patch(types.MergePatchType).
169 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
170 Name(name).
171 Body([]byte(`{"metadata":{"finalizers":["test-finalizer","another-one"]}}`)).
172 DoRaw(context.TODO())
173 if err != nil {
174 t.Fatalf("failed to add finalizer with merge patch: %v:\n%v", err, string(result))
175 }
176 verifyNumFinalizers(t, result, 2)
177 verifyFinalizersIncludes(t, result, "test-finalizer")
178 verifyFinalizersIncludes(t, result, "another-one")
179
180
181 result, err = rest.Patch(types.ApplyPatchType).
182 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
183 Name(name).
184 Param("fieldManager", "apply_test").
185 SetHeader("Accept", "application/json").
186 Body(yamlBody).
187 DoRaw(context.TODO())
188 if err != nil {
189 t.Fatalf("failed to apply same config after adding a finalizer: %v:\n%v", err, string(result))
190 }
191 verifyNumFinalizers(t, result, 2)
192 verifyFinalizersIncludes(t, result, "test-finalizer")
193 verifyFinalizersIncludes(t, result, "another-one")
194
195
196 result, err = rest.Patch(types.MergePatchType).
197 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
198 Name(name).
199 Body([]byte(`{"spec":{"replicas": 5}}`)).
200 DoRaw(context.TODO())
201 if err != nil {
202 t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
203 }
204 verifyReplicas(t, result, 5)
205
206
207 result, err = rest.Patch(types.ApplyPatchType).
208 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
209 Name(name).
210 Param("fieldManager", "apply_test").
211 Body(yamlBody).
212 DoRaw(context.TODO())
213 if err == nil {
214 t.Fatalf("Expecting to get conflicts when applying object after updating replicas, got no error: %s", result)
215 }
216 status, ok := err.(*apierrors.StatusError)
217 if !ok {
218 t.Fatalf("Expecting to get conflicts as API error")
219 }
220 if len(status.Status().Details.Causes) != 1 {
221 t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
222 }
223
224
225 result, err = rest.Patch(types.ApplyPatchType).
226 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
227 Name(name).
228 Param("force", "true").
229 Param("fieldManager", "apply_test").
230 Body(yamlBody).
231 DoRaw(context.TODO())
232 if err != nil {
233 t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
234 }
235 verifyReplicas(t, result, 1)
236
237
238 result, err = rest.Patch(types.ApplyPatchType).
239 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
240 Name(name).
241 Param("fieldManager", "apply_test_2").
242 Body([]byte(fmt.Sprintf(`
243 apiVersion: %s
244 kind: %s
245 metadata:
246 name: %s
247 spec:
248 ports:
249 - name: "y"
250 containerPort: 80
251 protocol: TCP`, apiVersion, kind, name))).
252 DoRaw(context.TODO())
253 if err == nil {
254 t.Fatalf("Expecting to get conflicts when a different applier updates existing list item, got no error: %s", result)
255 }
256 status, ok = err.(*apierrors.StatusError)
257 if !ok {
258 t.Fatalf("Expecting to get conflicts as API error")
259 }
260 if len(status.Status().Details.Causes) != 1 {
261 t.Fatalf("Expecting to get one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
262 }
263
264
265 result, err = rest.Patch(types.ApplyPatchType).
266 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
267 Name(name).
268 Param("fieldManager", "apply_test_2").
269 Body([]byte(fmt.Sprintf(`
270 apiVersion: %s
271 kind: %s
272 metadata:
273 name: %s
274 spec:
275 ports:
276 - name: "y"
277 containerPort: 8080
278 protocol: TCP`, apiVersion, kind, name))).
279 SetHeader("Accept", "application/json").
280 DoRaw(context.TODO())
281 if err != nil {
282 t.Fatalf("failed to add a new list item to the object as a different applier: %v:\n%v", err, string(result))
283 }
284 verifyNumPorts(t, result, 2)
285
286
287 notExistingYAMLBody := []byte(fmt.Sprintf(`
288 {
289 "apiVersion": "%s",
290 "kind": "%s",
291 "metadata": {
292 "name": "%s",
293 "finalizers": [
294 "test-finalizer"
295 ]
296 },
297 "spec": {
298 "cronSpec": "* * * * */5",
299 "replicas": 1,
300 "ports": [
301 {
302 "name": "x",
303 "containerPort": 80
304 }
305 ]
306 },
307 "protocol": "TCP"
308 }`, apiVersion, kind, "should-not-exist"))
309 _, err = rest.Put().
310 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
311 Name("should-not-exist").
312 Param("fieldManager", "apply_test").
313 Body(notExistingYAMLBody).
314 DoRaw(context.TODO())
315 if !apierrors.IsNotFound(err) {
316 t.Fatalf("create on update should fail with notFound, got %v", err)
317 }
318 }
319
320
321 func verifyNumFinalizers(t *testing.T, b []byte, n int) {
322 obj := unstructured.Unstructured{}
323 err := obj.UnmarshalJSON(b)
324 if err != nil {
325 t.Fatalf("failed to unmarshal response: %v", err)
326 }
327 if actual, expected := len(obj.GetFinalizers()), n; actual != expected {
328 t.Fatalf("expected %v finalizers but got %v:\n%v", expected, actual, string(b))
329 }
330 }
331
332
333 func verifyFinalizersIncludes(t *testing.T, b []byte, e string) {
334 obj := unstructured.Unstructured{}
335 err := obj.UnmarshalJSON(b)
336 if err != nil {
337 t.Fatalf("failed to unmarshal response: %v", err)
338 }
339 for _, a := range obj.GetFinalizers() {
340 if a == e {
341 return
342 }
343 }
344 t.Fatalf("expected finalizers to include %q but got: %v", e, obj.GetFinalizers())
345 }
346
347
348 func verifyReplicas(t *testing.T, b []byte, r int) {
349 obj := unstructured.Unstructured{}
350 err := obj.UnmarshalJSON(b)
351 if err != nil {
352 t.Fatalf("failed to find replicas number in response: %v:\n%v", err, string(b))
353 }
354 spec, ok := obj.Object["spec"]
355 if !ok {
356 t.Fatalf("failed to find replicas number in response:\n%v", string(b))
357 }
358 specMap, ok := spec.(map[string]interface{})
359 if !ok {
360 t.Fatalf("failed to find replicas number in response:\n%v", string(b))
361 }
362 replicas, ok := specMap["replicas"]
363 if !ok {
364 t.Fatalf("failed to find replicas number in response:\n%v", string(b))
365 }
366 replicasNumber, ok := replicas.(int64)
367 if !ok {
368 t.Fatalf("failed to find replicas number in response: expected int64 but got: %v", reflect.TypeOf(replicas))
369 }
370 if actual, expected := replicasNumber, int64(r); actual != expected {
371 t.Fatalf("expected %v ports but got %v:\n%v", expected, actual, string(b))
372 }
373 }
374
375
376 func verifyNumPorts(t *testing.T, b []byte, n int) {
377 obj := unstructured.Unstructured{}
378 err := obj.UnmarshalJSON(b)
379 if err != nil {
380 t.Fatalf("failed to find ports list in response: %v:\n%v", err, string(b))
381 }
382 spec, ok := obj.Object["spec"]
383 if !ok {
384 t.Fatalf("failed to find ports list in response:\n%v", string(b))
385 }
386 specMap, ok := spec.(map[string]interface{})
387 if !ok {
388 t.Fatalf("failed to find ports list in response:\n%v", string(b))
389 }
390 ports, ok := specMap["ports"]
391 if !ok {
392 t.Fatalf("failed to find ports list in response:\n%v", string(b))
393 }
394 portsList, ok := ports.([]interface{})
395 if !ok {
396 t.Fatalf("failed to find ports list in response: expected array but got: %v", reflect.TypeOf(ports))
397 }
398 if actual, expected := len(portsList), n; actual != expected {
399 t.Fatalf("expected %v ports but got %v:\n%v", expected, actual, string(b))
400 }
401 }
402
403 func findCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) *apiextensionsv1.CustomResourceDefinitionCondition {
404 for i := range crd.Status.Conditions {
405 if crd.Status.Conditions[i].Type == conditionType {
406 return &crd.Status.Conditions[i]
407 }
408 }
409
410 return nil
411 }
412
413
414
415 func TestApplyCRDUnhandledSchema(t *testing.T) {
416 storageConfig := framework.SharedEtcd()
417 tlsInfo := transport.TLSInfo{
418 CertFile: storageConfig.Transport.CertFile,
419 KeyFile: storageConfig.Transport.KeyFile,
420 TrustedCAFile: storageConfig.Transport.TrustedCAFile,
421 }
422 tlsConfig, err := tlsInfo.ClientConfig()
423 if err != nil {
424 t.Fatal(err)
425 }
426 etcdConfig := clientv3.Config{
427 Endpoints: storageConfig.Transport.ServerList,
428 DialTimeout: 20 * time.Second,
429 DialOptions: []grpc.DialOption{
430 grpc.WithBlock(),
431 },
432 TLS: tlsConfig,
433 }
434 etcdclient, err := clientv3.New(etcdConfig)
435 if err != nil {
436 t.Fatal(err)
437 }
438 defer etcdclient.Close()
439
440 server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, storageConfig)
441 if err != nil {
442 t.Fatal(err)
443 }
444 defer server.TearDownFn()
445 config := server.ClientConfig
446
447 apiExtensionClient, err := clientset.NewForConfig(config)
448 if err != nil {
449 t.Fatal(err)
450 }
451
452
453
454 noxuBetaDefinition := &apiextensionsv1beta1.CustomResourceDefinition{
455 TypeMeta: metav1.TypeMeta{
456 Kind: "CustomResourceDefinition",
457 APIVersion: "apiextensions.k8s.io/v1beta1",
458 },
459 ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
460 Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
461 Group: "mygroup.example.com",
462 Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{{
463 Name: "v1beta1",
464 Served: true,
465 Storage: true,
466 }},
467 Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
468 Plural: "noxus",
469 Singular: "nonenglishnoxu",
470 Kind: "WishIHadChosenNoxu",
471 ShortNames: []string{"foo", "bar", "abc", "def"},
472 ListKind: "NoxuItemList",
473 Categories: []string{"all"},
474 },
475 Scope: apiextensionsv1beta1.ClusterScoped,
476 },
477 }
478
479
480
481 var c apiextensionsv1beta1.CustomResourceValidation
482 err = json.Unmarshal([]byte(`{
483 "openAPIV3Schema": {
484 "properties": {
485 "TypeFooBar": {
486 "type": "array"
487 }
488 }
489 }
490 }`), &c)
491 if err != nil {
492 t.Fatal(err)
493 }
494 noxuBetaDefinition.Spec.Validation = &c
495
496 betaBytes, err := json.Marshal(noxuBetaDefinition)
497 if err != nil {
498 t.Fatal(err)
499 }
500 t.Log(string(betaBytes))
501 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceNone)
502 key := path.Join("/", storageConfig.Prefix, "apiextensions.k8s.io", "customresourcedefinitions", noxuBetaDefinition.Name)
503 if _, err := etcdclient.Put(ctx, key, string(betaBytes)); err != nil {
504 t.Fatalf("unexpected error: %v", err)
505 }
506
507 noxuDefinition, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuBetaDefinition.Name, metav1.GetOptions{})
508 if err != nil {
509 t.Fatal(err)
510 }
511
512 err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (bool, error) {
513 localCrd, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), noxuBetaDefinition.Name, metav1.GetOptions{})
514 if err != nil {
515 return false, err
516 }
517 condition := findCRDCondition(localCrd, apiextensionsv1.Established)
518 if condition == nil {
519 return false, nil
520 }
521 if condition.Status == apiextensionsv1.ConditionTrue {
522 return true, nil
523 }
524 return false, nil
525 })
526 if err != nil {
527 t.Fatal(err)
528 }
529
530 kind := noxuDefinition.Spec.Names.Kind
531 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
532 name := "mytest"
533
534 rest := apiExtensionClient.Discovery().RESTClient()
535 yamlBody := []byte(fmt.Sprintf(`
536 apiVersion: %s
537 kind: %s
538 metadata:
539 name: %s
540 spec:
541 replicas: 1`, apiVersion, kind, name))
542 result, err := rest.Patch(types.ApplyPatchType).
543 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
544 Name(name).
545 Param("fieldManager", "apply_test").
546 Body(yamlBody).
547 DoRaw(context.TODO())
548 if err != nil {
549 t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
550 }
551 verifyReplicas(t, result, 1)
552
553
554 result, err = rest.Patch(types.MergePatchType).
555 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
556 Name(name).
557 Body([]byte(`{"spec":{"replicas": 5}}`)).
558 DoRaw(context.TODO())
559 if err != nil {
560 t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
561 }
562 verifyReplicas(t, result, 5)
563
564
565 result, err = rest.Patch(types.ApplyPatchType).
566 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
567 Name(name).
568 Param("fieldManager", "apply_test").
569 Body(yamlBody).
570 DoRaw(context.TODO())
571 if err == nil {
572 t.Fatalf("Expecting to get conflicts when applying object after updating replicas, got no error: %s", result)
573 }
574 status, ok := err.(*apierrors.StatusError)
575 if !ok {
576 t.Fatalf("Expecting to get conflicts as API error")
577 }
578 if len(status.Status().Details.Causes) != 1 {
579 t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
580 }
581
582
583 result, err = rest.Patch(types.ApplyPatchType).
584 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
585 Name(name).
586 Param("force", "true").
587 Param("fieldManager", "apply_test").
588 Body(yamlBody).
589 DoRaw(context.TODO())
590 if err != nil {
591 t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
592 }
593 verifyReplicas(t, result, 1)
594 }
595
596 func getManagedFields(rawResponse []byte) ([]metav1.ManagedFieldsEntry, error) {
597 obj := unstructured.Unstructured{}
598 if err := obj.UnmarshalJSON(rawResponse); err != nil {
599 return nil, err
600 }
601 return obj.GetManagedFields(), nil
602 }
603
604 func TestDefaultMissingKeyCRD(t *testing.T) {
605 server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd())
606 if err != nil {
607 t.Fatal(err)
608 }
609 defer server.TearDownFn()
610 config := server.ClientConfig
611
612 apiExtensionClient, err := clientset.NewForConfig(config)
613 if err != nil {
614 t.Fatal(err)
615 }
616 dynamicClient, err := dynamic.NewForConfig(config)
617 if err != nil {
618 t.Fatal(err)
619 }
620
621 noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
622 err = json.Unmarshal([]byte(`{
623 "openAPIV3Schema": {
624 "type": "object",
625 "properties": {
626 "spec": {
627 "type": "object",
628 "x-kubernetes-preserve-unknown-fields": true,
629 "properties": {
630 "cronSpec": {
631 "type": "string",
632 "pattern": "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$"
633 },
634 "ports": {
635 "type": "array",
636 "x-kubernetes-list-map-keys": [
637 "containerPort",
638 "protocol"
639 ],
640 "x-kubernetes-list-type": "map",
641 "items": {
642 "properties": {
643 "containerPort": {
644 "format": "int32",
645 "type": "integer"
646 },
647 "hostIP": {
648 "type": "string"
649 },
650 "hostPort": {
651 "format": "int32",
652 "type": "integer"
653 },
654 "name": {
655 "type": "string"
656 },
657 "protocol": {
658 "default": "TCP",
659 "type": "string"
660 }
661 },
662 "required": [
663 "containerPort"
664 ],
665 "type": "object"
666 }
667 }
668 }
669 }
670 }
671 }
672 }`), &noxuDefinition.Spec.Versions[0].Schema)
673 if err != nil {
674 t.Fatal(err)
675 }
676 noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
677 if err != nil {
678 t.Fatal(err)
679 }
680
681 kind := noxuDefinition.Spec.Names.Kind
682 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name
683 name := "mytest"
684
685 rest := apiExtensionClient.Discovery().RESTClient()
686 yamlBody := []byte(fmt.Sprintf(`
687 apiVersion: %s
688 kind: %s
689 metadata:
690 name: %s
691 finalizers:
692 - test-finalizer
693 spec:
694 cronSpec: "* * * * */5"
695 replicas: 1
696 ports:
697 - name: x
698 containerPort: 80`, apiVersion, kind, name))
699 result, err := rest.Patch(types.ApplyPatchType).
700 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
701 Name(name).
702 Param("fieldManager", "apply_test").
703 Body(yamlBody).
704 DoRaw(context.TODO())
705 if err != nil {
706 t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
707 }
708
709
710 result, err = rest.Patch(types.ApplyPatchType).
711 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural).
712 Name(name).
713 Param("fieldManager", "apply_test_2").
714 Body([]byte(fmt.Sprintf(`
715 apiVersion: %s
716 kind: %s
717 metadata:
718 name: %s
719 spec:
720 ports:
721 - name: "y"
722 containerPort: 80
723 protocol: TCP`, apiVersion, kind, name))).
724 DoRaw(context.TODO())
725 if err == nil {
726 t.Fatalf("Expecting to get conflicts when a different applier updates existing list item, got no error: %s", result)
727 }
728 status, ok := err.(*apierrors.StatusError)
729 if !ok {
730 t.Fatalf("Expecting to get conflicts as API error")
731 }
732 if len(status.Status().Details.Causes) != 1 {
733 t.Fatalf("Expecting to get one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
734 }
735 }
736
View as plain text