Skip to content

Commit 80b5507

Browse files
committed
Add crd:validation:Schemaless marker
This marker will avoid trying to do any type detection on any struct field on which it is set. This gives users a safety valve when they hit an edge case where type inference does the wrong thing for them. This fixes kubernetes-sigs#291, which was recently re-broken by fixing kubernetes-sigs#502 Signed-off-by: Max Smythe <[email protected]>
1 parent 895eccc commit 80b5507

7 files changed

+46
-7
lines changed

.golangci.yml

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
run:
2-
modules-download-mode: readonly
3-
42
# Increase the default deadline from 1m as some module operations can take a
53
# while if uncached!
64
deadline: 5m

pkg/crd/markers/validation.go

+17
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import (
2626
"sigs.k8s.io/controller-tools/pkg/markers"
2727
)
2828

29+
const (
30+
SchemalessName = "kubebuilder:validation:Schemaless"
31+
)
32+
2933
// ValidationMarkers lists all available markers that affect CRD schema generation,
3034
// except for the few that don't make sense as type-level markers (see FieldOnlyMarkers).
3135
// All markers start with `+kubebuilder:validation:`, and continue with their type name.
@@ -82,6 +86,9 @@ var FieldOnlyMarkers = []*definitionWithHelp{
8286

8387
must(markers.MakeDefinition("kubebuilder:validation:EmbeddedResource", markers.DescribesField, XEmbeddedResource{})).
8488
WithHelp(XEmbeddedResource{}.Help()),
89+
90+
must(markers.MakeDefinition(SchemalessName, markers.DescribesField, Schemaless{})).
91+
WithHelp(Schemaless{}.Help()),
8592
}
8693

8794
// ValidationIshMarkers are field-and-type markers that don't fall under the
@@ -225,6 +232,16 @@ type XPreserveUnknownFields struct{}
225232
// field, yet it is possible. This can be combined with PreserveUnknownFields.
226233
type XEmbeddedResource struct{}
227234

235+
// +controllertools:marker:generateHelp:category="CRD validation"
236+
// Schemaless marks a field as being a schemaless object.
237+
//
238+
// Schemaless objects are not introspected, so you must provide
239+
// any type and validation information yourself. One use for this
240+
// tag is for embedding fields that hold JSONSchema typed objects.
241+
// Because this field disables all type checking, it is recommended
242+
// to be used only as a last resort.
243+
type Schemaless struct{}
244+
228245
func (m Maximum) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
229246
if schema.Type != "integer" {
230247
return fmt.Errorf("must apply maximum to an integer")

pkg/crd/markers/zz_generated.markerhelp.go

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/crd/schema.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424

2525
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26+
crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers"
2627

2728
"sigs.k8s.io/controller-tools/pkg/loader"
2829
"sigs.k8s.io/controller-tools/pkg/markers"
@@ -378,7 +379,12 @@ func structToSchema(ctx *schemaContext, structType *ast.StructType) *apiext.JSON
378379
}
379380
}
380381

381-
propSchema := typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type)
382+
var propSchema *apiext.JSONSchemaProps
383+
if field.Markers.Get(crdmarkers.SchemalessName) != nil {
384+
propSchema = &apiext.JSONSchemaProps{}
385+
} else {
386+
propSchema = typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type)
387+
}
382388
propSchema.Description = field.Doc
383389

384390
applyMarkers(ctx, field.Markers, propSchema, field.RawField)

pkg/crd/testdata/cronjob_types.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,19 @@ type CronJobSpec struct {
154154

155155
// This tests that min/max properties work
156156
MinMaxProperties MinMaxObject `json:"minMaxProperties,omitempty"`
157+
158+
// This tests that the schemaless marker works
159+
// +kubebuilder:validation:Schemaless
160+
Schemaless []byte `json:"schemaless,omitempty"`
157161
}
158162

159163
// +kubebuilder:validation:Type=object
160164
// +kubebuilder:pruning:PreserveUnknownFields
161165
type Preserved struct {
162-
ConcreteField string `json:"concreteField"`
163-
Rest map[string]interface{} `json:"-"`
166+
ConcreteField string `json:"concreteField"`
167+
Rest map[string]interface{} `json:"-"`
164168
}
169+
165170
func (p *Preserved) UnmarshalJSON(data []byte) error {
166171
if err := json.Unmarshal(data, &p.Rest); err != nil {
167172
return err

pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -5071,6 +5071,8 @@ spec:
50715071
schedule:
50725072
description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
50735073
type: string
5074+
schemaless:
5075+
description: This tests that the schemaless marker works
50745076
startingDeadlineSeconds:
50755077
description: Optional deadline in seconds for starting the job if
50765078
it misses scheduled time for any reason. Missed jobs executions

test.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ fetch_kb_tools
107107
# setup testing env
108108
setup_envs
109109

110-
header_text "running golangci-lint"
111-
112110
header_text "generating marker help"
113111
pushd cmd/controller-gen > /dev/null
114112
go generate
115113
popd > /dev/null
116114

115+
header_text "running golangci-lint"
116+
117117
golangci-lint run --disable-all \
118118
--enable=misspell \
119119
--enable=golint \

0 commit comments

Comments
 (0)