Skip to content

Commit ba9fe2a

Browse files
committed
Ignore unknown k8s:validation comments tags
1 parent 67ed584 commit ba9fe2a

File tree

3 files changed

+69
-6
lines changed

3 files changed

+69
-6
lines changed

pkg/generators/markers.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"fmt"
23+
"reflect"
2324
"regexp"
2425
"strconv"
2526
"strings"
27+
"sync"
2628

2729
"k8s.io/gengo/v2/types"
2830
openapi "k8s.io/kube-openapi/pkg/common"
@@ -61,6 +63,26 @@ func (c *CELTag) Validate() error {
6163
return nil
6264
}
6365

66+
func isKnownTagCommentKey(key string) bool {
67+
commentTag, _, _ := strings.Cut(key, ":")
68+
_, ok := tagKeys()[commentTag]
69+
return ok
70+
}
71+
72+
var tagKeys = sync.OnceValue(func() map[string]struct{} {
73+
result := map[string]struct{}{}
74+
t := reflect.TypeOf(commentTags{})
75+
for i := 0; i < t.NumField(); i++ {
76+
field := t.Field(i)
77+
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
78+
if key, _, _ := strings.Cut(jsonTag, ","); key != "" {
79+
result[key] = struct{}{}
80+
}
81+
}
82+
}
83+
return result
84+
})
85+
6486
// commentTags represents the parsed comment tags for a given type. These types are then used to generate schema validations.
6587
// These only include the newer prefixed tags. The older tags are still supported,
6688
// but are not included in this struct. Comment Tags are transformed into a
@@ -385,13 +407,30 @@ func memberWithJSONName(t *types.Type, key string) *types.Member {
385407
return nil
386408
}
387409

388-
// Parses the given comments into a CommentTags type. Validates the parsed comment tags, and returns the result.
410+
type commentTagsOptions struct {
411+
ignoreUnknown bool
412+
}
413+
414+
type ParseCommentTagsOptions func(*commentTagsOptions)
415+
416+
// IgnoreUnknown returns a ParseCommentTagsOptions that will ignore unknown comment tags.
417+
func IgnoreUnknown() ParseCommentTagsOptions {
418+
return func(o *commentTagsOptions) {
419+
o.ignoreUnknown = true
420+
}
421+
}
422+
423+
// ParseCommentTags parses the given comments into a CommentTags type. Validates the parsed comment tags, and returns the result.
389424
// Accepts an optional type to validate against, and a prefix to filter out markers not related to validation.
390425
// Accepts a prefix to filter out markers not related to validation.
391426
// Returns any errors encountered while parsing or validating the comment tags.
392-
func ParseCommentTags(t *types.Type, comments []string, prefix string) (*spec.Schema, error) {
427+
func ParseCommentTags(t *types.Type, comments []string, prefix string, opts ...ParseCommentTagsOptions) (*spec.Schema, error) {
428+
options := commentTagsOptions{}
429+
for _, opt := range opts {
430+
opt(&options)
431+
}
393432

394-
markers, err := parseMarkers(comments, prefix)
433+
markers, err := parseMarkers(comments, prefix, options.ignoreUnknown)
395434
if err != nil {
396435
return nil, fmt.Errorf("failed to parse marker comments: %w", err)
397436
}
@@ -597,7 +636,7 @@ func extractCommentTags(marker string, lines []string) (map[string]string, error
597636
// Accepts a prefix to filter out markers not related to validation.
598637
// The prefix is removed from the key in the returned map.
599638
// Empty keys and invalid values will return errors, refs are currently unsupported and will be skipped.
600-
func parseMarkers(markerComments []string, prefix string) (map[string]any, error) {
639+
func parseMarkers(markerComments []string, prefix string, ignoreUnknown bool) (map[string]any, error) {
601640
markers, err := extractCommentTags(prefix, markerComments)
602641
if err != nil {
603642
return nil, err
@@ -606,6 +645,9 @@ func parseMarkers(markerComments []string, prefix string) (map[string]any, error
606645
// Parse the values as JSON
607646
result := map[string]any{}
608647
for key, value := range markers {
648+
if ignoreUnknown && !isKnownTagCommentKey(key) {
649+
continue
650+
}
609651
var unmarshalled interface{}
610652

611653
if len(key) == 0 {

pkg/generators/markers_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"testing"
2020

2121
"github.com/stretchr/testify/require"
22+
2223
"k8s.io/gengo/v2/types"
2324
"k8s.io/kube-openapi/pkg/generators"
2425
"k8s.io/kube-openapi/pkg/validation/spec"
@@ -626,6 +627,7 @@ func TestCommentTags_Validate(t *testing.T) {
626627
comments []string
627628
t *types.Type
628629
errorMessage string
630+
options []generators.ParseCommentTagsOptions
629631
}{
630632
{
631633
name: "invalid minimum type",
@@ -960,11 +962,29 @@ func TestCommentTags_Validate(t *testing.T) {
960962
},
961963
errorMessage: `failed to validate property "name": pattern can only be used on string types`,
962964
},
965+
{
966+
name: "ignore unknown field with unparsable value",
967+
comments: []string{
968+
`+k8s:validation:xyz=a=b`, // a=b is not a valid value
969+
},
970+
t: &types.Type{
971+
Kind: types.Struct,
972+
Name: types.Name{Name: "struct"},
973+
Members: []types.Member{
974+
{
975+
Name: "name",
976+
Type: types.String,
977+
Tags: `json:"name"`,
978+
},
979+
},
980+
},
981+
options: []generators.ParseCommentTagsOptions{generators.IgnoreUnknown()},
982+
},
963983
}
964984

965985
for _, tc := range testCases {
966986
t.Run(tc.name, func(t *testing.T) {
967-
_, err := generators.ParseCommentTags(tc.t, tc.comments, "+k8s:validation:")
987+
_, err := generators.ParseCommentTags(tc.t, tc.comments, "+k8s:validation:", tc.options...)
968988
if tc.errorMessage != "" {
969989
require.Error(t, err)
970990
require.Equal(t, "invalid marker comments: "+tc.errorMessage, err.Error())

pkg/generators/openapi_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/google/go-cmp/cmp"
2828
"golang.org/x/tools/go/packages"
2929
"golang.org/x/tools/go/packages/packagestest"
30+
3031
"k8s.io/gengo/v2/generator"
3132
"k8s.io/gengo/v2/namer"
3233
"k8s.io/gengo/v2/parser"
@@ -2386,7 +2387,7 @@ func TestMarkerComments(t *testing.T) {
23862387
// +k8s:validation:pattern="^foo$[0-9]+"
23872388
StringValue string
23882389
2389-
// +k8s:validation:maxitems=10
2390+
// +k8s:validation:maxItems=10
23902391
// +k8s:validation:minItems=1
23912392
// +k8s:validation:uniqueItems
23922393
ArrayValue []string

0 commit comments

Comments
 (0)