Skip to content

Commit

Permalink
Server Side Field Validation
Browse files Browse the repository at this point in the history
Implements server side field validation behind the
`ServerSideFieldValidation` feature gate. With the
feature enabled, any create/update/patch request
with the `fieldValidation` query param set to
"Strict" will error if the object in the request
body have unknown fields. A value of "Warn"
(also the default when the feautre is enabled)
will succeed the request with a warning.

When the feature is disabled (or the query param
has a value of "Ignore"), the request will succeed
as it previously had with no indications of any
unknown or duplicate fields.

Kubernetes-commit: e50e2bbc889eb274ad1463a54188a2805767bfde
  • Loading branch information
kevindelgado authored and k8s-publishing-bot committed Aug 18, 2021
1 parent 9edaf59 commit db630ad
Show file tree
Hide file tree
Showing 16 changed files with 1,164 additions and 246 deletions.
470 changes: 296 additions & 174 deletions pkg/apis/meta/v1/generated.pb.go

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions pkg/apis/meta/v1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions pkg/apis/meta/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,15 @@ type DeleteOptions struct {
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,5,rep,name=dryRun"`
}

const (
// FieldValidationIgnore ignores unknown/duplicate fields
FieldValidationIgnore = "Ignore"
// FieldValidationWarn responds with a warning, but successfully serve the request
FieldValidationWarn = "Warn"
// FieldValidationStrict fails the request on unknown/duplicate fields
FieldValidationStrict = "Strict"
)

// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

Expand All @@ -544,6 +553,19 @@ type CreateOptions struct {
// as defined by https://golang.org/pkg/unicode/#IsPrint.
// +optional
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,3,name=fieldManager"`

// fieldValidation determines how the server should respond to
// unknown/duplicate fields in the object in the request.
// Introduced as alpha in 1.23, older servers or servers with the
// `ServerSideFieldValidation` feature disabled will discard valid values
// specified in this param and not perform any server side field validation.
// Valid values are:
// - Ignore: ignores unknown/duplicate fields.
// - Warn: responds with a warning for each
// unknown/duplicate field, but successfully serves the request.
// - Strict: fails the request on unknown/duplicate fields.
// +optional
FieldValidation string `json:"fieldValidation,omitempty" protobuf:"bytes,4,name=fieldValidation"`
}

// +k8s:conversion-gen:explicit-from=net/url.Values
Expand Down Expand Up @@ -577,6 +599,19 @@ type PatchOptions struct {
// types (JsonPatch, MergePatch, StrategicMergePatch).
// +optional
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,3,name=fieldManager"`

// fieldValidation determines how the server should respond to
// unknown/duplicate fields in the object in the request.
// Introduced as alpha in 1.23, older servers or servers with the
// `ServerSideFieldValidation` feature disabled will discard valid values
// specified in this param and not perform any server side field validation.
// Valid values are:
// - Ignore: ignores unknown/duplicate fields.
// - Warn: responds with a warning for each
// unknown/duplicate field, but successfully serves the request.
// - Strict: fails the request on unknown/duplicate fields.
// +optional
FieldValidation string `json:"fieldValidation,omitempty" protobuf:"bytes,4,name=fieldValidation"`
}

// ApplyOptions may be provided when applying an API object.
Expand Down Expand Up @@ -632,6 +667,19 @@ type UpdateOptions struct {
// as defined by https://golang.org/pkg/unicode/#IsPrint.
// +optional
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,2,name=fieldManager"`

// fieldValidation determines how the server should respond to
// unknown/duplicate fields in the object in the request.
// Introduced as alpha in 1.23, older servers or servers with the
// `ServerSideFieldValidation` feature disabled will discard valid values
// specified in this param and not perform any server side field validation.
// Valid values are:
// - Ignore: ignores unknown/duplicate fields.
// - Warn: responds with a warning for each
// unknown/duplicate field, but successfully serves the request.
// - Strict: fails the request on unknown/duplicate fields.
// +optional
FieldValidation string `json:"fieldValidation,omitempty" protobuf:"bytes,3,name=fieldValidation"`
}

// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
Expand Down
23 changes: 13 additions & 10 deletions pkg/apis/meta/v1/types_swagger_doc_generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ func (Condition) SwaggerDoc() map[string]string {
}

