From 04b9783485fb124fd77c8946b612d94ec9cc617e Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Fri, 6 Dec 2024 21:00:25 -0500 Subject: [PATCH] Ignore unknown k8s:validation comments tags --- pkg/generators/markers.go | 35 ++++++++++++++++++++++++++++++++-- pkg/generators/markers_test.go | 18 +++++++++++++++++ pkg/generators/openapi_test.go | 3 ++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/pkg/generators/markers.go b/pkg/generators/markers.go index c4dd67d3b..a8af60b6c 100644 --- a/pkg/generators/markers.go +++ b/pkg/generators/markers.go @@ -20,9 +20,11 @@ import ( "encoding/json" "errors" "fmt" + "reflect" "regexp" "strconv" "strings" + "sync" "k8s.io/gengo/v2/types" openapi "k8s.io/kube-openapi/pkg/common" @@ -61,6 +63,34 @@ func (c *CELTag) Validate() error { return nil } +// isKnownTagCommentKey returns true if the given key is a known comment tag key. +// Known keys are identified by the json field tags in the commentTags struct. +// If the key is a composite key, only the first key part is checked, and is +// expected to be separated by the remainder of the key by a ':' or '[' delimiter. +func isKnownTagCommentKey(key string) bool { + split := func(r rune) bool { return r == ':' || r == '[' } + commentTags := strings.FieldsFunc(key, split) + if len(commentTags) == 0 { + return false + } + _, ok := tagKeys()[commentTags[0]] + return ok +} + +var tagKeys = sync.OnceValue(func() map[string]struct{} { + result := map[string]struct{}{} + t := reflect.TypeOf(commentTags{}) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + if jsonTag := field.Tag.Get("json"); jsonTag != "" { + if key, _, _ := strings.Cut(jsonTag, ","); key != "" { + result[key] = struct{}{} + } + } + } + return result +}) + // commentTags represents the parsed comment tags for a given type. These types are then used to generate schema validations. // These only include the newer prefixed tags. The older tags are still supported, // but are not included in this struct. Comment Tags are transformed into a @@ -385,12 +415,11 @@ func memberWithJSONName(t *types.Type, key string) *types.Member { return nil } -// Parses the given comments into a CommentTags type. Validates the parsed comment tags, and returns the result. +// ParseCommentTags parses the given comments into a CommentTags type. Validates the parsed comment tags, and returns the result. // Accepts an optional type to validate against, and a prefix to filter out markers not related to validation. // Accepts a prefix to filter out markers not related to validation. // Returns any errors encountered while parsing or validating the comment tags. func ParseCommentTags(t *types.Type, comments []string, prefix string) (*spec.Schema, error) { - markers, err := parseMarkers(comments, prefix) if err != nil { return nil, fmt.Errorf("failed to parse marker comments: %w", err) @@ -610,6 +639,8 @@ func parseMarkers(markerComments []string, prefix string) (map[string]any, error if len(key) == 0 { return nil, fmt.Errorf("cannot have empty key for marker comment") + } else if !isKnownTagCommentKey(key) { + continue } else if _, ok := parseSymbolReference(value, ""); ok { // Skip ref markers continue diff --git a/pkg/generators/markers_test.go b/pkg/generators/markers_test.go index 28f6e551a..6377fc57d 100644 --- a/pkg/generators/markers_test.go +++ b/pkg/generators/markers_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "k8s.io/gengo/v2/types" "k8s.io/kube-openapi/pkg/generators" "k8s.io/kube-openapi/pkg/validation/spec" @@ -960,6 +961,23 @@ func TestCommentTags_Validate(t *testing.T) { }, errorMessage: `failed to validate property "name": pattern can only be used on string types`, }, + { + name: "ignore unknown field with unparsable value", + comments: []string{ + `+k8s:validation:xyz=a=b`, // a=b is not a valid value + }, + t: &types.Type{ + Kind: types.Struct, + Name: types.Name{Name: "struct"}, + Members: []types.Member{ + { + Name: "name", + Type: types.String, + Tags: `json:"name"`, + }, + }, + }, + }, } for _, tc := range testCases { diff --git a/pkg/generators/openapi_test.go b/pkg/generators/openapi_test.go index e53292129..55be4c557 100644 --- a/pkg/generators/openapi_test.go +++ b/pkg/generators/openapi_test.go @@ -27,6 +27,7 @@ import ( "github.com/google/go-cmp/cmp" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages/packagestest" + "k8s.io/gengo/v2/generator" "k8s.io/gengo/v2/namer" "k8s.io/gengo/v2/parser" @@ -2386,7 +2387,7 @@ func TestMarkerComments(t *testing.T) { // +k8s:validation:pattern="^foo$[0-9]+" StringValue string - // +k8s:validation:maxitems=10 + // +k8s:validation:maxItems=10 // +k8s:validation:minItems=1 // +k8s:validation:uniqueItems ArrayValue []string