@@ -18,15 +18,20 @@ package resource
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"testing"
22
23
24
+ jsonpatch "github.com/evanphx/json-patch"
23
25
"github.com/google/go-cmp/cmp"
26
+ corev1 "k8s.io/api/core/v1"
24
27
kerrors "k8s.io/apimachinery/pkg/api/errors"
25
28
"k8s.io/apimachinery/pkg/api/meta"
26
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27
31
"k8s.io/apimachinery/pkg/runtime"
28
32
"k8s.io/apimachinery/pkg/runtime/schema"
29
33
"sigs.k8s.io/controller-runtime/pkg/client"
34
+ "sigs.k8s.io/yaml"
30
35
31
36
"github.com/crossplane/crossplane-runtime/pkg/errors"
32
37
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
@@ -54,6 +59,14 @@ func TestAPIPatchingApplicator(t *testing.T) {
54
59
fakeRESTMapper := meta .NewDefaultRESTMapper ([]schema.GroupVersion {gvk .GroupVersion ()})
55
60
fakeRESTMapper .AddSpecific (gvk , gvr , singular , meta .RESTScopeRoot )
56
61
62
+ // for additive merge patch option test
63
+ currentYAML := `
64
+ metadata:
65
+ resourceVersion: "42"
66
+ a: old
67
+ b: old
68
+ `
69
+
57
70
type args struct {
58
71
ctx context.Context
59
72
o client.Object
@@ -221,6 +234,63 @@ func TestAPIPatchingApplicator(t *testing.T) {
221
234
err : kerrors .NewConflict (schema.GroupResource {Group : "example.com" , Resource : "things" }, current .GetName (), errors .New (errOptimisticLock )),
222
235
},
223
236
},
237
+ "AdditiveMergePatch" : {
238
+ reason : "No error with the old additive behaviour if desired" ,
239
+ c : & test.MockClient {
240
+ MockGet : test .NewMockGetFn (nil , func (o client.Object ) error {
241
+ o .(* unstructured.Unstructured ).Object = map [string ]interface {}{}
242
+ return yaml .Unmarshal ([]byte (currentYAML ), & o .(* unstructured.Unstructured ).Object )
243
+ }),
244
+ MockPatch : func (_ context.Context , o client.Object , patch client.Patch , _ ... client.PatchOption ) error {
245
+ bs , err := patch .Data (o )
246
+ if err != nil {
247
+ return err
248
+ }
249
+ currentJSON , err := yaml .YAMLToJSON ([]byte (currentYAML ))
250
+ if err != nil {
251
+ return err
252
+ }
253
+ patched , err := jsonpatch .MergePatch (currentJSON , bs )
254
+ if err != nil {
255
+ return err
256
+ }
257
+ o .(* unstructured.Unstructured ).Object = map [string ]interface {}{}
258
+ if err := json .Unmarshal (patched , & o .(* unstructured.Unstructured ).Object ); err != nil {
259
+ return err
260
+ }
261
+ o .SetResourceVersion ("43" )
262
+ return nil
263
+ },
264
+ MockGroupVersionKindFor : test .NewMockGroupVersionKindForFn (nil , gvk ),
265
+ MockRESTMapper : test .NewMockRESTMapperFn (fakeRESTMapper ),
266
+ },
267
+ args : args {
268
+ o : & unstructured.Unstructured {
269
+ Object : map [string ]interface {}{
270
+ "kind" : "Thing" ,
271
+ "metadata" : map [string ]interface {}{
272
+ "resourceVersion" : "42" ,
273
+ },
274
+ "b" : "changed" ,
275
+ "c" : "added" ,
276
+ },
277
+ },
278
+ ao : []ApplyOption {AdditiveMergePatchApplyOption },
279
+ },
280
+ want : want {
281
+ o : & unstructured.Unstructured {
282
+ Object : map [string ]interface {}{
283
+ "kind" : "Thing" ,
284
+ "metadata" : map [string ]interface {}{
285
+ "resourceVersion" : "43" ,
286
+ },
287
+ "a" : "old" ,
288
+ "b" : "changed" ,
289
+ "c" : "added" ,
290
+ },
291
+ },
292
+ },
293
+ },
224
294
}
225
295
226
296
for name , tc := range cases {
@@ -518,3 +588,137 @@ func TestAPIFinalizerAdder(t *testing.T) {
518
588
})
519
589
}
520
590
}
591
+
592
+ func TestAdditiveMergePatchApplyOption (t * testing.T ) {
593
+ type args struct {
594
+ current runtime.Object
595
+ desired runtime.Object
596
+ }
597
+ type want struct {
598
+ err error
599
+ current runtime.Object
600
+ desired runtime.Object
601
+ }
602
+ tests := []struct {
603
+ name string
604
+ args args
605
+ want want
606
+ }{
607
+ {
608
+ name : "equal unstructed" ,
609
+ args : args {
610
+ current : & unstructured.Unstructured {Object : map [string ]interface {}{
611
+ "kind" : "Thing" ,
612
+ "a" : "foo" ,
613
+ }},
614
+ desired : & unstructured.Unstructured {Object : map [string ]interface {}{
615
+ "kind" : "Thing" ,
616
+ "a" : "foo" ,
617
+ }},
618
+ },
619
+ want : want {
620
+ current : & unstructured.Unstructured {Object : map [string ]interface {}{
621
+ "kind" : "Thing" ,
622
+ "a" : "foo" ,
623
+ }},
624
+ desired : & unstructured.Unstructured {Object : map [string ]interface {}{
625
+ "kind" : "Thing" ,
626
+ "a" : "foo" ,
627
+ }},
628
+ },
629
+ },
630
+ {
631
+ name : "overlapping unstructed" ,
632
+ args : args {
633
+ current : & unstructured.Unstructured {Object : map [string ]interface {}{
634
+ "kind" : "Thing" ,
635
+ "a" : "foo" ,
636
+ "b" : "foo" ,
637
+ "c" : "foo" ,
638
+ }},
639
+ desired : & unstructured.Unstructured {Object : map [string ]interface {}{
640
+ "kind" : "Thing" ,
641
+ "a" : "foo" ,
642
+ "b" : "bar" ,
643
+ "d" : "bar" ,
644
+ }},
645
+ },
646
+ want : want {
647
+ current : & unstructured.Unstructured {Object : map [string ]interface {}{
648
+ "kind" : "Thing" ,
649
+ "a" : "foo" ,
650
+ "b" : "foo" ,
651
+ "c" : "foo" ,
652
+ }},
653
+ desired : & unstructured.Unstructured {Object : map [string ]interface {}{
654
+ "kind" : "Thing" ,
655
+ "a" : "foo" ,
656
+ "b" : "bar" ,
657
+ "c" : "foo" ,
658
+ "d" : "bar" ,
659
+ }},
660
+ },
661
+ },
662
+ {
663
+ name : "equal typed" ,
664
+ args : args {
665
+ current : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
666
+ "a" : "foo" ,
667
+ }}},
668
+ desired : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
669
+ "a" : "foo" ,
670
+ }}},
671
+ },
672
+ want : want {
673
+ current : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
674
+ "a" : "foo" ,
675
+ }}},
676
+ desired : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
677
+ "a" : "foo" ,
678
+ }}},
679
+ },
680
+ },
681
+ {
682
+ name : "overlapping typed" ,
683
+ args : args {
684
+ current : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
685
+ "a" : "foo" ,
686
+ "b" : "foo" ,
687
+ "c" : "foo" ,
688
+ }}},
689
+ desired : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
690
+ "a" : "foo" ,
691
+ "b" : "bar" ,
692
+ "d" : "bar" ,
693
+ }}},
694
+ },
695
+ want : want {
696
+ current : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
697
+ "a" : "foo" ,
698
+ "b" : "foo" ,
699
+ "c" : "foo" ,
700
+ }}},
701
+ desired : & corev1.ConfigMap {ObjectMeta : metav1.ObjectMeta {Labels : map [string ]string {
702
+ "a" : "foo" ,
703
+ "b" : "bar" ,
704
+ "c" : "foo" ,
705
+ "d" : "bar" ,
706
+ }}},
707
+ },
708
+ },
709
+ }
710
+ for _ , tt := range tests {
711
+ t .Run (tt .name , func (t * testing.T ) {
712
+ err := AdditiveMergePatchApplyOption (context .Background (), tt .args .current , tt .args .desired )
713
+ if diff := cmp .Diff (tt .want .err , err , test .EquateErrors ()); diff != "" {
714
+ t .Errorf ("AdditiveMergePatchApplyOption() error = %v, wantErr %v" , err , tt .want .err )
715
+ }
716
+ if diff := cmp .Diff (tt .want .current , tt .args .current ); diff != "" {
717
+ t .Errorf ("AdditiveMergePatchApplyOption() current = %v, want %v" , tt .args .current , tt .want .current )
718
+ }
719
+ if diff := cmp .Diff (tt .want .desired , tt .args .desired ); diff != "" {
720
+ t .Errorf ("AdditiveMergePatchApplyOption() current = %v, want %v" , tt .args .desired , tt .want .desired )
721
+ }
722
+ })
723
+ }
724
+ }
0 commit comments