1
1
package client
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
6
7
"strconv"
@@ -9,14 +10,20 @@ import (
9
10
. "github.com/onsi/gomega"
10
11
"helm.sh/helm/v3/pkg/action"
11
12
"helm.sh/helm/v3/pkg/chartutil"
13
+ "helm.sh/helm/v3/pkg/kube"
12
14
"helm.sh/helm/v3/pkg/release"
13
15
"helm.sh/helm/v3/pkg/releaseutil"
14
16
"helm.sh/helm/v3/pkg/storage/driver"
17
+ appsv1 "k8s.io/api/apps/v1"
15
18
v1 "k8s.io/api/core/v1"
16
19
apierrors "k8s.io/apimachinery/pkg/api/errors"
17
20
"k8s.io/apimachinery/pkg/api/meta"
21
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
22
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
19
23
"k8s.io/apimachinery/pkg/runtime"
24
+ "k8s.io/apimachinery/pkg/runtime/schema"
25
+ apitypes "k8s.io/apimachinery/pkg/types"
26
+ "k8s.io/cli-runtime/pkg/resource"
20
27
"sigs.k8s.io/controller-runtime/pkg/client"
21
28
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
22
29
"sigs.k8s.io/yaml"
@@ -40,6 +47,21 @@ var _ = Describe("ActionClient", func() {
40
47
})
41
48
})
42
49
50
+ var _ = Describe ("ActionClientGetterFunc" , func () {
51
+ It ("implements the ActionClientGetter interface" , func () {
52
+ gvk := schema.GroupVersionKind {Group : "test" , Version : "v1alpha1" , Kind : "Test" }
53
+ expectedObj := & unstructured.Unstructured {}
54
+ expectedObj .SetGroupVersionKind (gvk )
55
+ var actualObj Object
56
+ f := ActionClientGetterFunc (func (obj Object ) (ActionInterface , error ) {
57
+ actualObj = obj
58
+ return nil , nil
59
+ })
60
+ _ , _ = f .ActionClientFor (expectedObj )
61
+ Expect (actualObj .GetObjectKind ().GroupVersionKind ()).To (Equal (gvk ))
62
+ })
63
+ })
64
+
43
65
var _ = Describe ("ActionClientFor" , func () {
44
66
var obj Object
45
67
BeforeEach (func () {
@@ -310,6 +332,201 @@ var _ = Describe("ActionClient", func() {
310
332
})
311
333
})
312
334
})
335
+
336
+ var _ = Describe ("createPatch" , func () {
337
+ It ("ignores extra fields in custom resource types" , func () {
338
+ o1 := newTestUnstructured ([]interface {}{
339
+ map [string ]interface {}{
340
+ "name" : "test1" ,
341
+ },
342
+ map [string ]interface {}{
343
+ "name" : "test2" ,
344
+ },
345
+ })
346
+ o2 := & resource.Info {
347
+ Object : newTestUnstructured ([]interface {}{
348
+ map [string ]interface {}{
349
+ "name" : "test1" ,
350
+ },
351
+ }),
352
+ }
353
+ patch , patchType , err := createPatch (o1 , o2 )
354
+ Expect (err ).To (BeNil ())
355
+ Expect (string (patch )).To (Equal (`` ))
356
+ Expect (patchType ).To (Equal (apitypes .JSONPatchType ))
357
+ })
358
+ It ("patches missing fields in custom resource types" , func () {
359
+ o1 := newTestUnstructured ([]interface {}{
360
+ map [string ]interface {}{
361
+ "name" : "test1" ,
362
+ },
363
+ })
364
+ o2 := & resource.Info {
365
+ Object : newTestUnstructured ([]interface {}{
366
+ map [string ]interface {}{
367
+ "name" : "test1" ,
368
+ },
369
+ map [string ]interface {}{
370
+ "name" : "test2" ,
371
+ },
372
+ }),
373
+ }
374
+ patch , patchType , err := createPatch (o1 , o2 )
375
+ Expect (err ).To (BeNil ())
376
+ Expect (string (patch )).To (Equal (`[{"op":"add","path":"/spec/template/spec/containers/1","value":{"name":"test2"}}]` ))
377
+ Expect (patchType ).To (Equal (apitypes .JSONPatchType ))
378
+ })
379
+ It ("ignores nil fields in custom resource types" , func () {
380
+ o1 := newTestUnstructured ([]interface {}{
381
+ map [string ]interface {}{
382
+ "name" : "test1" ,
383
+ },
384
+ })
385
+ o2 := & resource.Info {
386
+ Object : newTestUnstructured ([]interface {}{
387
+ map [string ]interface {}{
388
+ "name" : "test1" ,
389
+ "test" : nil ,
390
+ },
391
+ }),
392
+ }
393
+ patch , patchType , err := createPatch (o1 , o2 )
394
+ Expect (err ).To (BeNil ())
395
+ Expect (string (patch )).To (Equal (`` ))
396
+ Expect (patchType ).To (Equal (apitypes .JSONPatchType ))
397
+ })
398
+ It ("replaces incorrect fields in custom resource types" , func () {
399
+ o1 := newTestUnstructured ([]interface {}{
400
+ map [string ]interface {}{
401
+ "name" : "test1" ,
402
+ },
403
+ })
404
+ o2 := & resource.Info {
405
+ Object : newTestUnstructured ([]interface {}{
406
+ map [string ]interface {}{
407
+ "name" : "test2" ,
408
+ },
409
+ }),
410
+ }
411
+ patch , patchType , err := createPatch (o1 , o2 )
412
+ Expect (err ).To (BeNil ())
413
+ Expect (string (patch )).To (Equal (`[{"op":"replace","path":"/spec/template/spec/containers/0/name","value":"test2"}]` ))
414
+ Expect (patchType ).To (Equal (apitypes .JSONPatchType ))
415
+ })
416
+ It ("ignores extra fields in core types" , func () {
417
+ o1 := newTestDeployment ([]v1.Container {
418
+ {Name : "test1" },
419
+ {Name : "test2" },
420
+ })
421
+ o2 := & resource.Info {
422
+ Object : newTestDeployment ([]v1.Container {
423
+ {Name : "test1" },
424
+ }),
425
+ }
426
+ patch , patchType , err := createPatch (o1 , o2 )
427
+ Expect (err ).To (BeNil ())
428
+ Expect (string (patch )).To (Equal (`{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"test1"}]}}}}` ))
429
+ Expect (patchType ).To (Equal (apitypes .StrategicMergePatchType ))
430
+ })
431
+ It ("patches missing fields in core types" , func () {
432
+ o1 := newTestDeployment ([]v1.Container {
433
+ {Name : "test1" },
434
+ })
435
+ o2 := & resource.Info {
436
+ Object : newTestDeployment ([]v1.Container {
437
+ {Name : "test1" },
438
+ {Name : "test2" },
439
+ }),
440
+ }
441
+ patch , patchType , err := createPatch (o1 , o2 )
442
+ Expect (err ).To (BeNil ())
443
+ Expect (string (patch )).To (Equal (`{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"test1"},{"name":"test2"}],"containers":[{"name":"test2","resources":{}}]}}}}` ))
444
+ Expect (patchType ).To (Equal (apitypes .StrategicMergePatchType ))
445
+ })
446
+ It ("ignores nil fields in core types" , func () {
447
+ o1 := newTestDeployment ([]v1.Container {
448
+ {Name : "test1" },
449
+ })
450
+ o2 := & resource.Info {
451
+ Object : newTestDeployment ([]v1.Container {
452
+ {Name : "test1" , LivenessProbe : nil },
453
+ }),
454
+ }
455
+ patch , patchType , err := createPatch (o1 , o2 )
456
+ Expect (err ).To (BeNil ())
457
+ Expect (string (patch )).To (Equal (`{}` ))
458
+ Expect (patchType ).To (Equal (apitypes .StrategicMergePatchType ))
459
+ })
460
+ It ("replaces incorrect fields in core types" , func () {
461
+ o1 := newTestDeployment ([]v1.Container {
462
+ {Name : "test1" },
463
+ })
464
+ o2 := & resource.Info {
465
+ Object : newTestDeployment ([]v1.Container {
466
+ {Name : "test2" },
467
+ }),
468
+ }
469
+ patch , patchType , err := createPatch (o1 , o2 )
470
+ Expect (err ).To (BeNil ())
471
+ Expect (string (patch )).To (Equal (`{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"test2"}],"containers":[{"name":"test2","resources":{}}]}}}}` ))
472
+ Expect (patchType ).To (Equal (apitypes .StrategicMergePatchType ))
473
+ })
474
+ It ("does not remove extra annotations in core types" , func () {
475
+ o1 := & appsv1.Deployment {
476
+ TypeMeta : metav1.TypeMeta {Kind : "Deployment" , APIVersion : "apps/v1" },
477
+ ObjectMeta : metav1.ObjectMeta {
478
+ Name : "test" ,
479
+ Namespace : "ns" ,
480
+ Annotations : map [string ]string {
481
+ "testannotation" : "testvalue" ,
482
+ },
483
+ },
484
+ Spec : appsv1.DeploymentSpec {},
485
+ }
486
+ o2 := & resource.Info {
487
+ Object : & appsv1.Deployment {
488
+ TypeMeta : metav1.TypeMeta {Kind : "Deployment" , APIVersion : "apps/v1" },
489
+ ObjectMeta : metav1.ObjectMeta {
490
+ Name : "test" ,
491
+ Namespace : "ns" ,
492
+ },
493
+ Spec : appsv1.DeploymentSpec {},
494
+ },
495
+ }
496
+ patch , patchType , err := createPatch (o1 , o2 )
497
+ Expect (err ).To (BeNil ())
498
+ Expect (string (patch )).To (Equal (`{}` ))
499
+ Expect (patchType ).To (Equal (apitypes .StrategicMergePatchType ))
500
+ })
501
+ })
502
+
503
+ var _ = Describe ("ownerPostRenderer" , func () {
504
+ var (
505
+ pr ownerPostRenderer
506
+ owner Object
507
+ )
508
+
509
+ BeforeEach (func () {
510
+ rm , err := apiutil .NewDynamicRESTMapper (cfg )
511
+ Expect (err ).To (BeNil ())
512
+
513
+ owner = newTestUnstructured ([]interface {}{
514
+ map [string ]interface {}{
515
+ "name" : "test1" ,
516
+ },
517
+ })
518
+ pr = ownerPostRenderer {
519
+ owner : owner ,
520
+ rm : rm ,
521
+ kubeClient : kube .New (newRESTClientGetter (cfg , rm , owner .GetNamespace ())),
522
+ }
523
+ })
524
+
525
+ It ("fails on invalid input" , func () {
526
+ _ , err := pr .Run (bytes .NewBufferString ("test" ))
527
+ Expect (err ).NotTo (BeNil ())
528
+ })
529
+ })
313
530
})
314
531
315
532
func manifestToObjects (manifest string ) []runtime.Object {
@@ -378,3 +595,37 @@ func verifyNoRelease(cl client.Client, ns string, name string, rel *release.Rele
378
595
}
379
596
})
380
597
}
598
+
599
+ func newTestUnstructured (containers []interface {}) * unstructured.Unstructured {
600
+ return & unstructured.Unstructured {
601
+ Object : map [string ]interface {}{
602
+ "kind" : "MyResource" ,
603
+ "apiVersion" : "myApi" ,
604
+ "metadata" : map [string ]interface {}{
605
+ "name" : "test" ,
606
+ "namespace" : "ns" ,
607
+ },
608
+ "spec" : map [string ]interface {}{
609
+ "template" : map [string ]interface {}{
610
+ "spec" : map [string ]interface {}{
611
+ "containers" : containers ,
612
+ },
613
+ },
614
+ },
615
+ },
616
+ }
617
+ }
618
+
619
+ func newTestDeployment (containers []v1.Container ) * appsv1.Deployment {
620
+ return & appsv1.Deployment {
621
+ TypeMeta : metav1.TypeMeta {Kind : "Deployment" , APIVersion : "apps/v1" },
622
+ ObjectMeta : metav1.ObjectMeta {Name : "test" , Namespace : "ns" },
623
+ Spec : appsv1.DeploymentSpec {
624
+ Template : v1.PodTemplateSpec {
625
+ Spec : v1.PodSpec {
626
+ Containers : containers ,
627
+ },
628
+ },
629
+ },
630
+ }
631
+ }
0 commit comments