diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher.go index 0ec6c7ef2da..b874b76f1e5 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher.go @@ -40,11 +40,12 @@ type ErrorMatcher struct { matchField bool // TODO(thockin): consider whether value could be assumed - if the // "want" error has a nil value, don't match on value. - matchValue bool - matchOrigin bool - matchDetail func(want, got string) bool - requireOriginWhenInvalid bool - matchDeclarativeNative bool + matchValue bool + matchOrigin bool + matchDetail func(want, got string) bool + requireOriginWhenInvalid bool + matchDeclarativeNative bool + matchValidationStabilityLevel bool // normalizationRules holds the pre-compiled regex patterns for path normalization. normalizationRules []NormalizationRule } @@ -90,6 +91,9 @@ func (m ErrorMatcher) Matches(want, got *Error) bool { if m.matchDeclarativeNative && want.DeclarativeNative != got.DeclarativeNative { return false } + if m.matchValidationStabilityLevel && want.ValidationStabilityLevel != got.ValidationStabilityLevel { + return false + } return true } @@ -157,6 +161,10 @@ func (m ErrorMatcher) Render(e *Error) string { comma() buf.WriteString(fmt.Sprintf("DeclarativeNative=%t", e.DeclarativeNative)) } + if m.matchValidationStabilityLevel { + comma() + buf.WriteString(fmt.Sprintf("ValidationStabilityLevel=%s", e.ValidationStabilityLevel)) + } return "{" + buf.String() + "}" } @@ -240,6 +248,13 @@ func (m ErrorMatcher) ByDeclarativeNative() ErrorMatcher { return m } +// ByValidationStabilityLevel returns a derived ErrorMatcher which also matches by the validation stability level +// value of field errors. +func (m ErrorMatcher) ByValidationStabilityLevel() ErrorMatcher { + m.matchValidationStabilityLevel = true + return m +} + // ByDetailExact returns a derived ErrorMatcher which also matches errors by // the exact detail string. func (m ErrorMatcher) ByDetailExact() ErrorMatcher { diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher_test.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher_test.go index 02e07fb8be5..44e51f2556e 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/error_matcher_test.go @@ -437,6 +437,17 @@ func TestErrorMatcher_Test(t *testing.T) { want: ErrorList{{DeclarativeNative: true}}, got: ErrorList{{DeclarativeNative: false}}, expectedErrors: []string{"expected an error matching:", "unmatched error:"}, + }, { + name: "validation level: match", + matcher: ErrorMatcher{}.ByValidationStabilityLevel(), + want: ErrorList{{}}.MarkAlpha(), + got: ErrorList{{}}.MarkAlpha(), + }, { + name: "validation level: no match", + matcher: ErrorMatcher{}.ByValidationStabilityLevel(), + want: ErrorList{{}}.MarkAlpha(), + got: ErrorList{{}}.MarkBeta(), + expectedErrors: []string{"expected an error matching:", "unmatched error:"}, }, { name: "with origin: single match", matcher: ErrorMatcher{}.ByField().ByOrigin(), diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go index 4b83eed3104..5d3a39141fd 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go @@ -59,6 +59,29 @@ type Error struct { // DeclarativeNative is true when this error originates from a declarative-native validation. // This field is used to distinguish errors that are exclusively declarative and lack an imperative counterpart. DeclarativeNative bool + + // ValidationStabilityLevel denotes the validation stability level of the declarative validation from this error is returned. This should be used in the declarative validations only. + ValidationStabilityLevel validationStabilityLevel +} + +// ValidationLevel denotes the stability level of a validation. +type validationStabilityLevel int + +const ( + unknown validationStabilityLevel = iota + stabilityLevelAlpha + stabilityLevelBeta +) + +func (v validationStabilityLevel) String() string { + switch v { + case stabilityLevelAlpha: + return "alpha" + case stabilityLevelBeta: + return "beta" + default: + return "unknown" + } } var _ error = &Error{} @@ -117,6 +140,7 @@ func (e *Error) ErrorBody() string { if len(e.Detail) != 0 { s += fmt.Sprintf(": %s", e.Detail) } + return s } @@ -464,6 +488,34 @@ func (list ErrorList) MarkDeclarativeNative() ErrorList { return list } +// MarkAlpha marks the error as an alpha validation error. +func (e *Error) MarkAlpha() *Error { + e.ValidationStabilityLevel = stabilityLevelAlpha + return e +} + +// MarkAlpha marks the errors as alpha validation errors. +func (list ErrorList) MarkAlpha() ErrorList { + for _, err := range list { + err.ValidationStabilityLevel = stabilityLevelAlpha + } + return list +} + +// MarkBeta marks the error as a beta validation error. +func (e *Error) MarkBeta() *Error { + e.ValidationStabilityLevel = stabilityLevelBeta + return e +} + +// MarkBeta marks the errors as beta validation errors. +func (list ErrorList) MarkBeta() ErrorList { + for _, err := range list { + err.ValidationStabilityLevel = stabilityLevelBeta + } + return list +} + // RemoveCoveredByDeclarative returns a new ErrorList containing only the errors that should not be covered by declarative validation. func (list ErrorList) RemoveCoveredByDeclarative() ErrorList { newList := ErrorList{}