Skip to content

Commit 2e946af

Browse files
authored
Merge pull request #134 from sivchari/ignore-schemaless
Ignore field that has schemaless marker
2 parents 48643eb + 70e112a commit 2e946af

File tree

7 files changed

+100
-40
lines changed

7 files changed

+100
-40
lines changed

pkg/analysis/helpers/inspector/inspector.go

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/extractjsontags"
2525
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/markers"
2626
"sigs.k8s.io/kube-api-linter/pkg/analysis/utils"
27+
markersconsts "sigs.k8s.io/kube-api-linter/pkg/markers"
2728
)
2829

2930
// Inspector is an interface that allows for the inspection of fields in structs.
@@ -65,58 +66,69 @@ func (i *inspector) InspectFields(inspectField func(field *ast.Field, stack []as
6566
return false
6667
}
6768

68-
if len(stack) < 3 {
69-
return true
70-
}
71-
72-
// The 0th node in the stack is the *ast.File.
73-
// The 1st node in the stack is the *ast.GenDecl.
74-
decl, ok := stack[1].(*ast.GenDecl)
75-
if !ok {
76-
// Make sure that we don't inspect structs within a function.
77-
return false
69+
field, ok := n.(*ast.Field)
70+
if !ok || !i.shouldProcessField(stack) {
71+
return ok
7872
}
7973

80-
if decl.Tok != token.TYPE {
81-
// Returning false here means we won't inspect non-type declarations (e.g. var, const, import).
74+
if i.shouldSkipField(field) {
8275
return false
8376
}
8477

85-
structType, ok := stack[len(stack)-3].(*ast.StructType)
86-
if !ok {
87-
// A field within a struct has a FieldList parent and then a StructType parent.
88-
// If we don't have a StructType parent, then we're not in a struct.
89-
return false
90-
}
78+
i.processFieldWithRecovery(field, stack, inspectField)
9179

92-
if isItemsType(structType) {
93-
// The field belongs to an items type, we don't need to report lint errors for this.
94-
return false
95-
}
80+
return true
81+
})
82+
}
9683

97-
field, ok := n.(*ast.Field)
98-
if !ok {
99-
return true
100-
}
84+
// shouldProcessField checks if the field should be processed.
85+
func (i *inspector) shouldProcessField(stack []ast.Node) bool {
86+
if len(stack) < 3 {
87+
return false
88+
}
10189

102-
tagInfo := i.jsonTags.FieldTags(field)
103-
if tagInfo.Ignored {
104-
// Returning false here means we won't inspect the children of an ignored field.
105-
return false
106-
}
90+
// The 0th node in the stack is the *ast.File.
91+
// The 1st node in the stack is the *ast.GenDecl.
92+
decl, ok := stack[1].(*ast.GenDecl)
93+
if !ok || decl.Tok != token.TYPE {
94+
// Make sure that we don't inspect structs within a function or non-type declarations.
95+
return false
96+
}
10797

108-
defer func() {
109-
if r := recover(); r != nil {
110-
// If the inspectField function panics, we recover and log information that will help identify the issue.
111-
debug := printDebugInfo(field)
112-
panic(fmt.Sprintf("%s %v", debug, r)) // Re-panic to propagate the error.
113-
}
114-
}()
98+
structType, ok := stack[len(stack)-3].(*ast.StructType)
99+
if !ok || isItemsType(structType) {
100+
// Not in a struct or belongs to an items type.
101+
return false
102+
}
115103

116-
inspectField(field, stack, tagInfo, i.markers)
104+
return true
105+
}
117106

107+
// shouldSkipField checks if a field should be skipped.
108+
func (i *inspector) shouldSkipField(field *ast.Field) bool {
109+
tagInfo := i.jsonTags.FieldTags(field)
110+
if tagInfo.Ignored {
118111
return true
119-
})
112+
}
113+
114+
markerSet := i.markers.FieldMarkers(field)
115+
116+
return isSchemalessType(markerSet)
117+
}
118+
119+
// processFieldWithRecovery processes a field with panic recovery.
120+
func (i *inspector) processFieldWithRecovery(field *ast.Field, stack []ast.Node, inspectField func(field *ast.Field, stack []ast.Node, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markers.Markers)) {
121+
tagInfo := i.jsonTags.FieldTags(field)
122+
123+
defer func() {
124+
if r := recover(); r != nil {
125+
// If the inspectField function panics, we recover and log information that will help identify the issue.
126+
debug := printDebugInfo(field)
127+
panic(fmt.Sprintf("%s %v", debug, r)) // Re-panic to propagate the error.
128+
}
129+
}()
130+
131+
inspectField(field, stack, tagInfo, i.markers)
120132
}
121133

122134
// InspectTypeSpec inspects the type spec and calls the provided inspectTypeSpec function.
@@ -163,6 +175,12 @@ func isItemsType(structType *ast.StructType) bool {
163175
return true
164176
}
165177

