From 1248f569917511c37501b6ef6bbb215ce87353dc Mon Sep 17 00:00:00 2001 From: Cao Shufeng Date: Tue, 11 Sep 2018 14:11:39 +0800 Subject: [PATCH] return 400 status when invalid json patch passed to apiserver --- .../apiserver/pkg/endpoints/handlers/patch.go | 16 ++++-- .../pkg/endpoints/handlers/rest_test.go | 57 ++++++++++++++++++- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go index fbd6d9ae15b..3ae49723dea 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go @@ -265,7 +265,7 @@ func (p *jsonPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (r // Apply the patch. patchedObjJS, err := p.applyJSPatch(currentObjJS) if err != nil { - return nil, interpretPatchError(err) + return nil, err } // Construct the resulting typed, unversioned object. @@ -284,9 +284,13 @@ func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr case types.JSONPatchType: patchObj, err := jsonpatch.DecodePatch(p.patchJS) if err != nil { - return nil, err + return nil, errors.NewBadRequest(err.Error()) } - return patchObj.Apply(versionedJS) + patchedJS, err := patchObj.Apply(versionedJS) + if err != nil { + return nil, errors.NewGenericServerResponse(http.StatusUnprocessableEntity, "", schema.GroupResource{}, "", err.Error(), 0, false) + } + return patchedJS, nil case types.MergePatchType: return jsonpatch.MergePatch(versionedJS, p.patchJS) default: @@ -415,7 +419,7 @@ func applyPatchToObject( ) error { patchedObjMap, err := strategicpatch.StrategicMergeMapPatch(originalMap, patchMap, schemaReferenceObj) if err != nil { - return interpretPatchError(err) + return interpretStrategicMergePatchError(err) } // Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object @@ -428,8 +432,8 @@ func applyPatchToObject( return nil } -// interpretPatchError interprets the error type and returns an error with appropriate HTTP code. -func interpretPatchError(err error) error { +// interpretStrategicMergePatchError interprets the error type and returns an error with appropriate HTTP code. +func interpretStrategicMergePatchError(err error) error { switch err { case mergepatch.ErrBadJSONDoc, mergepatch.ErrBadPatchFormatForPrimitiveList, mergepatch.ErrBadPatchFormatForRetainKeys, mergepatch.ErrBadPatchFormatForSetElementOrderList, mergepatch.ErrUnsupportedStrategicMergePatchFormat: return errors.NewBadRequest(err.Error()) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go index 822ed3e926e..b190f8d299b 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go @@ -101,7 +101,7 @@ func TestPatchAnonymousField(t *testing.T) { } } -func TestPatchInvalid(t *testing.T) { +func TestStrategicMergePatchInvalid(t *testing.T) { testGV := schema.GroupVersion{Group: "", Version: "v"} scheme.AddKnownTypes(testGV, &testPatchType{}) defaulter := runtime.ObjectDefaulter(scheme) @@ -123,6 +123,61 @@ func TestPatchInvalid(t *testing.T) { } } +func TestJSONPatch(t *testing.T) { + for _, test := range []struct { + name string + patch string + expectedError string + expectedErrorType metav1.StatusReason + }{ + { + name: "valid", + patch: `[{"op": "test", "value": "podA", "path": "/metadata/name"}]`, + }, + { + name: "invalid-syntax", + patch: `invalid json patch`, + expectedError: "invalid character 'i' looking for beginning of value", + expectedErrorType: metav1.StatusReasonBadRequest, + }, + { + name: "invalid-semantics", + patch: `[{"op": "test", "value": "podA", "path": "/invalid/path"}]`, + expectedError: "the server rejected our request due to an error in our request", + expectedErrorType: metav1.StatusReasonInvalid, + }, + } { + p := &patcher{ + patchType: types.JSONPatchType, + patchJS: []byte(test.patch), + } + jp := jsonPatcher{p} + codec := codecs.LegacyCodec(examplev1.SchemeGroupVersion) + pod := &examplev1.Pod{} + pod.Name = "podA" + versionedJS, err := runtime.Encode(codec, pod) + if err != nil { + t.Errorf("%s: unexpected error: %v", test.name, err) + continue + } + _, err = jp.applyJSPatch(versionedJS) + if err != nil { + if len(test.expectedError) == 0 { + t.Errorf("%s: expect no error when applying json patch, but got %v", test.name, err) + continue + } + if err.Error() != test.expectedError { + t.Errorf("%s: expected error %v, but got %v", test.name, test.expectedError, err) + } + if test.expectedErrorType != apierrors.ReasonForError(err) { + t.Errorf("%s: expected error type %v, but got %v", test.name, test.expectedErrorType, apierrors.ReasonForError(err)) + } + } else if len(test.expectedError) > 0 { + t.Errorf("%s: expected err %s", test.name, test.expectedError) + } + } +} + func TestPatchCustomResource(t *testing.T) { testGV := schema.GroupVersion{Group: "mygroup.example.com", Version: "v1beta1"} scheme.AddKnownTypes(testGV, &unstructured.Unstructured{})