Skip to content

Commit 1901c31

Browse files
authored
Merge pull request #76 from JoelSpeed/declarative-gen-optional-required
Add declarative validation linting to optionalorrequired
2 parents d6f5e8a + 639bca9 commit 1901c31

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed

pkg/analysis/optionalorrequired/analyzer.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ const (
4141

4242
// KubebuilderRequiredMarker is the marker that indicates that a field is required in kubebuilder.
4343
KubebuilderRequiredMarker = "kubebuilder:validation:Required"
44+
45+
// K8sOptionalMarker is the marker that indicates that a field is optional in k8s declarative validation.
46+
K8sOptionalMarker = "k8s:Optional"
47+
48+
// K8sRequiredMarker is the marker that indicates that a field is required in k8s declarative validation.
49+
K8sRequiredMarker = "k8s:Required"
4450
)
4551

4652
func init() {
@@ -49,6 +55,8 @@ func init() {
4955
RequiredMarker,
5056
KubebuilderOptionalMarker,
5157
KubebuilderRequiredMarker,
58+
K8sOptionalMarker,
59+
K8sRequiredMarker,
5260
)
5361
}
5462

@@ -135,6 +143,8 @@ func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, fieldMarker
135143
hasBothOptional := hasPrimaryOptional && hasSecondaryOptional
136144
hasBothRequired := hasPrimaryRequired && hasSecondaryRequired
137145

146+
a.checkK8sMarkers(pass, field, fieldMarkers, prefix, hasEitherOptional, hasEitherRequired)
147+
138148
switch {
139149
case hasEitherOptional && hasEitherRequired:
140150
pass.Reportf(field.Pos(), "%s must not be marked as both optional and required", prefix)
@@ -159,6 +169,23 @@ func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, fieldMarker
159169
}
160170
}
161171

