diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index a5bc218a52b..1cdfb843280 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -13070,6 +13070,10 @@ "type": "string", "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)." }, + "group": { + "type": "string", + "description": "The group attribute of the resource associated with the status StatusReason." + }, "kind": { "type": "string", "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" diff --git a/api/swagger-spec/v1beta1.json b/api/swagger-spec/v1beta1.json index a8f9d1dc18d..5139f1f6ea7 100644 --- a/api/swagger-spec/v1beta1.json +++ b/api/swagger-spec/v1beta1.json @@ -2900,6 +2900,10 @@ "type": "string", "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)." }, + "group": { + "type": "string", + "description": "The group attribute of the resource associated with the status StatusReason." + }, "kind": { "type": "string", "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" diff --git a/docs/api-reference/extensions/v1beta1/definitions.html b/docs/api-reference/extensions/v1beta1/definitions.html index 3acf3acf7ed..88d6f56d4c1 100755 --- a/docs/api-reference/extensions/v1beta1/definitions.html +++ b/docs/api-reference/extensions/v1beta1/definitions.html @@ -1331,6 +1331,13 @@ Both these may change in the future. Incoming requests are matched against the h +

group

+

The group attribute of the resource associated with the status StatusReason.

+

false

+

string

+ + +

kind

The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds

false

@@ -4278,7 +4285,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html index 8ecd9266a27..bb4255bacda 100755 --- a/docs/api-reference/v1/definitions.html +++ b/docs/api-reference/v1/definitions.html @@ -4435,6 +4435,13 @@ The resulting set of endpoints can be viewed as:
+

group

+

The group attribute of the resource associated with the status StatusReason.

+

false

+

string

+ + +

kind

The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds

false

@@ -6918,7 +6925,7 @@ The resulting set of endpoints can be viewed as:
diff --git a/pkg/admission/errors.go b/pkg/admission/errors.go index b009e86a022..390f9dab178 100644 --- a/pkg/admission/errors.go +++ b/pkg/admission/errors.go @@ -23,14 +23,14 @@ import ( utilerrors "k8s.io/kubernetes/pkg/util/errors" ) -func extractKindName(a Attributes) (name string, kind unversioned.GroupKind, err error) { +func extractResourceName(a Attributes) (name string, resource unversioned.GroupResource, err error) { name = "Unknown" - kind = a.GetKind() + resource = a.GetResource() obj := a.GetObject() if obj != nil { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { - return "", unversioned.GroupKind{}, err + return "", unversioned.GroupResource{}, err } // this is necessary because name object name generation has not occurred yet @@ -40,7 +40,7 @@ func extractKindName(a Attributes) (name string, kind unversioned.GroupKind, err name = objectMeta.GenerateName } } - return name, kind, nil + return name, resource, nil } // NewForbidden is a utility function to return a well-formatted admission control error response @@ -49,18 +49,18 @@ func NewForbidden(a Attributes, internalError error) error { if apierrors.IsForbidden(internalError) { return internalError } - name, kind, err := extractKindName(a) + name, resource, err := extractResourceName(a) if err != nil { return apierrors.NewInternalError(utilerrors.NewAggregate([]error{internalError, err})) } - return apierrors.NewForbidden(kind.Kind, name, internalError) + return apierrors.NewForbidden(resource, name, internalError) } // NewNotFound is a utility function to return a well-formatted admission control error response func NewNotFound(a Attributes) error { - name, kind, err := extractKindName(a) + name, resource, err := extractResourceName(a) if err != nil { return apierrors.NewInternalError(err) } - return apierrors.NewNotFound(kind.Kind, name) + return apierrors.NewNotFound(resource, name) } diff --git a/pkg/api/errors/errors.go b/pkg/api/errors/errors.go index 92cc423a9b9..a78b7bc240e 100644 --- a/pkg/api/errors/errors.go +++ b/pkg/api/errors/errors.go @@ -93,30 +93,32 @@ func FromObject(obj runtime.Object) error { } // NewNotFound returns a new error which indicates that the resource of the kind and the name was not found. -func NewNotFound(kind, name string) error { +func NewNotFound(qualifiedResource unversioned.GroupResource, name string) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusNotFound, Reason: unversioned.StatusReasonNotFound, Details: &unversioned.StatusDetails{ - Kind: kind, - Name: name, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, + Name: name, }, - Message: fmt.Sprintf("%s %q not found", kind, name), + Message: fmt.Sprintf("%s %q not found", qualifiedResource.String(), name), }} } // NewAlreadyExists returns an error indicating the item requested exists by that identifier. -func NewAlreadyExists(kind, name string) error { +func NewAlreadyExists(qualifiedResource unversioned.GroupResource, name string) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusConflict, Reason: unversioned.StatusReasonAlreadyExists, Details: &unversioned.StatusDetails{ - Kind: kind, - Name: name, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, + Name: name, }, - Message: fmt.Sprintf("%s %q already exists", kind, name), + Message: fmt.Sprintf("%s %q already exists", qualifiedResource.String(), name), }} } @@ -136,30 +138,32 @@ func NewUnauthorized(reason string) error { } // NewForbidden returns an error indicating the requested action was forbidden -func NewForbidden(kind, name string, err error) error { +func NewForbidden(qualifiedResource unversioned.GroupResource, name string, err error) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusForbidden, Reason: unversioned.StatusReasonForbidden, Details: &unversioned.StatusDetails{ - Kind: kind, - Name: name, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, + Name: name, }, - Message: fmt.Sprintf("%s %q is forbidden: %v", kind, name, err), + Message: fmt.Sprintf("%s %q is forbidden: %v", qualifiedResource.String(), name, err), }} } // NewConflict returns an error indicating the item can't be updated as provided. -func NewConflict(kind, name string, err error) error { +func NewConflict(qualifiedResource unversioned.GroupResource, name string, err error) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusConflict, Reason: unversioned.StatusReasonConflict, Details: &unversioned.StatusDetails{ - Kind: kind, - Name: name, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, + Name: name, }, - Message: fmt.Sprintf("%s %q cannot be updated: %v", kind, name, err), + Message: fmt.Sprintf("%s %q cannot be updated: %v", qualifiedResource.String(), name, err), }} } @@ -174,7 +178,7 @@ func NewGone(message string) error { } // NewInvalid returns an error indicating the item is invalid and cannot be processed. -func NewInvalid(kind, name string, errs field.ErrorList) error { +func NewInvalid(qualifiedKind unversioned.GroupKind, name string, errs field.ErrorList) error { causes := make([]unversioned.StatusCause, 0, len(errs)) for i := range errs { err := errs[i] @@ -189,11 +193,12 @@ func NewInvalid(kind, name string, errs field.ErrorList) error { Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity Reason: unversioned.StatusReasonInvalid, Details: &unversioned.StatusDetails{ - Kind: kind, + Group: qualifiedKind.Group, + Kind: qualifiedKind.Kind, Name: name, Causes: causes, }, - Message: fmt.Sprintf("%s %q is invalid: %v", kind, name, errs.ToAggregate()), + Message: fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, errs.ToAggregate()), }} } @@ -218,34 +223,42 @@ func NewServiceUnavailable(reason string) error { } // NewMethodNotSupported returns an error indicating the requested action is not supported on this kind. -func NewMethodNotSupported(kind, action string) error { +func NewMethodNotSupported(qualifiedResource unversioned.GroupResource, action string) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusMethodNotAllowed, Reason: unversioned.StatusReasonMethodNotAllowed, Details: &unversioned.StatusDetails{ - Kind: kind, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, }, - Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, kind), + Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, qualifiedResource.String()), }} } // NewServerTimeout returns an error indicating the requested action could not be completed due to a // transient error, and the client should try again. -func NewServerTimeout(kind, operation string, retryAfterSeconds int) error { +func NewServerTimeout(qualifiedResource unversioned.GroupResource, operation string, retryAfterSeconds int) error { return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: http.StatusInternalServerError, Reason: unversioned.StatusReasonServerTimeout, Details: &unversioned.StatusDetails{ - Kind: kind, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, Name: operation, RetryAfterSeconds: int32(retryAfterSeconds), }, - Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, kind), + Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, qualifiedResource.String()), }} } +// NewServerTimeoutForKind should not exist. Server timeouts happen when accessing resources, the Kind is just what we +// happened to be looking at when the request failed. This delegates to keep code sane, but we should work towards removing this. +func NewServerTimeoutForKind(qualifiedKind unversioned.GroupKind, operation string, retryAfterSeconds int) error { + return NewServerTimeout(unversioned.GroupResource{Group: qualifiedKind.Group, Resource: qualifiedKind.Kind}, operation, retryAfterSeconds) +} + // NewInternalError returns an error indicating the item is invalid and cannot be processed. func NewInternalError(err error) error { return &StatusError{unversioned.Status{ @@ -274,7 +287,7 @@ func NewTimeoutError(message string, retryAfterSeconds int) error { } // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form. -func NewGenericServerResponse(code int, verb, kind, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) error { +func NewGenericServerResponse(code int, verb string, qualifiedResource unversioned.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) error { reason := unversioned.StatusReasonUnknown message := fmt.Sprintf("the server responded with the status code %d but did not return more information", code) switch code { @@ -316,10 +329,10 @@ func NewGenericServerResponse(code int, verb, kind, name, serverMessage string, } } switch { - case len(kind) > 0 && len(name) > 0: - message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), kind, name) - case len(kind) > 0: - message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), kind) + case !qualifiedResource.IsEmpty() && len(name) > 0: + message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), qualifiedResource.String(), name) + case !qualifiedResource.IsEmpty(): + message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), qualifiedResource.String()) } var causes []unversioned.StatusCause if isUnexpectedResponse { @@ -337,8 +350,9 @@ func NewGenericServerResponse(code int, verb, kind, name, serverMessage string, Code: int32(code), Reason: reason, Details: &unversioned.StatusDetails{ - Kind: kind, - Name: name, + Group: qualifiedResource.Group, + Kind: qualifiedResource.Resource, + Name: name, Causes: causes, RetryAfterSeconds: int32(retryAfterSeconds), diff --git a/pkg/api/errors/errors_test.go b/pkg/api/errors/errors_test.go index 0e7cebbb74f..633bd118db2 100644 --- a/pkg/api/errors/errors_test.go +++ b/pkg/api/errors/errors_test.go @@ -22,13 +22,14 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation/field" ) func TestErrorNew(t *testing.T) { - err := NewAlreadyExists("test", "1") + err := NewAlreadyExists(api.Resource("tests"), "1") if !IsAlreadyExists(err) { t.Errorf("expected to be %s", unversioned.StatusReasonAlreadyExists) } @@ -54,34 +55,34 @@ func TestErrorNew(t *testing.T) { t.Errorf("expected to not be %s", unversioned.StatusReasonMethodNotAllowed) } - if !IsConflict(NewConflict("test", "2", errors.New("message"))) { + if !IsConflict(NewConflict(api.Resource("tests"), "2", errors.New("message"))) { t.Errorf("expected to be conflict") } - if !IsNotFound(NewNotFound("test", "3")) { + if !IsNotFound(NewNotFound(api.Resource("tests"), "3")) { t.Errorf("expected to be %s", unversioned.StatusReasonNotFound) } - if !IsInvalid(NewInvalid("test", "2", nil)) { + if !IsInvalid(NewInvalid(api.Kind("Test"), "2", nil)) { t.Errorf("expected to be %s", unversioned.StatusReasonInvalid) } if !IsBadRequest(NewBadRequest("reason")) { t.Errorf("expected to be %s", unversioned.StatusReasonBadRequest) } - if !IsForbidden(NewForbidden("test", "2", errors.New("reason"))) { + if !IsForbidden(NewForbidden(api.Resource("tests"), "2", errors.New("reason"))) { t.Errorf("expected to be %s", unversioned.StatusReasonForbidden) } if !IsUnauthorized(NewUnauthorized("reason")) { t.Errorf("expected to be %s", unversioned.StatusReasonUnauthorized) } - if !IsServerTimeout(NewServerTimeout("test", "reason", 0)) { + if !IsServerTimeout(NewServerTimeout(api.Resource("tests"), "reason", 0)) { t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) } - if time, ok := SuggestsClientDelay(NewServerTimeout("test", "doing something", 10)); time != 10 || !ok { + if time, ok := SuggestsClientDelay(NewServerTimeout(api.Resource("tests"), "doing something", 10)); time != 10 || !ok { t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) } if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok { t.Errorf("expected to be %s", unversioned.StatusReasonTimeout) } - if !IsMethodNotSupported(NewMethodNotSupported("foo", "delete")) { + if !IsMethodNotSupported(NewMethodNotSupported(api.Resource("foos"), "delete")) { t.Errorf("expected to be %s", unversioned.StatusReasonMethodNotAllowed) } } @@ -94,7 +95,7 @@ func TestNewInvalid(t *testing.T) { { field.Duplicate(field.NewPath("field[0].name"), "bar"), &unversioned.StatusDetails{ - Kind: "kind", + Kind: "Kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueDuplicate, @@ -105,7 +106,7 @@ func TestNewInvalid(t *testing.T) { { field.Invalid(field.NewPath("field[0].name"), "bar", "detail"), &unversioned.StatusDetails{ - Kind: "kind", + Kind: "Kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueInvalid, @@ -116,7 +117,7 @@ func TestNewInvalid(t *testing.T) { { field.NotFound(field.NewPath("field[0].name"), "bar"), &unversioned.StatusDetails{ - Kind: "kind", + Kind: "Kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotFound, @@ -127,7 +128,7 @@ func TestNewInvalid(t *testing.T) { { field.NotSupported(field.NewPath("field[0].name"), "bar", nil), &unversioned.StatusDetails{ - Kind: "kind", + Kind: "Kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotSupported, @@ -138,7 +139,7 @@ func TestNewInvalid(t *testing.T) { { field.Required(field.NewPath("field[0].name")), &unversioned.StatusDetails{ - Kind: "kind", + Kind: "Kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueRequired, @@ -150,7 +151,7 @@ func TestNewInvalid(t *testing.T) { for i, testCase := range testCases { vErr, expected := testCase.Err, testCase.Details expected.Causes[0].Message = vErr.ErrorBody() - err := NewInvalid("kind", "name", field.ErrorList{vErr}) + err := NewInvalid(api.Kind("Kind"), "name", field.ErrorList{vErr}) status := err.(*StatusError).ErrStatus if status.Code != 422 || status.Reason != unversioned.StatusReasonInvalid { t.Errorf("%d: unexpected status: %#v", i, status) diff --git a/pkg/api/errors/etcd/etcd.go b/pkg/api/errors/etcd/etcd.go index d0ad3e11df3..3e09aebaa0f 100644 --- a/pkg/api/errors/etcd/etcd.go +++ b/pkg/api/errors/etcd/etcd.go @@ -18,17 +18,18 @@ package etcd import ( "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/storage" ) // InterpretListError converts a generic error on a retrieval // operation into the appropriate API error. -func InterpretListError(err error, kind string) error { +func InterpretListError(err error, qualifiedResource unversioned.GroupResource) error { switch { case storage.IsNotFound(err): - return errors.NewNotFound(kind, "") + return errors.NewNotFound(qualifiedResource, "") case storage.IsUnreachable(err): - return errors.NewServerTimeout(kind, "list", 2) // TODO: make configurable or handled at a higher level + return errors.NewServerTimeout(qualifiedResource, "list", 2) // TODO: make configurable or handled at a higher level default: return err } @@ -36,12 +37,12 @@ func InterpretListError(err error, kind string) error { // InterpretGetError converts a generic error on a retrieval // operation into the appropriate API error. -func InterpretGetError(err error, kind, name string) error { +func InterpretGetError(err error, qualifiedResource unversioned.GroupResource, name string) error { switch { case storage.IsNotFound(err): - return errors.NewNotFound(kind, name) + return errors.NewNotFound(qualifiedResource, name) case storage.IsUnreachable(err): - return errors.NewServerTimeout(kind, "get", 2) // TODO: make configurable or handled at a higher level + return errors.NewServerTimeout(qualifiedResource, "get", 2) // TODO: make configurable or handled at a higher level default: return err } @@ -49,12 +50,12 @@ func InterpretGetError(err error, kind, name string) error { // InterpretCreateError converts a generic error on a create // operation into the appropriate API error. -func InterpretCreateError(err error, kind, name string) error { +func InterpretCreateError(err error, qualifiedResource unversioned.GroupResource, name string) error { switch { case storage.IsNodeExist(err): - return errors.NewAlreadyExists(kind, name) + return errors.NewAlreadyExists(qualifiedResource, name) case storage.IsUnreachable(err): - return errors.NewServerTimeout(kind, "create", 2) // TODO: make configurable or handled at a higher level + return errors.NewServerTimeout(qualifiedResource, "create", 2) // TODO: make configurable or handled at a higher level default: return err } @@ -62,12 +63,12 @@ func InterpretCreateError(err error, kind, name string) error { // InterpretUpdateError converts a generic error on a update // operation into the appropriate API error. -func InterpretUpdateError(err error, kind, name string) error { +func InterpretUpdateError(err error, qualifiedResource unversioned.GroupResource, name string) error { switch { case storage.IsTestFailed(err), storage.IsNodeExist(err): - return errors.NewConflict(kind, name, err) + return errors.NewConflict(qualifiedResource, name, err) case storage.IsUnreachable(err): - return errors.NewServerTimeout(kind, "update", 2) // TODO: make configurable or handled at a higher level + return errors.NewServerTimeout(qualifiedResource, "update", 2) // TODO: make configurable or handled at a higher level default: return err } @@ -75,12 +76,12 @@ func InterpretUpdateError(err error, kind, name string) error { // InterpretDeleteError converts a generic error on a delete // operation into the appropriate API error. -func InterpretDeleteError(err error, kind, name string) error { +func InterpretDeleteError(err error, qualifiedResource unversioned.GroupResource, name string) error { switch { case storage.IsNotFound(err): - return errors.NewNotFound(kind, name) + return errors.NewNotFound(qualifiedResource, name) case storage.IsUnreachable(err): - return errors.NewServerTimeout(kind, "delete", 2) // TODO: make configurable or handled at a higher level + return errors.NewServerTimeout(qualifiedResource, "delete", 2) // TODO: make configurable or handled at a higher level default: return err } diff --git a/pkg/api/rest/create.go b/pkg/api/rest/create.go index d20c122056d..6c39e81247c 100644 --- a/pkg/api/rest/create.go +++ b/pkg/api/rest/create.go @@ -19,6 +19,7 @@ package rest import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation/field" @@ -71,14 +72,14 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Obje api.GenerateName(strategy, objectMeta) if errs := strategy.Validate(ctx, obj); len(errs) > 0 { - return errors.NewInvalid(kind, objectMeta.Name, errs) + return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) } // Custom validation (including name validation) passed // Now run common validation on object meta // Do this *after* custom validation so that specific error messages are shown whenever possible if errs := validation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), validation.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 { - return errors.NewInvalid(kind, objectMeta.Name, errs) + return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) } strategy.Canonicalize(obj) @@ -102,18 +103,18 @@ func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime return err } - return errors.NewServerTimeout(kind, "POST", 0) + return errors.NewServerTimeoutForKind(kind.GroupKind(), "POST", 0) } // objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error. -func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, string, error) { +func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, unversioned.GroupVersionKind, error) { objectMeta, err := api.ObjectMetaFor(obj) if err != nil { - return nil, "", errors.NewInternalError(err) + return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) } - gvk, err := typer.ObjectKind(obj) + kind, err := typer.ObjectKind(obj) if err != nil { - return nil, "", errors.NewInternalError(err) + return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) } - return objectMeta, gvk.Kind, nil + return objectMeta, kind, nil } diff --git a/pkg/api/rest/update.go b/pkg/api/rest/update.go index 8f08d49b23c..80ad14f866b 100644 --- a/pkg/api/rest/update.go +++ b/pkg/api/rest/update.go @@ -96,7 +96,7 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...) if len(errs) > 0 { - return errors.NewInvalid(kind, objectMeta.Name, errs) + return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) } strategy.Canonicalize(obj) diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index 7cc74e843a4..e1bae179d8d 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -33,8 +33,15 @@ func (gr GroupResource) WithVersion(version string) GroupVersionResource { return GroupVersionResource{Group: gr.Group, Version: version, Resource: gr.Resource} } +func (gr GroupResource) IsEmpty() bool { + return len(gr.Group) == 0 && len(gr.Resource) == 0 +} + func (gr *GroupResource) String() string { - return strings.Join([]string{gr.Group, ", Resource=", gr.Resource}, "") + if len(gr.Group) == 0 { + return gr.Resource + } + return gr.Resource + "." + gr.Group } // GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion @@ -66,12 +73,19 @@ type GroupKind struct { Kind string } +func (gk GroupKind) IsEmpty() bool { + return len(gk.Group) == 0 && len(gk.Kind) == 0 +} + func (gk GroupKind) WithVersion(version string) GroupVersionKind { return GroupVersionKind{Group: gk.Group, Version: version, Kind: gk.Kind} } func (gk *GroupKind) String() string { - return gk.Group + ", Kind=" + gk.Kind + if len(gk.Group) == 0 { + return gk.Kind + } + return gk.Kind + "." + gk.Group } // GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion diff --git a/pkg/api/unversioned/types.go b/pkg/api/unversioned/types.go index a38f53b2e41..f2c018ae1a0 100644 --- a/pkg/api/unversioned/types.go +++ b/pkg/api/unversioned/types.go @@ -121,6 +121,8 @@ type StatusDetails struct { // The name attribute of the resource associated with the status StatusReason // (when there is a single name which can be described). Name string `json:"name,omitempty"` + // The group attribute of the resource associated with the status StatusReason. + Group string `json:"group,omitempty"` // The kind attribute of the resource associated with the status StatusReason. // On some operations may differ from the requested resource Kind. // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds diff --git a/pkg/api/unversioned/types_swagger_doc_generated.go b/pkg/api/unversioned/types_swagger_doc_generated.go index e1a8c1b8506..8e337dd820f 100644 --- a/pkg/api/unversioned/types_swagger_doc_generated.go +++ b/pkg/api/unversioned/types_swagger_doc_generated.go @@ -164,6 +164,7 @@ func (StatusCause) SwaggerDoc() map[string]string { var map_StatusDetails = map[string]string{ "": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", "name": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "group": "The group attribute of the resource associated with the status StatusReason.", "kind": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", "causes": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", "retryAfterSeconds": "If specified, the time in seconds before the operation should be retried.", diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 1a2042fbfc8..6ca4dc17c4a 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -211,7 +211,7 @@ func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) glog.Errorln(buffer.String()) // TODO: make status unversioned or plumb enough of the request to deduce the requested API version - errorJSON(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", "", "", "", 0, false), latest.GroupOrDie(api.GroupName).Codec, httpWriter) + errorJSON(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), latest.GroupOrDie(api.GroupName).Codec, httpWriter) } func InstallServiceErrorHandler(container *restful.Container, requestResolver *RequestInfoResolver, apiVersions []string) { @@ -234,7 +234,7 @@ func serviceErrorHandler(requestResolver *RequestInfoResolver, apiVersions []str } } - errorJSON(apierrors.NewGenericServerResponse(serviceErr.Code, "", "", "", "", 0, false), codec, response.ResponseWriter) + errorJSON(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), codec, response.ResponseWriter) } // Adds a service to return the supported api versions at the legacy /api. diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 02f626b4f5e..b91761369b7 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -1489,7 +1489,7 @@ func TestGetNamespaceSelfLink(t *testing.T) { func TestGetMissing(t *testing.T) { storage := map[string]rest.Storage{} simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")}, + errors: map[string]error{"get": apierrs.NewNotFound(api.Resource("simples"), "id")}, } storage["simple"] = &simpleStorage handler := handle(storage) @@ -1590,7 +1590,7 @@ func TestConnectResponderError(t *testing.T) { connectStorage := &ConnecterRESTStorage{} connectStorage.handlerFunc = func() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - connectStorage.receivedResponder.Error(apierrs.NewForbidden("simple", itemID, errors.New("you are terminated"))) + connectStorage.receivedResponder.Error(apierrs.NewForbidden(api.Resource("simples"), itemID, errors.New("you are terminated"))) }) } storage := map[string]rest.Storage{ @@ -1897,7 +1897,7 @@ func TestDeleteMissing(t *testing.T) { storage := map[string]rest.Storage{} ID := "id" simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)}, + errors: map[string]error{"delete": apierrs.NewNotFound(api.Resource("simples"), ID)}, } storage["simple"] = &simpleStorage handler := handle(storage) @@ -2198,7 +2198,7 @@ func TestUpdateMissing(t *testing.T) { storage := map[string]rest.Storage{} ID := "id" simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)}, + errors: map[string]error{"update": apierrs.NewNotFound(api.Resource("simples"), ID)}, } storage["simple"] = &simpleStorage handler := handle(storage) @@ -2233,7 +2233,7 @@ func TestCreateNotFound(t *testing.T) { "simple": &SimpleRESTStorage{ // storage.Create can fail with not found error in theory. // See http://pr.k8s.io/486#discussion_r15037092. - errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")}, + errors: map[string]error{"create": apierrs.NewNotFound(api.Resource("simples"), "id")}, }, }) server := httptest.NewServer(handler) @@ -2729,7 +2729,7 @@ func expectApiStatus(t *testing.T, method, url string, data []byte, code int) *u func TestDelayReturnsError(t *testing.T) { storage := SimpleRESTStorage{ injectedFunction: func(obj runtime.Object) (runtime.Object, error) { - return nil, apierrs.NewAlreadyExists("foo", "bar") + return nil, apierrs.NewAlreadyExists(api.Resource("foos"), "bar") }, } handler := handle(map[string]rest.Storage{"foo": &storage}) diff --git a/pkg/apiserver/errors_test.go b/pkg/apiserver/errors_test.go index e5cdfae53ca..4dbf789a344 100644 --- a/pkg/apiserver/errors_test.go +++ b/pkg/apiserver/errors_test.go @@ -22,40 +22,44 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" ) func TestErrorsToAPIStatus(t *testing.T) { cases := map[error]unversioned.Status{ - errors.NewNotFound("foo", "bar"): { + errors.NewNotFound(unversioned.GroupResource{Group: "legacy.kubernetes.io", Resource: "foos"}, "bar"): { Status: unversioned.StatusFailure, Code: http.StatusNotFound, Reason: unversioned.StatusReasonNotFound, - Message: "foo \"bar\" not found", + Message: "foos.legacy.kubernetes.io \"bar\" not found", Details: &unversioned.StatusDetails{ - Kind: "foo", - Name: "bar", + Group: "legacy.kubernetes.io", + Kind: "foos", + Name: "bar", }, }, - errors.NewAlreadyExists("foo", "bar"): { + errors.NewAlreadyExists(api.Resource("foos"), "bar"): { Status: unversioned.StatusFailure, Code: http.StatusConflict, Reason: "AlreadyExists", - Message: "foo \"bar\" already exists", + Message: "foos \"bar\" already exists", Details: &unversioned.StatusDetails{ - Kind: "foo", - Name: "bar", + Group: "", + Kind: "foos", + Name: "bar", }, }, - errors.NewConflict("foo", "bar", stderrs.New("failure")): { + errors.NewConflict(api.Resource("foos"), "bar", stderrs.New("failure")): { Status: unversioned.StatusFailure, Code: http.StatusConflict, Reason: "Conflict", - Message: "foo \"bar\" cannot be updated: failure", + Message: "foos \"bar\" cannot be updated: failure", Details: &unversioned.StatusDetails{ - Kind: "foo", - Name: "bar", + Group: "", + Kind: "foos", + Name: "bar", }, }, } diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index 83862485c09..2f7e439f32f 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -245,7 +245,7 @@ func (tw *baseTimeoutWriter) timeout(msg string) { tw.w.Write([]byte(msg)) } else { enc := json.NewEncoder(tw.w) - enc.Encode(errors.NewServerTimeout("", "", 0)) + enc.Encode(errors.NewServerTimeout(api.Resource(""), "", 0)) } } tw.timedOut = true diff --git a/pkg/apiserver/proxy.go b/pkg/apiserver/proxy.go index f19f2143e3d..df88d726da3 100644 --- a/pkg/apiserver/proxy.go +++ b/pkg/apiserver/proxy.go @@ -101,7 +101,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) - httpCode = errorJSON(errors.NewMethodNotSupported(resource, "proxy"), r.codec, w) + httpCode = errorJSON(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.codec, w) return } diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index ab060b1045d..1870ed389d9 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -78,7 +78,7 @@ func (p *testPatcher) New() runtime.Object { func (p *testPatcher) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { inPod := obj.(*api.Pod) if inPod.ResourceVersion != p.updatePod.ResourceVersion { - return nil, false, apierrors.NewConflict("Pod", inPod.Name, fmt.Errorf("existing %v, new %v", p.updatePod.ResourceVersion, inPod.ResourceVersion)) + return nil, false, apierrors.NewConflict(api.Resource("pods"), inPod.Name, fmt.Errorf("existing %v, new %v", p.updatePod.ResourceVersion, inPod.ResourceVersion)) } return inPod, false, nil @@ -306,7 +306,7 @@ func TestPatchResourceWithConflict(t *testing.T) { changedPod: &api.Pod{}, updatePod: &api.Pod{}, - expectedError: `Pod "foo" cannot be updated: existing 2, new 1`, + expectedError: `pods "foo" cannot be updated: existing 2, new 1`, } tc.startingPod.Name = name diff --git a/pkg/client/leaderelection/leaderelection_test.go b/pkg/client/leaderelection/leaderelection_test.go index 486b80c17dd..63f38c1e6d6 100644 --- a/pkg/client/leaderelection/leaderelection_test.go +++ b/pkg/client/leaderelection/leaderelection_test.go @@ -58,7 +58,7 @@ func TestTryAcquireOrRenew(t *testing.T) { { verb: "get", reaction: func(action testclient.Action) (handled bool, ret runtime.Object, err error) { - return true, nil, errors.NewNotFound(action.GetVerb(), action.(testclient.GetAction).GetName()) + return true, nil, errors.NewNotFound(api.Resource(action.(testclient.GetAction).GetResource()), action.(testclient.GetAction).GetName()) }, }, { diff --git a/pkg/client/unversioned/request.go b/pkg/client/unversioned/request.go index ac0121db5ac..85d29802c10 100644 --- a/pkg/client/unversioned/request.go +++ b/pkg/client/unversioned/request.go @@ -896,7 +896,7 @@ func (r *Request) transformUnstructuredResponseError(resp *http.Response, req *h message = strings.TrimSpace(string(body)) } retryAfter, _ := retryAfterSeconds(resp) - return errors.NewGenericServerResponse(resp.StatusCode, req.Method, r.resource, r.resourceName, message, retryAfter, true) + return errors.NewGenericServerResponse(resp.StatusCode, req.Method, unversioned.GroupResource{Group: r.groupVersion.Group, Resource: r.resource}, r.resourceName, message, retryAfter, true) } // isTextResponse returns true if the response appears to be a textual media type. diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index 9407acc4554..fad088e99de 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -181,7 +181,7 @@ func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.O } return out, nil } - return nilValue, errors.NewNotFound(kind.Kind, name) + return nilValue, errors.NewNotFound(unversioned.GroupResource{Group: kind.Group, Resource: kind.Kind}, name) } index := o.last[kind.Kind] @@ -189,7 +189,7 @@ func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.O index = len(arr) - 1 } if index < 0 { - return nilValue, errors.NewNotFound(kind.Kind, name) + return nilValue, errors.NewNotFound(unversioned.GroupResource{Group: kind.Group, Resource: kind.Kind}, name) } out, err := o.scheme.Copy(arr[index]) if err != nil { diff --git a/pkg/client/unversioned/testclient/testclient_test.go b/pkg/client/unversioned/testclient/testclient_test.go index cacf76b7dc0..6c7075e2846 100644 --- a/pkg/client/unversioned/testclient/testclient_test.go +++ b/pkg/client/unversioned/testclient/testclient_test.go @@ -56,9 +56,9 @@ func TestErrors(t *testing.T) { o.Add(&api.List{ Items: []runtime.Object{ // This first call to List will return this error - &(errors.NewNotFound("ServiceList", "").(*errors.StatusError).ErrStatus), + &(errors.NewNotFound(api.Resource("ServiceList"), "").(*errors.StatusError).ErrStatus), // The second call to List will return this error - &(errors.NewForbidden("ServiceList", "", nil).(*errors.StatusError).ErrStatus), + &(errors.NewForbidden(api.Resource("ServiceList"), "", nil).(*errors.StatusError).ErrStatus), }, }) client := &Fake{} diff --git a/pkg/controller/namespace/namespace_controller_test.go b/pkg/controller/namespace/namespace_controller_test.go index ff083ec9028..597ed9df275 100644 --- a/pkg/controller/namespace/namespace_controller_test.go +++ b/pkg/controller/namespace/namespace_controller_test.go @@ -180,7 +180,7 @@ func TestRetryOnConflictError(t *testing.T) { retryOnce := func(kubeClient client.Interface, namespace *api.Namespace) (*api.Namespace, error) { numTries++ if numTries <= 1 { - return namespace, errors.NewConflict(namespace.Kind, namespace.Name, fmt.Errorf("ERROR!")) + return namespace, errors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("ERROR!")) } return namespace, nil } diff --git a/pkg/controller/node/nodecontroller_test.go b/pkg/controller/node/nodecontroller_test.go index 3ea10699f68..614bbcaa042 100644 --- a/pkg/controller/node/nodecontroller_test.go +++ b/pkg/controller/node/nodecontroller_test.go @@ -73,7 +73,7 @@ func (m *FakeNodeHandler) Create(node *api.Node) (*api.Node, error) { }() for _, n := range m.Existing { if n.Name == node.Name { - return nil, apierrors.NewAlreadyExists("Node", node.Name) + return nil, apierrors.NewAlreadyExists(api.Resource("nodes"), node.Name) } } if m.CreateHook == nil || m.CreateHook(m, node) { diff --git a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go index 96762d6bdab..b0fcc135646 100644 --- a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go @@ -419,7 +419,7 @@ func (c *mockBinderClient) GetPersistentVolumeClaim(namespace, name string) (*ap if c.claim != nil { return c.claim, nil } else { - return nil, errors.NewNotFound("persistentVolume", name) + return nil, errors.NewNotFound(api.Resource("persistentvolumes"), name) } } diff --git a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go index 3a15b7ad616..d05fa38b860 100644 --- a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go @@ -211,7 +211,7 @@ func (c *mockControllerClient) GetPersistentVolumeClaim(namespace, name string) if c.claim != nil { return c.claim, nil } else { - return nil, errors.NewNotFound("persistentVolume", name) + return nil, errors.NewNotFound(api.Resource("persistentvolumes"), name) } } diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index e02bee4c433..61da45f4f88 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -192,7 +192,7 @@ func TestDeleteAllNotFound(t *testing.T) { // Add an item to the list which will result in a 404 on delete svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - notFoundError := &errors.NewNotFound("Service", "foo").(*errors.StatusError).ErrStatus + notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").(*errors.StatusError).ErrStatus tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ @@ -234,7 +234,7 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) { // Add an item to the list which will result in a 404 on delete svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - notFoundError := &errors.NewNotFound("Service", "foo").(*errors.StatusError).ErrStatus + notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").(*errors.StatusError).ErrStatus tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ diff --git a/pkg/kubectl/cmd/util/helpers_test.go b/pkg/kubectl/cmd/util/helpers_test.go index ef641d98d0e..574012a2f63 100644 --- a/pkg/kubectl/cmd/util/helpers_test.go +++ b/pkg/kubectl/cmd/util/helpers_test.go @@ -274,15 +274,15 @@ func TestCheckInvalidErr(t *testing.T) { expected string }{ { - errors.NewInvalid("Invalid1", "invalidation", field.ErrorList{field.Invalid(field.NewPath("field"), "single", "details")}), + errors.NewInvalid(api.Kind("Invalid1"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field"), "single", "details")}), `Error from server: Invalid1 "invalidation" is invalid: field: invalid value 'single', Details: details`, }, { - errors.NewInvalid("Invalid2", "invalidation", field.ErrorList{field.Invalid(field.NewPath("field1"), "multi1", "details"), field.Invalid(field.NewPath("field2"), "multi2", "details")}), + errors.NewInvalid(api.Kind("Invalid2"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field1"), "multi1", "details"), field.Invalid(field.NewPath("field2"), "multi2", "details")}), `Error from server: Invalid2 "invalidation" is invalid: [field1: invalid value 'multi1', Details: details, field2: invalid value 'multi2', Details: details]`, }, { - errors.NewInvalid("Invalid3", "invalidation", field.ErrorList{}), + errors.NewInvalid(api.Kind("Invalid3"), "invalidation", field.ErrorList{}), `Error from server: Invalid3 "invalidation" is invalid: `, }, } diff --git a/pkg/kubectl/rolling_updater.go b/pkg/kubectl/rolling_updater.go index 92bd08c1850..7266ff4cfb8 100644 --- a/pkg/kubectl/rolling_updater.go +++ b/pkg/kubectl/rolling_updater.go @@ -431,7 +431,7 @@ func (r *RollingUpdater) getOrCreateTargetControllerWithClient(controller *api.R func (r *RollingUpdater) existingController(controller *api.ReplicationController) (*api.ReplicationController, error) { // without rc name but generate name, there's no existing rc if len(controller.Name) == 0 && len(controller.GenerateName) > 0 { - return nil, errors.NewNotFound("ReplicationController", controller.Name) + return nil, errors.NewNotFound(api.Resource("replicationcontrollers"), controller.Name) } // controller name is required to get rc back return r.c.ReplicationControllers(controller.Namespace).Get(controller.Name) diff --git a/pkg/kubectl/scale_test.go b/pkg/kubectl/scale_test.go index 58a9d4e413d..02d222ecfe5 100644 --- a/pkg/kubectl/scale_test.go +++ b/pkg/kubectl/scale_test.go @@ -34,7 +34,7 @@ type ErrorReplicationControllers struct { func (c *ErrorReplicationControllers) Update(controller *api.ReplicationController) (*api.ReplicationController, error) { if c.invalid { - return nil, kerrors.NewInvalid(controller.Kind, controller.Name, nil) + return nil, kerrors.NewInvalid(api.Kind(controller.Kind), controller.Name, nil) } return nil, errors.New("Replication controller update failure") } @@ -254,7 +254,7 @@ type ErrorJobs struct { func (c *ErrorJobs) Update(job *extensions.Job) (*extensions.Job, error) { if c.invalid { - return nil, kerrors.NewInvalid(job.Kind, job.Name, nil) + return nil, kerrors.NewInvalid(extensions.Kind(job.Kind), job.Name, nil) } return nil, errors.New("Job update failure") } @@ -495,7 +495,7 @@ type ErrorScales struct { func (c *ErrorScales) Update(kind string, scale *extensions.Scale) (*extensions.Scale, error) { if c.invalid { - return nil, kerrors.NewInvalid(scale.Kind, scale.Name, nil) + return nil, kerrors.NewInvalid(extensions.Kind(scale.Kind), scale.Name, nil) } return nil, errors.New("scale update failure") } @@ -515,7 +515,7 @@ type ErrorDeployments struct { func (c *ErrorDeployments) Update(deployment *extensions.Deployment) (*extensions.Deployment, error) { if c.invalid { - return nil, kerrors.NewInvalid(deployment.Kind, deployment.Name, nil) + return nil, kerrors.NewInvalid(extensions.Kind(deployment.Kind), deployment.Name, nil) } return nil, errors.New("deployment update failure") } diff --git a/pkg/kubelet/status/manager_test.go b/pkg/kubelet/status/manager_test.go index e12970125fb..3a4fa4dfb33 100644 --- a/pkg/kubelet/status/manager_test.go +++ b/pkg/kubelet/status/manager_test.go @@ -256,7 +256,7 @@ func TestSyncBatchIgnoresNotFound(t *testing.T) { client := testclient.Fake{} syncer := newTestManager(&client) client.AddReactor("get", "pods", func(action testclient.Action) (bool, runtime.Object, error) { - return true, nil, errors.NewNotFound("pods", "test-pod") + return true, nil, errors.NewNotFound(api.Resource("pods"), "test-pod") }) syncer.SetPodStatus(testPod, getRandomPodStatus()) syncer.syncBatch() @@ -322,7 +322,7 @@ func TestSyncBatchNoDeadlock(t *testing.T) { // Pod not found. ret = *pod - err = errors.NewNotFound("pods", pod.Name) + err = errors.NewNotFound(api.Resource("pods"), pod.Name) m.SetPodStatus(pod, getRandomPodStatus()) m.syncBatch() verifyActions(t, client, []testclient.Action{getAction}) diff --git a/pkg/master/master.go b/pkg/master/master.go index 3433ef569e6..928d28d4bdb 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -262,7 +262,7 @@ func (m *Master) initV1ResourcesStorage(c *Config) { } serviceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) - etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", "serviceipallocation", dbClient("services")) + etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), dbClient("services")) serviceClusterIPRegistry = etcd return etcd }) @@ -271,7 +271,7 @@ func (m *Master) initV1ResourcesStorage(c *Config) { var serviceNodePortRegistry service.RangeRegistry serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.ServiceNodePortRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) - etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", "servicenodeportallocation", dbClient("services")) + etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", api.Resource("servicenodeportallocations"), dbClient("services")) serviceNodePortRegistry = etcd return etcd }) diff --git a/pkg/registry/configmap/etcd/etcd.go b/pkg/registry/configmap/etcd/etcd.go index d92e4007c0f..9a27c04257f 100644 --- a/pkg/registry/configmap/etcd/etcd.go +++ b/pkg/registry/configmap/etcd/etcd.go @@ -68,7 +68,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE // Matches objects based on labels/fields for list and watch PredicateFunc: configmap.MatchConfigMap, - EndpointName: "configmaps", + QualifiedResource: extensions.Resource("configmaps"), CreateStrategy: configmap.Strategy, UpdateStrategy: configmap.Strategy, diff --git a/pkg/registry/controller/etcd/etcd.go b/pkg/registry/controller/etcd/etcd.go index f967e2f62b7..ee0739d24ac 100644 --- a/pkg/registry/controller/etcd/etcd.go +++ b/pkg/registry/controller/etcd/etcd.go @@ -62,7 +62,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return controller.MatchController(label, field) }, - EndpointName: "replicationControllers", + QualifiedResource: api.Resource("replicationcontrollers"), // Used to validate controller creation CreateStrategy: controller.Strategy, diff --git a/pkg/registry/daemonset/etcd/etcd.go b/pkg/registry/daemonset/etcd/etcd.go index 329b4d77c1d..92936d59eac 100644 --- a/pkg/registry/daemonset/etcd/etcd.go +++ b/pkg/registry/daemonset/etcd/etcd.go @@ -64,7 +64,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return daemonset.MatchDaemonSet(label, field) }, - EndpointName: "daemonsets", + QualifiedResource: extensions.Resource("daemonsets"), // Used to validate daemon set creation CreateStrategy: daemonset.Strategy, diff --git a/pkg/registry/deployment/etcd/etcd.go b/pkg/registry/deployment/etcd/etcd.go index 026d224135e..5c3370f72e0 100644 --- a/pkg/registry/deployment/etcd/etcd.go +++ b/pkg/registry/deployment/etcd/etcd.go @@ -86,7 +86,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return deployment.MatchDeployment(label, field) }, - EndpointName: "deployments", + QualifiedResource: extensions.Resource("deployments"), // Used to validate deployment creation. CreateStrategy: deployment.Strategy, @@ -130,7 +130,7 @@ func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { deployment, err := (*r.registry).GetDeployment(ctx, name) if err != nil { - return nil, errors.NewNotFound("scale", name) + return nil, errors.NewNotFound(extensions.Resource("deployments/scale"), name) } return extensions.ScaleFromDeployment(deployment), nil } @@ -145,17 +145,17 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { - return nil, false, errors.NewInvalid("scale", scale.Name, errs) + return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } deployment, err := (*r.registry).GetDeployment(ctx, scale.Name) if err != nil { - return nil, false, errors.NewNotFound("scale", scale.Name) + return nil, false, errors.NewNotFound(extensions.Resource("deployments/scale"), scale.Name) } deployment.Spec.Replicas = scale.Spec.Replicas deployment, err = (*r.registry).UpdateDeployment(ctx, deployment) if err != nil { - return nil, false, errors.NewConflict("scale", scale.Name, err) + return nil, false, errors.NewConflict(extensions.Resource("deployments/scale"), scale.Name, err) } return extensions.ScaleFromDeployment(deployment), false, nil } diff --git a/pkg/registry/endpoint/etcd/etcd.go b/pkg/registry/endpoint/etcd/etcd.go index 14bd3990c40..dff62ac091f 100644 --- a/pkg/registry/endpoint/etcd/etcd.go +++ b/pkg/registry/endpoint/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return endpoint.MatchEndpoints(label, field) }, - EndpointName: "endpoints", + QualifiedResource: api.Resource("endpoints"), CreateStrategy: endpoint.Strategy, UpdateStrategy: endpoint.Strategy, diff --git a/pkg/registry/event/etcd/etcd.go b/pkg/registry/event/etcd/etcd.go index 7a2dca1124b..d6c405d3a89 100644 --- a/pkg/registry/event/etcd/etcd.go +++ b/pkg/registry/event/etcd/etcd.go @@ -57,7 +57,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator, ttl TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, - EndpointName: "events", + QualifiedResource: api.Resource("events"), CreateStrategy: event.Strategy, UpdateStrategy: event.Strategy, diff --git a/pkg/registry/experimental/controller/etcd/etcd.go b/pkg/registry/experimental/controller/etcd/etcd.go index d4bd681249d..39611c3e53f 100644 --- a/pkg/registry/experimental/controller/etcd/etcd.go +++ b/pkg/registry/experimental/controller/etcd/etcd.go @@ -66,7 +66,7 @@ func (r *ScaleREST) New() runtime.Object { func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { rc, err := (*r.registry).GetController(ctx, name) if err != nil { - return nil, errors.NewNotFound("scale", name) + return nil, errors.NewNotFound(extensions.Resource("replicationcontrollers/scale"), name) } return &extensions.Scale{ ObjectMeta: api.ObjectMeta{ @@ -94,17 +94,17 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, } if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { - return nil, false, errors.NewInvalid("scale", scale.Name, errs) + return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) } rc, err := (*r.registry).GetController(ctx, scale.Name) if err != nil { - return nil, false, errors.NewNotFound("scale", scale.Name) + return nil, false, errors.NewNotFound(extensions.Resource("replicationcontrollers/scale"), scale.Name) } rc.Spec.Replicas = scale.Spec.Replicas rc, err = (*r.registry).UpdateController(ctx, rc) if err != nil { - return nil, false, errors.NewConflict("scale", scale.Name, err) + return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err) } return &extensions.Scale{ ObjectMeta: api.ObjectMeta{ diff --git a/pkg/registry/generic/etcd/etcd.go b/pkg/registry/generic/etcd/etcd.go index 06ebb6c66ab..33599ac1ad3 100644 --- a/pkg/registry/generic/etcd/etcd.go +++ b/pkg/registry/generic/etcd/etcd.go @@ -63,7 +63,7 @@ type Etcd struct { NewListFunc func() runtime.Object // Used for error reporting - EndpointName string + QualifiedResource unversioned.GroupResource // Used for listing/watching; should not include trailing "/" KeyRootFunc func(ctx api.Context) string @@ -181,7 +181,7 @@ func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher, options *unvers if name, ok := m.MatchesSingle(); ok { if key, err := e.KeyFunc(ctx, name); err == nil { err := e.Storage.GetToList(ctx, key, filterFunc, list) - return list, etcderr.InterpretListError(err, e.EndpointName) + return list, etcderr.InterpretListError(err, e.QualifiedResource) } // if we cannot extract a key based on the current context, the optimization is skipped } @@ -190,7 +190,7 @@ func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher, options *unvers options = &unversioned.ListOptions{ResourceVersion: "0"} } err := e.Storage.List(ctx, e.KeyRootFunc(ctx), options.ResourceVersion, filterFunc, list) - return list, etcderr.InterpretListError(err, e.EndpointName) + return list, etcderr.InterpretListError(err, e.QualifiedResource) } // Create inserts a new item according to the unique key from the object. @@ -212,7 +212,7 @@ func (e *Etcd) Create(ctx api.Context, obj runtime.Object) (runtime.Object, erro } out := e.NewFunc() if err := e.Storage.Create(ctx, key, obj, out, ttl); err != nil { - err = etcderr.InterpretCreateError(err, e.EndpointName, name) + err = etcderr.InterpretCreateError(err, e.QualifiedResource, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) return nil, err } @@ -259,7 +259,7 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool } if version == 0 { if !e.UpdateStrategy.AllowCreateOnUpdate() { - return nil, nil, kubeerr.NewNotFound(e.EndpointName, name) + return nil, nil, kubeerr.NewNotFound(e.QualifiedResource, name) } creating = true if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil { @@ -286,7 +286,7 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool return nil, nil, err } if newVersion != version { - return nil, nil, kubeerr.NewConflict(e.EndpointName, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again")) + return nil, nil, kubeerr.NewConflict(e.QualifiedResource, name, fmt.Errorf("the object has been modified; please apply your changes to the latest version and try again")) } } if err := rest.BeforeUpdate(e.UpdateStrategy, ctx, obj, existing); err != nil { @@ -304,10 +304,10 @@ func (e *Etcd) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool if err != nil { if creating { - err = etcderr.InterpretCreateError(err, e.EndpointName, name) + err = etcderr.InterpretCreateError(err, e.QualifiedResource, name) err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj) } else { - err = etcderr.InterpretUpdateError(err, e.EndpointName, name) + err = etcderr.InterpretUpdateError(err, e.QualifiedResource, name) } return nil, false, err } @@ -340,7 +340,7 @@ func (e *Etcd) Get(ctx api.Context, name string) (runtime.Object, error) { return nil, err } if err := e.Storage.Get(ctx, key, obj, false); err != nil { - return nil, etcderr.InterpretGetError(err, e.EndpointName, name) + return nil, etcderr.InterpretGetError(err, e.QualifiedResource, name) } if e.Decorator != nil { if err := e.Decorator(obj); err != nil { @@ -364,7 +364,7 @@ func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions) obj := e.NewFunc() if err := e.Storage.Get(ctx, key, obj, false); err != nil { - return nil, etcderr.InterpretDeleteError(err, e.EndpointName, name) + return nil, etcderr.InterpretDeleteError(err, e.QualifiedResource, name) } // support older consumers of delete by treating "nil" as delete immediately @@ -410,14 +410,14 @@ func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions) case errAlreadyDeleting: return e.finalizeDelete(obj, true) default: - return nil, etcderr.InterpretUpdateError(err, e.EndpointName, name) + return nil, etcderr.InterpretUpdateError(err, e.QualifiedResource, name) } } // delete immediately, or no graceful deletion supported out := e.NewFunc() if err := e.Storage.Delete(ctx, key, out); err != nil { - return nil, etcderr.InterpretDeleteError(err, e.EndpointName, name) + return nil, etcderr.InterpretDeleteError(err, e.QualifiedResource, name) } return e.finalizeDelete(out, true) } diff --git a/pkg/registry/generic/etcd/etcd_test.go b/pkg/registry/generic/etcd/etcd_test.go index 3bcde8040cc..0a8109d61ff 100644 --- a/pkg/registry/generic/etcd/etcd_test.go +++ b/pkg/registry/generic/etcd/etcd_test.go @@ -92,11 +92,11 @@ func NewTestGenericEtcdRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Etc strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} return server, &Etcd{ - NewFunc: func() runtime.Object { return &api.Pod{} }, - NewListFunc: func() runtime.Object { return &api.PodList{} }, - EndpointName: "pods", - CreateStrategy: strategy, - UpdateStrategy: strategy, + NewFunc: func() runtime.Object { return &api.Pod{} }, + NewListFunc: func() runtime.Object { return &api.PodList{} }, + QualifiedResource: api.Resource("pods"), + CreateStrategy: strategy, + UpdateStrategy: strategy, KeyRootFunc: func(ctx api.Context) string { return podPrefix }, diff --git a/pkg/registry/generic/rest/response_checker.go b/pkg/registry/generic/rest/response_checker.go index 9213229df4d..b0c61075c13 100644 --- a/pkg/registry/generic/rest/response_checker.go +++ b/pkg/registry/generic/rest/response_checker.go @@ -23,6 +23,7 @@ import ( "net/http" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/unversioned" ) // Check the http error status from a location URL. @@ -39,8 +40,8 @@ const ( // A generic http response checker to transform the error. type GenericHttpResponseChecker struct { - Kind string - Name string + QualifiedResource unversioned.GroupResource + Name string } func (checker GenericHttpResponseChecker) Check(resp *http.Response) error { @@ -58,13 +59,13 @@ func (checker GenericHttpResponseChecker) Check(resp *http.Response) error { case resp.StatusCode == http.StatusBadRequest: return errors.NewBadRequest(bodyText) case resp.StatusCode == http.StatusNotFound: - return errors.NewGenericServerResponse(resp.StatusCode, "", checker.Kind, checker.Name, bodyText, 0, false) + return errors.NewGenericServerResponse(resp.StatusCode, "", checker.QualifiedResource, checker.Name, bodyText, 0, false) } - return errors.NewGenericServerResponse(resp.StatusCode, "", checker.Kind, checker.Name, bodyText, 0, false) + return errors.NewGenericServerResponse(resp.StatusCode, "", checker.QualifiedResource, checker.Name, bodyText, 0, false) } return nil } -func NewGenericHttpResponseChecker(kind, name string) GenericHttpResponseChecker { - return GenericHttpResponseChecker{Kind: kind, Name: name} +func NewGenericHttpResponseChecker(qualifiedResource unversioned.GroupResource, name string) GenericHttpResponseChecker { + return GenericHttpResponseChecker{QualifiedResource: qualifiedResource, Name: name} } diff --git a/pkg/registry/generic/rest/response_checker_test.go b/pkg/registry/generic/rest/response_checker_test.go index ece5cd0a588..f1ad62020a8 100644 --- a/pkg/registry/generic/rest/response_checker_test.go +++ b/pkg/registry/generic/rest/response_checker_test.go @@ -25,11 +25,12 @@ import ( "strings" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" ) func TestGenericHttpResponseChecker(t *testing.T) { - responseChecker := NewGenericHttpResponseChecker("Pod", "foo") + responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo") tests := []struct { resp *http.Response expectError bool @@ -78,7 +79,7 @@ func TestGenericHttpResponseChecker(t *testing.T) { } func TestGenericHttpResponseCheckerLimitReader(t *testing.T) { - responseChecker := NewGenericHttpResponseChecker("Pod", "foo") + responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo") excessedString := strings.Repeat("a", (maxReadLength + 10000)) resp := &http.Response{ Body: ioutil.NopCloser(bytes.NewBufferString(excessedString)), diff --git a/pkg/registry/generic/rest/streamer_test.go b/pkg/registry/generic/rest/streamer_test.go index c81c22439f3..95622283764 100644 --- a/pkg/registry/generic/rest/streamer_test.go +++ b/pkg/registry/generic/rest/streamer_test.go @@ -27,6 +27,7 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" ) @@ -131,7 +132,7 @@ func TestInputStreamInternalServerErrorTransport(t *testing.T) { streamer := &LocationStreamer{ Location: location, Transport: fakeInternalServerErrorTransport("text/plain", message), - ResponseChecker: NewGenericHttpResponseChecker("", ""), + ResponseChecker: NewGenericHttpResponseChecker(api.Resource(""), ""), } expectedError := errors.NewInternalError(fmt.Errorf("%s", message)) diff --git a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go index a6b1872be79..d0147bbb26b 100644 --- a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go +++ b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go @@ -62,7 +62,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return horizontalpodautoscaler.MatchAutoscaler(label, field) }, - EndpointName: "horizontalPodAutoscalers", + QualifiedResource: extensions.Resource("horizontalpodautoscalers"), // Used to validate autoscaler creation CreateStrategy: horizontalpodautoscaler.Strategy, diff --git a/pkg/registry/ingress/etcd/etcd.go b/pkg/registry/ingress/etcd/etcd.go index 717cc656857..25bc86a7526 100644 --- a/pkg/registry/ingress/etcd/etcd.go +++ b/pkg/registry/ingress/etcd/etcd.go @@ -64,7 +64,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return ingress.MatchIngress(label, field) }, - EndpointName: "ingresses", + QualifiedResource: extensions.Resource("ingresses"), // Used to validate controller creation CreateStrategy: ingress.Strategy, diff --git a/pkg/registry/job/etcd/etcd.go b/pkg/registry/job/etcd/etcd.go index 8750a4d0977..adf2f717f7d 100644 --- a/pkg/registry/job/etcd/etcd.go +++ b/pkg/registry/job/etcd/etcd.go @@ -64,7 +64,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return job.MatchJob(label, field) }, - EndpointName: "jobs", + QualifiedResource: extensions.Resource("jobs"), // Used to validate job creation CreateStrategy: job.Strategy, diff --git a/pkg/registry/limitrange/etcd/etcd.go b/pkg/registry/limitrange/etcd/etcd.go index 11779045cf9..b8ac3c479e1 100644 --- a/pkg/registry/limitrange/etcd/etcd.go +++ b/pkg/registry/limitrange/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return limitrange.MatchLimitRange(label, field) }, - EndpointName: "limitranges", + QualifiedResource: api.Resource("limitranges"), CreateStrategy: limitrange.Strategy, UpdateStrategy: limitrange.Strategy, diff --git a/pkg/registry/namespace/etcd/etcd.go b/pkg/registry/namespace/etcd/etcd.go index 171512ed776..a6ae0a34c38 100644 --- a/pkg/registry/namespace/etcd/etcd.go +++ b/pkg/registry/namespace/etcd/etcd.go @@ -70,7 +70,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return namespace.MatchNamespace(label, field) }, - EndpointName: "namespaces", + QualifiedResource: api.Resource("namespaces"), CreateStrategy: namespace.Strategy, UpdateStrategy: namespace.Strategy, @@ -108,7 +108,7 @@ func (r *REST) Delete(ctx api.Context, name string, options *api.DeleteOptions) // prior to final deletion, we must ensure that finalizers is empty if len(namespace.Spec.Finalizers) != 0 { - err = apierrors.NewConflict("Namespace", namespace.Name, fmt.Errorf("The system is ensuring all content is removed from this namespace. Upon completion, this namespace will automatically be purged by the system.")) + err = apierrors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("The system is ensuring all content is removed from this namespace. Upon completion, this namespace will automatically be purged by the system.")) return nil, err } return r.Etcd.Delete(ctx, name, nil) diff --git a/pkg/registry/node/etcd/etcd.go b/pkg/registry/node/etcd/etcd.go index 91d2b1d7276..1972a377740 100644 --- a/pkg/registry/node/etcd/etcd.go +++ b/pkg/registry/node/etcd/etcd.go @@ -71,8 +71,8 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator, con ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Node).Name, nil }, - PredicateFunc: node.MatchNode, - EndpointName: "node", + PredicateFunc: node.MatchNode, + QualifiedResource: api.Resource("nodes"), CreateStrategy: node.Strategy, UpdateStrategy: node.Strategy, diff --git a/pkg/registry/persistentvolume/etcd/etcd.go b/pkg/registry/persistentvolume/etcd/etcd.go index 445a5f59d9d..b41abb41198 100644 --- a/pkg/registry/persistentvolume/etcd/etcd.go +++ b/pkg/registry/persistentvolume/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return persistentvolume.MatchPersistentVolumes(label, field) }, - EndpointName: "persistentvolume", + QualifiedResource: api.Resource("persistentvolumes"), CreateStrategy: persistentvolume.Strategy, UpdateStrategy: persistentvolume.Strategy, diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd.go b/pkg/registry/persistentvolumeclaim/etcd/etcd.go index 69e916a8d2b..3233a9c1287 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return persistentvolumeclaim.MatchPersistentVolumeClaim(label, field) }, - EndpointName: "persistentvolumeclaims", + QualifiedResource: api.Resource("persistentvolumeclaims"), CreateStrategy: persistentvolumeclaim.Strategy, UpdateStrategy: persistentvolumeclaim.Strategy, diff --git a/pkg/registry/pod/etcd/etcd.go b/pkg/registry/pod/etcd/etcd.go index 93f5e38f870..a2bb1d8b93f 100644 --- a/pkg/registry/pod/etcd/etcd.go +++ b/pkg/registry/pod/etcd/etcd.go @@ -84,7 +84,7 @@ func NewStorage( PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return pod.MatchPod(label, field) }, - EndpointName: "pods", + QualifiedResource: api.Resource("pods"), CreateStrategy: pod.Strategy, UpdateStrategy: pod.Strategy, @@ -178,10 +178,10 @@ func (r *BindingREST) setPodHostAndAnnotations(ctx api.Context, podID, oldMachin // assignPod assigns the given pod to the given machine. func (r *BindingREST) assignPod(ctx api.Context, podID string, machine string, annotations map[string]string) (err error) { if _, err = r.setPodHostAndAnnotations(ctx, podID, "", machine, annotations); err != nil { - err = etcderr.InterpretGetError(err, "pod", podID) - err = etcderr.InterpretUpdateError(err, "pod", podID) + err = etcderr.InterpretGetError(err, api.Resource("pods"), podID) + err = etcderr.InterpretUpdateError(err, api.Resource("pods"), podID) if _, ok := err.(*errors.StatusError); !ok { - err = errors.NewConflict("binding", podID, err) + err = errors.NewConflict(api.Resource("pods/binding"), podID, err) } } return diff --git a/pkg/registry/pod/etcd/etcd_test.go b/pkg/registry/pod/etcd/etcd_test.go index f387b0f647c..3e5e6e23c20 100644 --- a/pkg/registry/pod/etcd/etcd_test.go +++ b/pkg/registry/pod/etcd/etcd_test.go @@ -354,7 +354,7 @@ func TestEtcdCreateBindingNoPod(t *testing.T) { if err == nil { t.Fatalf("Expected not-found-error but got nothing") } - if !errors.IsNotFound(etcderrors.InterpretGetError(err, "Pod", "foo")) { + if !errors.IsNotFound(etcderrors.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error returned: %#v", err) } @@ -362,7 +362,7 @@ func TestEtcdCreateBindingNoPod(t *testing.T) { if err == nil { t.Fatalf("Expected not-found-error but got nothing") } - if !errors.IsNotFound(etcderrors.InterpretGetError(err, "Pod", "foo")) { + if !errors.IsNotFound(etcderrors.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error: %v", err) } } diff --git a/pkg/registry/pod/rest/log.go b/pkg/registry/pod/rest/log.go index db54e422b74..602fcefee6f 100644 --- a/pkg/registry/pod/rest/log.go +++ b/pkg/registry/pod/rest/log.go @@ -52,7 +52,7 @@ func (r *LogREST) Get(ctx api.Context, name string, opts runtime.Object) (runtim return nil, fmt.Errorf("invalid options object: %#v", opts) } if errs := validation.ValidatePodLogOptions(logOpts); len(errs) > 0 { - return nil, errors.NewInvalid("podlogs", name, errs) + return nil, errors.NewInvalid(api.Kind("PodLogOptions"), name, errs) } location, transport, err := pod.LogLocation(r.Store, r.KubeletConn, ctx, name, logOpts) if err != nil { @@ -63,7 +63,7 @@ func (r *LogREST) Get(ctx api.Context, name string, opts runtime.Object) (runtim Transport: transport, ContentType: "text/plain", Flush: logOpts.Follow, - ResponseChecker: genericrest.NewGenericHttpResponseChecker("Pod", name), + ResponseChecker: genericrest.NewGenericHttpResponseChecker(api.Resource("pods/log"), name), }, nil } diff --git a/pkg/registry/podtemplate/etcd/etcd.go b/pkg/registry/podtemplate/etcd/etcd.go index 6435602585f..50b9d91a8e1 100644 --- a/pkg/registry/podtemplate/etcd/etcd.go +++ b/pkg/registry/podtemplate/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return podtemplate.MatchPodTemplate(label, field) }, - EndpointName: "podtemplates", + QualifiedResource: api.Resource("podtemplates"), CreateStrategy: podtemplate.Strategy, UpdateStrategy: podtemplate.Strategy, diff --git a/pkg/registry/registrytest/endpoint.go b/pkg/registry/registrytest/endpoint.go index 659dcab0a3b..42c459510e9 100644 --- a/pkg/registry/registrytest/endpoint.go +++ b/pkg/registry/registrytest/endpoint.go @@ -57,7 +57,7 @@ func (e *EndpointRegistry) GetEndpoints(ctx api.Context, name string) (*api.Endp } } } - return nil, errors.NewNotFound("Endpoints", name) + return nil, errors.NewNotFound(api.Resource("endpoints"), name) } func (e *EndpointRegistry) WatchEndpoints(ctx api.Context, options *unversioned.ListOptions) (watch.Interface, error) { diff --git a/pkg/registry/registrytest/node.go b/pkg/registry/registrytest/node.go index fa8d7c719e1..1f47c3e7b02 100644 --- a/pkg/registry/registrytest/node.go +++ b/pkg/registry/registrytest/node.go @@ -95,7 +95,7 @@ func (r *NodeRegistry) GetNode(ctx api.Context, nodeID string) (*api.Node, error return &node, nil } } - return nil, errors.NewNotFound("node", nodeID) + return nil, errors.NewNotFound(api.Resource("nodes"), nodeID) } func (r *NodeRegistry) DeleteNode(ctx api.Context, nodeID string) error { diff --git a/pkg/registry/resourcequota/etcd/etcd.go b/pkg/registry/resourcequota/etcd/etcd.go index 2c15d62c2a5..629860012b9 100644 --- a/pkg/registry/resourcequota/etcd/etcd.go +++ b/pkg/registry/resourcequota/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) (*R PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return resourcequota.MatchResourceQuota(label, field) }, - EndpointName: "resourcequotas", + QualifiedResource: api.Resource("resourcequotas"), CreateStrategy: resourcequota.Strategy, UpdateStrategy: resourcequota.Strategy, diff --git a/pkg/registry/secret/etcd/etcd.go b/pkg/registry/secret/etcd/etcd.go index a6f59200a7f..89739446f39 100644 --- a/pkg/registry/secret/etcd/etcd.go +++ b/pkg/registry/secret/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return secret.Matcher(label, field) }, - EndpointName: "secrets", + QualifiedResource: api.Resource("secrets"), CreateStrategy: secret.Strategy, UpdateStrategy: secret.Strategy, diff --git a/pkg/registry/secret/strategy.go b/pkg/registry/secret/strategy.go index 6f8284fcbc8..18138e8c766 100644 --- a/pkg/registry/secret/strategy.go +++ b/pkg/registry/secret/strategy.go @@ -88,7 +88,7 @@ func (s strategy) Export(obj runtime.Object, exact bool) error { errs := []*field.Error{ field.Invalid(field.NewPath("type"), t, "can not export service account secrets"), } - return errors.NewInvalid("Secret", t.Name, errs) + return errors.NewInvalid(api.Kind("Secret"), t.Name, errs) } return nil } diff --git a/pkg/registry/service/allocator/etcd/etcd.go b/pkg/registry/service/allocator/etcd/etcd.go index 74487e37a9b..366b814b4fc 100644 --- a/pkg/registry/service/allocator/etcd/etcd.go +++ b/pkg/registry/service/allocator/etcd/etcd.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/api" k8serr "k8s.io/kubernetes/pkg/api/errors" etcderr "k8s.io/kubernetes/pkg/api/errors/etcd" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/registry/service" "k8s.io/kubernetes/pkg/registry/service/allocator" "k8s.io/kubernetes/pkg/runtime" @@ -47,8 +48,8 @@ type Etcd struct { storage storage.Interface last string - baseKey string - kind string + baseKey string + resource unversioned.GroupResource } // Etcd implements allocator.Interface and service.RangeRegistry @@ -57,12 +58,12 @@ var _ service.RangeRegistry = &Etcd{} // NewEtcd returns an allocator that is backed by Etcd and can manage // persisting the snapshot state of allocation after each allocation is made. -func NewEtcd(alloc allocator.Snapshottable, baseKey string, kind string, storage storage.Interface) *Etcd { +func NewEtcd(alloc allocator.Snapshottable, baseKey string, resource unversioned.GroupResource, storage storage.Interface) *Etcd { return &Etcd{ - alloc: alloc, - storage: storage, - baseKey: baseKey, - kind: kind, + alloc: alloc, + storage: storage, + baseKey: baseKey, + resource: resource, } } @@ -146,7 +147,7 @@ func (e *Etcd) tryUpdate(fn func() error) error { storage.SimpleUpdate(func(input runtime.Object) (output runtime.Object, err error) { existing := input.(*api.RangeAllocation) if len(existing.ResourceVersion) == 0 { - return nil, fmt.Errorf("cannot allocate resources of type %s at this time", e.kind) + return nil, fmt.Errorf("cannot allocate resources of type %s at this time", e.resource.String()) } if existing.ResourceVersion != e.last { if err := e.alloc.Restore(existing.Range, existing.Data); err != nil { @@ -163,7 +164,7 @@ func (e *Etcd) tryUpdate(fn func() error) error { return existing, nil }), ) - return etcderr.InterpretUpdateError(err, e.kind, "") + return etcderr.InterpretUpdateError(err, e.resource, "") } // Refresh reloads the RangeAllocation from etcd. @@ -176,7 +177,7 @@ func (e *Etcd) Refresh() (*api.RangeAllocation, error) { if storage.IsNotFound(err) { return nil, nil } - return nil, etcderr.InterpretGetError(err, e.kind, "") + return nil, etcderr.InterpretGetError(err, e.resource, "") } return existing, nil @@ -187,7 +188,7 @@ func (e *Etcd) Refresh() (*api.RangeAllocation, error) { func (e *Etcd) Get() (*api.RangeAllocation, error) { existing := &api.RangeAllocation{} if err := e.storage.Get(context.TODO(), e.baseKey, existing, true); err != nil { - return nil, etcderr.InterpretGetError(err, e.kind, "") + return nil, etcderr.InterpretGetError(err, e.resource, "") } return existing, nil } @@ -205,17 +206,17 @@ func (e *Etcd) CreateOrUpdate(snapshot *api.RangeAllocation) error { switch { case len(snapshot.ResourceVersion) != 0 && len(existing.ResourceVersion) != 0: if snapshot.ResourceVersion != existing.ResourceVersion { - return nil, k8serr.NewConflict(e.kind, "", fmt.Errorf("the provided resource version does not match")) + return nil, k8serr.NewConflict(e.resource, "", fmt.Errorf("the provided resource version does not match")) } case len(existing.ResourceVersion) != 0: - return nil, k8serr.NewConflict(e.kind, "", fmt.Errorf("another caller has already initialized the resource")) + return nil, k8serr.NewConflict(e.resource, "", fmt.Errorf("another caller has already initialized the resource")) } last = snapshot.ResourceVersion return snapshot, nil }), ) if err != nil { - return etcderr.InterpretUpdateError(err, e.kind, "") + return etcderr.InterpretUpdateError(err, e.resource, "") } err = e.alloc.Restore(snapshot.Range, snapshot.Data) if err == nil { diff --git a/pkg/registry/service/allocator/etcd/etcd_test.go b/pkg/registry/service/allocator/etcd/etcd_test.go index b19e4a10418..d40cc0f1b10 100644 --- a/pkg/registry/service/allocator/etcd/etcd_test.go +++ b/pkg/registry/service/allocator/etcd/etcd_test.go @@ -32,7 +32,7 @@ import ( func newStorage(t *testing.T) (*Etcd, *etcdtesting.EtcdTestServer, allocator.Interface) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") mem := allocator.NewAllocationMap(100, "rangeSpecValue") - etcd := NewEtcd(mem, "/ranges/serviceips", "serviceipallocation", etcdStorage) + etcd := NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), etcdStorage) return etcd, server, mem } @@ -50,7 +50,7 @@ func key() string { func TestEmpty(t *testing.T) { storage, server, _ := newStorage(t) defer server.Terminate(t) - if _, err := storage.Allocate(1); !strings.Contains(err.Error(), "cannot allocate resources of type serviceipallocation at this time") { + if _, err := storage.Allocate(1); !strings.Contains(err.Error(), "cannot allocate resources of type serviceipallocations at this time") { t.Fatal(err) } } @@ -93,7 +93,7 @@ func TestStore(t *testing.T) { } other = allocator.NewAllocationMap(100, "rangeSpecValue") - otherStorage := NewEtcd(other, "/ranges/serviceips", "serviceipallocation", storage.storage) + otherStorage := NewEtcd(other, "/ranges/serviceips", api.Resource("serviceipallocations"), storage.storage) if ok, err := otherStorage.Allocate(2); ok || err != nil { t.Fatal(err) } diff --git a/pkg/registry/service/etcd/etcd.go b/pkg/registry/service/etcd/etcd.go index d71308836d7..de905ab2969 100644 --- a/pkg/registry/service/etcd/etcd.go +++ b/pkg/registry/service/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return service.MatchServices(label, field) }, - EndpointName: "services", + QualifiedResource: api.Resource("services"), CreateStrategy: service.Strategy, UpdateStrategy: service.Strategy, diff --git a/pkg/registry/service/ipallocator/etcd/etcd_test.go b/pkg/registry/service/ipallocator/etcd/etcd_test.go index f70c7287161..ae0c8b90d20 100644 --- a/pkg/registry/service/ipallocator/etcd/etcd_test.go +++ b/pkg/registry/service/ipallocator/etcd/etcd_test.go @@ -44,7 +44,7 @@ func newStorage(t *testing.T) (*etcdtesting.EtcdTestServer, ipallocator.Interfac storage := ipallocator.NewAllocatorCIDRRange(cidr, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) backing = mem - etcd := allocatoretcd.NewEtcd(mem, "/ranges/serviceips", "serviceipallocation", etcdStorage) + etcd := allocatoretcd.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), etcdStorage) return etcd }) @@ -66,7 +66,7 @@ func key() string { func TestEmpty(t *testing.T) { server, storage, _, _ := newStorage(t) defer server.Terminate(t) - if err := storage.Allocate(net.ParseIP("192.168.1.2")); !strings.Contains(err.Error(), "cannot allocate resources of type serviceipallocation at this time") { + if err := storage.Allocate(net.ParseIP("192.168.1.2")); !strings.Contains(err.Error(), "cannot allocate resources of type serviceipallocations at this time") { t.Fatal(err) } } diff --git a/pkg/registry/service/rest.go b/pkg/registry/service/rest.go index 26a455b06c1..f971d1d9304 100644 --- a/pkg/registry/service/rest.go +++ b/pkg/registry/service/rest.go @@ -96,7 +96,7 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err if err := rs.serviceIPs.Allocate(net.ParseIP(service.Spec.ClusterIP)); err != nil { // TODO: when validation becomes versioned, this gets more complicated. el := field.ErrorList{field.Invalid(field.NewPath("spec", "clusterIP"), service.Spec.ClusterIP, err.Error())} - return nil, errors.NewInvalid("Service", service.Name, el) + return nil, errors.NewInvalid(api.Kind("Service"), service.Name, el) } releaseServiceIP = true } @@ -109,7 +109,7 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err if err != nil { // TODO: when validation becomes versioned, this gets more complicated. el := field.ErrorList{field.Invalid(field.NewPath("spec", "ports").Index(i).Child("nodePort"), servicePort.NodePort, err.Error())} - return nil, errors.NewInvalid("Service", service.Name, el) + return nil, errors.NewInvalid(api.Kind("Service"), service.Name, el) } } else if assignNodePorts { nodePort, err := nodePortOp.AllocateNext() @@ -199,7 +199,7 @@ func (*REST) NewList() runtime.Object { func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { - return nil, false, errors.NewConflict("service", service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) + return nil, false, errors.NewConflict(api.Resource("services"), service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) } oldService, err := rs.registry.GetService(ctx, service.Name) @@ -210,7 +210,7 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo // Copy over non-user fields // TODO: make this a merge function if errs := validation.ValidateServiceUpdate(service, oldService); len(errs) > 0 { - return nil, false, errors.NewInvalid("service", service.Name, errs) + return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, errs) } nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) @@ -230,7 +230,7 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo err := nodePortOp.Allocate(nodePort) if err != nil { el := field.ErrorList{field.Invalid(field.NewPath("spec", "ports").Index(i).Child("nodePort"), nodePort, err.Error())} - return nil, false, errors.NewInvalid("Service", service.Name, el) + return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, el) } } } else { diff --git a/pkg/registry/service/strategy_test.go b/pkg/registry/service/strategy_test.go index be730d95088..5252d957447 100644 --- a/pkg/registry/service/strategy_test.go +++ b/pkg/registry/service/strategy_test.go @@ -105,17 +105,17 @@ func TestExportService(t *testing.T) { } func TestCheckGeneratedNameError(t *testing.T) { - expect := errors.NewNotFound("foo", "bar") + expect := errors.NewNotFound(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("NotFoundError should be ignored: %v", err) } - expect = errors.NewAlreadyExists("foo", "bar") + expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err) } - expect = errors.NewAlreadyExists("foo", "bar") + expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{ObjectMeta: api.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsServerTimeout(err) { t.Errorf("expected try again later error: %v", err) } diff --git a/pkg/registry/serviceaccount/etcd/etcd.go b/pkg/registry/serviceaccount/etcd/etcd.go index ff0cb209662..906dacca4b6 100644 --- a/pkg/registry/serviceaccount/etcd/etcd.go +++ b/pkg/registry/serviceaccount/etcd/etcd.go @@ -54,7 +54,7 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return serviceaccount.Matcher(label, field) }, - EndpointName: "serviceaccounts", + QualifiedResource: api.Resource("serviceaccounts"), CreateStrategy: serviceaccount.Strategy, UpdateStrategy: serviceaccount.Strategy, diff --git a/pkg/registry/thirdpartyresource/etcd/etcd.go b/pkg/registry/thirdpartyresource/etcd/etcd.go index b4d0da67560..db282c6cbb7 100644 --- a/pkg/registry/thirdpartyresource/etcd/etcd.go +++ b/pkg/registry/thirdpartyresource/etcd/etcd.go @@ -55,9 +55,9 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator) *RE PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return thirdpartyresource.Matcher(label, field) }, - EndpointName: "thirdPartyResources", - CreateStrategy: thirdpartyresource.Strategy, - UpdateStrategy: thirdpartyresource.Strategy, + QualifiedResource: extensions.Resource("thirdpartyresources"), + CreateStrategy: thirdpartyresource.Strategy, + UpdateStrategy: thirdpartyresource.Strategy, Storage: storageInterface, } diff --git a/pkg/registry/thirdpartyresourcedata/etcd/etcd.go b/pkg/registry/thirdpartyresourcedata/etcd/etcd.go index ee55f8752a3..505cb8f7801 100644 --- a/pkg/registry/thirdpartyresourcedata/etcd/etcd.go +++ b/pkg/registry/thirdpartyresourcedata/etcd/etcd.go @@ -57,9 +57,9 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator, gro PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return thirdpartyresourcedata.Matcher(label, field) }, - EndpointName: "thirdpartyresourcedata", - CreateStrategy: thirdpartyresourcedata.Strategy, - UpdateStrategy: thirdpartyresourcedata.Strategy, + QualifiedResource: extensions.Resource("thirdpartyresourcedatas"), + CreateStrategy: thirdpartyresourcedata.Strategy, + UpdateStrategy: thirdpartyresourcedata.Strategy, Storage: storageInterface, } diff --git a/pkg/storage/util.go b/pkg/storage/util.go index 0a741b805a7..bf3574da4fa 100644 --- a/pkg/storage/util.go +++ b/pkg/storage/util.go @@ -22,6 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation/field" @@ -47,7 +48,7 @@ func ParseWatchResourceVersion(resourceVersion string) (uint64, error) { } version, err := strconv.ParseUint(resourceVersion, 10, 64) if err != nil { - return 0, errors.NewInvalid("", "", field.ErrorList{ + return 0, errors.NewInvalid(unversioned.GroupKind{}, "", field.ErrorList{ // Validation errors are supposed to return version-specific field // paths, but this is probably close enough. field.Invalid(field.NewPath("resourceVersion"), resourceVersion, err.Error()), diff --git a/plugin/pkg/admission/namespace/autoprovision/admission_test.go b/plugin/pkg/admission/namespace/autoprovision/admission_test.go index 1422577b890..5fbaabe84ce 100644 --- a/plugin/pkg/admission/namespace/autoprovision/admission_test.go +++ b/plugin/pkg/admission/namespace/autoprovision/admission_test.go @@ -109,7 +109,7 @@ func TestAdmissionNamespaceExistsUnknownToHandler(t *testing.T) { namespace := "test" mockClient := &testclient.Fake{} mockClient.AddReactor("create", "namespaces", func(action testclient.Action) (bool, runtime.Object, error) { - return true, nil, errors.NewAlreadyExists("namespaces", namespace) + return true, nil, errors.NewAlreadyExists(api.Resource("namespaces"), namespace) }) store := cache.NewStore(cache.MetaNamespaceKeyFunc) diff --git a/plugin/pkg/admission/namespace/lifecycle/admission.go b/plugin/pkg/admission/namespace/lifecycle/admission.go index 9358291d230..8344f6e0a58 100644 --- a/plugin/pkg/admission/namespace/lifecycle/admission.go +++ b/plugin/pkg/admission/namespace/lifecycle/admission.go @@ -50,7 +50,7 @@ type lifecycle struct { func (l *lifecycle) Admit(a admission.Attributes) (err error) { // prevent deletion of immortal namespaces if a.GetOperation() == admission.Delete && a.GetKind() == api.Kind("Namespace") && l.immortalNamespaces.Has(a.GetName()) { - return errors.NewForbidden(a.GetKind().Kind, a.GetName(), fmt.Errorf("this namespace may not be deleted")) + return errors.NewForbidden(a.GetResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted")) } kind, err := api.RESTMapper.KindFor(a.GetResource().WithVersion("")) diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission.go b/plugin/pkg/admission/securitycontext/scdeny/admission.go index bb547b277bc..44b83b2f115 100644 --- a/plugin/pkg/admission/securitycontext/scdeny/admission.go +++ b/plugin/pkg/admission/securitycontext/scdeny/admission.go @@ -58,28 +58,28 @@ func (p *plugin) Admit(a admission.Attributes) (err error) { } if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroups != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden")) } if pod.Spec.SecurityContext != nil { if pod.Spec.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SELinuxOptions is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SELinuxOptions is forbidden")) } if pod.Spec.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("pod.Spec.SecurityContext.RunAsUser is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.RunAsUser is forbidden")) } } if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("SecurityContext.FSGroup is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.FSGroup is forbidden")) } for _, v := range pod.Spec.Containers { if v.SecurityContext != nil { if v.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) } if v.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().Resource, pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) + return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) } } }