From 449036fdcd89e7a44f97d6527180d053e7634875 Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 15 May 2019 14:55:45 -0400 Subject: [PATCH] Add +optional/+required markers at package and struct level for CRD validation Using +kubebuilder:validation:Optional/Required at the package level sets the default value for fields in that package to that value, while using the opposite marker on fields in that package sets them as an exception --- pkg/crd/markers/package.go | 3 +++ pkg/crd/markers/validation.go | 4 ++++ pkg/crd/parser.go | 1 + pkg/crd/schema.go | 21 +++++++++++++++++++-- pkg/markers/zip.go | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pkg/crd/markers/package.go b/pkg/crd/markers/package.go index ea49988a6..b86445ef6 100644 --- a/pkg/crd/markers/package.go +++ b/pkg/crd/markers/package.go @@ -24,5 +24,8 @@ func init() { AllDefinitions = append(AllDefinitions, markers.Must(markers.MakeDefinition("groupName", markers.DescribesPackage, "")), markers.Must(markers.MakeDefinition("versionName", markers.DescribesPackage, "")), + markers.Must(markers.MakeDefinition("kubebuilder:validation:Optional", markers.DescribesPackage, struct{}{})), + markers.Must(markers.MakeDefinition("kubebuilder:validation:Required", markers.DescribesPackage, struct{}{})), + ) } diff --git a/pkg/crd/markers/validation.go b/pkg/crd/markers/validation.go index 7b3c1fe8c..ab2defd59 100644 --- a/pkg/crd/markers/validation.go +++ b/pkg/crd/markers/validation.go @@ -57,6 +57,8 @@ var ValidationMarkers = mustMakeAllWithPrefix("kubebuilder:validation", markers. Enum(nil), Format(""), Type(""), + Required(struct{}{}), + Optional(struct{}{}), ) func init() { @@ -86,6 +88,8 @@ type UniqueItems bool type Enum []interface{} type Format string type Type string +type Required struct{} +type Optional struct{} func (m Maximum) ApplyToSchema(schema *v1beta1.JSONSchemaProps) error { if schema.Type != "integer" { diff --git a/pkg/crd/parser.go b/pkg/crd/parser.go index ee88cc2bb..2efb592a8 100644 --- a/pkg/crd/parser.go +++ b/pkg/crd/parser.go @@ -121,6 +121,7 @@ func (p *Parser) indexTypes(pkg *loader.Package) { Name: info.Name, } + info.PackageMarkers = pkgMarkers p.Types[ident] = info }); err != nil { pkg.AddError(err) diff --git a/pkg/crd/schema.go b/pkg/crd/schema.go index 6e545698f..8ff359af5 100644 --- a/pkg/crd/schema.go +++ b/pkg/crd/schema.go @@ -342,9 +342,26 @@ func structToSchema(ctx *schemaContext, structType *ast.StructType) *v1beta1.JSO fieldName := jsonOpts[0] inline = inline || fieldName == "" // anonymous fields are inline fields in YAML/JSON - if !inline && !omitEmpty { - props.Required = append(props.Required, fieldName) + // if no default required mode is set, default to required + defaultMode := "required" + if ctx.info.PackageMarkers.Get("kubebuilder:validation:Optional") != nil { + defaultMode = "optional" } + // if this package isn't set to optional default... + if defaultMode == "required" { + // ...everything that's not inline, omitempty, or explicitly optional is required + if !inline && !omitEmpty && field.Markers.Get("kubebuilder:validation:Optional") == nil { + props.Required = append(props.Required, fieldName) + } + } + // if this package isn't set to required default... + if defaultMode == "optional" { + // ...everything that isn't explicitly required is optional + if field.Markers.Get("kubebuilder:validation:Required") != nil { + props.Required = append(props.Required, fieldName) + } + } + propSchema := typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type) propSchema.Description = field.Doc diff --git a/pkg/markers/zip.go b/pkg/markers/zip.go index a9b4c98af..dbbcddff3 100644 --- a/pkg/markers/zip.go +++ b/pkg/markers/zip.go @@ -120,6 +120,8 @@ type TypeInfo struct { // Markers are all registered markers associated with the type. Markers MarkerValues + // PackageMarkers are the registered markers for the package of the type + PackageMarkers MarkerValues // Fields are all the fields associated with the type, if it's a struct. // (if not, Fields will be nil).