var map_CreateOptions = map[string]string{
"": "CreateOptions may be provided when creating an API object.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"": "CreateOptions may be provided when creating an API object.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"fieldValidation": "fieldValidation determines how the server should respond to unknown/duplicate fields in the object in the request. Introduced as alpha in 1.23, older servers or servers with the `ServerSideFieldValidation` feature disabled will discard valid values specified in this param and not perform any server side field validation. Valid values are: - Ignore: ignores unknown/duplicate fields. - Warn: responds with a warning for each unknown/duplicate field, but successfully serves the request. - Strict: fails the request on unknown/duplicate fields.",
}

func (CreateOptions) SwaggerDoc() map[string]string {
Expand Down Expand Up @@ -302,10 +303,11 @@ func (Patch) SwaggerDoc() map[string]string {
}

var map_PatchOptions = map[string]string{
"": "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"force": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"": "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"force": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"fieldValidation": "fieldValidation determines how the server should respond to unknown/duplicate fields in the object in the request. Introduced as alpha in 1.23, older servers or servers with the `ServerSideFieldValidation` feature disabled will discard valid values specified in this param and not perform any server side field validation. Valid values are: - Ignore: ignores unknown/duplicate fields. - Warn: responds with a warning for each unknown/duplicate field, but successfully serves the request. - Strict: fails the request on unknown/duplicate fields.",
}

func (PatchOptions) SwaggerDoc() map[string]string {
Expand Down Expand Up @@ -447,9 +449,10 @@ func (TypeMeta) SwaggerDoc() map[string]string {
}

var map_UpdateOptions = map[string]string{
"": "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"": "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"fieldManager": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"fieldValidation": "fieldValidation determines how the server should respond to unknown/duplicate fields in the object in the request. Introduced as alpha in 1.23, older servers or servers with the `ServerSideFieldValidation` feature disabled will discard valid values specified in this param and not perform any server side field validation. Valid values are: - Ignore: ignores unknown/duplicate fields. - Warn: responds with a warning for each unknown/duplicate field, but successfully serves the request. - Strict: fails the request on unknown/duplicate fields.",
}

func (UpdateOptions) SwaggerDoc() map[string]string {
Expand Down
31 changes: 23 additions & 8 deletions pkg/apis/meta/v1/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,19 @@ func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList {
}

func ValidateCreateOptions(options *metav1.CreateOptions) field.ErrorList {
return append(
ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager")),
ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...,
)
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
return allErrs
}

func ValidateUpdateOptions(options *metav1.UpdateOptions) field.ErrorList {
return append(
ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager")),
ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...,
)
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
return allErrs
}

func ValidatePatchOptions(options *metav1.PatchOptions, patchType types.PatchType) field.ErrorList {
Expand All @@ -123,6 +125,7 @@ func ValidatePatchOptions(options *metav1.PatchOptions, patchType types.PatchTyp
}
allErrs = append(allErrs, ValidateFieldManager(options.FieldManager, field.NewPath("fieldManager"))...)
allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
allErrs = append(allErrs, ValidateFieldValidation(field.NewPath("fieldValidation"), options.FieldValidation)...)
return allErrs
}

Expand Down Expand Up @@ -159,6 +162,18 @@ func ValidateDryRun(fldPath *field.Path, dryRun []string) field.ErrorList {
return allErrs
}

var allowedFieldValidationValues = sets.NewString("", metav1.FieldValidationIgnore, metav1.FieldValidationWarn, metav1.FieldValidationStrict)

// ValidateFieldValidation validates that a fieldValidation query param only contains allowed values.
func ValidateFieldValidation(fldPath *field.Path, fieldValidation string) field.ErrorList {
allErrs := field.ErrorList{}
if !allowedFieldValidationValues.Has(fieldValidation) {
allErrs = append(allErrs, field.NotSupported(fldPath, fieldValidation, allowedFieldValidationValues.List()))
}
return allErrs

}

const UninitializedStatusUpdateErrorMsg string = `must not update status when the object is uninitialized`

// ValidateTableOptions returns any invalid flags on TableOptions.
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/meta/v1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit db630ad

Please sign in to comment.