diff --git a/pkg/endpoints/handlers/BUILD b/pkg/endpoints/handlers/BUILD index 16901d6a6..2bf780bd6 100644 --- a/pkg/endpoints/handlers/BUILD +++ b/pkg/endpoints/handlers/BUILD @@ -71,6 +71,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/proxy:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", diff --git a/pkg/endpoints/handlers/patch.go b/pkg/endpoints/handlers/patch.go index 1ac736d09..a54054127 100644 --- a/pkg/endpoints/handlers/patch.go +++ b/pkg/endpoints/handlers/patch.go @@ -32,17 +32,34 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/mergepatch" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/audit" + "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" ) // PatchResource returns a function that will handle a resource patch // TODO: Eventually PatchResource should just use GuaranteedUpdate and this routine should be a bit cleaner -func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, converter runtime.ObjectConvertor) http.HandlerFunc { +func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, converter runtime.ObjectConvertor, patchTypes []string) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { + // Do this first, otherwise name extraction can fail for unrecognized content types + // TODO: handle this in negotiation + contentType := req.Header.Get("Content-Type") + // Remove "; charset=" if included in header. + if idx := strings.Index(contentType, ";"); idx > 0 { + contentType = contentType[:idx] + } + patchType := types.PatchType(contentType) + + // Ensure the patchType is one we support + if !sets.NewString(patchTypes...).Has(contentType) { + scope.err(negotiation.NewUnsupportedMediaTypeError(patchTypes), w, req) + return + } + // TODO: we either want to remove timeout or document it (if we // document, move timeout out of this function and declare it in // api_installer) @@ -63,14 +80,6 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface return } - // TODO: handle this in negotiation - contentType := req.Header.Get("Content-Type") - // Remove "; charset=" if included in header. - if idx := strings.Index(contentType, ";"); idx > 0 { - contentType = contentType[:idx] - } - patchType := types.PatchType(contentType) - patchJS, err := readBody(req) if err != nil { scope.err(err, w, req) diff --git a/pkg/endpoints/installer.go b/pkg/endpoints/installer.go index 87bf1700d..11f658ee8 100644 --- a/pkg/endpoints/installer.go +++ b/pkg/endpoints/installer.go @@ -690,7 +690,12 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag if hasSubresource { doc = "partially update " + subresource + " of the specified " + kind } - handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulPatchResource(patcher, reqScope, admit, mapping.ObjectConvertor)) + supportedTypes := []string{ + string(types.JSONPatchType), + string(types.MergePatchType), + string(types.StrategicMergePatchType), + } + handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulPatchResource(patcher, reqScope, admit, mapping.ObjectConvertor, supportedTypes)) route := ws.PATCH(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). @@ -1099,9 +1104,9 @@ func restfulUpdateResource(r rest.Updater, scope handlers.RequestScope, typer ru } } -func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit admission.Interface, converter runtime.ObjectConvertor) restful.RouteFunction { +func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit admission.Interface, converter runtime.ObjectConvertor, supportedTypes []string) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { - handlers.PatchResource(r, scope, admit, converter)(res.ResponseWriter, req.Request) + handlers.PatchResource(r, scope, admit, converter, supportedTypes)(res.ResponseWriter, req.Request) } }