mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Add a Causes array to status details for validation errors
Uses the same constant values as api/errors for each nested reason.
This commit is contained in:
parent
60126bfe64
commit
d326ba2976
@ -362,9 +362,9 @@ type Status struct {
|
|||||||
JSONBase `json:",inline" yaml:",inline"`
|
JSONBase `json:",inline" yaml:",inline"`
|
||||||
// One of: "success", "failure", "working" (for operations not yet completed)
|
// One of: "success", "failure", "working" (for operations not yet completed)
|
||||||
Status string `json:"status,omitempty" yaml:"status,omitempty"`
|
Status string `json:"status,omitempty" yaml:"status,omitempty"`
|
||||||
// A human readable description of the status of this operation.
|
// A human-readable description of the status of this operation.
|
||||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||||
// A machine readable description of why this operation is in the
|
// A machine-readable description of why this operation is in the
|
||||||
// "failure" or "working" status. If this value is empty there
|
// "failure" or "working" status. If this value is empty there
|
||||||
// is no information available. A Reason clarifies an HTTP status
|
// is no information available. A Reason clarifies an HTTP status
|
||||||
// code but does not override it.
|
// code but does not override it.
|
||||||
@ -391,6 +391,9 @@ type StatusDetails struct {
|
|||||||
// The kind attribute of the resource associated with the status ReasonType.
|
// The kind attribute of the resource associated with the status ReasonType.
|
||||||
// On some operations may differ from the requested resource Kind.
|
// On some operations may differ from the requested resource Kind.
|
||||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||||
|
// The Causes array includes more details associated with the ReasonType
|
||||||
|
// failure. Not all ReasonTypes may provide detailed causes.
|
||||||
|
Causes []StatusCause `json:"causes,omitempty" yaml:"causes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values of Status.Status
|
// Values of Status.Status
|
||||||
@ -447,6 +450,62 @@ const (
|
|||||||
// conflict.
|
// conflict.
|
||||||
// Status code 409
|
// Status code 409
|
||||||
ReasonTypeConflict ReasonType = "conflict"
|
ReasonTypeConflict ReasonType = "conflict"
|
||||||
|
|
||||||
|
// ResourceTypeInvalid means the requested create or update operation cannot be
|
||||||
|
// completed due to invalid data provided as part of the request. The client may
|
||||||
|
// need to alter the request. When set, the client may use the StatusDetails
|
||||||
|
// message field as a summary of the issues encountered.
|
||||||
|
// Details (optional):
|
||||||
|
// "kind" string - the kind attribute of the invalid resource
|
||||||
|
// "id" string - the identifier of the invalid resource
|
||||||
|
// "causes" - one or more StatusCause entries indicating the data in the
|
||||||
|
// provided resource that was invalid. The code, message, and
|
||||||
|
// field attributes will be set.
|
||||||
|
// Status code 422
|
||||||
|
ReasonTypeInvalid ReasonType = "invalid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusCause provides more information about an api.Status failure, including
|
||||||
|
// cases when multiple errors are encountered.
|
||||||
|
type StatusCause struct {
|
||||||
|
// A machine-readable description of the cause of the error. If this value is
|
||||||
|
// empty there is no information available.
|
||||||
|
Reason CauseReasonType `json:"reason,omitempty" yaml:"reason,omitempty"`
|
||||||
|
// A human-readable description of the cause of the error. This field may be
|
||||||
|
// presented as-is to a reader.
|
||||||
|
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||||
|
// The field of the resource that has caused this error, as named by its JSON
|
||||||
|
// serialization. May include dot and postfix notation for nested attributes.
|
||||||
|
// Arrays are zero-indexed. Fields may appear more than once in an array of
|
||||||
|
// causes due to fields having multiple errors.
|
||||||
|
// Optional.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// "name" - the field "name" on the current resource
|
||||||
|
// "items[0].name" - the field "name" on the first array entry in "items"
|
||||||
|
Field string `json:"field,omitempty" yaml:"field,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CauseReasonType is a machine readable value providing more detail about why
|
||||||
|
// an operation failed. An operation may have multiple causes for a failure.
|
||||||
|
type CauseReasonType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CauseReasonTypeFieldValueNotFound is used to report failure to find a requested value
|
||||||
|
// (e.g. looking up an ID).
|
||||||
|
CauseReasonTypeFieldValueNotFound CauseReasonType = "fieldValueNotFound"
|
||||||
|
// CauseReasonTypeFieldValueInvalid is used to report required values that are not
|
||||||
|
// provided (e.g. empty strings, null values, or empty arrays).
|
||||||
|
CauseReasonTypeFieldValueRequired CauseReasonType = "fieldValueRequired"
|
||||||
|
// CauseReasonTypeFieldValueDuplicate is used to report collisions of values that must be
|
||||||
|
// unique (e.g. unique IDs).
|
||||||
|
CauseReasonTypeFieldValueDuplicate CauseReasonType = "fieldValueDuplicate"
|
||||||
|
// CauseReasonTypeFieldValueInvalid is used to report malformed values (e.g. failed regex
|
||||||
|
// match).
|
||||||
|
CauseReasonTypeFieldValueInvalid CauseReasonType = "fieldValueInvalid"
|
||||||
|
// CauseReasonTypeFieldValueNotSupported is used to report valid (as per formatting rules)
|
||||||
|
// values that can not be handled (e.g. an enumerated string).
|
||||||
|
CauseReasonTypeFieldValueNotSupported CauseReasonType = "fieldValueNotSupported"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServerOp is an operation delivered to API clients.
|
// ServerOp is an operation delivered to API clients.
|
||||||
|
@ -395,6 +395,9 @@ type StatusDetails struct {
|
|||||||
// The kind attribute of the resource associated with the status ReasonType.
|
// The kind attribute of the resource associated with the status ReasonType.
|
||||||
// On some operations may differ from the requested resource Kind.
|
// On some operations may differ from the requested resource Kind.
|
||||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||||
|
// The Causes array includes more details associated with the ReasonType
|
||||||
|
// failure. Not all ReasonTypes may provide detailed causes.
|
||||||
|
Causes []StatusCause `json:"causes,omitempty" yaml:"causes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values of Status.Status
|
// Values of Status.Status
|
||||||
@ -453,6 +456,49 @@ const (
|
|||||||
ReasonTypeConflict ReasonType = "conflict"
|
ReasonTypeConflict ReasonType = "conflict"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StatusCause provides more information about an api.Status failure, including
|
||||||
|
// cases when multiple errors are encountered.
|
||||||
|
type StatusCause struct {
|
||||||
|
// A machine-readable description of the cause of the error. If this value is
|
||||||
|
// empty there is no information available.
|
||||||
|
Reason CauseReasonType `json:"reason,omitempty" yaml:"reason,omitempty"`
|
||||||
|
// A human-readable description of the cause of the error. This field may be
|
||||||
|
// presented as-is to a reader.
|
||||||
|
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||||
|
// The field of the resource that has caused this error, as named by its JSON
|
||||||
|
// serialization. May include dot and postfix notation for nested attributes.
|
||||||
|
// Arrays are zero-indexed. Fields may appear more than once in an array of
|
||||||
|
// causes due to fields having multiple errors.
|
||||||
|
// Optional.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// "name" - the field "name" on the current resource
|
||||||
|
// "items[0].name" - the field "name" on the first array entry in "items"
|
||||||
|
Field string `json:"field,omitempty" yaml:"field,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CauseReasonType is a machine readable value providing more detail about why
|
||||||
|
// an operation failed. An operation may have multiple causes for a failure.
|
||||||
|
type CauseReasonType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CauseReasonTypeFieldValueNotFound is used to report failure to find a requested value
|
||||||
|
// (e.g. looking up an ID).
|
||||||
|
CauseReasonTypeFieldValueNotFound CauseReasonType = "fieldValueNotFound"
|
||||||
|
// CauseReasonTypeFieldValueInvalid is used to report required values that are not
|
||||||
|
// provided (e.g. empty strings, null values, or empty arrays).
|
||||||
|
CauseReasonTypeFieldValueRequired CauseReasonType = "fieldValueRequired"
|
||||||
|
// CauseReasonTypeFieldValueDuplicate is used to report collisions of values that must be
|
||||||
|
// unique (e.g. unique IDs).
|
||||||
|
CauseReasonTypeFieldValueDuplicate CauseReasonType = "fieldValueDuplicate"
|
||||||
|
// CauseReasonTypeFieldValueInvalid is used to report malformed values (e.g. failed regex
|
||||||
|
// match).
|
||||||
|
CauseReasonTypeFieldValueInvalid CauseReasonType = "fieldValueInvalid"
|
||||||
|
// CauseReasonTypeFieldValueNotSupported is used to report valid (as per formatting rules)
|
||||||
|
// values that can not be handled (e.g. an enumerated string).
|
||||||
|
CauseReasonTypeFieldValueNotSupported CauseReasonType = "fieldValueNotSupported"
|
||||||
|
)
|
||||||
|
|
||||||
// ServerOp is an operation delivered to API clients.
|
// ServerOp is an operation delivered to API clients.
|
||||||
type ServerOp struct {
|
type ServerOp struct {
|
||||||
JSONBase `yaml:",inline" json:",inline"`
|
JSONBase `yaml:",inline" json:",inline"`
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,6 +77,21 @@ func NewConflictErr(kind, name string, err error) error {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewInvalidError returns an error indicating the item is invalid and cannot be processed.
|
||||||
|
func NewInvalidError(kind, name string, errs errors.ErrorList) error {
|
||||||
|
return &apiServerError{api.Status{
|
||||||
|
Status: api.StatusFailure,
|
||||||
|
Code: 422, // RFC 4918
|
||||||
|
Reason: api.ReasonTypeInvalid,
|
||||||
|
Details: &api.StatusDetails{
|
||||||
|
Kind: kind,
|
||||||
|
ID: name,
|
||||||
|
// TODO: causes
|
||||||
|
},
|
||||||
|
Message: fmt.Sprintf("%s %q is invalid: %s", kind, name, errs.ToError()),
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
// IsNotFound returns true if the specified error was created by NewNotFoundErr
|
// IsNotFound returns true if the specified error was created by NewNotFoundErr
|
||||||
func IsNotFound(err error) bool {
|
func IsNotFound(err error) bool {
|
||||||
return reasonForError(err) == api.ReasonTypeNotFound
|
return reasonForError(err) == api.ReasonTypeNotFound
|
||||||
@ -91,6 +107,11 @@ func IsConflict(err error) bool {
|
|||||||
return reasonForError(err) == api.ReasonTypeConflict
|
return reasonForError(err) == api.ReasonTypeConflict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.ReasonTypeInvalid
|
||||||
|
}
|
||||||
|
|
||||||
func reasonForError(err error) api.ReasonType {
|
func reasonForError(err error) api.ReasonType {
|
||||||
switch t := err.(type) {
|
switch t := err.(type) {
|
||||||
case *apiServerError:
|
case *apiServerError:
|
||||||
@ -110,7 +131,7 @@ func errToAPIStatus(err error) *api.Status {
|
|||||||
default:
|
default:
|
||||||
status := http.StatusInternalServerError
|
status := http.StatusInternalServerError
|
||||||
switch {
|
switch {
|
||||||
//TODO: replace me with NewUpdateConflictErr
|
//TODO: replace me with NewConflictErr
|
||||||
case tools.IsEtcdTestFailed(err):
|
case tools.IsEtcdTestFailed(err):
|
||||||
status = http.StatusConflict
|
status = http.StatusConflict
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,31 @@ func TestErrorNew(t *testing.T) {
|
|||||||
if IsNotFound(err) {
|
if IsNotFound(err) {
|
||||||
t.Errorf(fmt.Sprintf("expected to not be %s", api.ReasonTypeNotFound))
|
t.Errorf(fmt.Sprintf("expected to not be %s", api.ReasonTypeNotFound))
|
||||||
}
|
}
|
||||||
|
if IsInvalid(err) {
|
||||||
|
t.Errorf("expected to not be invalid")
|
||||||
|
}
|
||||||
|
|
||||||
if !IsConflict(NewConflictErr("test", "2", errors.New("message"))) {
|
if !IsConflict(NewConflictErr("test", "2", errors.New("message"))) {
|
||||||
t.Errorf("expected to be confict")
|
t.Errorf("expected to be conflict")
|
||||||
}
|
}
|
||||||
if !IsNotFound(NewNotFoundErr("test", "3")) {
|
if !IsNotFound(NewNotFoundErr("test", "3")) {
|
||||||
t.Errorf("expected to be not found")
|
t.Errorf("expected to be not found")
|
||||||
}
|
}
|
||||||
|
if !IsInvalid(NewInvalidError("test", "2", nil)) {
|
||||||
|
t.Errorf("expected to be invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_errToAPIStatus(t *testing.T) {
|
||||||
|
err := &apiServerError{}
|
||||||
|
status := errToAPIStatus(err)
|
||||||
|
if status.Reason != api.ReasonTypeUnknown || status.Status != api.StatusFailure {
|
||||||
|
t.Errorf("unexpected status object: %#v", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_reasonForError(t *testing.T) {
|
||||||
|
if e, a := api.ReasonTypeUnknown, reasonForError(nil); e != a {
|
||||||
|
t.Errorf("unexpected reason type: %#v", a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user