diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/BUILD b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/BUILD index 20733ff3848..85695ff8635 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/BUILD @@ -21,6 +21,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", @@ -33,6 +34,7 @@ go_test( "//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library", "//vendor/k8s.io/utils/trace:go_default_library", 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 5b76cfa455f..6adbf84955d 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go @@ -229,11 +229,11 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, } } -// transformDecodeError adds additional information when a decode fails. +// transformDecodeError adds additional information into a bad-request api error when a decode fails. func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *schema.GroupVersionKind, body []byte) error { objGVKs, _, err := typer.ObjectKinds(into) if err != nil { - return err + return errors.NewBadRequest(err.Error()) } objGVK := objGVKs[0] if gvk != nil && len(gvk.Kind) > 0 { 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 1003de9d6f3..2c9dab5b94e 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 @@ -31,6 +31,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + testapigroupv1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -43,6 +44,7 @@ import ( examplev1 "k8s.io/apiserver/pkg/apis/example/v1" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" utiltrace "k8s.io/utils/trace" ) @@ -950,3 +952,51 @@ func (f mutateObjectUpdateFunc) Handles(operation admission.Operation) bool { func (f mutateObjectUpdateFunc) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) { return f(a.GetObject(), a.GetOldObject()) } + +func TestTransformDecodeErrorEnsuresBadRequestError(t *testing.T) { + testCases := []struct { + name string + typer runtime.ObjectTyper + decodedGVK *schema.GroupVersionKind + decodeIntoObject runtime.Object + baseErr error + expectedErr error + }{ + { + name: "decoding normal objects fails and returns a bad-request error", + typer: clientgoscheme.Scheme, + decodedGVK: &schema.GroupVersionKind{ + Group: testapigroupv1.GroupName, + Version: "v1", + Kind: "Carp", + }, + decodeIntoObject: &testapigroupv1.Carp{}, // which client-go's scheme doesn't recognize + baseErr: fmt.Errorf("plain error"), + }, + { + name: "decoding objects with unknown GVK fails and returns a bad-request error", + typer: alwaysErrorTyper{}, + decodedGVK: nil, + decodeIntoObject: &testapigroupv1.Carp{}, // which client-go's scheme doesn't recognize + baseErr: nil, + }, + } + for _, testCase := range testCases { + err := transformDecodeError(testCase.typer, testCase.baseErr, testCase.decodeIntoObject, testCase.decodedGVK, []byte(``)) + if apiStatus, ok := err.(apierrors.APIStatus); !ok || apiStatus.Status().Code != http.StatusBadRequest { + t.Errorf("expected bad request error but got: %v", err) + } + } +} + +var _ runtime.ObjectTyper = alwaysErrorTyper{} + +type alwaysErrorTyper struct{} + +func (alwaysErrorTyper) ObjectKinds(runtime.Object) ([]schema.GroupVersionKind, bool, error) { + return nil, false, fmt.Errorf("always error") +} + +func (alwaysErrorTyper) Recognizes(gvk schema.GroupVersionKind) bool { + return false +}