178+
func isSchemalessType(markerSet markers.MarkerSet) bool {
179+
// Check if the field is marked as schemaless.
180+
schemalessMarker := markerSet.Get(markersconsts.KubebuilderSchemaLessMarker)
181+
return len(schemalessMarker) > 0
182+
}
183+
166184
// printDebugInfo prints debug information about the field that caused a panic during inspection.
167185
// This function is designed to allow us to help identify which fields are causing issues during inspection.
168186
func printDebugInfo(field *ast.Field) string {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ type MaxLength struct {
8686

8787
StringWithoutMaxLength string // want "field StringWithoutMaxLength must have a maximum length, add kubebuilder:validation:MaxLength marker"
8888
} `json:"struct"`
89+
90+
// +optional
91+
// +kubebuilder:pruning:PreserveUnknownFields
92+
// +kubebuilder:validation:Schemaless
93+
AllOf []JSONSchemaProps `json:"allOf,omitempty"`
8994
}
9095

9196
// StringAlias is a string without a MaxLength.
@@ -105,3 +110,6 @@ type ByteSliceAlias []byte
105110
// ByteSliceAliasWithMaxLength is a byte slice with a MaxLength.
106111
// +kubebuilder:validation:MaxLength:=512
107112
type ByteSliceAliasWithMaxLength []byte
113+
114+
// JSONSchemaProps is a placeholder for the JSON schema properties.
115+
type JSONSchemaProps struct{}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,12 @@ type SSATagsTestSpec struct {
190190
// Byte array with markers - should be ignored even if markers are present
191191
// +listType=atomic
192192
ByteArrayWithMarker []byte `json:"byteArrayWithMarker,omitempty"` // want "ByteArrayWithMarker is a byte array, which does not support the listType marker. Remove the listType marker"
193+
194+
// +optional
195+
// +kubebuilder:pruning:PreserveUnknownFields
196+
// +kubebuilder:validation:Schemaless
197+
AllOf []JSONSchemaProps `json:"allOf,omitempty"`
193198
}
199+
200+
// JSONSchemaProps is a placeholder for the JSON schema properties.
201+
type JSONSchemaProps struct{}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,12 @@ type SSATagsTestSpec struct {
189189

190190
// Byte array with markers - should be ignored even if markers are present
191191
ByteArrayWithMarker []byte `json:"byteArrayWithMarker,omitempty"` // want "ByteArrayWithMarker is a byte array, which does not support the listType marker. Remove the listType marker"
192+
193+
// +optional
194+
// +kubebuilder:pruning:PreserveUnknownFields
195+
// +kubebuilder:validation:Schemaless
196+
AllOf []JSONSchemaProps `json:"allOf,omitempty"`
192197
}
198+
199+
// JSONSchemaProps is a placeholder for the JSON schema properties.
200+
type JSONSchemaProps struct{}

pkg/analysis/ssatags/testdata/src/b/b.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,12 @@ type SSATagsTestSpec struct {
185185
// Byte array with markers - should be ignored even if markers are present
186186
// +listType=atomic
187187
ByteArrayWithMarker []byte `json:"byteArrayWithMarker,omitempty"` // want "ByteArrayWithMarker is a byte array, which does not support the listType marker. Remove the listType marker"
188+
189+
// +optional
190+
// +kubebuilder:pruning:PreserveUnknownFields
191+
// +kubebuilder:validation:Schemaless
192+
AllOf []JSONSchemaProps `json:"allOf,omitempty"`
188193
}
194+
195+
// JSONSchemaProps is a placeholder for the JSON schema properties.
196+
type JSONSchemaProps struct{}

pkg/analysis/ssatags/testdata/src/b/b.go.golden

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,5 +184,12 @@ type SSATagsTestSpec struct {
184184

185185
// Byte array with markers - should be ignored even if markers are present
186186
ByteArrayWithMarker []byte `json:"byteArrayWithMarker,omitempty"` // want "ByteArrayWithMarker is a byte array, which does not support the listType marker. Remove the listType marker"
187+
188+
// +optional
189+
// +kubebuilder:pruning:PreserveUnknownFields
190+
// +kubebuilder:validation:Schemaless
191+
AllOf []JSONSchemaProps `json:"allOf,omitempty"`
187192
}
188193

194+
// JSONSchemaProps is a placeholder for the JSON schema properties.
195+
type JSONSchemaProps struct{}

pkg/markers/markers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ const (
155155

156156
// KubebuilderListMapKeyMarker is the marker used to specify the key field for map-type lists.
157157
KubebuilderListMapKeyMarker = "listMapKey"
158+
159+
// KubebuilderSchemaLessMarker is the marker that indicates that a struct is schemaless.
160+
KubebuilderSchemaLessMarker = "kubebuilder:validation:Schemaless"
158161
)
159162

160163
const (

0 commit comments

Comments
 (0)