172+
func (a *analyzer) checkK8sMarkers(pass *analysis.Pass, field *ast.Field, fieldMarkers markers.MarkerSet, prefix string, hasEitherOptional, hasEitherRequired bool) {
173+
hasK8sOptional := fieldMarkers.Has(K8sOptionalMarker)
174+
hasK8sRequired := fieldMarkers.Has(K8sRequiredMarker)
175+
176+
if hasK8sOptional && hasK8sRequired {
177+
pass.Reportf(field.Pos(), "%s must not be marked as both %s and %s", prefix, K8sOptionalMarker, K8sRequiredMarker)
178+
}
179+
180+
if hasK8sOptional && hasEitherRequired {
181+
pass.Reportf(field.Pos(), "%s must not be marked as both %s and %s", prefix, K8sOptionalMarker, RequiredMarker)
182+
}
183+
184+
if hasK8sRequired && hasEitherOptional {
185+
pass.Reportf(field.Pos(), "%s must not be marked as both %s and %s", prefix, OptionalMarker, K8sRequiredMarker)
186+
}
187+
}
188+
162189
func reportShouldReplaceSecondaryMarker(field *ast.Field, marker []markers.Marker, primaryMarker, secondaryMarker, prefix string) analysis.Diagnostic {
163190
textEdits := make([]analysis.TextEdit, len(marker))
164191

@@ -221,7 +248,7 @@ func (a *analyzer) checkTypeSpec(pass *analysis.Pass, typeSpec *ast.TypeSpec, ma
221248

222249
for _, marker := range set.UnsortedList() {
223250
switch marker.Identifier {
224-
case a.primaryOptionalMarker, a.secondaryOptionalMarker, a.primaryRequiredMarker, a.secondaryRequiredMarker:
251+
case a.primaryOptionalMarker, a.secondaryOptionalMarker, a.primaryRequiredMarker, a.secondaryRequiredMarker, K8sOptionalMarker, K8sRequiredMarker:
225252
pass.Report(analysis.Diagnostic{
226253
Pos: typeSpec.Pos(),
227254
Message: fmt.Sprintf("type %s should not be marked as %s", name, marker.String()),

pkg/analysis/optionalorrequired/testdata/src/a/a.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,47 @@ type OptionalOrRequiredTestStruct struct {
4141
// +optional
4242
MarkedWithKubeBuilderOptionalTwiceAndOptional string // want "field MarkedWithKubeBuilderOptionalTwiceAndOptional should use only the marker optional, kubebuilder:validation:Optional is not required"
4343

44+
// MarkedWithK8sRequiredAndKubeBuilderRequired is a field with both k8s and kubebuilder required markers.
45+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
46+
// +k8s:Required
47+
// +kubebuilder:validation:Required
48+
MarkedWithK8sRequiredAndKubeBuilderRequired string // want "field MarkedWithK8sRequiredAndKubeBuilderRequired should use marker required instead of kubebuilder:validation:Required"
49+
50+
// MarkedWithK8sRequiredAndRequired is a field with both k8s and required markers.
51+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
52+
// +k8s:Required
53+
// +required
54+
MarkedWithK8sRequiredAndRequired string
55+
56+
// MarkedWithK8sOptionalAndKubeBuilderOptional is a field with both k8s and kubebuilder optional markers.
57+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
58+
// +k8s:Optional
59+
// +kubebuilder:validation:Optional
60+
MarkedWithK8sOptionalAndKubeBuilderOptional string // want "field MarkedWithK8sOptionalAndKubeBuilderOptional should use marker optional instead of kubebuilder:validation:Optional"
61+
62+
// MarkedWithK8sOptionalAndOptional is a field with both k8s and optional markers.
63+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
64+
// +k8s:Optional
65+
// +optional
66+
MarkedWithK8sOptionalAndOptional string
67+
68+
// MarkedWithK8sOptionalAndRequired is a field with both k8s and required markers.
69+
// The k8s versions of the markers are currently in addition to other markers, but they should match the semantics.
70+
// +k8s:Optional
71+
// +required
72+
MarkedWithK8sOptionalAndRequired string // want "field MarkedWithK8sOptionalAndRequired must not be marked as both k8s:Optional and required"
73+
74+
// MarkedWithK8sRequiredAndOptional is a field with both k8s and optional markers.
75+
// The k8s versions of the markers are currently in addition to other markers, but they should match the semantics.
76+
// +k8s:Required
77+
// +optional
78+
MarkedWithK8sRequiredAndOptional string // want "field MarkedWithK8sRequiredAndOptional must not be marked as both optional and k8s:Required"
79+
80+
// MarkedWithK8sRequiredAndK8sOptional is a field with both k8s and kubebuilder optional markers.
81+
// +k8s:Required
82+
// +k8s:Optional
83+
MarkedWithK8sRequiredAndK8sOptional string // want "field MarkedWithK8sRequiredAndK8sOptional must be marked as optional or required" "field MarkedWithK8sRequiredAndK8sOptional must not be marked as both k8s:Optional and k8s:Required"
84+
4485
A `json:",inline"`
4586

4687
B `json:"b,omitempty"` // want "embedded field B must be marked as optional or required"

pkg/analysis/optionalorrequired/testdata/src/a/a.go.golden

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,47 @@ type OptionalOrRequiredTestStruct struct {
3636
// +optional
3737
MarkedWithKubeBuilderOptionalTwiceAndOptional string // want "field MarkedWithKubeBuilderOptionalTwiceAndOptional should use only the marker optional, kubebuilder:validation:Optional is not required"
3838

39+
// MarkedWithK8sRequiredAndKubeBuilderRequired is a field with both k8s and kubebuilder required markers.
40+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
41+
// +k8s:Required
42+
// +required
43+
MarkedWithK8sRequiredAndKubeBuilderRequired string // want "field MarkedWithK8sRequiredAndKubeBuilderRequired should use marker required instead of kubebuilder:validation:Required"
44+
45+
// MarkedWithK8sRequiredAndRequired is a field with both k8s and required markers.
46+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
47+
// +k8s:Required
48+
// +required
49+
MarkedWithK8sRequiredAndRequired string
50+
51+
// MarkedWithK8sOptionalAndKubeBuilderOptional is a field with both k8s and kubebuilder optional markers.
52+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
53+
// +k8s:Optional
54+
// +optional
55+
MarkedWithK8sOptionalAndKubeBuilderOptional string // want "field MarkedWithK8sOptionalAndKubeBuilderOptional should use marker optional instead of kubebuilder:validation:Optional"
56+
57+
// MarkedWithK8sOptionalAndOptional is a field with both k8s and optional markers.
58+
// The k8s versions of the markers are currently in addition to other markers so this is accepted.
59+
// +k8s:Optional
60+
// +optional
61+
MarkedWithK8sOptionalAndOptional string
62+
63+
// MarkedWithK8sOptionalAndRequired is a field with both k8s and required markers.
64+
// The k8s versions of the markers are currently in addition to other markers, but they should match the semantics.
65+
// +k8s:Optional
66+
// +required
67+
MarkedWithK8sOptionalAndRequired string // want "field MarkedWithK8sOptionalAndRequired must not be marked as both k8s:Optional and required"
68+
69+
// MarkedWithK8sRequiredAndOptional is a field with both k8s and optional markers.
70+
// The k8s versions of the markers are currently in addition to other markers, but they should match the semantics.
71+
// +k8s:Required
72+
// +optional
73+
MarkedWithK8sRequiredAndOptional string // want "field MarkedWithK8sRequiredAndOptional must not be marked as both optional and k8s:Required"
74+
75+
// MarkedWithK8sRequiredAndK8sOptional is a field with both k8s and kubebuilder optional markers.
76+
// +k8s:Required
77+
// +k8s:Optional
78+
MarkedWithK8sRequiredAndK8sOptional string // want "field MarkedWithK8sRequiredAndK8sOptional must be marked as optional or required" "field MarkedWithK8sRequiredAndK8sOptional must not be marked as both k8s:Optional and k8s:Required"
79+
3980
A `json:",inline"`
4081

4182
B `json:"b,omitempty"` // want "embedded field B must be marked as optional or required"

pkg/analysis/optionalorrequired/testdata/src/c/c.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ type RequiredEnum string // want "type RequiredEnum should not be marked as requ
1818
// +kubebuilder:validation:Enum=Foo;Bar;Baz
1919
type KubeBuilderRequiredEnum string // want "type KubeBuilderRequiredEnum should not be marked as kubebuilder:validation:Required"
2020

21+
// +k8s:Required
22+
// +kubebuilder:validation:Enum=Foo;Bar;Baz
23+
type K8sRequiredEnum string // want "type K8sRequiredEnum should not be marked as k8s:Required"
24+
2125
// +optional
2226
// +kubebuilder:validation:Enum=Foo;Bar;Baz
2327
type OptionalEnum string // want "type OptionalEnum should not be marked as optional"
2428

2529
// +kubebuilder:validation:Optional
2630
// +kubebuilder:validation:Enum=Foo;Bar;Baz
2731
type KubeBuilderOptionalEnum string // want "type KubeBuilderOptionalEnum should not be marked as kubebuilder:validation:Optional"
32+
33+
// +k8s:Optional
34+
// +kubebuilder:validation:Enum=Foo;Bar;Baz
35+
type K8sOptionalEnum string // want "type K8sOptionalEnum should not be marked as k8s:Optional"

pkg/analysis/optionalorrequired/testdata/src/c/c.go.golden

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ type RequiredEnum string // want "type RequiredEnum should not be marked as requ
1616
// +kubebuilder:validation:Enum=Foo;Bar;Baz
1717
type KubeBuilderRequiredEnum string // want "type KubeBuilderRequiredEnum should not be marked as kubebuilder:validation:Required"
1818

19+
// +kubebuilder:validation:Enum=Foo;Bar;Baz
20+
type K8sRequiredEnum string // want "type K8sRequiredEnum should not be marked as k8s:Required"
21+
1922
// +kubebuilder:validation:Enum=Foo;Bar;Baz
2023
type OptionalEnum string // want "type OptionalEnum should not be marked as optional"
2124

2225
// +kubebuilder:validation:Enum=Foo;Bar;Baz
2326
type KubeBuilderOptionalEnum string // want "type KubeBuilderOptionalEnum should not be marked as kubebuilder:validation:Optional"
27+
28+
// +kubebuilder:validation:Enum=Foo;Bar;Baz
29+
type K8sOptionalEnum string // want "type K8sOptionalEnum should not be marked as k8s:Optional"

0 commit comments

Comments
 (0)