diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go index 4fbdc9c1189..42b93dcf940 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go @@ -565,11 +565,12 @@ func patchResource( namespace := request.NamespaceValue(ctx) var ( - originalObjJS []byte - originalPatchedObjJS []byte - originalObjMap map[string]interface{} - originalPatchMap map[string]interface{} - lastConflictErr error + originalObjJS []byte + originalPatchedObjJS []byte + originalObjMap map[string]interface{} + originalPatchMap map[string]interface{} + lastConflictErr error + originalResourceVersion string ) // applyPatch is called every time GuaranteedUpdate asks for the updated object, @@ -582,12 +583,18 @@ func patchResource( return nil, errors.NewNotFound(resource.GroupResource(), name) } + currentResourceVersion := "" + if currentMetadata, err := meta.Accessor(currentObject); err == nil { + currentResourceVersion = currentMetadata.GetResourceVersion() + } + switch { case originalObjJS == nil && originalObjMap == nil: // first time through, // 1. apply the patch // 2. save the original and patched to detect whether there were conflicting changes on retries + originalResourceVersion = currentResourceVersion objToUpdate := patcher.New() // For performance reasons, in case of strategicpatch, we avoid json @@ -690,11 +697,12 @@ func patchResource( if err != nil { return nil, err } + if hasConflicts { diff1, _ := json.Marshal(currentPatchMap) diff2, _ := json.Marshal(originalPatchMap) - patchDiffErr := fmt.Errorf("there is a meaningful conflict:\n diff1=%v\n, diff2=%v\n", diff1, diff2) - glog.V(4).Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2) + patchDiffErr := fmt.Errorf("there is a meaningful conflict (firstResourceVersion: %q, currentResourceVersion: %q):\n diff1=%v\n, diff2=%v\n", originalResourceVersion, currentResourceVersion, string(diff1), string(diff2)) + glog.V(4).Infof("patchResource failed for resource %s, because there is a meaningful conflict(firstResourceVersion: %q, currentResourceVersion: %q):\n diff1=%v\n, diff2=%v\n", name, originalResourceVersion, currentResourceVersion, string(diff1), string(diff2)) // Return the last conflict error we got if we have one if lastConflictErr != nil { diff --git a/test/integration/client/client_test.go b/test/integration/client/client_test.go index 07956ac1a7a..2b9da7dfa89 100644 --- a/test/integration/client/client_test.go +++ b/test/integration/client/client_test.go @@ -268,14 +268,26 @@ func TestPatch(t *testing.T) { pb := patchBodies[c.Core().RESTClient().APIVersion()] execPatch := func(pt types.PatchType, body []byte) error { - return c.Core().RESTClient().Patch(pt). + result := c.Core().RESTClient().Patch(pt). Resource(resource). Namespace(ns.Name). Name(name). Body(body). - Do(). - Error() + Do() + if result.Error() != nil { + return result.Error() + } + + // trying to chase flakes, this should give us resource versions of objects as we step through + jsonObj, err := result.Raw() + if err != nil { + t.Log(err) + } else { + t.Logf("%v", string(jsonObj)) + } + return nil } + for k, v := range pb { // add label err := execPatch(k, v.AddLabelBody)