Add tests for resourceVersion precondition failures on patch

pull/8/head
Jordan Liggitt 2018-04-25 22:44:46 -04:00
parent fbd6f38084
commit b526532c8a
No known key found for this signature in database
GPG Key ID: 39928704103C7229
1 changed files with 124 additions and 25 deletions

View File

@ -266,15 +266,21 @@ type patchTestCase struct {
// startingPod is used as the starting point for the first Update
startingPod *example.Pod
// changedPod is the "destination" pod for the patch. The test will create a patch from the startingPod to the changedPod
// to use when calling the patch operation
// changedPod can be set as the "destination" pod for the patch, and the test will compute a patch from the startingPod to the changedPod,
// or patches can be set directly using strategicMergePatch, mergePatch, and jsonPatch
changedPod *example.Pod
strategicMergePatch string
mergePatch string
jsonPatch string
// updatePod is the pod that is used for conflict comparison and as the starting point for the second Update
updatePod *example.Pod
// expectedPod is the pod that you expect to get back after the patch is complete
expectedPod *example.Pod
expectedError string
// if set, indicates the number of times patching was expected to be attempted
expectedTries int
}
func (tc *patchTestCase) Run(t *testing.T) {
@ -317,12 +323,13 @@ func (tc *patchTestCase) Run(t *testing.T) {
updatePod: tc.updatePod,
}
// TODO SUPPORT THIS!
if patchType == types.JSONPatchType {
continue
}
t.Logf("Working with patchType %v", patchType)
patch := []byte{}
switch patchType {
case types.StrategicMergePatchType:
patch = []byte(tc.strategicMergePatch)
if len(patch) == 0 {
originalObjJS, err := runtime.Encode(codec, tc.startingPod)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
@ -333,23 +340,42 @@ func (tc *patchTestCase) Run(t *testing.T) {
t.Errorf("%s: unexpected error: %v", tc.name, err)
continue
}
patch := []byte{}
switch patchType {
case types.StrategicMergePatchType:
patch, err = strategicpatch.CreateTwoWayMergePatch(originalObjJS, changedJS, schemaReferenceObj)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
continue
}
}
case types.MergePatchType:
patch = []byte(tc.mergePatch)
if len(patch) == 0 {
originalObjJS, err := runtime.Encode(codec, tc.startingPod)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
continue
}
changedJS, err := runtime.Encode(codec, tc.changedPod)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
continue
}
patch, err = jsonpatch.CreateMergePatch(originalObjJS, changedJS)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
continue
}
}
case types.JSONPatchType:
patch = []byte(tc.jsonPatch)
if len(patch) == 0 {
// TODO SUPPORT THIS!
continue
}
default:
t.Error("unsupported patch type")
}
p := patcher{
@ -391,6 +417,12 @@ func (tc *patchTestCase) Run(t *testing.T) {
}
}
if tc.expectedTries > 0 {
if tc.expectedTries != testPatcher.numUpdates {
t.Errorf("%s: expected %d tries, got %d", tc.expectedTries, testPatcher.numUpdates)
}
}
if tc.expectedPod == nil {
if resultObj != nil {
t.Errorf("%s: unexpected result: %v", tc.name, resultObj)
@ -552,6 +584,73 @@ func TestPatchResourceWithVersionConflict(t *testing.T) {
tc.Run(t)
}
func TestPatchResourceWithStaleVersionConflict(t *testing.T) {
namespace := "bar"
name := "foo"
uid := types.UID("uid")
tc := &patchTestCase{
name: "TestPatchResourceWithStaleVersionConflict",
startingPod: &example.Pod{},
updatePod: &example.Pod{},
expectedError: `Operation cannot be fulfilled on pods.example.apiserver.k8s.io "foo": existing 2, new 1`,
expectedTries: 1,
}
// starting pod is at rv=2
tc.startingPod.Name = name
tc.startingPod.Namespace = namespace
tc.startingPod.UID = uid
tc.startingPod.ResourceVersion = "2"
tc.startingPod.APIVersion = examplev1.SchemeGroupVersion.String()
// same pod is still in place when attempting to persist the update
tc.updatePod = tc.startingPod
// patches are submitted with a stale rv=1
tc.mergePatch = `{"metadata":{"resourceVersion":"1"},"spec":{"nodeName":"foo"}}`
tc.strategicMergePatch = `{"metadata":{"resourceVersion":"1"},"spec":{"nodeName":"foo"}}`
tc.Run(t)
}
func TestPatchResourceWithRacingVersionConflict(t *testing.T) {
namespace := "bar"
name := "foo"
uid := types.UID("uid")
tc := &patchTestCase{
name: "TestPatchResourceWithRacingVersionConflict",
startingPod: &example.Pod{},
updatePod: &example.Pod{},
expectedError: `Operation cannot be fulfilled on pods.example.apiserver.k8s.io "foo": existing 3, new 2`,
expectedTries: 2,
}
// starting pod is at rv=2
tc.startingPod.Name = name
tc.startingPod.Namespace = namespace
tc.startingPod.UID = uid
tc.startingPod.ResourceVersion = "2"
tc.startingPod.APIVersion = examplev1.SchemeGroupVersion.String()
// pod with rv=3 is found when attempting to persist the update
tc.updatePod.Name = name
tc.updatePod.Namespace = namespace
tc.updatePod.UID = uid
tc.updatePod.ResourceVersion = "3"
tc.updatePod.APIVersion = examplev1.SchemeGroupVersion.String()
// patches are submitted with a rv=2
tc.mergePatch = `{"metadata":{"resourceVersion":"2"},"spec":{"nodeName":"foo"}}`
tc.strategicMergePatch = `{"metadata":{"resourceVersion":"2"},"spec":{"nodeName":"foo"}}`
tc.Run(t)
}
func TestPatchResourceWithConflict(t *testing.T) {
namespace := "bar"
name := "foo"