diff --git a/pkg/api/errors/errors.go b/pkg/api/errors/errors.go new file mode 100644 index 00000000000..65791c0af7b --- /dev/null +++ b/pkg/api/errors/errors.go @@ -0,0 +1,134 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +import ( + "fmt" + "net/http" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// statusError is an error intended for consumption by a REST API server. +type statusError struct { + status api.Status +} + +// Error implements the Error interface. +func (e *statusError) Error() string { + return e.status.Message +} + +// Status converts this error into an api.Status object. +func (e *statusError) Status() api.Status { + return e.status +} + +// 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 { + return &statusError{api.Status{ + Status: api.StatusFailure, + Code: http.StatusNotFound, + Reason: api.StatusReasonNotFound, + Details: &api.StatusDetails{ + Kind: kind, + ID: name, + }, + Message: fmt.Sprintf("%s %q not found", kind, name), + }} +} + +// NewAlreadyExists returns an error indicating the item requested exists by that identifier. +func NewAlreadyExists(kind, name string) error { + return &statusError{api.Status{ + Status: api.StatusFailure, + Code: http.StatusConflict, + Reason: api.StatusReasonAlreadyExists, + Details: &api.StatusDetails{ + Kind: kind, + ID: name, + }, + Message: fmt.Sprintf("%s %q already exists", kind, name), + }} +} + +// NewConflict returns an error indicating the item can't be updated as provided. +func NewConflict(kind, name string, err error) error { + return &statusError{api.Status{ + Status: api.StatusFailure, + Code: http.StatusConflict, + Reason: api.StatusReasonConflict, + Details: &api.StatusDetails{ + Kind: kind, + ID: name, + }, + Message: fmt.Sprintf("%s %q cannot be updated: %s", kind, name, err), + }} +} + +// NewInvalid returns an error indicating the item is invalid and cannot be processed. +func NewInvalid(kind, name string, errs ErrorList) error { + causes := make([]api.StatusCause, 0, len(errs)) + for i := range errs { + if err, ok := errs[i].(ValidationError); ok { + causes = append(causes, api.StatusCause{ + Type: api.CauseType(err.Type), + Message: err.Error(), + Field: err.Field, + }) + } + } + return &statusError{api.Status{ + Status: api.StatusFailure, + Code: 422, // RFC 4918 + Reason: api.StatusReasonInvalid, + Details: &api.StatusDetails{ + Kind: kind, + ID: name, + Causes: causes, + }, + Message: fmt.Sprintf("%s %q is invalid: %s", kind, name, errs.ToError()), + }} +} + +// IsNotFound returns true if the specified error was created by NewNotFoundErr. +func IsNotFound(err error) bool { + return reasonForError(err) == api.StatusReasonNotFound +} + +// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists. +func IsAlreadyExists(err error) bool { + return reasonForError(err) == api.StatusReasonAlreadyExists +} + +// IsConflict determines if the err is an error which indicates the provided update conflicts. +func IsConflict(err error) bool { + return reasonForError(err) == api.StatusReasonConflict +} + +// IsInvalid determines if the err is an error which indicates the provided resource is not valid. +func IsInvalid(err error) bool { + return reasonForError(err) == api.StatusReasonInvalid +} + +func reasonForError(err error) api.StatusReason { + switch t := err.(type) { + case *statusError: + return t.status.Reason + } + return api.StatusReasonUnknown +} diff --git a/pkg/api/errors/errors_test.go b/pkg/api/errors/errors_test.go new file mode 100644 index 00000000000..cde0ea3dfb5 --- /dev/null +++ b/pkg/api/errors/errors_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +import ( + "errors" + "fmt" + "reflect" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestErrorNew(t *testing.T) { + err := NewAlreadyExists("test", "1") + if !IsAlreadyExists(err) { + t.Errorf("expected to be already_exists") + } + if IsConflict(err) { + t.Errorf("expected to not be confict") + } + if IsNotFound(err) { + t.Errorf(fmt.Sprintf("expected to not be %s", api.StatusReasonNotFound)) + } + if IsInvalid(err) { + t.Errorf("expected to not be invalid") + } + + if !IsConflict(NewConflict("test", "2", errors.New("message"))) { + t.Errorf("expected to be conflict") + } + if !IsNotFound(NewNotFound("test", "3")) { + t.Errorf("expected to be not found") + } + if !IsInvalid(NewInvalid("test", "2", nil)) { + t.Errorf("expected to be invalid") + } +} + +func TestNewInvalid(t *testing.T) { + testCases := []struct { + Err ValidationError + Details *api.StatusDetails + }{ + { + NewFieldDuplicate("field[0].name", "bar"), + &api.StatusDetails{ + Kind: "kind", + ID: "name", + Causes: []api.StatusCause{{ + Type: api.CauseTypeFieldValueDuplicate, + Field: "field[0].name", + }}, + }, + }, + { + NewFieldInvalid("field[0].name", "bar"), + &api.StatusDetails{ + Kind: "kind", + ID: "name", + Causes: []api.StatusCause{{ + Type: api.CauseTypeFieldValueInvalid, + Field: "field[0].name", + }}, + }, + }, + { + NewFieldNotFound("field[0].name", "bar"), + &api.StatusDetails{ + Kind: "kind", + ID: "name", + Causes: []api.StatusCause{{ + Type: api.CauseTypeFieldValueNotFound, + Field: "field[0].name", + }}, + }, + }, + { + NewFieldNotSupported("field[0].name", "bar"), + &api.StatusDetails{ + Kind: "kind", + ID: "name", + Causes: []api.StatusCause{{ + Type: api.CauseTypeFieldValueNotSupported, + Field: "field[0].name", + }}, + }, + }, + { + NewFieldRequired("field[0].name", "bar"), + &api.StatusDetails{ + Kind: "kind", + ID: "name", + Causes: []api.StatusCause{{ + Type: api.CauseTypeFieldValueRequired, + Field: "field[0].name", + }}, + }, + }, + } + for i, testCase := range testCases { + vErr, expected := testCase.Err, testCase.Details + expected.Causes[0].Message = vErr.Error() + err := NewInvalid("kind", "name", ErrorList{vErr}) + status := err.(*statusError).Status() + if status.Code != 422 || status.Reason != api.StatusReasonInvalid { + t.Errorf("%d: unexpected status: %#v", i, status) + } + if !reflect.DeepEqual(expected, status.Details) { + t.Errorf("%d: expected %#v, got %#v", expected, status.Details) + } + } +} + +func Test_reasonForError(t *testing.T) { + if e, a := api.StatusReasonUnknown, reasonForError(nil); e != a { + t.Errorf("unexpected reason type: %#v", a) + } +} diff --git a/pkg/api/errors/validation.go b/pkg/api/errors/validation.go index 149653e0226..6626f0c76bb 100644 --- a/pkg/api/errors/validation.go +++ b/pkg/api/errors/validation.go @@ -75,28 +75,28 @@ func (v ValidationError) Error() string { return fmt.Sprintf("%s: %v '%v'", v.Field, ValueOf(v.Type), v.BadValue) } -// NewInvalid returns a ValidationError indicating "value required" -func NewRequired(field string, value interface{}) ValidationError { +// NewFieldRequired returns a ValidationError indicating "value required" +func NewFieldRequired(field string, value interface{}) ValidationError { return ValidationError{ValidationErrorTypeRequired, field, value} } -// NewInvalid returns a ValidationError indicating "invalid value" -func NewInvalid(field string, value interface{}) ValidationError { +// NewFieldInvalid returns a ValidationError indicating "invalid value" +func NewFieldInvalid(field string, value interface{}) ValidationError { return ValidationError{ValidationErrorTypeInvalid, field, value} } -// NewNotSupported returns a ValidationError indicating "unsupported value" -func NewNotSupported(field string, value interface{}) ValidationError { +// NewFieldNotSupported returns a ValidationError indicating "unsupported value" +func NewFieldNotSupported(field string, value interface{}) ValidationError { return ValidationError{ValidationErrorTypeNotSupported, field, value} } -// NewDuplicate returns a ValidationError indicating "duplicate value" -func NewDuplicate(field string, value interface{}) ValidationError { +// NewFieldDuplicate returns a ValidationError indicating "duplicate value" +func NewFieldDuplicate(field string, value interface{}) ValidationError { return ValidationError{ValidationErrorTypeDuplicate, field, value} } -// NewNotFound returns a ValidationError indicating "value not found" -func NewNotFound(field string, value interface{}) ValidationError { +// NewFieldNotFound returns a ValidationError indicating "value not found" +func NewFieldNotFound(field string, value interface{}) ValidationError { return ValidationError{ValidationErrorTypeNotFound, field, value} } diff --git a/pkg/api/errors/validation_test.go b/pkg/api/errors/validation_test.go index bd4a72a1541..7e95b308034 100644 --- a/pkg/api/errors/validation_test.go +++ b/pkg/api/errors/validation_test.go @@ -28,23 +28,23 @@ func TestMakeFuncs(t *testing.T) { expected ValidationErrorType }{ { - func() ValidationError { return NewInvalid("f", "v") }, + func() ValidationError { return NewFieldInvalid("f", "v") }, ValidationErrorTypeInvalid, }, { - func() ValidationError { return NewNotSupported("f", "v") }, + func() ValidationError { return NewFieldNotSupported("f", "v") }, ValidationErrorTypeNotSupported, }, { - func() ValidationError { return NewDuplicate("f", "v") }, + func() ValidationError { return NewFieldDuplicate("f", "v") }, ValidationErrorTypeDuplicate, }, { - func() ValidationError { return NewNotFound("f", "v") }, + func() ValidationError { return NewFieldNotFound("f", "v") }, ValidationErrorTypeNotFound, }, { - func() ValidationError { return NewRequired("f", "v") }, + func() ValidationError { return NewFieldRequired("f", "v") }, ValidationErrorTypeRequired, }, } @@ -58,7 +58,7 @@ func TestMakeFuncs(t *testing.T) { } func TestValidationError(t *testing.T) { - s := NewInvalid("foo", "bar").Error() + s := NewFieldInvalid("foo", "bar").Error() if !strings.Contains(s, "foo") || !strings.Contains(s, "bar") || !strings.Contains(s, ValueOf(ValidationErrorTypeInvalid)) { t.Errorf("error message did not contain expected values, got %s", s) } @@ -72,7 +72,7 @@ func TestErrorList(t *testing.T) { if a := errorListInternal(errList).Error(); a != "" { t.Errorf("expected empty string, got %v", a) } - errList = append(errList, NewInvalid("field", "value")) + errList = append(errList, NewFieldInvalid("field", "value")) // The fact that this compiles is the test. } @@ -108,15 +108,15 @@ func TestErrListPrefix(t *testing.T) { Expected string }{ { - NewNotFound("[0].bar", "value"), + NewFieldNotFound("[0].bar", "value"), "foo[0].bar", }, { - NewInvalid("field", "value"), + NewFieldInvalid("field", "value"), "foo.field", }, { - NewDuplicate("", "value"), + NewFieldDuplicate("", "value"), "foo", }, } @@ -138,15 +138,15 @@ func TestErrListPrefixIndex(t *testing.T) { Expected string }{ { - NewNotFound("[0].bar", "value"), + NewFieldNotFound("[0].bar", "value"), "[1][0].bar", }, { - NewInvalid("field", "value"), + NewFieldInvalid("field", "value"), "[1].field", }, { - NewDuplicate("", "value"), + NewFieldDuplicate("", "value"), "[1]", }, } diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 49bbb8dc7ff..e362699e1e4 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -37,11 +37,11 @@ func validateVolumes(volumes []api.Volume) (util.StringSet, errs.ErrorList) { el = validateSource(vol.Source).Prefix("source") } if len(vol.Name) == 0 { - el = append(el, errs.NewRequired("name", vol.Name)) + el = append(el, errs.NewFieldRequired("name", vol.Name)) } else if !util.IsDNSLabel(vol.Name) { - el = append(el, errs.NewInvalid("name", vol.Name)) + el = append(el, errs.NewFieldInvalid("name", vol.Name)) } else if allNames.Has(vol.Name) { - el = append(el, errs.NewDuplicate("name", vol.Name)) + el = append(el, errs.NewFieldDuplicate("name", vol.Name)) } if len(el) == 0 { allNames.Insert(vol.Name) @@ -64,7 +64,7 @@ func validateSource(source *api.VolumeSource) errs.ErrorList { //EmptyDirs have nothing to validate } if numVolumes != 1 { - allErrs = append(allErrs, errs.NewInvalid("", source)) + allErrs = append(allErrs, errs.NewFieldInvalid("", source)) } return allErrs } @@ -88,25 +88,25 @@ func validatePorts(ports []api.Port) errs.ErrorList { port := &ports[i] // so we can set default values if len(port.Name) > 0 { if len(port.Name) > 63 || !util.IsDNSLabel(port.Name) { - pErrs = append(pErrs, errs.NewInvalid("name", port.Name)) + pErrs = append(pErrs, errs.NewFieldInvalid("name", port.Name)) } else if allNames.Has(port.Name) { - pErrs = append(pErrs, errs.NewDuplicate("name", port.Name)) + pErrs = append(pErrs, errs.NewFieldDuplicate("name", port.Name)) } else { allNames.Insert(port.Name) } } if port.ContainerPort == 0 { - pErrs = append(pErrs, errs.NewRequired("containerPort", port.ContainerPort)) + pErrs = append(pErrs, errs.NewFieldRequired("containerPort", port.ContainerPort)) } else if !util.IsValidPortNum(port.ContainerPort) { - pErrs = append(pErrs, errs.NewInvalid("containerPort", port.ContainerPort)) + pErrs = append(pErrs, errs.NewFieldInvalid("containerPort", port.ContainerPort)) } if port.HostPort != 0 && !util.IsValidPortNum(port.HostPort) { - pErrs = append(pErrs, errs.NewInvalid("hostPort", port.HostPort)) + pErrs = append(pErrs, errs.NewFieldInvalid("hostPort", port.HostPort)) } if len(port.Protocol) == 0 { port.Protocol = "TCP" } else if !supportedPortProtocols.Has(strings.ToUpper(port.Protocol)) { - pErrs = append(pErrs, errs.NewNotSupported("protocol", port.Protocol)) + pErrs = append(pErrs, errs.NewFieldNotSupported("protocol", port.Protocol)) } allErrs = append(allErrs, pErrs.PrefixIndex(i)...) } @@ -120,10 +120,10 @@ func validateEnv(vars []api.EnvVar) errs.ErrorList { vErrs := errs.ErrorList{} ev := &vars[i] // so we can set default values if len(ev.Name) == 0 { - vErrs = append(vErrs, errs.NewRequired("name", ev.Name)) + vErrs = append(vErrs, errs.NewFieldRequired("name", ev.Name)) } if !util.IsCIdentifier(ev.Name) { - vErrs = append(vErrs, errs.NewInvalid("name", ev.Name)) + vErrs = append(vErrs, errs.NewFieldInvalid("name", ev.Name)) } allErrs = append(allErrs, vErrs.PrefixIndex(i)...) } @@ -137,12 +137,12 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs mErrs := errs.ErrorList{} mnt := &mounts[i] // so we can set default values if len(mnt.Name) == 0 { - mErrs = append(mErrs, errs.NewRequired("name", mnt.Name)) + mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name)) } else if !volumes.Has(mnt.Name) { mErrs = append(mErrs, errs.NewNotFound("name", mnt.Name)) } if len(mnt.MountPath) == 0 { - mErrs = append(mErrs, errs.NewRequired("mountPath", mnt.MountPath)) + mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath)) } allErrs = append(allErrs, mErrs.PrefixIndex(i)...) } @@ -163,7 +163,7 @@ func AccumulateUniquePorts(containers []api.Container, accumulator map[int]bool, continue } if accumulator[port] { - cErrs = append(cErrs, errs.NewDuplicate("Port", port)) + cErrs = append(cErrs, errs.NewFieldDuplicate("Port", port)) } else { accumulator[port] = true } @@ -188,16 +188,16 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs cErrs := errs.ErrorList{} ctr := &containers[i] // so we can set default values if len(ctr.Name) == 0 { - cErrs = append(cErrs, errs.NewRequired("name", ctr.Name)) + cErrs = append(cErrs, errs.NewFieldRequired("name", ctr.Name)) } else if !util.IsDNSLabel(ctr.Name) { - cErrs = append(cErrs, errs.NewInvalid("name", ctr.Name)) + cErrs = append(cErrs, errs.NewFieldInvalid("name", ctr.Name)) } else if allNames.Has(ctr.Name) { - cErrs = append(cErrs, errs.NewDuplicate("name", ctr.Name)) + cErrs = append(cErrs, errs.NewFieldDuplicate("name", ctr.Name)) } else { allNames.Insert(ctr.Name) } if len(ctr.Image) == 0 { - cErrs = append(cErrs, errs.NewRequired("image", ctr.Image)) + cErrs = append(cErrs, errs.NewFieldRequired("image", ctr.Image)) } cErrs = append(cErrs, validatePorts(ctr.Ports).Prefix("ports")...) cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...) @@ -224,9 +224,9 @@ func ValidateManifest(manifest *api.ContainerManifest) errs.ErrorList { allErrs := errs.ErrorList{} if len(manifest.Version) == 0 { - allErrs = append(allErrs, errs.NewRequired("version", manifest.Version)) + allErrs = append(allErrs, errs.NewFieldRequired("version", manifest.Version)) } else if !supportedManifestVersions.Has(strings.ToLower(manifest.Version)) { - allErrs = append(allErrs, errs.NewNotSupported("version", manifest.Version)) + allErrs = append(allErrs, errs.NewFieldNotSupported("version", manifest.Version)) } allVolumes, errs := validateVolumes(manifest.Volumes) allErrs = append(allErrs, errs.Prefix("volumes")...) @@ -241,7 +241,7 @@ func ValidatePodState(podState *api.PodState) errs.ErrorList { } else if podState.RestartPolicy.Type != api.RestartAlways && podState.RestartPolicy.Type != api.RestartOnFailure && podState.RestartPolicy.Type != api.RestartNever { - allErrs = append(allErrs, errs.NewNotSupported("restartPolicy.type", podState.RestartPolicy.Type)) + allErrs = append(allErrs, errs.NewFieldNotSupported("restartPolicy.type", podState.RestartPolicy.Type)) } return allErrs @@ -251,7 +251,7 @@ func ValidatePodState(podState *api.PodState) errs.ErrorList { func ValidatePod(pod *api.Pod) errs.ErrorList { allErrs := errs.ErrorList{} if len(pod.ID) == 0 { - allErrs = append(allErrs, errs.NewRequired("id", pod.ID)) + allErrs = append(allErrs, errs.NewFieldRequired("id", pod.ID)) } allErrs = append(allErrs, ValidatePodState(&pod.DesiredState).Prefix("desiredState")...) return allErrs @@ -261,15 +261,15 @@ func ValidatePod(pod *api.Pod) errs.ErrorList { func ValidateService(service *api.Service) errs.ErrorList { allErrs := errs.ErrorList{} if len(service.ID) == 0 { - allErrs = append(allErrs, errs.NewRequired("id", service.ID)) + allErrs = append(allErrs, errs.NewFieldRequired("id", service.ID)) } else if !util.IsDNS952Label(service.ID) { - allErrs = append(allErrs, errs.NewInvalid("id", service.ID)) + allErrs = append(allErrs, errs.NewFieldInvalid("id", service.ID)) } if !util.IsValidPortNum(service.Port) { - allErrs = append(allErrs, errs.NewInvalid("Service.Port", service.Port)) + allErrs = append(allErrs, errs.NewFieldInvalid("Service.Port", service.Port)) } if labels.Set(service.Selector).AsSelector().Empty() { - allErrs = append(allErrs, errs.NewRequired("selector", service.Selector)) + allErrs = append(allErrs, errs.NewFieldRequired("selector", service.Selector)) } return allErrs } @@ -278,18 +278,18 @@ func ValidateService(service *api.Service) errs.ErrorList { func ValidateReplicationController(controller *api.ReplicationController) errs.ErrorList { allErrs := errs.ErrorList{} if len(controller.ID) == 0 { - allErrs = append(allErrs, errs.NewRequired("id", controller.ID)) + allErrs = append(allErrs, errs.NewFieldRequired("id", controller.ID)) } if labels.Set(controller.DesiredState.ReplicaSelector).AsSelector().Empty() { - allErrs = append(allErrs, errs.NewRequired("desiredState.replicaSelector", controller.DesiredState.ReplicaSelector)) + allErrs = append(allErrs, errs.NewFieldRequired("desiredState.replicaSelector", controller.DesiredState.ReplicaSelector)) } selector := labels.Set(controller.DesiredState.ReplicaSelector).AsSelector() labels := labels.Set(controller.DesiredState.PodTemplate.Labels) if !selector.Matches(labels) { - allErrs = append(allErrs, errs.NewInvalid("desiredState.podTemplate.labels", controller.DesiredState.PodTemplate)) + allErrs = append(allErrs, errs.NewFieldInvalid("desiredState.podTemplate.labels", controller.DesiredState.PodTemplate)) } if controller.DesiredState.Replicas < 0 { - allErrs = append(allErrs, errs.NewInvalid("desiredState.replicas", controller.DesiredState.Replicas)) + allErrs = append(allErrs, errs.NewFieldInvalid("desiredState.replicas", controller.DesiredState.Replicas)) } allErrs = append(allErrs, ValidateManifest(&controller.DesiredState.PodTemplate.DesiredState.Manifest).Prefix("desiredState.podTemplate.desiredState.manifest")...) return allErrs diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 5d6789bf740..75bbbab51ab 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -31,6 +31,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/version" @@ -331,7 +332,7 @@ func TestGet(t *testing.T) { func TestGetMissing(t *testing.T) { storage := map[string]RESTStorage{} simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"get": NewNotFoundErr("simple", "id")}, + errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")}, } storage["simple"] = &simpleStorage handler := Handle(storage, codec, "/prefix/version") @@ -371,7 +372,7 @@ func TestDeleteMissing(t *testing.T) { storage := map[string]RESTStorage{} ID := "id" simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"delete": NewNotFoundErr("simple", ID)}, + errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)}, } storage["simple"] = &simpleStorage handler := Handle(storage, codec, "/prefix/version") @@ -421,7 +422,7 @@ func TestUpdateMissing(t *testing.T) { storage := map[string]RESTStorage{} ID := "id" simpleStorage := SimpleRESTStorage{ - errors: map[string]error{"update": NewNotFoundErr("simple", ID)}, + errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)}, } storage["simple"] = &simpleStorage handler := Handle(storage, codec, "/prefix/version") @@ -490,7 +491,7 @@ func TestCreateNotFound(t *testing.T) { "simple": &SimpleRESTStorage{ // storage.Create can fail with not found error in theory. // See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092. - errors: map[string]error{"create": NewNotFoundErr("simple", "id")}, + errors: map[string]error{"create": apierrs.NewNotFound("simple", "id")}, }, }, codec, "/prefix/version") server := httptest.NewServer(handler) @@ -597,41 +598,10 @@ func expectApiStatus(t *testing.T, method, url string, data []byte, code int) *a return &status } -func TestErrorsToAPIStatus(t *testing.T) { - cases := map[error]api.Status{ - NewAlreadyExistsErr("foo", "bar"): { - Status: api.StatusFailure, - Code: http.StatusConflict, - Reason: "already_exists", - Message: "foo \"bar\" already exists", - Details: &api.StatusDetails{ - Kind: "foo", - ID: "bar", - }, - }, - NewConflictErr("foo", "bar", errors.New("failure")): { - Status: api.StatusFailure, - Code: http.StatusConflict, - Reason: "conflict", - Message: "foo \"bar\" cannot be updated: failure", - Details: &api.StatusDetails{ - Kind: "foo", - ID: "bar", - }, - }, - } - for k, v := range cases { - actual := errToAPIStatus(k) - if !reflect.DeepEqual(actual, &v) { - t.Errorf("Expected %#v, Got %#v", v, actual) - } - } -} - func TestAsyncDelayReturnsError(t *testing.T) { storage := SimpleRESTStorage{ injectedFunction: func(obj interface{}) (interface{}, error) { - return nil, NewAlreadyExistsErr("foo", "bar") + return nil, apierrs.NewAlreadyExists("foo", "bar") }, } handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix/version") @@ -649,7 +619,7 @@ func TestAsyncCreateError(t *testing.T) { storage := SimpleRESTStorage{ injectedFunction: func(obj interface{}) (interface{}, error) { <-ch - return nil, NewAlreadyExistsErr("foo", "bar") + return nil, apierrs.NewAlreadyExists("foo", "bar") }, } handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix/version") @@ -673,7 +643,7 @@ func TestAsyncCreateError(t *testing.T) { time.Sleep(time.Millisecond) finalStatus := expectApiStatus(t, "GET", fmt.Sprintf("%s/prefix/version/operations/%s?after=1", server.URL, status.Details.ID), []byte{}, http.StatusOK) - expectedErr := NewAlreadyExistsErr("foo", "bar") + expectedErr := apierrs.NewAlreadyExists("foo", "bar") expectedStatus := &api.Status{ Status: api.StatusFailure, Code: http.StatusConflict, diff --git a/pkg/apiserver/errors.go b/pkg/apiserver/errors.go index 54384544b60..5172ef303b9 100644 --- a/pkg/apiserver/errors.go +++ b/pkg/apiserver/errors.go @@ -21,120 +21,19 @@ import ( "net/http" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" ) -// apiServerError is an error intended for consumption by a REST API server. -type apiServerError struct { - api.Status -} - -// Error implements the Error interface. -func (e *apiServerError) Error() string { - return e.Status.Message -} - -// NewNotFoundErr returns a new error which indicates that the resource of the kind and the name was not found. -func NewNotFoundErr(kind, name string) error { - return &apiServerError{api.Status{ - Status: api.StatusFailure, - Code: http.StatusNotFound, - Reason: api.StatusReasonNotFound, - Details: &api.StatusDetails{ - Kind: kind, - ID: name, - }, - Message: fmt.Sprintf("%s %q not found", kind, name), - }} -} - -// NewAlreadyExistsErr returns an error indicating the item requested exists by that identifier. -func NewAlreadyExistsErr(kind, name string) error { - return &apiServerError{api.Status{ - Status: api.StatusFailure, - Code: http.StatusConflict, - Reason: api.StatusReasonAlreadyExists, - Details: &api.StatusDetails{ - Kind: kind, - ID: name, - }, - Message: fmt.Sprintf("%s %q already exists", kind, name), - }} -} - -// NewConflictErr returns an error indicating the item can't be updated as provided. -func NewConflictErr(kind, name string, err error) error { - return &apiServerError{api.Status{ - Status: api.StatusFailure, - Code: http.StatusConflict, - Reason: api.StatusReasonConflict, - Details: &api.StatusDetails{ - Kind: kind, - ID: name, - }, - Message: fmt.Sprintf("%s %q cannot be updated: %s", kind, name, err), - }} -} - -// NewInvalidErr returns an error indicating the item is invalid and cannot be processed. -func NewInvalidErr(kind, name string, errs errors.ErrorList) error { - causes := make([]api.StatusCause, 0, len(errs)) - for i := range errs { - if err, ok := errs[i].(errors.ValidationError); ok { - causes = append(causes, api.StatusCause{ - Type: api.CauseType(err.Type), - Message: err.Error(), - Field: err.Field, - }) - } - } - return &apiServerError{api.Status{ - Status: api.StatusFailure, - Code: 422, // RFC 4918 - Reason: api.StatusReasonInvalid, - Details: &api.StatusDetails{ - Kind: kind, - ID: name, - Causes: causes, - }, - Message: fmt.Sprintf("%s %q is invalid: %s", kind, name, errs.ToError()), - }} -} - -// IsNotFound returns true if the specified error was created by NewNotFoundErr. -func IsNotFound(err error) bool { - return reasonForError(err) == api.StatusReasonNotFound -} - -// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists. -func IsAlreadyExists(err error) bool { - return reasonForError(err) == api.StatusReasonAlreadyExists -} - -// IsConflict determines if the err is an error which indicates the provided update conflicts. -func IsConflict(err error) bool { - return reasonForError(err) == api.StatusReasonConflict -} - -// IsInvalid determines if the err is an error which indicates the provided resource is not valid. -func IsInvalid(err error) bool { - return reasonForError(err) == api.StatusReasonInvalid -} - -func reasonForError(err error) api.StatusReason { - switch t := err.(type) { - case *apiServerError: - return t.Status.Reason - } - return api.StatusReasonUnknown +// statusError is an object that can be converted into an api.Status +type statusError interface { + Status() api.Status } // errToAPIStatus converts an error to an api.Status object. func errToAPIStatus(err error) *api.Status { switch t := err.(type) { - case *apiServerError: - status := t.Status + case statusError: + status := t.Status() status.Status = api.StatusFailure //TODO: check for invalid responses return &status diff --git a/pkg/apiserver/errors_test.go b/pkg/apiserver/errors_test.go index d887faa9874..13b3ae4ad83 100644 --- a/pkg/apiserver/errors_test.go +++ b/pkg/apiserver/errors_test.go @@ -17,126 +17,50 @@ limitations under the License. package apiserver import ( - "errors" - "fmt" + stderrs "errors" + "net/http" "reflect" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" ) -func TestErrorNew(t *testing.T) { - err := NewAlreadyExistsErr("test", "1") - if !IsAlreadyExists(err) { - t.Errorf("expected to be already_exists") - } - if IsConflict(err) { - t.Errorf("expected to not be confict") - } - if IsNotFound(err) { - t.Errorf(fmt.Sprintf("expected to not be %s", api.StatusReasonNotFound)) - } - if IsInvalid(err) { - t.Errorf("expected to not be invalid") - } - - if !IsConflict(NewConflictErr("test", "2", errors.New("message"))) { - t.Errorf("expected to be conflict") - } - if !IsNotFound(NewNotFoundErr("test", "3")) { - t.Errorf("expected to be not found") - } - if !IsInvalid(NewInvalidErr("test", "2", nil)) { - t.Errorf("expected to be invalid") - } -} - -func TestNewInvalidErr(t *testing.T) { - testCases := []struct { - Err apierrors.ValidationError - Details *api.StatusDetails - }{ - { - apierrors.NewDuplicate("field[0].name", "bar"), - &api.StatusDetails{ - Kind: "kind", - ID: "name", - Causes: []api.StatusCause{{ - Type: api.CauseTypeFieldValueDuplicate, - Field: "field[0].name", - }}, - }, - }, - { - apierrors.NewInvalid("field[0].name", "bar"), - &api.StatusDetails{ - Kind: "kind", - ID: "name", - Causes: []api.StatusCause{{ - Type: api.CauseTypeFieldValueInvalid, - Field: "field[0].name", - }}, - }, - }, - { - apierrors.NewNotFound("field[0].name", "bar"), - &api.StatusDetails{ - Kind: "kind", - ID: "name", - Causes: []api.StatusCause{{ - Type: api.CauseTypeFieldValueNotFound, - Field: "field[0].name", - }}, - }, - }, - { - apierrors.NewNotSupported("field[0].name", "bar"), - &api.StatusDetails{ - Kind: "kind", - ID: "name", - Causes: []api.StatusCause{{ - Type: api.CauseTypeFieldValueNotSupported, - Field: "field[0].name", - }}, - }, - }, - { - apierrors.NewRequired("field[0].name", "bar"), - &api.StatusDetails{ - Kind: "kind", - ID: "name", - Causes: []api.StatusCause{{ - Type: api.CauseTypeFieldValueRequired, - Field: "field[0].name", - }}, - }, - }, - } - for i := range testCases { - vErr, expected := testCases[i].Err, testCases[i].Details - expected.Causes[0].Message = vErr.Error() - err := NewInvalidErr("kind", "name", apierrors.ErrorList{vErr}) - status := errToAPIStatus(err) - if status.Code != 422 || status.Reason != api.StatusReasonInvalid { - t.Errorf("unexpected status: %#v", status) - } - if !reflect.DeepEqual(expected, status.Details) { - t.Errorf("expected %#v, got %#v", expected, status.Details) - } - } -} - func Test_errToAPIStatus(t *testing.T) { - err := &apiServerError{} + err := errors.NewNotFound("foo", "bar") status := errToAPIStatus(err) - if status.Reason != api.StatusReasonUnknown || status.Status != api.StatusFailure { + if status.Reason != api.StatusReasonNotFound || status.Status != api.StatusFailure { t.Errorf("unexpected status object: %#v", status) } } -func Test_reasonForError(t *testing.T) { - if e, a := api.StatusReasonUnknown, reasonForError(nil); e != a { - t.Errorf("unexpected reason type: %#v", a) +func TestErrorsToAPIStatus(t *testing.T) { + cases := map[error]api.Status{ + errors.NewAlreadyExists("foo", "bar"): { + Status: api.StatusFailure, + Code: http.StatusConflict, + Reason: "already_exists", + Message: "foo \"bar\" already exists", + Details: &api.StatusDetails{ + Kind: "foo", + ID: "bar", + }, + }, + errors.NewConflict("foo", "bar", stderrs.New("failure")): { + Status: api.StatusFailure, + Code: http.StatusConflict, + Reason: "conflict", + Message: "foo \"bar\" cannot be updated: failure", + Details: &api.StatusDetails{ + Kind: "foo", + ID: "bar", + }, + }, + } + for k, v := range cases { + actual := errToAPIStatus(k) + if !reflect.DeepEqual(actual, &v) { + t.Errorf("%s: Expected %#v, Got %#v", k, v, actual) + } } } diff --git a/pkg/kubelet/config/config.go b/pkg/kubelet/config/config.go index 4f2ce2c1f1c..3f38067db2b 100644 --- a/pkg/kubelet/config/config.go +++ b/pkg/kubelet/config/config.go @@ -251,7 +251,7 @@ func filterInvalidPods(pods []kubelet.Pod, source string) (filtered []*kubelet.P for i := range pods { var errors []error if names.Has(pods[i].Name) { - errors = append(errors, apierrs.NewDuplicate("Pod.Name", pods[i].Name)) + errors = append(errors, apierrs.NewFieldDuplicate("name", pods[i].Name)) } else { names.Insert(pods[i].Name) } diff --git a/pkg/kubelet/validation.go b/pkg/kubelet/validation.go index 74b0b90a9d4..6cf3cfbada9 100644 --- a/pkg/kubelet/validation.go +++ b/pkg/kubelet/validation.go @@ -24,7 +24,7 @@ import ( func ValidatePod(pod *Pod) (errors []error) { if !util.IsDNSSubdomain(pod.Name) { - errors = append(errors, apierrs.NewInvalid("Pod.Name", pod.Name)) + errors = append(errors, apierrs.NewFieldInvalid("name", pod.Name)) } if errs := validation.ValidateManifest(&pod.Manifest); len(errs) != 0 { errors = append(errors, errs...) diff --git a/pkg/registry/binding/storage.go b/pkg/registry/binding/storage.go index 6f320f95744..c6aeef564c6 100644 --- a/pkg/registry/binding/storage.go +++ b/pkg/registry/binding/storage.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" ) @@ -40,17 +41,17 @@ func NewBindingStorage(bindingRegistry Registry) *BindingStorage { // List returns an error because bindings are write-only objects. func (*BindingStorage) List(selector labels.Selector) (interface{}, error) { - return nil, apiserver.NewNotFoundErr("binding", "list") + return nil, errors.NewNotFound("binding", "list") } // Get returns an error because bindings are write-only objects. func (*BindingStorage) Get(id string) (interface{}, error) { - return nil, apiserver.NewNotFoundErr("binding", id) + return nil, errors.NewNotFound("binding", id) } // Delete returns an error because bindings are write-only objects. func (*BindingStorage) Delete(id string) (<-chan interface{}, error) { - return nil, apiserver.NewNotFoundErr("binding", id) + return nil, errors.NewNotFound("binding", id) } // New returns a new binding object fit for having data unmarshalled into it. diff --git a/pkg/registry/controller/storage.go b/pkg/registry/controller/storage.go index 4eafbbef61c..8ce5e5fe469 100644 --- a/pkg/registry/controller/storage.go +++ b/pkg/registry/controller/storage.go @@ -21,6 +21,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" @@ -61,7 +62,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { // Pod Manifest ID should be assigned by the pod API controller.DesiredState.PodTemplate.DesiredState.Manifest.ID = "" if errs := validation.ValidateReplicationController(controller); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("replicationController", controller.ID, errs) + return nil, errors.NewInvalid("replicationController", controller.ID, errs) } controller.CreationTimestamp = util.Now() @@ -120,7 +121,7 @@ func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { return nil, fmt.Errorf("not a replication controller: %#v", obj) } if errs := validation.ValidateReplicationController(controller); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("replicationController", controller.ID, errs) + return nil, errors.NewInvalid("replicationController", controller.ID, errs) } return apiserver.MakeAsync(func() (interface{}, error) { err := rs.registry.UpdateController(*controller) diff --git a/pkg/registry/controller/storage_test.go b/pkg/registry/controller/storage_test.go index 6bd157e3331..d9514031d3b 100644 --- a/pkg/registry/controller/storage_test.go +++ b/pkg/registry/controller/storage_test.go @@ -25,7 +25,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -280,7 +280,7 @@ func TestControllerStorageValidatesCreate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } @@ -310,7 +310,7 @@ func TestControllerStorageValidatesUpdate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } diff --git a/pkg/registry/endpoint/storage_test.go b/pkg/registry/endpoint/storage_test.go index e57c8e25958..06a7305270e 100644 --- a/pkg/registry/endpoint/storage_test.go +++ b/pkg/registry/endpoint/storage_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" ) @@ -45,13 +45,13 @@ func TestGetEndpoints(t *testing.T) { func TestGetEndpointsMissingService(t *testing.T) { registry := ®istrytest.ServiceRegistry{ - Err: apiserver.NewNotFoundErr("service", "foo"), + Err: errors.NewNotFound("service", "foo"), } storage := NewStorage(registry) // returns service not found _, err := storage.Get("foo") - if !apiserver.IsNotFound(err) || !reflect.DeepEqual(err, apiserver.NewNotFoundErr("service", "foo")) { + if !errors.IsNotFound(err) || !reflect.DeepEqual(err, errors.NewNotFound("service", "foo")) { t.Errorf("expected NotFound error, got %#v", err) } diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index b458e73f6cd..38dd5f5e48c 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -20,7 +20,7 @@ import ( "fmt" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/constraint" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -184,7 +184,7 @@ func (r *Registry) DeletePod(podID string) error { podKey := makePodKey(podID) err := r.ExtractObj(podKey, &pod, false) if tools.IsEtcdNotFound(err) { - return apiserver.NewNotFoundErr("pod", podID) + return errors.NewNotFound("pod", podID) } if err != nil { return err @@ -193,7 +193,7 @@ func (r *Registry) DeletePod(podID string) error { // machine and attempt to put it somewhere. err = r.Delete(podKey, true) if tools.IsEtcdNotFound(err) { - return apiserver.NewNotFoundErr("pod", podID) + return errors.NewNotFound("pod", podID) } if err != nil { return err @@ -249,7 +249,7 @@ func (r *Registry) GetController(controllerID string) (*api.ReplicationControlle key := makeControllerKey(controllerID) err := r.ExtractObj(key, &controller, false) if tools.IsEtcdNotFound(err) { - return nil, apiserver.NewNotFoundErr("replicationController", controllerID) + return nil, errors.NewNotFound("replicationController", controllerID) } if err != nil { return nil, err @@ -261,7 +261,7 @@ func (r *Registry) GetController(controllerID string) (*api.ReplicationControlle func (r *Registry) CreateController(controller api.ReplicationController) error { err := r.CreateObj(makeControllerKey(controller.ID), controller) if tools.IsEtcdNodeExist(err) { - return apiserver.NewAlreadyExistsErr("replicationController", controller.ID) + return errors.NewAlreadyExists("replicationController", controller.ID) } return err } @@ -276,7 +276,7 @@ func (r *Registry) DeleteController(controllerID string) error { key := makeControllerKey(controllerID) err := r.Delete(key, false) if tools.IsEtcdNotFound(err) { - return apiserver.NewNotFoundErr("replicationController", controllerID) + return errors.NewNotFound("replicationController", controllerID) } return err } @@ -296,7 +296,7 @@ func (r *Registry) ListServices() (*api.ServiceList, error) { func (r *Registry) CreateService(svc api.Service) error { err := r.CreateObj(makeServiceKey(svc.ID), svc) if tools.IsEtcdNodeExist(err) { - return apiserver.NewAlreadyExistsErr("service", svc.ID) + return errors.NewAlreadyExists("service", svc.ID) } return err } @@ -307,7 +307,7 @@ func (r *Registry) GetService(name string) (*api.Service, error) { var svc api.Service err := r.ExtractObj(key, &svc, false) if tools.IsEtcdNotFound(err) { - return nil, apiserver.NewNotFoundErr("service", name) + return nil, errors.NewNotFound("service", name) } if err != nil { return nil, err @@ -321,7 +321,7 @@ func (r *Registry) GetEndpoints(name string) (*api.Endpoints, error) { var endpoints api.Endpoints err := r.ExtractObj(key, &endpoints, false) if tools.IsEtcdNotFound(err) { - return nil, apiserver.NewNotFoundErr("endpoints", name) + return nil, errors.NewNotFound("endpoints", name) } if err != nil { return nil, err @@ -338,7 +338,7 @@ func (r *Registry) DeleteService(name string) error { key := makeServiceKey(name) err := r.Delete(key, true) if tools.IsEtcdNotFound(err) { - return apiserver.NewNotFoundErr("service", name) + return errors.NewNotFound("service", name) } if err != nil { return err diff --git a/pkg/registry/etcd/etcd_test.go b/pkg/registry/etcd/etcd_test.go index 2ad142c5081..cae9740b6c3 100644 --- a/pkg/registry/etcd/etcd_test.go +++ b/pkg/registry/etcd/etcd_test.go @@ -21,8 +21,8 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" - "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -627,7 +627,7 @@ func TestEtcdCreateControllerAlreadyExisting(t *testing.T) { ID: "foo", }, }) - if !apiserver.IsAlreadyExists(err) { + if !errors.IsAlreadyExists(err) { t.Errorf("expected already exists err, got %#v", err) } } @@ -716,7 +716,7 @@ func TestEtcdCreateServiceAlreadyExisting(t *testing.T) { err := registry.CreateService(api.Service{ JSONBase: api.JSONBase{ID: "foo"}, }) - if !apiserver.IsAlreadyExists(err) { + if !errors.IsAlreadyExists(err) { t.Errorf("expected already exists err, got %#v", err) } } diff --git a/pkg/registry/pod/storage.go b/pkg/registry/pod/storage.go index a3ee524ee44..730351067f4 100644 --- a/pkg/registry/pod/storage.go +++ b/pkg/registry/pod/storage.go @@ -23,6 +23,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" @@ -70,7 +71,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { } pod.DesiredState.Manifest.ID = pod.ID if errs := validation.ValidatePod(pod); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("pod", pod.ID, errs) + return nil, errors.NewInvalid("pod", pod.ID, errs) } pod.CreationTimestamp = util.Now() @@ -137,7 +138,7 @@ func (rs RegistryStorage) New() interface{} { func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { pod := obj.(*api.Pod) if errs := validation.ValidatePod(pod); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("pod", pod.ID, errs) + return nil, errors.NewInvalid("pod", pod.ID, errs) } return apiserver.MakeAsync(func() (interface{}, error) { if err := rs.registry.UpdatePod(*pod); err != nil { diff --git a/pkg/registry/pod/storage_test.go b/pkg/registry/pod/storage_test.go index 7268306788b..424333bb0a1 100644 --- a/pkg/registry/pod/storage_test.go +++ b/pkg/registry/pod/storage_test.go @@ -23,7 +23,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" @@ -337,7 +337,7 @@ func TestPodStorageValidatesCreate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } @@ -353,7 +353,7 @@ func TestPodStorageValidatesUpdate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } diff --git a/pkg/registry/service/storage.go b/pkg/registry/service/storage.go index 742847c4b4b..44da947b49c 100644 --- a/pkg/registry/service/storage.go +++ b/pkg/registry/service/storage.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" @@ -51,7 +52,7 @@ func NewRegistryStorage(registry Registry, cloud cloudprovider.Interface, machin func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { srv := obj.(*api.Service) if errs := validation.ValidateService(srv); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("service", srv.ID, errs) + return nil, errors.NewInvalid("service", srv.ID, errs) } srv.CreationTimestamp = util.Now() @@ -157,7 +158,7 @@ func GetServiceEnvironmentVariables(registry Registry, machine string) ([]api.En func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { srv := obj.(*api.Service) if errs := validation.ValidateService(srv); len(errs) > 0 { - return nil, apiserver.NewInvalidErr("service", srv.ID, errs) + return nil, errors.NewInvalid("service", srv.ID, errs) } return apiserver.MakeAsync(func() (interface{}, error) { // TODO: check to see if external load balancer status changed diff --git a/pkg/registry/service/storage_test.go b/pkg/registry/service/storage_test.go index 0186a4635f0..bddfc782cda 100644 --- a/pkg/registry/service/storage_test.go +++ b/pkg/registry/service/storage_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" cloud "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" @@ -79,7 +80,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } @@ -140,7 +141,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) { if c != nil { t.Errorf("Expected nil channel") } - if !apiserver.IsInvalid(err) { + if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } }