diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 24663107f387..4ad837260d03 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -591,11 +591,11 @@ func patchResource( if err != nil { return nil, err } - currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj, strategicpatch.SMPatchVersionLatest) + currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj) if err != nil { return nil, err } - originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj, strategicpatch.SMPatchVersionLatest) + originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj) if err != nil { return nil, err } diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 577092e16ad6..fe85840541ce 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -213,7 +213,7 @@ func (tc *patchTestCase) Run(t *testing.T) { continue case api.StrategicMergePatchType: - patch, err = strategicpatch.CreateStrategicMergePatch(originalObjJS, changedJS, versionedObj, strategicpatch.SMPatchVersionLatest) + patch, err = strategicpatch.CreateStrategicMergePatch(originalObjJS, changedJS, versionedObj) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) return diff --git a/pkg/client/record/events_cache.go b/pkg/client/record/events_cache.go index 5d988e237a29..8ff65776cb11 100644 --- a/pkg/client/record/events_cache.go +++ b/pkg/client/record/events_cache.go @@ -244,8 +244,7 @@ func (e *eventLogger) eventObserve(newEvent *api.Event) (*api.Event, []byte, err newData, _ := json.Marshal(event) oldData, _ := json.Marshal(eventCopy2) - // Defaulting to SMPatchVersion_1_5 is safe, since we only update Count and LastTimestamp, and none of them has list of primitives - patch, err = strategicpatch.CreateStrategicMergePatch(oldData, newData, event, strategicpatch.SMPatchVersion_1_5) + patch, err = strategicpatch.CreateStrategicMergePatch(oldData, newData, event) } // record our new observation diff --git a/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go b/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go index 32a9e1f46bd3..0cca9f1464c9 100644 --- a/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go +++ b/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go @@ -107,9 +107,8 @@ func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error { err) } - // Defaulting to SMPatchVersion_1_5 is safe, since updateNodeStatus doesn't update any lists of primitives patchBytes, err := - strategicpatch.CreateStrategicMergePatch(oldData, newData, node, strategicpatch.SMPatchVersion_1_5) + strategicpatch.CreateStrategicMergePatch(oldData, newData, node) if err != nil { return fmt.Errorf( "failed to CreateStrategicMergePatch for node %q. %v", diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index f82463d870cd..70a3d0f4cf4a 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -191,11 +191,9 @@ go_test( "//pkg/runtime/serializer/streaming:go_default_library", "//pkg/types:go_default_library", "//pkg/util/intstr:go_default_library", - "//pkg/util/strategicpatch:go_default_library", "//pkg/util/strings:go_default_library", "//pkg/util/term:go_default_library", "//pkg/util/wait:go_default_library", - "//pkg/version:go_default_library", "//pkg/watch:go_default_library", "//pkg/watch/versioned:go_default_library", "//vendor:github.com/spf13/cobra", diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 552b13abed94..6562ab5a6eea 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -239,8 +239,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro if err != nil { return err } - // Defaulting to SMPatchVersion_1_5 is safe, since it just update the annotation which is a map[string]string - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj, strategicpatch.SMPatchVersion_1_5) + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) createdPatch := err == nil if err != nil { glog.V(2).Infof("couldn't compute patch: %v", err) diff --git a/pkg/kubectl/cmd/annotate_test.go b/pkg/kubectl/cmd/annotate_test.go index e16e1c30b1d6..c1c8661f3411 100644 --- a/pkg/kubectl/cmd/annotate_test.go +++ b/pkg/kubectl/cmd/annotate_test.go @@ -24,6 +24,8 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/restclient/fake" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/runtime" @@ -392,7 +394,7 @@ func TestAnnotateErrors(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdAnnotate(f, buf) @@ -430,12 +432,6 @@ func TestAnnotateObject(t *testing.T) { switch req.Method { case "GET": switch req.URL.Path { - case "/version": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case "/namespaces/test/pods/foo": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil default: @@ -457,7 +453,7 @@ func TestAnnotateObject(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdAnnotate(f, buf) @@ -486,12 +482,6 @@ func TestAnnotateObjectFromFile(t *testing.T) { switch req.Method { case "GET": switch req.URL.Path { - case "/version": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case "/namespaces/test/replicationcontrollers/cassandra": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil default: @@ -513,7 +503,7 @@ func TestAnnotateObjectFromFile(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdAnnotate(f, buf) @@ -542,7 +532,7 @@ func TestAnnotateLocal(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdAnnotate(f, buf) @@ -572,12 +562,6 @@ func TestAnnotateMultipleObjects(t *testing.T) { switch req.Method { case "GET": switch req.URL.Path { - case "/version": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil default: @@ -601,7 +585,7 @@ func TestAnnotateMultipleObjects(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdAnnotate(f, buf) diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index 620e68c5147d..f31c2a479ca2 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -531,23 +531,13 @@ func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, names } // Compute a three way strategic merge patch to send to server. - patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite, strategicpatch.SMPatchVersion_1_5) - // If creating a patch fails, retrying with SMPatchVersion_1_0 is not helpful. So we return the error. + patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite) if err != nil { format := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:" return nil, cmdutil.AddSourceToErr(fmt.Sprintf(format, original, modified, current), source, err) } + _, err = p.helper.Patch(namespace, name, api.StrategicMergePatchType, patch) - if errors.IsInternalError(err) { - // Retry SMPatchVersion_1_0 when applying the SMPatchVersion_1_5 patch returns an Internal Error (500). - // Because the failure may be due to the server not supporting the SMPatchVersion_1_5 patch. - patch, err = strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite, strategicpatch.SMPatchVersion_1_0) - if err != nil { - format := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:" - return nil, cmdutil.AddSourceToErr(fmt.Sprintf(format, original, modified, current), source, err) - } - _, err = p.helper.Patch(namespace, name, api.StrategicMergePatchType, patch) - } return patch, err } diff --git a/pkg/kubectl/cmd/apply_test.go b/pkg/kubectl/cmd/apply_test.go index 2918f7894da0..d3803638efd2 100644 --- a/pkg/kubectl/cmd/apply_test.go +++ b/pkg/kubectl/cmd/apply_test.go @@ -23,7 +23,6 @@ import ( "io/ioutil" "net/http" "os" - "strings" "testing" "github.com/spf13/cobra" @@ -38,7 +37,6 @@ import ( cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/strategicpatch" ) func TestApplyExtraArgsFail(t *testing.T) { @@ -144,58 +142,6 @@ func readAndAnnotateService(t *testing.T, filename string) (string, []byte) { return annotateRuntimeObject(t, svc1, svc2, "Service") } -func setFinalizersRuntimeObject(t *testing.T, originalObj, currentObj runtime.Object) (string, []byte) { - originalAccessor, err := meta.Accessor(originalObj) - if err != nil { - t.Fatal(err) - } - - originalFinalizers := []string{"a/a"} - originalAccessor.SetFinalizers(originalFinalizers) - original, err := runtime.Encode(testapi.Default.Codec(), originalObj) - if err != nil { - t.Fatal(err) - } - - currentAccessor, err := meta.Accessor(currentObj) - if err != nil { - t.Fatal(err) - } - - currentFinalizers := []string{"b/b"} - currentAccessor.SetFinalizers(currentFinalizers) - - currentAnnotations := currentAccessor.GetAnnotations() - if currentAnnotations == nil { - currentAnnotations = make(map[string]string) - } - currentAnnotations[annotations.LastAppliedConfigAnnotation] = string(original) - currentAccessor.SetAnnotations(currentAnnotations) - current, err := runtime.Encode(testapi.Default.Codec(), currentObj) - if err != nil { - t.Fatal(err) - } - - return currentAccessor.GetName(), current -} - -func readAndSetFinalizersReplicationController(t *testing.T, filename string) (string, []byte) { - rc1 := readReplicationControllerFromFile(t, filename) - rc2 := readReplicationControllerFromFile(t, filename) - name, rcBytes := setFinalizersRuntimeObject(t, rc1, rc2) - return name, rcBytes -} - -func isSMPatchVersion_1_5(t *testing.T, req *http.Request) bool { - patch, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatal(err) - } - - // SMPatchVersion_1_5 patch should has string "mergeprimitiveslist" - return strings.Contains(string(patch), strategicpatch.MergePrimitivesListDirective) -} - func validatePatchApplication(t *testing.T, req *http.Request) { patch, err := ioutil.ReadAll(req.Body) if err != nil { @@ -242,12 +188,6 @@ func TestApplyObject(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == pathRC && m == "GET": bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil @@ -262,62 +202,6 @@ func TestApplyObject(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdApply(f, buf) - cmd.Flags().Set("filename", filenameRC) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - expectRC := "replicationcontroller/" + nameRC + "\n" - if buf.String() != expectRC { - t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expectRC) - } -} - -func TestApplyRetryWithSMPatchVersion_1_5(t *testing.T) { - initTestErrorHandler(t) - nameRC, currentRC := readAndSetFinalizersReplicationController(t, filenameRC) - pathRC := "/namespaces/test/replicationcontrollers/" + nameRC - - firstPatch := true - retry := false - f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - NegotiatedSerializer: ns, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == pathRC && m == "GET": - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil - case p == pathRC && m == "PATCH": - if firstPatch { - if !isSMPatchVersion_1_5(t, req) { - t.Fatalf("apply didn't try to send SMPatchVersion_1_5 for the first time") - } - firstPatch = false - statusErr := kubeerr.NewInternalError(fmt.Errorf("Server encountered internal error.")) - bodyBytes, _ := json.Marshal(statusErr) - bodyErr := ioutil.NopCloser(bytes.NewReader(bodyBytes)) - return &http.Response{StatusCode: http.StatusInternalServerError, Header: defaultHeader(), Body: bodyErr}, nil - } - retry = true - if isSMPatchVersion_1_5(t, req) { - t.Fatalf("apply didn't try to send SMPatchVersion_1_0 after SMPatchVersion_1_5 patch encounter an Internal Error (500)") - } - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdApply(f, buf) @@ -325,10 +209,6 @@ func TestApplyRetryWithSMPatchVersion_1_5(t *testing.T) { cmd.Flags().Set("output", "name") cmd.Run(cmd, []string{}) - if !retry { - t.Fatalf("apply didn't retry when get Internal Error (500)") - } - // uses the name from the file, not the response expectRC := "replicationcontroller/" + nameRC + "\n" if buf.String() != expectRC { @@ -350,12 +230,6 @@ func TestApplyRetry(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == pathRC && m == "GET": getCount++ bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) @@ -379,7 +253,6 @@ func TestApplyRetry(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdApply(f, buf) @@ -409,12 +282,6 @@ func TestApplyNonExistObject(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == "/api/v1/namespaces/test" && m == "GET": return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader(nil))}, nil case p == pathNameRC && m == "GET": @@ -429,7 +296,6 @@ func TestApplyNonExistObject(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdApply(f, buf) @@ -465,12 +331,6 @@ func testApplyMultipleObjects(t *testing.T, asList bool) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == pathRC && m == "GET": bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil @@ -492,7 +352,6 @@ func testApplyMultipleObjects(t *testing.T, asList bool) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdApply(f, buf) diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 869934bbddae..d5a3bdae566a 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -39,13 +39,8 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strings" - "k8s.io/kubernetes/pkg/version" ) -var serverVersion_1_5_0 = version.Info{ - GitVersion: "v1.5.0", -} - func initTestErrorHandler(t *testing.T) { cmdutil.BehaviorOnFatal(func(str string, code int) { t.Errorf("Error running command (exit code %d): %s", code, str) diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index 36bc46e7956c..fa01f7c00bf5 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -291,7 +291,7 @@ func runEdit(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args switch editMode { case NormalEditMode: - err = visitToPatch(originalObj, updates, f, mapper, resourceMapper, encoder, out, errOut, defaultVersion, &results, file) + err = visitToPatch(originalObj, updates, mapper, resourceMapper, encoder, out, errOut, defaultVersion, &results, file) case EditBeforeCreateMode: err = visitToCreate(updates, mapper, resourceMapper, out, errOut, defaultVersion, &results, file) default: @@ -415,15 +415,7 @@ func getMapperAndResult(f cmdutil.Factory, args []string, options *resource.File return mapper, resourceMapper, r, cmdNamespace, err } -func visitToPatch(originalObj runtime.Object, updates *resource.Info, - f cmdutil.Factory, - mapper meta.RESTMapper, resourceMapper *resource.Mapper, - encoder runtime.Encoder, - out, errOut io.Writer, - defaultVersion unversioned.GroupVersion, - results *editResults, - file string) error { - +func visitToPatch(originalObj runtime.Object, updates *resource.Info, mapper meta.RESTMapper, resourceMapper *resource.Mapper, encoder runtime.Encoder, out, errOut io.Writer, defaultVersion unversioned.GroupVersion, results *editResults, file string) error { patchVisitor := resource.NewFlattenListVisitor(updates, resourceMapper) err := patchVisitor.Visit(func(info *resource.Info, incomingErr error) error { currOriginalObj := originalObj @@ -486,8 +478,7 @@ func visitToPatch(originalObj runtime.Object, updates *resource.Info, preconditions := []strategicpatch.PreconditionFunc{strategicpatch.RequireKeyUnchanged("apiVersion"), strategicpatch.RequireKeyUnchanged("kind"), strategicpatch.RequireMetadataKeyUnchanged("name")} - patch, err := strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, currOriginalObj, strategicpatch.SMPatchVersion_1_5, preconditions...) - // If creating a patch fails, retrying with SMPatchVersion_1_0 is not helpful. So we return the error. + patch, err := strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, currOriginalObj, preconditions...) if err != nil { glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) if strategicpatch.IsPreconditionFailed(err) { @@ -499,20 +490,8 @@ func visitToPatch(originalObj runtime.Object, updates *resource.Info, results.version = defaultVersion patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) if err != nil { - // Retry SMPatchVersion_1_0 when applying the SMPatchVersion_1_5 patch returns an Internal Error (500). - // Because the failure may be due to the server not supporting the SMPatchVersion_1_5 patch. - if errors.IsInternalError(err) { - patch, err = strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, currOriginalObj, strategicpatch.SMPatchVersion_1_0) - if err != nil { - glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) - return err - } - patched, err = resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) - } - if err != nil { - fmt.Fprintln(out, results.addError(err, info)) - return nil - } + fmt.Fprintln(out, results.addError(err, info)) + return nil } info.Refresh(patched, true) cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, false, "edited") diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index d310be71f64a..99e853056d9d 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -246,8 +246,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error { if !reflect.DeepEqual(oldData, newData) { dataChangeMsg = "labeled" } - // Defaulting to SMPatchVersion_1_5 is safe, since we only update labels and change cause, and none of them has list of primitives - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj, strategicpatch.SMPatchVersion_1_5) + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) createdPatch := err == nil if err != nil { glog.V(2).Infof("couldn't compute patch: %v", err) diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index 39000a0accec..83a50cb1779e 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -354,12 +354,6 @@ func TestLabelForResourceFromFile(t *testing.T) { switch req.Method { case "GET": switch req.URL.Path { - case "/version": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case "/namespaces/test/replicationcontrollers/cassandra": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil default: @@ -381,7 +375,7 @@ func TestLabelForResourceFromFile(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) @@ -443,12 +437,6 @@ func TestLabelMultipleObjects(t *testing.T) { switch req.Method { case "GET": switch req.URL.Path { - case "/version": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil default: @@ -472,7 +460,7 @@ func TestLabelMultipleObjects(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() + tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdLabel(f, buf) diff --git a/pkg/kubectl/cmd/patch_test.go b/pkg/kubectl/cmd/patch_test.go index fc4109ec9e77..9bb8f98dbf35 100644 --- a/pkg/kubectl/cmd/patch_test.go +++ b/pkg/kubectl/cmd/patch_test.go @@ -34,12 +34,6 @@ func TestPatchObject(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil default: @@ -49,7 +43,6 @@ func TestPatchObject(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdPatch(f, buf) @@ -73,12 +66,6 @@ func TestPatchObjectFromFile(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { - case p == "/version" && m == "GET": - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil default: @@ -88,7 +75,6 @@ func TestPatchObjectFromFile(t *testing.T) { }), } tf.Namespace = "test" - tf.ClientConfig = defaultClientConfig() buf := bytes.NewBuffer([]byte{}) cmd := NewCmdPatch(f, buf) diff --git a/pkg/kubectl/cmd/rollout/BUILD b/pkg/kubectl/cmd/rollout/BUILD index 999e05e10b42..d90722d5113b 100644 --- a/pkg/kubectl/cmd/rollout/BUILD +++ b/pkg/kubectl/cmd/rollout/BUILD @@ -32,7 +32,6 @@ go_library( "//pkg/runtime:go_default_library", "//pkg/util/errors:go_default_library", "//pkg/util/interrupt:go_default_library", - "//pkg/util/strategicpatch:go_default_library", "//pkg/watch:go_default_library", "//vendor:github.com/renstrom/dedent", "//vendor:github.com/spf13/cobra", diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 427b8b7e642b..4e02d384eaf7 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -31,7 +31,6 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/util/strategicpatch" ) // PauseConfig is the start of the data required to perform the operation. As new fields are added, add them here instead of @@ -39,7 +38,6 @@ import ( type PauseConfig struct { resource.FilenameOptions - f cmdutil.Factory Pauser func(info *resource.Info) (bool, error) Mapper meta.RESTMapper Typer runtime.ObjectTyper @@ -101,7 +99,6 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i return cmdutil.UsageError(cmd, cmd.Use) } - o.f = f o.Mapper, o.Typer = f.Object() o.Encoder = f.JSONEncoder() @@ -135,8 +132,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i func (o PauseConfig) RunPause() error { allErrs := []error{} - // Defaulting to SMPatchVersion_1_5 is safe, since Pauser only update a boolean variable - for _, patch := range set.CalculatePatches(o.f, o.Infos, o.Encoder, strategicpatch.SMPatchVersion_1_5, o.Pauser) { + for _, patch := range set.CalculatePatches(o.Infos, o.Encoder, o.Pauser) { info := patch.Info if patch.Err != nil { allErrs = append(allErrs, fmt.Errorf("error: %s %q %v", info.Mapping.Resource, info.Name, patch.Err)) diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index af28cba335f1..56623934083a 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -31,7 +31,6 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/util/strategicpatch" ) // ResumeConfig is the start of the data required to perform the operation. As new fields are added, add them here instead of @@ -39,7 +38,6 @@ import ( type ResumeConfig struct { resource.FilenameOptions - f cmdutil.Factory Resumer func(object *resource.Info) (bool, error) Mapper meta.RESTMapper Typer runtime.ObjectTyper @@ -99,7 +97,6 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out return cmdutil.UsageError(cmd, cmd.Use) } - o.f = f o.Mapper, o.Typer = f.Object() o.Encoder = f.JSONEncoder() @@ -139,8 +136,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out func (o ResumeConfig) RunResume() error { allErrs := []error{} - // Defaulting to SMPatchVersion_1_5 is safe, since Resumer only update a boolean variable - for _, patch := range set.CalculatePatches(o.f, o.Infos, o.Encoder, strategicpatch.SMPatchVersion_1_5, o.Resumer) { + for _, patch := range set.CalculatePatches(o.Infos, o.Encoder, o.Resumer) { info := patch.Info if patch.Err != nil { diff --git a/pkg/kubectl/cmd/set/helper.go b/pkg/kubectl/cmd/set/helper.go index b95abd17e87c..7b4a380a5c53 100644 --- a/pkg/kubectl/cmd/set/helper.go +++ b/pkg/kubectl/cmd/set/helper.go @@ -23,7 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strategicpatch" @@ -61,7 +61,7 @@ func handlePodUpdateError(out io.Writer, err error, resource string) { return } } else { - if ok := cmdutil.PrintErrorWithCauses(err, out); ok { + if ok := kcmdutil.PrintErrorWithCauses(err, out); ok { return } } @@ -120,10 +120,7 @@ type Patch struct { // CalculatePatches calls the mutation function on each provided info object, and generates a strategic merge patch for // the changes in the object. Encoder must be able to encode the info into the appropriate destination type. If mutateFn // returns false, the object is not included in the final list of patches. -// If local is true, it will be default to use SMPatchVersionLatest to calculate a patch without contacting the server to -// get the server supported SMPatchVersion. If you are using a patch's Patch field generated in local mode, be careful. -// If local is false, it will talk to the server to check which StategicMergePatchVersion to use. -func CalculatePatches(f cmdutil.Factory, infos []*resource.Info, encoder runtime.Encoder, smPatchVersion strategicpatch.StrategicMergePatchVersion, mutateFn func(*resource.Info) (bool, error)) []*Patch { +func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn func(*resource.Info) (bool, error)) []*Patch { var patches []*Patch for _, info := range infos { patch := &Patch{Info: info} @@ -159,7 +156,7 @@ func CalculatePatches(f cmdutil.Factory, infos []*resource.Info, encoder runtime continue } - patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned, smPatchVersion) + patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned) } return patches } diff --git a/pkg/kubectl/cmd/set/set_image.go b/pkg/kubectl/cmd/set/set_image.go index a0e6486235ae..5baa00b3d43e 100644 --- a/pkg/kubectl/cmd/set/set_image.go +++ b/pkg/kubectl/cmd/set/set_image.go @@ -28,7 +28,6 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/util/strategicpatch" ) // ImageOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of @@ -36,7 +35,6 @@ import ( type ImageOptions struct { resource.FilenameOptions - f cmdutil.Factory Mapper meta.RESTMapper Typer runtime.ObjectTyper Infos []*resource.Info @@ -110,7 +108,6 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command { } func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { - o.f = f o.Mapper, o.Typer = f.Object() o.UpdatePodSpecForObject = f.UpdatePodSpecForObject o.Encoder = f.JSONEncoder() @@ -164,8 +161,8 @@ func (o *ImageOptions) Validate() error { func (o *ImageOptions) Run() error { allErrs := []error{} - // Defauting to SMPatchVersion_1_5, since the func passed in doesn't update any lists of primitive - patches := CalculatePatches(o.f, o.Infos, o.Encoder, strategicpatch.SMPatchVersion_1_5, func(info *resource.Info) (bool, error) { + + patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) (bool, error) { transformed := false _, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error { for name, image := range o.ContainerImages { diff --git a/pkg/kubectl/cmd/set/set_resources.go b/pkg/kubectl/cmd/set/set_resources.go index 4601041b9b23..9d991242746b 100644 --- a/pkg/kubectl/cmd/set/set_resources.go +++ b/pkg/kubectl/cmd/set/set_resources.go @@ -31,7 +31,6 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/util/strategicpatch" ) var ( @@ -61,7 +60,6 @@ var ( type ResourcesOptions struct { resource.FilenameOptions - f cmdutil.Factory Mapper meta.RESTMapper Typer runtime.ObjectTyper Infos []*resource.Info @@ -126,7 +124,6 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra. } func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { - o.f = f o.Mapper, o.Typer = f.Object() o.UpdatePodSpecForObject = f.UpdatePodSpecForObject o.Encoder = f.JSONEncoder() @@ -177,8 +174,7 @@ func (o *ResourcesOptions) Validate() error { func (o *ResourcesOptions) Run() error { allErrs := []error{} - // Defauting to SMPatchVersion_1_5, since the func passed in doesn't update any lists of primitive - patches := CalculatePatches(o.f, o.Infos, o.Encoder, strategicpatch.SMPatchVersion_1_5, func(info *resource.Info) (bool, error) { + patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) (bool, error) { transformed := false _, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error { containers, _ := selectContainers(spec.Containers, o.ContainerSelector) diff --git a/pkg/kubectl/cmd/taint.go b/pkg/kubectl/cmd/taint.go index c57ba9013935..142137f587d3 100644 --- a/pkg/kubectl/cmd/taint.go +++ b/pkg/kubectl/cmd/taint.go @@ -343,8 +343,7 @@ func (o TaintOptions) RunTaint() error { if err != nil { return err } - // Defaulting to SMPatchVersion_1_5 is safe, since we don't update list of primitives. - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj, strategicpatch.SMPatchVersion_1_5) + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) createdPatch := err == nil if err != nil { glog.V(2).Infof("couldn't compute patch: %v", err) diff --git a/pkg/kubectl/cmd/taint_test.go b/pkg/kubectl/cmd/taint_test.go index f7a6fcf634fa..32d693851fbc 100644 --- a/pkg/kubectl/cmd/taint_test.go +++ b/pkg/kubectl/cmd/taint_test.go @@ -252,6 +252,7 @@ func TestTaint(t *testing.T) { for _, test := range tests { oldNode, expectNewNode := generateNodeAndTaintedNode(test.oldTaints, test.newTaints) + new_node := &api.Node{} tainted := false f, tf, codec, ns := cmdtesting.NewAPIFactory() @@ -261,12 +262,6 @@ func TestTaint(t *testing.T) { Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} switch { - case m.isFor("GET", "/version"): - resp, err := genResponseWithJsonEncodedBody(serverVersion_1_5_0) - if err != nil { - t.Fatalf("error: failed to generate server version response: %#v\n", serverVersion_1_5_0) - } - return resp, nil case m.isFor("GET", "/nodes/node-name"): return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, oldNode)}, nil case m.isFor("PATCH", "/nodes/node-name"), m.isFor("PUT", "/nodes/node-name"): diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index ad85354cca43..d5452846c6bc 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -427,12 +427,8 @@ func (f *fakeAPIFactory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTy return cmdutil.NewShortcutExpander(mapper, nil), typer, nil } -func (f *fakeAPIFactory) Decoder(toInternal bool) runtime.Decoder { - if toInternal { - return api.Codecs.UniversalDecoder() - } else { - return api.Codecs.UniversalDeserializer() - } +func (f *fakeAPIFactory) Decoder(bool) runtime.Decoder { + return testapi.Default.Codec() } func (f *fakeAPIFactory) JSONEncoder() runtime.Encoder { diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 0e1af7815d82..01e34a64edf1 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -533,8 +533,7 @@ func ChangeResourcePatch(info *resource.Info, changeCause string) ([]byte, error if err != nil { return nil, err } - // Using SMPatchVersion_1_5, since RecordChangeCause() just update the annotation which is a map[string]string - return strategicpatch.CreateTwoWayMergePatch(oldData, newData, info.Object, strategicpatch.SMPatchVersion_1_5) + return strategicpatch.CreateTwoWayMergePatch(oldData, newData, info.Object) } // containsChangeCause checks if input resource info contains change-cause annotation. diff --git a/pkg/util/strategicpatch/patch.go b/pkg/util/strategicpatch/patch.go index b9c2c3ab882a..66a33f7ad08a 100644 --- a/pkg/util/strategicpatch/patch.go +++ b/pkg/util/strategicpatch/patch.go @@ -38,20 +38,11 @@ import ( // Some of the content of this package was borrowed with minor adaptations from // evanphx/json-patch and openshift/origin. -type StrategicMergePatchVersion string - const ( - directiveMarker = "$patch" - deleteDirective = "delete" - replaceDirective = "replace" - mergeDirective = "merge" - MergePrimitivesListDirective = "mergeprimitiveslist" - - // different versions of StrategicMergePatch - SMPatchVersion_1_0 StrategicMergePatchVersion = "v1.0.0" - SMPatchVersion_1_5 StrategicMergePatchVersion = "v1.5.0" - Unknown StrategicMergePatchVersion = "Unknown" - SMPatchVersionLatest = SMPatchVersion_1_5 + directiveMarker = "$patch" + deleteDirective = "delete" + replaceDirective = "replace" + mergeDirective = "merge" ) // IsPreconditionFailed returns true if the provided error indicates @@ -96,7 +87,6 @@ func IsConflict(err error) bool { var errBadJSONDoc = fmt.Errorf("Invalid JSON document") var errNoListOfLists = fmt.Errorf("Lists of lists are not supported") -var errNoElementsInSlice = fmt.Errorf("no elements in any of the given slices") // The following code is adapted from github.com/openshift/origin/pkg/util/jsonmerge. // Instead of defining a Delta that holds an original, a patch and a set of preconditions, @@ -143,15 +133,15 @@ func RequireMetadataKeyUnchanged(key string) PreconditionFunc { } // Deprecated: Use the synonym CreateTwoWayMergePatch, instead. -func CreateStrategicMergePatch(original, modified []byte, dataStruct interface{}, smPatchVersion StrategicMergePatchVersion) ([]byte, error) { - return CreateTwoWayMergePatch(original, modified, dataStruct, smPatchVersion) +func CreateStrategicMergePatch(original, modified []byte, dataStruct interface{}) ([]byte, error) { + return CreateTwoWayMergePatch(original, modified, dataStruct) } // CreateTwoWayMergePatch creates a patch that can be passed to StrategicMergePatch from an original // document and a modified document, which are passed to the method as json encoded content. It will // return a patch that yields the modified document when applied to the original document, or an error // if either of the two documents is invalid. -func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, smPatchVersion StrategicMergePatchVersion, fns ...PreconditionFunc) ([]byte, error) { +func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...PreconditionFunc) ([]byte, error) { originalMap := map[string]interface{}{} if len(original) > 0 { if err := json.Unmarshal(original, &originalMap); err != nil { @@ -171,7 +161,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, s return nil, err } - patchMap, err := diffMaps(originalMap, modifiedMap, t, false, false, smPatchVersion) + patchMap, err := diffMaps(originalMap, modifiedMap, t, false, false) if err != nil { return nil, err } @@ -187,7 +177,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, s } // Returns a (recursive) strategic merge patch that yields modified when applied to original. -func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (map[string]interface{}, error) { +func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) { patch := map[string]interface{}{} if t.Kind() == reflect.Ptr { t = t.Elem() @@ -240,7 +230,7 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC return nil, err } - patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion) + patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions) if err != nil { return nil, err } @@ -258,25 +248,13 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC } if fieldPatchStrategy == mergeDirective { - patchValue, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion) + patchValue, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions) if err != nil { return nil, err } - if patchValue == nil { - continue - } - switch typedPatchValue := patchValue.(type) { - case []interface{}: - if len(typedPatchValue) > 0 { - patch[key] = typedPatchValue - } - case map[string]interface{}: - if len(typedPatchValue) > 0 { - patch[key] = typedPatchValue - } - default: - return nil, fmt.Errorf("invalid type of patch: %v", reflect.TypeOf(patchValue)) + if len(patchValue) > 0 { + patch[key] = patchValue } continue @@ -306,7 +284,7 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC // Returns a (recursive) strategic merge patch that yields modified when applied to original, // for a pair of lists with merge semantics. -func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (interface{}, error) { +func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) { if len(original) == 0 { if len(modified) == 0 || ignoreChangesAndAdditions { return nil, nil @@ -320,14 +298,12 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string return nil, err } - var patch interface{} + var patch []interface{} if elementType.Kind() == reflect.Map { - patch, err = diffListsOfMaps(original, modified, t, mergeKey, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion) - } else if elementType.Kind() == reflect.Slice { - err = errNoListOfLists - } else { - patch, err = diffListsOfScalars(original, modified, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion) + patch, err = diffListsOfMaps(original, modified, t, mergeKey, ignoreChangesAndAdditions, ignoreDeletions) + } else if !ignoreChangesAndAdditions { + patch, err = diffListsOfScalars(original, modified) } if err != nil { @@ -339,23 +315,8 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string // Returns a (recursive) strategic merge patch that yields modified when applied to original, // for a pair of lists of scalars with merge semantics. -func diffListsOfScalars(original, modified []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (interface{}, error) { - originalScalars := uniqifyAndSortScalars(original) - modifiedScalars := uniqifyAndSortScalars(modified) - - switch smPatchVersion { - case SMPatchVersion_1_5: - return diffListsOfScalarsIntoMap(originalScalars, modifiedScalars, ignoreChangesAndAdditions, ignoreDeletions) - case SMPatchVersion_1_0: - return diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars, ignoreChangesAndAdditions, ignoreDeletions) - default: - return nil, fmt.Errorf("Unknown StrategicMergePatchVersion: %v", smPatchVersion) - } -} - -func diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) { - originalIndex, modifiedIndex := 0, 0 - if len(modifiedScalars) == 0 { +func diffListsOfScalars(original, modified []interface{}) ([]interface{}, error) { + if len(modified) == 0 { // There is no need to check the length of original because there is no way to create // a patch that deletes a scalar from a list of scalars with merge semantics. return nil, nil @@ -363,14 +324,18 @@ func diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars []interface{}, patch := []interface{}{} + originalScalars := uniqifyAndSortScalars(original) + modifiedScalars := uniqifyAndSortScalars(modified) + originalIndex, modifiedIndex := 0, 0 + loopB: for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ { for ; originalIndex < len(originalScalars); originalIndex++ { - originalString := fmt.Sprintf("%v", originalScalars[originalIndex]) - modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex]) + originalString := fmt.Sprintf("%v", original[originalIndex]) + modifiedString := fmt.Sprintf("%v", modified[modifiedIndex]) if originalString >= modifiedString { if originalString != modifiedString { - patch = append(patch, modifiedScalars[modifiedIndex]) + patch = append(patch, modified[modifiedIndex]) } continue loopB @@ -384,57 +349,7 @@ loopB: // Add any remaining items found only in modified for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ { - patch = append(patch, modifiedScalars[modifiedIndex]) - } - - return patch, nil -} - -func diffListsOfScalarsIntoMap(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) { - originalIndex, modifiedIndex := 0, 0 - patch := map[string]interface{}{} - patch[directiveMarker] = MergePrimitivesListDirective - - for originalIndex < len(originalScalars) && modifiedIndex < len(modifiedScalars) { - originalString := fmt.Sprintf("%v", originalScalars[originalIndex]) - modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex]) - - // objects are identical - if originalString == modifiedString { - originalIndex++ - modifiedIndex++ - continue - } - - if originalString > modifiedString { - if !ignoreChangesAndAdditions { - modifiedValue := modifiedScalars[modifiedIndex] - patch[modifiedString] = modifiedValue - } - modifiedIndex++ - } else { - if !ignoreDeletions { - patch[originalString] = nil - } - originalIndex++ - } - } - - // Delete any remaining items found only in original - if !ignoreDeletions { - for ; originalIndex < len(originalScalars); originalIndex++ { - originalString := fmt.Sprintf("%v", originalScalars[originalIndex]) - patch[originalString] = nil - } - } - - // Add any remaining items found only in modified - if !ignoreChangesAndAdditions { - for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ { - modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex]) - modifiedValue := modifiedScalars[modifiedIndex] - patch[modifiedString] = modifiedValue - } + patch = append(patch, modified[modifiedIndex]) } return patch, nil @@ -445,7 +360,7 @@ var errBadArgTypeFmt = "expected a %s, but received a %s" // Returns a (recursive) strategic merge patch that yields modified when applied to original, // for a pair of lists of maps with merge semantics. -func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) ([]interface{}, error) { +func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) { patch := make([]interface{}, 0) originalSorted, err := sortMergeListsByNameArray(original, t, mergeKey, false) @@ -491,7 +406,7 @@ loopB: if originalString >= modifiedString { if originalString == modifiedString { // Merge key values are equal, so recurse - patchValue, err := diffMaps(originalMap, modifiedMap, t, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion) + patchValue, err := diffMaps(originalMap, modifiedMap, t, ignoreChangesAndAdditions, ignoreDeletions) if err != nil { return nil, err } @@ -627,15 +542,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin return map[string]interface{}{}, nil } - if v == MergePrimitivesListDirective { - // delete the directiveMarker's key-value pair to avoid delta map and delete map - // overlaping with each other when calculating a ThreeWayDiff for list of Primitives. - // Otherwise, the overlaping will cause it calling LookupPatchMetadata() which will - // return an error since the metadata shows it's a slice but it is actually a map. - delete(original, directiveMarker) - } else { - return nil, fmt.Errorf(errBadPatchTypeFmt, v, patch) - } + return nil, fmt.Errorf(errBadPatchTypeFmt, v, patch) } // nil is an accepted value for original to simplify logic in other places. @@ -671,9 +578,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin // If they're both maps or lists, recurse into the value. originalType := reflect.TypeOf(original[k]) patchType := reflect.TypeOf(patchV) - // check if we are trying to merge a slice with a map for list of primitives - isMergeSliceOfPrimitivesWithAPatchMap := originalType != nil && patchType != nil && originalType.Kind() == reflect.Slice && patchType.Kind() == reflect.Map - if originalType == patchType || isMergeSliceOfPrimitivesWithAPatchMap { + if originalType == patchType { // First find the fieldPatchStrategy and fieldPatchMergeKey. fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k) if err != nil { @@ -695,8 +600,9 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin if originalType.Kind() == reflect.Slice && fieldPatchStrategy == mergeDirective { elemType := fieldType.Elem() typedOriginal := original[k].([]interface{}) + typedPatch := patchV.([]interface{}) var err error - original[k], err = mergeSlice(typedOriginal, patchV, elemType, fieldPatchMergeKey) + original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey) if err != nil { return nil, err } @@ -717,34 +623,13 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin // Merge two slices together. Note: This may modify both the original slice and // the patch because getting a deep copy of a slice in golang is highly // non-trivial. -// The patch could be a map[string]interface{} representing a slice of primitives. -// If the patch map doesn't has the specific directiveMarker (MergePrimitivesListDirective), -// it returns an error. Please check patch_test.go and find the test case named -// "merge lists of scalars for list of primitives" to see what the patch looks like. -// Patch is still []interface{} for all the other types. -func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type, mergeKey string) ([]interface{}, error) { - t, err := sliceElementType(original) - if err != nil && err != errNoElementsInSlice { - return nil, err - } - - if patchMap, ok := patch.(map[string]interface{}); ok { - // We try to merge the original slice with a patch map only when the map has - // a specific directiveMarker. Otherwise, this patch will be treated as invalid. - if directiveValue, ok := patchMap[directiveMarker]; ok && directiveValue == MergePrimitivesListDirective { - return mergeSliceOfScalarsWithPatchMap(original, patchMap) - } else { - return nil, fmt.Errorf("Unable to merge a slice with an invalid map") - } - } - - typedPatch := patch.([]interface{}) - if len(original) == 0 && len(typedPatch) == 0 { +func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string) ([]interface{}, error) { + if len(original) == 0 && len(patch) == 0 { return original, nil } // All the values must be of the same type, but not a list. - t, err = sliceElementType(original, typedPatch) + t, err := sliceElementType(original, patch) if err != nil { return nil, err } @@ -753,7 +638,7 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type if t.Kind() != reflect.Map { // Maybe in the future add a "concat" mode that doesn't // uniqify. - both := append(original, typedPatch...) + both := append(original, patch...) return uniqifyScalars(both), nil } @@ -764,7 +649,7 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type // First look for any special $patch elements. patchWithoutSpecialElements := []interface{}{} replace := false - for _, v := range typedPatch { + for _, v := range patch { typedV := v.(map[string]interface{}) patchType, ok := typedV[directiveMarker] if ok { @@ -800,10 +685,10 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type return patchWithoutSpecialElements, nil } - typedPatch = patchWithoutSpecialElements + patch = patchWithoutSpecialElements // Merge patch into original. - for _, v := range typedPatch { + for _, v := range patch { // Because earlier we confirmed that all the elements are maps. typedV := v.(map[string]interface{}) mergeValue, ok := typedV[mergeKey] @@ -836,36 +721,6 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type return original, nil } -// mergeSliceOfScalarsWithPatchMap merges the original slice with a patch map and -// returns an uniqified and sorted slice of primitives. -// The patch map must have the specific directiveMarker (MergePrimitivesListDirective). -func mergeSliceOfScalarsWithPatchMap(original []interface{}, patch map[string]interface{}) ([]interface{}, error) { - // make sure the patch has the specific directiveMarker () - if directiveValue, ok := patch[directiveMarker]; ok && directiveValue != MergePrimitivesListDirective { - return nil, fmt.Errorf("Unable to merge a slice with an invalid map") - } - delete(patch, directiveMarker) - output := make([]interface{}, 0, len(original)+len(patch)) - for _, value := range original { - valueString := fmt.Sprintf("%v", value) - if v, ok := patch[valueString]; ok { - if v != nil { - output = append(output, v) - } - delete(patch, valueString) - } else { - output = append(output, value) - } - } - for _, value := range patch { - if value != nil { - output = append(output, value) - } - // No action required to delete items that missing from the original slice. - } - return uniqifyAndSortScalars(output), nil -} - // This method no longer panics if any element of the slice is not a map. func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) { for k, v := range m { @@ -1091,7 +946,7 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) { } if prevType == nil { - return nil, errNoElementsInSlice + return nil, fmt.Errorf("no elements in any of the given slices") } return prevType, nil @@ -1180,10 +1035,6 @@ func mergingMapFieldsHaveConflicts( if leftMarker != rightMarker { return true, nil } - - if leftMarker == MergePrimitivesListDirective && rightMarker == MergePrimitivesListDirective { - return false, nil - } } // Check the individual keys. @@ -1206,29 +1057,12 @@ func mergingMapFieldsHaveConflicts( } func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) { - isForListOfPrimitives := false - if leftDirective, ok := typedLeft[directiveMarker]; ok { - if rightDirective, ok := typedRight[directiveMarker]; ok { - if leftDirective == MergePrimitivesListDirective && rightDirective == rightDirective { - isForListOfPrimitives = true - } - } - } for key, leftValue := range typedLeft { if key != directiveMarker { if rightValue, ok := typedRight[key]; ok { - var fieldType reflect.Type - var fieldPatchStrategy, fieldPatchMergeKey string - var err error - if isForListOfPrimitives { - fieldType = reflect.TypeOf(leftValue) - fieldPatchStrategy = "" - fieldPatchMergeKey = "" - } else { - fieldType, fieldPatchStrategy, fieldPatchMergeKey, err = forkedjson.LookupPatchMetadata(structType, key) - if err != nil { - return true, err - } + fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(structType, key) + if err != nil { + return true, err } if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, @@ -1338,7 +1172,7 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc // than from original to current. In other words, a conflict occurs if modified changes any key // in a way that is different from how it is changed in current (e.g., deleting it, changing its // value). -func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, smPatchVersion StrategicMergePatchVersion, fns ...PreconditionFunc) ([]byte, error) { +func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...PreconditionFunc) ([]byte, error) { originalMap := map[string]interface{}{} if len(original) > 0 { if err := json.Unmarshal(original, &originalMap); err != nil { @@ -1369,12 +1203,12 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int // from original to modified. To find it, we compute deletions, which are the deletions from // original to modified, and delta, which is the difference from current to modified without // deletions, and then apply delta to deletions as a patch, which should be strictly additive. - deltaMap, err := diffMaps(currentMap, modifiedMap, t, false, true, smPatchVersion) + deltaMap, err := diffMaps(currentMap, modifiedMap, t, false, true) if err != nil { return nil, err } - deletionsMap, err := diffMaps(originalMap, modifiedMap, t, true, false, smPatchVersion) + deletionsMap, err := diffMaps(originalMap, modifiedMap, t, true, false) if err != nil { return nil, err } @@ -1394,7 +1228,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int // If overwrite is false, and the patch contains any keys that were changed differently, // then return a conflict error. if !overwrite { - changedMap, err := diffMaps(originalMap, currentMap, t, false, false, smPatchVersion) + changedMap, err := diffMaps(originalMap, currentMap, t, false, false) if err != nil { return nil, err } diff --git a/pkg/util/strategicpatch/patch_test.go b/pkg/util/strategicpatch/patch_test.go index 279beda48d23..0aa7c37cb222 100644 --- a/pkg/util/strategicpatch/patch_test.go +++ b/pkg/util/strategicpatch/patch_test.go @@ -17,7 +17,6 @@ limitations under the License. package strategicpatch import ( - "bytes" "encoding/json" "fmt" "reflect" @@ -48,12 +47,12 @@ type StrategicMergePatchTestCase struct { } type StrategicMergePatchTestCaseData struct { - Original []byte - TwoWay []byte - Modified []byte - Current []byte - ThreeWay []byte - Result []byte + Original map[string]interface{} + TwoWay map[string]interface{} + Modified map[string]interface{} + Current map[string]interface{} + ThreeWay map[string]interface{} + Result map[string]interface{} } type MergeItem struct { @@ -249,140 +248,79 @@ func TestSortMergeLists(t *testing.T) { // These are test cases for StrategicMergePatch that cannot be generated using // CreateTwoWayMergePatch because it doesn't use the replace directive, generate // duplicate integers for a merging list patch, or generate empty merging lists. -var customStrategicMergePatchTestCaseData = StrategicMergePatchTestCases{ - TestCases: []StrategicMergePatchTestCase{ - { - Description: "unique scalars when merging lists using SMPatchVersion_1_0", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -mergingIntList: - - 2 - - 3 -`), - Modified: []byte(` -mergingIntList: - - 1 - - 2 - - 3 -`), - }, - }, - { - Description: "unique scalars when merging lists for list of primitives", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -mergingIntList: - $patch: mergeprimitiveslist - "2": 2 - "3": 3 -`), - Modified: []byte(` -mergingIntList: - - 1 - - 2 - - 3 -`), - }, - }, - { - Description: "delete map from nested map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -simpleMap: - key1: 1 - key2: 1 -`), - TwoWay: []byte(` -simpleMap: - $patch: delete -`), - Modified: []byte(` -simpleMap: - {} -`), - }, - }, - { - Description: "delete all items from merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - $patch: replace -`), - Modified: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "merge empty merging lists", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: [] -`), - TwoWay: []byte(` -mergingList: [] -`), - Modified: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "delete all keys from map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -$patch: replace -`), - Modified: []byte(` -{} -`), - }, - }, - { - Description: "add key and delete all keys from map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -other: a -$patch: replace -`), - Modified: []byte(` -other: a -`), - }, - }, - }, -} +var customStrategicMergePatchTestCaseData = []byte(` +testCases: + - description: unique scalars when merging lists + original: + mergingIntList: + - 1 + - 2 + twoWay: + mergingIntList: + - 2 + - 3 + modified: + mergingIntList: + - 1 + - 2 + - 3 + - description: delete map from nested map + original: + simpleMap: + key1: 1 + key2: 1 + twoWay: + simpleMap: + $patch: delete + modified: + simpleMap: + {} + - description: delete all items from merging list + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - $patch: replace + modified: + mergingList: [] + - description: merge empty merging lists + original: + mergingList: [] + twoWay: + mergingList: [] + modified: + mergingList: [] + - description: delete all keys from map + original: + name: 1 + value: 1 + twoWay: + $patch: replace + modified: {} + - description: add key and delete all keys from map + original: + name: 1 + value: 1 + twoWay: + other: a + $patch: replace + modified: + other: a +`) func TestCustomStrategicMergePatch(t *testing.T) { - for _, c := range customStrategicMergePatchTestCaseData.TestCases { - originalJSON := yamlToJSONOrError(c.Original) - twoWayJSON := yamlToJSONOrError(c.TwoWay) - modifiedJSON := yamlToJSONOrError(c.Modified) - testPatchApplication(t, originalJSON, twoWayJSON, modifiedJSON, c.Description) + tc := StrategicMergePatchTestCases{} + err := yaml.Unmarshal(customStrategicMergePatchTestCaseData, &tc) + if err != nil { + t.Errorf("can't unmarshal test cases: %v\n", err) + return + } + + for _, c := range tc.TestCases { + original, twoWay, modified := twoWayTestCaseToJSONOrFail(t, c) + testPatchApplication(t, original, twoWay, modified, c.Description) } } @@ -390,2003 +328,1455 @@ func TestCustomStrategicMergePatch(t *testing.T) { // yields the correct outcome. They are also test cases for CreateTwoWayMergePatch // and CreateThreeWayMergePatch, to assert that they both generate the correct patch // for the given set of input documents. -var createStrategicMergePatchTestCaseData = StrategicMergePatchTestCases{ - TestCases: []StrategicMergePatchTestCase{ - { - Description: "nil original", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - TwoWay: []byte(` -name: 1 -value: 1 -`), - Modified: []byte(` -name: 1 -value: 1 -`), - Current: []byte(` -name: 1 -other: a -`), - ThreeWay: []byte(` -value: 1 -`), - Result: []byte(` -name: 1 -value: 1 -other: a -`), - }, - }, - { - Description: "nil patch", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -`), - TwoWay: []byte(` -{} -`), - Modified: []byte(` -name: 1 -`), - Current: []byte(` -name: 1 -`), - ThreeWay: []byte(` -{} -`), - Result: []byte(` -name: 1 -`), - }, - }, - { - Description: "add field to map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -`), - TwoWay: []byte(` -value: 1 -`), - Modified: []byte(` -name: 1 -value: 1 -`), - Current: []byte(` -name: 1 -other: a -`), - ThreeWay: []byte(` -value: 1 -`), - Result: []byte(` -name: 1 -value: 1 -other: a -`), - }, - }, - { - Description: "add field to map with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -`), - TwoWay: []byte(` -value: 1 -`), - Modified: []byte(` -name: 1 -value: 1 -`), - Current: []byte(` -name: a -other: a -`), - ThreeWay: []byte(` -name: 1 -value: 1 -`), - Result: []byte(` -name: 1 -value: 1 -other: a -`), - }, - }, - { - Description: "add field and delete field from map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -`), - TwoWay: []byte(` -name: null -value: 1 -`), - Modified: []byte(` -value: 1 -`), - Current: []byte(` -name: 1 -other: a -`), - ThreeWay: []byte(` -name: null -value: 1 -`), - Result: []byte(` -value: 1 -other: a -`), - }, - }, - { - Description: "add field and delete field from map with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -`), - TwoWay: []byte(` -name: null -value: 1 -`), - Modified: []byte(` -value: 1 -`), - Current: []byte(` -name: a -other: a -`), - ThreeWay: []byte(` -name: null -value: 1 -`), - Result: []byte(` -value: 1 -other: a -`), - }, - }, - { - Description: "delete field from nested map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -simpleMap: - key1: 1 - key2: 1 -`), - TwoWay: []byte(` -simpleMap: - key2: null -`), - Modified: []byte(` -simpleMap: - key1: 1 -`), - Current: []byte(` -simpleMap: - key1: 1 - key2: 1 - other: a -`), - ThreeWay: []byte(` -simpleMap: - key2: null -`), - Result: []byte(` -simpleMap: - key1: 1 - other: a -`), - }, - }, - { - Description: "delete field from nested map with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -simpleMap: - key1: 1 - key2: 1 -`), - TwoWay: []byte(` -simpleMap: - key2: null -`), - Modified: []byte(` -simpleMap: - key1: 1 -`), - Current: []byte(` -simpleMap: - key1: a - key2: 1 - other: a -`), - ThreeWay: []byte(` -simpleMap: - key1: 1 - key2: null -`), - Result: []byte(` -simpleMap: - key1: 1 - other: a -`), - }, - }, - { - Description: "delete all fields from map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -name: null -value: null -`), - Modified: []byte(` -{} -`), - Current: []byte(` -name: 1 -value: 1 -other: a -`), - ThreeWay: []byte(` -name: null -value: null -`), - Result: []byte(` -other: a -`), - }, - }, - { - Description: "delete all fields from map with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -name: null -value: null -`), - Modified: []byte(` -{} -`), - Current: []byte(` -name: 1 -value: a -other: a -`), - ThreeWay: []byte(` -name: null -value: null -`), - Result: []byte(` -other: a -`), - }, - }, - { - Description: "add field and delete all fields from map", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -name: null -value: null -other: a -`), - Modified: []byte(` -other: a -`), - Current: []byte(` -name: 1 -value: 1 -other: a -`), - ThreeWay: []byte(` -name: null -value: null -`), - Result: []byte(` -other: a -`), - }, - }, - { - Description: "add field and delete all fields from map with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -name: 1 -value: 1 -`), - TwoWay: []byte(` -name: null -value: null -other: a -`), - Modified: []byte(` -other: a -`), - Current: []byte(` -name: 1 -value: 1 -other: b -`), - ThreeWay: []byte(` -name: null -value: null -other: a -`), - Result: []byte(` -other: a -`), - }, - }, - { - Description: "replace list of scalars", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -nonMergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Modified: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Current: []byte(` -nonMergingIntList: - - 1 - - 2 -`), - ThreeWay: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Result: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - }, - }, - { - Description: "replace list of scalars with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -nonMergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Modified: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Current: []byte(` -nonMergingIntList: - - 1 - - 4 -`), - ThreeWay: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - Result: []byte(` -nonMergingIntList: - - 2 - - 3 -`), - }, - }, - { - Description: "merge lists of scalars using SMPatchVersion_1_0", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -mergingIntList: - - 3 -`), - Modified: []byte(` -mergingIntList: - - 1 - - 2 - - 3 -`), - Current: []byte(` -mergingIntList: - - 1 - - 2 - - 4 -`), - ThreeWay: []byte(` -mergingIntList: - - 3 -`), - Result: []byte(` -mergingIntList: - - 1 - - 2 - - 3 - - 4 -`), - }, - }, - { - Description: "merge lists of scalars for list of primitives", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -mergingIntList: - $patch: mergeprimitiveslist - "1": null - "3": 3 -`), - Modified: []byte(` -mergingIntList: - - 2 - - 3 -`), - Current: []byte(` -mergingIntList: - - 1 - - 2 - - 4 -`), - ThreeWay: []byte(` -mergingIntList: - $patch: mergeprimitiveslist - "1": null - "3": 3 -`), - Result: []byte(` -mergingIntList: - - 2 - - 3 - - 4 -`), - }, - }, - { - Description: "another merge lists of scalars for list of primitives", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingIntList: - - 1 - - 2 -`), - TwoWay: []byte(` -mergingIntList: - $patch: mergeprimitiveslist - "1": null - "3": 3 -`), - Modified: []byte(` -mergingIntList: - - 2 - - 3 -`), - Current: []byte(` -mergingIntList: - - 2 - - 4 -`), - ThreeWay: []byte(` -mergingIntList: - $patch: mergeprimitiveslist - "1": null - "3": 3 -`), - Result: []byte(` -mergingIntList: - - 2 - - 3 - - 4 -`), - }, - }, - { - Description: "merge lists of maps", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 3 - value: 3 - - name: 4 - value: 4 -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 3 - value: 3 - - name: 4 - value: 4 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 3 - value: 3 - - name: 4 - value: 4 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b - - name: 3 - value: 3 - - name: 4 - value: 4 -`), - }, - }, - { - Description: "merge lists of maps with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 3 - value: 3 -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 3 - value: 3 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 3 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 2 - value: 2 - - name: 3 - value: 3 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b - - name: 3 - value: 3 -`), - }, - }, - { - Description: "add field to map in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "add field to map in merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 3 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Result: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - - name: 3 - value: 2 - other: b -`), - }, - }, - { - Description: "add duplicate field to map in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -{} -`), - Result: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "add duplicate field to map in merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 3 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 2 - value: 2 -`), - Result: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "replace map field value in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: a -`), - Modified: []byte(` -mergingList: - - name: 1 - value: a - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: a -`), - Result: []byte(` -mergingList: - - name: 1 - value: a - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "replace map field value in merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: a -`), - Modified: []byte(` -mergingList: - - name: 1 - value: a - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: 3 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: a -`), - Result: []byte(` -mergingList: - - name: 1 - value: a - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "delete map from merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Modified: []byte(` -mergingList: - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Result: []byte(` -mergingList: - - name: 2 - other: b -`), - }, - }, - { - Description: "delete map from merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Modified: []byte(` -mergingList: - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Result: []byte(` -mergingList: - - name: 2 - other: b -`), - }, - }, - { - Description: "delete missing map from merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Modified: []byte(` -mergingList: - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Result: []byte(` -mergingList: - - name: 2 - other: b -`), - }, - }, - { - Description: "delete missing map from merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete -`), - Modified: []byte(` -mergingList: - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 3 - other: a -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 -`), - Result: []byte(` -mergingList: - - name: 2 - - name: 3 - other: a -`), - }, - }, - { - Description: "add map and delete map from merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 3 -`), - Modified: []byte(` -mergingList: - - name: 2 - - name: 3 -`), - Current: []byte(` -mergingList: - - name: 1 - - name: 2 - other: b - - name: 4 - other: c -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 3 -`), - Result: []byte(` -mergingList: - - name: 2 - other: b - - name: 3 - - name: 4 - other: c -`), - }, - }, - { - Description: "add map and delete map from merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 3 -`), - Modified: []byte(` -mergingList: - - name: 2 - - name: 3 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 4 - other: c -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - - name: 3 -`), - Result: []byte(` -mergingList: - - name: 2 - - name: 3 - - name: 4 - other: c -`), - }, - }, - { - Description: "delete all maps from merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Modified: []byte(` -mergingList: [] -`), - Current: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Result: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "delete all maps from merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Modified: []byte(` -mergingList: [] -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Result: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "delete all maps from empty merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Modified: []byte(` -mergingList: [] -`), - Current: []byte(` -mergingList: [] -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - $patch: delete - - name: 2 - $patch: delete -`), - Result: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "delete field from map in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "delete field from map in merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - value: a - other: a - - name: 2 - value: 2 -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 -`), - }, - }, - { - Description: "delete missing field from map in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "delete missing field from map in merging list with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - value: null -`), - Modified: []byte(` -mergingList: - - name: 1 - - name: 2 - value: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - value: null - - name: 2 - value: 2 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - - name: 2 - value: 2 - other: b -`), - }, - }, - { - Description: "replace non merging list nested in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 1 - value: 1 - - name: 2 - other: b -`), - }, - }, - { - Description: "replace non merging list nested in merging list with value conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 1 - value: c - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 1 - value: 1 - - name: 2 - other: b -`), - }, - }, - { - Description: "replace non merging list nested in merging list with deletion conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 2 - value: 2 - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - nonMergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - nonMergingList: - - name: 1 - value: 1 - - name: 2 - other: b -`), - }, - }, - { - Description: "add field to map in merging list nested in merging list", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 - other: b -`), - }, - }, - { - Description: "add field to map in merging list nested in merging list with value conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 1 - value: a - other: c - - name: 2 - value: b - other: d - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 1 - value: 1 - other: c - - name: 2 - value: 2 - other: d - - name: 2 - other: b -`), - }, - }, - { - Description: "add field to map in merging list nested in merging list with deletion conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 -`), - Current: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 2 - value: 2 - other: d - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergingList: - - name: 1 - mergingList: - - name: 1 - value: 1 -`), - Result: []byte(` -mergingList: - - name: 1 - other: a - mergingList: - - name: 1 - value: 1 - - name: 2 - value: 2 - other: d - - name: 2 - other: b -`), - }, - }, - { - Description: "merge empty merging lists", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergingList: [] -`), - TwoWay: []byte(` -{} -`), - Modified: []byte(` -mergingList: [] -`), - Current: []byte(` -mergingList: [] -`), - ThreeWay: []byte(` -{} -`), - Result: []byte(` -mergingList: [] -`), - }, - }, - { - Description: "add map to merging list by pointer", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergeItemPtr: - - name: 1 -`), - TwoWay: []byte(` -mergeItemPtr: - - name: 2 -`), - Modified: []byte(` -mergeItemPtr: - - name: 1 - - name: 2 -`), - Current: []byte(` -mergeItemPtr: - - name: 1 - other: a - - name: 3 -`), - ThreeWay: []byte(` -mergeItemPtr: - - name: 2 -`), - Result: []byte(` -mergeItemPtr: - - name: 1 - other: a - - name: 2 - - name: 3 -`), - }, - }, - { - Description: "add map to merging list by pointer with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergeItemPtr: - - name: 1 -`), - TwoWay: []byte(` -mergeItemPtr: - - name: 2 -`), - Modified: []byte(` -mergeItemPtr: - - name: 1 - - name: 2 -`), - Current: []byte(` -mergeItemPtr: - - name: 3 -`), - ThreeWay: []byte(` -mergeItemPtr: - - name: 1 - - name: 2 -`), - Result: []byte(` -mergeItemPtr: - - name: 1 - - name: 2 - - name: 3 -`), - }, - }, - { - Description: "add field to map in merging list by pointer", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 -`), - Current: []byte(` -mergeItemPtr: - - name: 1 - other: a - mergeItemPtr: - - name: 1 +// +var createStrategicMergePatchTestCaseData = []byte(` +testCases: + - description: nil original + twoWay: + name: 1 + value: 1 + modified: + name: 1 + value: 1 + current: + name: 1 + other: a + threeWay: + value: 1 + result: + name: 1 + value: 1 + other: a + - description: nil patch + original: + name: 1 + twoWay: + {} + modified: + name: 1 + current: + name: 1 + threeWay: + {} + result: + name: 1 + - description: add field to map + original: + name: 1 + twoWay: + value: 1 + modified: + name: 1 + value: 1 + current: + name: 1 + other: a + threeWay: + value: 1 + result: + name: 1 + value: 1 + other: a + - description: add field to map with conflict + original: + name: 1 + twoWay: + value: 1 + modified: + name: 1 + value: 1 + current: + name: a + other: a + threeWay: + name: 1 + value: 1 + result: + name: 1 + value: 1 + other: a + - description: add field and delete field from map + original: + name: 1 + twoWay: + name: null + value: 1 + modified: + value: 1 + current: + name: 1 + other: a + threeWay: + name: null + value: 1 + result: + value: 1 + other: a + - description: add field and delete field from map with conflict + original: + name: 1 + twoWay: + name: null + value: 1 + modified: + value: 1 + current: + name: a + other: a + threeWay: + name: null + value: 1 + result: + value: 1 + other: a + - description: delete field from nested map + original: + simpleMap: + key1: 1 + key2: 1 + twoWay: + simpleMap: + key2: null + modified: + simpleMap: + key1: 1 + current: + simpleMap: + key1: 1 + key2: 1 other: a - - name: 2 - value: 2 - other: b - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 -`), - Result: []byte(` -mergeItemPtr: - - name: 1 - other: a - mergeItemPtr: - - name: 1 - value: 1 + threeWay: + simpleMap: + key2: null + result: + simpleMap: + key1: 1 other: a - - name: 2 - value: 2 - other: b - - name: 2 - other: b -`), - }, - }, - { - Description: "add field to map in merging list by pointer with conflict", - StrategicMergePatchTestCaseData: StrategicMergePatchTestCaseData{ - Original: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - - name: 2 - value: 2 - - name: 2 -`), - TwoWay: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 -`), - Modified: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 - - name: 2 - value: 2 - - name: 2 -`), - Current: []byte(` -mergeItemPtr: - - name: 1 - other: a - mergeItemPtr: - - name: 1 - value: a - - name: 2 - value: 2 - other: b - - name: 2 - other: b -`), - ThreeWay: []byte(` -mergeItemPtr: - - name: 1 - mergeItemPtr: - - name: 1 - value: 1 -`), - Result: []byte(` -mergeItemPtr: - - name: 1 - other: a - mergeItemPtr: - - name: 1 - value: 1 - - name: 2 - value: 2 - other: b - - name: 2 - other: b -`), - }, - }, - }, -} + - description: delete field from nested map with conflict + original: + simpleMap: + key1: 1 + key2: 1 + twoWay: + simpleMap: + key2: null + modified: + simpleMap: + key1: 1 + current: + simpleMap: + key1: a + key2: 1 + other: a + threeWay: + simpleMap: + key1: 1 + key2: null + result: + simpleMap: + key1: 1 + other: a + - description: delete all fields from map + original: + name: 1 + value: 1 + twoWay: + name: null + value: null + modified: {} + current: + name: 1 + value: 1 + other: a + threeWay: + name: null + value: null + result: + other: a + - description: delete all fields from map with conflict + original: + name: 1 + value: 1 + twoWay: + name: null + value: null + modified: {} + current: + name: 1 + value: a + other: a + threeWay: + name: null + value: null + result: + other: a + - description: add field and delete all fields from map + original: + name: 1 + value: 1 + twoWay: + name: null + value: null + other: a + modified: + other: a + current: + name: 1 + value: 1 + other: a + threeWay: + name: null + value: null + result: + other: a + - description: add field and delete all fields from map with conflict + original: + name: 1 + value: 1 + twoWay: + name: null + value: null + other: a + modified: + other: a + current: + name: 1 + value: 1 + other: b + threeWay: + name: null + value: null + other: a + result: + other: a + - description: replace list of scalars + original: + nonMergingIntList: + - 1 + - 2 + twoWay: + nonMergingIntList: + - 2 + - 3 + modified: + nonMergingIntList: + - 2 + - 3 + current: + nonMergingIntList: + - 1 + - 2 + threeWay: + nonMergingIntList: + - 2 + - 3 + result: + nonMergingIntList: + - 2 + - 3 + - description: replace list of scalars with conflict + original: + nonMergingIntList: + - 1 + - 2 + twoWay: + nonMergingIntList: + - 2 + - 3 + modified: + nonMergingIntList: + - 2 + - 3 + current: + nonMergingIntList: + - 1 + - 4 + threeWay: + nonMergingIntList: + - 2 + - 3 + result: + nonMergingIntList: + - 2 + - 3 + - description: merge lists of scalars + original: + mergingIntList: + - 1 + - 2 + twoWay: + mergingIntList: + - 3 + modified: + mergingIntList: + - 1 + - 2 + - 3 + current: + mergingIntList: + - 1 + - 2 + - 4 + threeWay: + mergingIntList: + - 3 + result: + mergingIntList: + - 1 + - 2 + - 3 + - 4 + - description: merge lists of maps + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 3 + value: 3 + - name: 4 + value: 4 + modified: + mergingList: + - name: 4 + value: 4 + - name: 1 + - name: 2 + value: 2 + - name: 3 + value: 3 + current: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 3 + value: 3 + - name: 4 + value: 4 + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - name: 3 + value: 3 + - name: 4 + value: 4 + - description: merge lists of maps with conflict + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 3 + value: 3 + modified: + mergingList: + - name: 1 + - name: 2 + value: 2 + - name: 3 + value: 3 + current: + mergingList: + - name: 1 + other: a + - name: 2 + value: 3 + other: b + threeWay: + mergingList: + - name: 2 + value: 2 + - name: 3 + value: 3 + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - name: 3 + value: 3 + - description: add field to map in merging list + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + - description: add field to map in merging list with conflict + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + other: a + - name: 3 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + result: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + - name: 3 + value: 2 + other: b + - description: add duplicate field to map in merging list + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + {} + result: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + - description: add duplicate field to map in merging list with conflict + original: + mergingList: + - name: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 3 + other: b + threeWay: + mergingList: + - name: 2 + value: 2 + result: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + - description: replace map field value in merging list + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: a + modified: + mergingList: + - name: 1 + value: a + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: a + result: + mergingList: + - name: 1 + value: a + other: a + - name: 2 + value: 2 + other: b + - description: replace map field value in merging list with conflict + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: a + modified: + mergingList: + - name: 1 + value: a + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: 3 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: a + result: + mergingList: + - name: 1 + value: a + other: a + - name: 2 + value: 2 + other: b + - description: delete map from merging list + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + modified: + mergingList: + - name: 2 + current: + mergingList: + - name: 1 + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + $patch: delete + result: + mergingList: + - name: 2 + other: b + - description: delete map from merging list with conflict + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + modified: + mergingList: + - name: 2 + current: + mergingList: + - name: 1 + other: a + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + $patch: delete + result: + mergingList: + - name: 2 + other: b + - description: delete missing map from merging list + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + modified: + mergingList: + - name: 2 + current: + mergingList: + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + $patch: delete + result: + mergingList: + - name: 2 + other: b + - description: delete missing map from merging list with conflict + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + modified: + mergingList: + - name: 2 + current: + mergingList: + - name: 3 + other: a + threeWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + result: + mergingList: + - name: 2 + - name: 3 + other: a + - description: add map and delete map from merging list + original: + merginglist: + - name: 1 + - name: 2 + twoWay: + merginglist: + - name: 1 + $patch: delete + - name: 3 + modified: + merginglist: + - name: 2 + - name: 3 + current: + merginglist: + - name: 1 + - name: 2 + other: b + - name: 4 + other: c + threeWay: + merginglist: + - name: 1 + $patch: delete + - name: 3 + result: + merginglist: + - name: 2 + other: b + - name: 3 + - name: 4 + other: c + - description: add map and delete map from merging list with conflict + original: + merginglist: + - name: 1 + - name: 2 + twoWay: + merginglist: + - name: 1 + $patch: delete + - name: 3 + modified: + merginglist: + - name: 2 + - name: 3 + current: + merginglist: + - name: 1 + other: a + - name: 4 + other: c + threeWay: + merginglist: + - name: 1 + $patch: delete + - name: 2 + - name: 3 + result: + merginglist: + - name: 2 + - name: 3 + - name: 4 + other: c + - description: delete all maps from merging list + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + modified: + mergingList: [] + current: + mergingList: + - name: 1 + - name: 2 + threeWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + result: + mergingList: [] + - description: delete all maps from merging list with conflict + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + modified: + mergingList: [] + current: + mergingList: + - name: 1 + other: a + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + result: + mergingList: [] + - description: delete all maps from empty merging list + original: + mergingList: + - name: 1 + - name: 2 + twoWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + modified: + mergingList: [] + current: + mergingList: [] + threeWay: + mergingList: + - name: 1 + $patch: delete + - name: 2 + $patch: delete + result: + mergingList: [] + - description: delete field from map in merging list + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: null + modified: + mergingList: + - name: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: null + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - description: delete field from map in merging list with conflict + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: null + modified: + mergingList: + - name: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + value: a + other: a + - name: 2 + value: 2 + threeWay: + mergingList: + - name: 1 + value: null + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + - description: delete missing field from map in merging list + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: null + modified: + mergingList: + - name: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: null + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - description: delete missing field from map in merging list with conflict + original: + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + twoWay: + mergingList: + - name: 1 + value: null + modified: + mergingList: + - name: 1 + - name: 2 + value: 2 + current: + mergingList: + - name: 1 + other: a + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + value: null + - name: 2 + value: 2 + result: + mergingList: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - description: replace non merging list nested in merging list + original: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + - name: 2 + current: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 1 + value: 1 + - name: 2 + other: b + - description: replace non merging list nested in merging list with value conflict + original: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + - name: 2 + current: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 1 + value: c + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 1 + value: 1 + - name: 2 + other: b + - description: replace non merging list nested in merging list with deletion conflict + original: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + - name: 2 + current: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 2 + value: 2 + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + nonMergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + other: a + nonMergingList: + - name: 1 + value: 1 + - name: 2 + other: b + - description: add field to map in merging list nested in merging list + original: + mergingList: + - name: 1 + mergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + current: + mergingList: + - name: 1 + other: a + mergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + other: a + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + other: b + - description: add field to map in merging list nested in merging list with value conflict + original: + mergingList: + - name: 1 + mergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + current: + mergingList: + - name: 1 + other: a + mergingList: + - name: 1 + value: a + other: c + - name: 2 + value: b + other: d + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + result: + mergingList: + - name: 1 + other: a + mergingList: + - name: 1 + value: 1 + other: c + - name: 2 + value: 2 + other: d + - name: 2 + other: b + - description: add field to map in merging list nested in merging list with deletion conflict + original: + mergingList: + - name: 1 + mergingList: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + modified: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + current: + mergingList: + - name: 1 + other: a + mergingList: + - name: 2 + value: 2 + other: d + - name: 2 + other: b + threeWay: + mergingList: + - name: 1 + mergingList: + - name: 1 + value: 1 + result: + mergingList: + - name: 1 + other: a + mergingList: + - name: 1 + value: 1 + - name: 2 + value: 2 + other: d + - name: 2 + other: b + - description: merge empty merging lists + original: + mergingList: [] + twoWay: + {} + modified: + mergingList: [] + current: + mergingList: [] + threeWay: + {} + result: + mergingList: [] + - description: add map to merging list by pointer + original: + mergeItemPtr: + - name: 1 + twoWay: + mergeItemPtr: + - name: 2 + modified: + mergeItemPtr: + - name: 1 + - name: 2 + current: + mergeItemPtr: + - name: 1 + other: a + - name: 3 + threeWay: + mergeItemPtr: + - name: 2 + result: + mergeItemPtr: + - name: 1 + other: a + - name: 2 + - name: 3 + - description: add map to merging list by pointer with conflict + original: + mergeItemPtr: + - name: 1 + twoWay: + mergeItemPtr: + - name: 2 + modified: + mergeItemPtr: + - name: 1 + - name: 2 + current: + mergeItemPtr: + - name: 3 + threeWay: + mergeItemPtr: + - name: 1 + - name: 2 + result: + mergeItemPtr: + - name: 1 + - name: 2 + - name: 3 + - description: add field to map in merging list by pointer + original: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + modified: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + current: + mergeItemPtr: + - name: 1 + other: a + mergeItemPtr: + - name: 1 + other: a + - name: 2 + value: 2 + other: b + - name: 2 + other: b + threeWay: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + result: + mergeItemPtr: + - name: 1 + other: a + mergeItemPtr: + - name: 1 + value: 1 + other: a + - name: 2 + value: 2 + other: b + - name: 2 + other: b + - description: add field to map in merging list by pointer with conflict + original: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + - name: 2 + value: 2 + - name: 2 + twoWay: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + modified: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + - name: 2 + value: 2 + - name: 2 + current: + mergeItemPtr: + - name: 1 + other: a + mergeItemPtr: + - name: 1 + value: a + - name: 2 + value: 2 + other: b + - name: 2 + other: b + threeWay: + mergeItemPtr: + - name: 1 + mergeItemPtr: + - name: 1 + value: 1 + result: + mergeItemPtr: + - name: 1 + other: a + mergeItemPtr: + - name: 1 + value: 1 + - name: 2 + value: 2 + other: b + - name: 2 + other: b +`) func TestStrategicMergePatch(t *testing.T) { testStrategicMergePatchWithCustomArguments(t, "bad original", @@ -2398,7 +1788,14 @@ func TestStrategicMergePatch(t *testing.T) { testStrategicMergePatchWithCustomArguments(t, "nil struct", "{}", "{}", nil, fmt.Errorf(errBadArgTypeFmt, "struct", "nil")) - for _, c := range createStrategicMergePatchTestCaseData.TestCases { + tc := StrategicMergePatchTestCases{} + err := yaml.Unmarshal(createStrategicMergePatchTestCaseData, &tc) + if err != nil { + t.Errorf("can't unmarshal test cases: %s\n", err) + return + } + + for _, c := range tc.TestCases { testTwoWayPatch(t, c) testThreeWayPatch(t, c) } @@ -2420,88 +1817,72 @@ func testStrategicMergePatchWithCustomArguments(t *testing.T, description, origi } func testTwoWayPatch(t *testing.T, c StrategicMergePatchTestCase) { - originalJSON := yamlToJSONOrError(c.Original) - modifiedJSON := yamlToJSONOrError(c.Modified) - expectedJSON := yamlToJSONOrError(c.TwoWay) - smPatchVersion := SMPatchVersion_1_5 - if strings.Contains(c.Description, "using SMPatchVersion_1_0") { - smPatchVersion = SMPatchVersion_1_0 - } - actualJSON, err := CreateTwoWayMergePatch(originalJSON, modifiedJSON, mergeItem, smPatchVersion) + original, expected, modified := twoWayTestCaseToJSONOrFail(t, c) + + actual, err := CreateTwoWayMergePatch(original, modified, mergeItem) if err != nil { - t.Errorf("error: %s\nin test case: %s\ncannot create two way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n", - err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result) + t.Errorf("error: %s\nin test case: %s\ncannot create two way patch: %s:\n%s\n", + err, c.Description, original, toYAMLOrError(c.StrategicMergePatchTestCaseData)) return } - if !strings.Contains(c.Description, "for list of primitives") { - testPatchCreation(t, expectedJSON, actualJSON, c.Description) - } else { - testPatchCreationWithoutSorting(t, expectedJSON, actualJSON, c.Description) - } - testPatchApplication(t, originalJSON, actualJSON, modifiedJSON, c.Description) + testPatchCreation(t, expected, actual, c.Description) + testPatchApplication(t, original, actual, modified, c.Description) +} + +func twoWayTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchTestCase) ([]byte, []byte, []byte) { + return testObjectToJSONOrFail(t, c.Original, c.Description), + testObjectToJSONOrFail(t, c.TwoWay, c.Description), + testObjectToJSONOrFail(t, c.Modified, c.Description) } func testThreeWayPatch(t *testing.T, c StrategicMergePatchTestCase) { - originalJSON := yamlToJSONOrError(c.Original) - modifiedJSON := yamlToJSONOrError(c.Modified) - currentJSON := yamlToJSONOrError(c.Current) - expectedJSON := yamlToJSONOrError(c.ThreeWay) - resultJSON := yamlToJSONOrError(c.Result) - smPatchVersion := SMPatchVersion_1_5 - if strings.Contains(c.Description, "using SMPatchVersion_1_0") { - smPatchVersion = SMPatchVersion_1_0 - } - actualJSON, err := CreateThreeWayMergePatch(originalJSON, modifiedJSON, currentJSON, mergeItem, false, smPatchVersion) + original, modified, current, expected, result := threeWayTestCaseToJSONOrFail(t, c) + actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, false) if err != nil { if !IsConflict(err) { - t.Errorf("error: %s\nin test case: %s\ncannot create three way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n", - err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result) + t.Errorf("error: %s\nin test case: %s\ncannot create three way patch:\n%s\n", + err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData)) return } if !strings.Contains(c.Description, "conflict") { - t.Errorf("unexpected conflict: %s\nin test case: %s\ncannot create three way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n", - err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result) + t.Errorf("unexpected conflict: %s\nin test case: %s\ncannot create three way patch:\n%s\n", + err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData)) + return } if len(c.Result) > 0 { - actualJSON, err := CreateThreeWayMergePatch(originalJSON, modifiedJSON, currentJSON, mergeItem, true, smPatchVersion) + actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, true) if err != nil { - t.Errorf("error: %s\nin test case: %s\ncannot force three way patch application:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n", - err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result) + t.Errorf("error: %s\nin test case: %s\ncannot force three way patch application:\n%s\n", + err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData)) return } - if !strings.Contains(c.Description, "for list of primitives") { - testPatchCreation(t, expectedJSON, actualJSON, c.Description) - } else { - testPatchCreationWithoutSorting(t, expectedJSON, actualJSON, c.Description) - } - testPatchApplication(t, currentJSON, actualJSON, resultJSON, c.Description) + testPatchCreation(t, expected, actual, c.Description) + testPatchApplication(t, current, actual, result, c.Description) } return } if strings.Contains(c.Description, "conflict") || len(c.Result) < 1 { - t.Errorf("error: %s\nin test case: %s\nexpected conflict did not occur:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n", - err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result) + t.Errorf("error in test case: %s\nexpected conflict did not occur:\n%s\n", + c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData)) + return } - if !strings.Contains(c.Description, "for list of primitives") { - testPatchCreation(t, expectedJSON, actualJSON, c.Description) - } else { - testPatchCreationWithoutSorting(t, expectedJSON, actualJSON, c.Description) - } - testPatchApplication(t, currentJSON, actualJSON, resultJSON, c.Description) + testPatchCreation(t, expected, actual, c.Description) + testPatchApplication(t, current, actual, result, c.Description) } -func testPatchCreationWithoutSorting(t *testing.T, expectedYAML, actualYAML []byte, description string) { - if bytes.Compare(actualYAML, expectedYAML) != 0 { - t.Errorf("error in test case: %s\nexpected patch:\n%s\ngot:\n%s\n", - description, jsonToYAMLOrError(expectedYAML), jsonToYAMLOrError(actualYAML)) - } +func threeWayTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchTestCase) ([]byte, []byte, []byte, []byte, []byte) { + return testObjectToJSONOrFail(t, c.Original, c.Description), + testObjectToJSONOrFail(t, c.Modified, c.Description), + testObjectToJSONOrFail(t, c.Current, c.Description), + testObjectToJSONOrFail(t, c.ThreeWay, c.Description), + testObjectToJSONOrFail(t, c.Result, c.Description) } func testPatchCreation(t *testing.T, expected, actual []byte, description string) { @@ -2526,6 +1907,7 @@ func testPatchApplication(t *testing.T, original, patch, expected []byte, descri err, description, jsonToYAMLOrError(patch), jsonToYAMLOrError(original)) return } + sorted, err := sortMergeListsByName(result, mergeItem) if err != nil { t.Errorf("error: %s\nin test case: %s\ncannot sort result object:\n%s\n", @@ -2588,24 +1970,6 @@ func jsonToYAML(j []byte) ([]byte, error) { return y, nil } -func yamlToJSON(y []byte) ([]byte, error) { - j, err := yaml.YAMLToJSON(y) - if err != nil { - return nil, fmt.Errorf("yaml to json failed: %v\n%v\n", err, y) - } - - return j, nil -} - -func yamlToJSONOrError(y []byte) []byte { - j, err := yamlToJSON(y) - if err != nil { - return []byte(err.Error()) - } - - return j -} - func TestHasConflicts(t *testing.T) { testCases := []struct { A interface{} @@ -2731,7 +2095,7 @@ func TestNumberConversion(t *testing.T) { } for k, tc := range testcases { - patch, err := CreateTwoWayMergePatch([]byte(tc.Old), []byte(tc.New), precisionItem, SMPatchVersionLatest) + patch, err := CreateTwoWayMergePatch([]byte(tc.Old), []byte(tc.New), precisionItem) if err != nil { t.Errorf("%s: unexpected error %v", k, err) continue diff --git a/test/fixtures/pkg/kubectl/cmd/apply/rc.yaml b/test/fixtures/pkg/kubectl/cmd/apply/rc.yaml index 267c7148fb1a..1c1bf15be7fc 100644 --- a/test/fixtures/pkg/kubectl/cmd/apply/rc.yaml +++ b/test/fixtures/pkg/kubectl/cmd/apply/rc.yaml @@ -1,8 +1,6 @@ apiVersion: v1 kind: ReplicationController metadata: - finalizers: - - b/b name: test-rc labels: name: test-rc