Skip to content

Commit fe69f4a

Browse files
authored
Merge pull request #27 from solo-io/ignore_specific_markers
Allow the user to provide a list of specific kube markers to be ignored
2 parents 22e66cd + bd88584 commit fe69f4a

11 files changed

+564
-13
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@ Other supported options are:
7676
* when set to `true`, the native openapi schemas will be used for Integer types instead of Solo wrappers that add Kubernetes extension headers to the schema to treat int as strings.
7777
* `disable_kube_markers`
7878
* when set to `true`, kubebuilder markers and validations such as PreserveUnknownFields, MinItems, default, and all CEL rules will be omitted from the OpenAPI schema. The Type and Required markers will be maintained.
79+
* `ignored_kube_marker_substrings`
80+
* when set, this list of substrings will be used to identify kubebuilder markers to ignore. When multiple are
81+
supplied, this will function as a logical OR i.e. any rule which contains a provided substring will be ignored
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
changelog:
2+
- type: NEW_FEATURE
3+
issueLink: https://github.com/solo-io/gloo-mesh-enterprise/issues/18119
4+
resolvesIssue: false
5+
description: |
6+
Allows the user to define one or more kube markers to ignore. This is useful when using protos that contain
7+
unsupported kubebuilder decorators.

integration_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,36 @@ func TestOpenAPIGeneration(t *testing.T) {
109109
},
110110
wantFiles: []string{"test7/openapiv3.yaml"},
111111
},
112+
{
113+
name: "Test no markers are ignored when ignored_kube_markers is zero length",
114+
id: "test8",
115+
perPackage: false,
116+
genOpts: "yaml=true,single_file=true,proto_oneof=true,int_native=true,multiline_description=true,disable_kube_markers=false,ignored_kube_marker_substrings=",
117+
inputFiles: map[string][]string{
118+
"test8": {"./testdata/test8/markers.proto"},
119+
},
120+
wantFiles: []string{"test8/openapiv3.yaml"},
121+
},
122+
{
123+
name: "Test ignored_kube_markers option ignores a single marker",
124+
id: "test9",
125+
perPackage: false,
126+
genOpts: "yaml=true,single_file=true,proto_oneof=true,int_native=true,multiline_description=true,disable_kube_markers=false,ignored_kube_marker_substrings=Required",
127+
inputFiles: map[string][]string{
128+
"test9": {"./testdata/test9/markers.proto"},
129+
},
130+
wantFiles: []string{"test9/openapiv3.yaml"},
131+
},
132+
{
133+
name: "Test ignored_kube_markers option ignores multiple markers",
134+
id: "test10",
135+
perPackage: false,
136+
genOpts: "yaml=true,single_file=true,proto_oneof=true,int_native=true,multiline_description=true,disable_kube_markers=false,ignored_kube_marker_substrings=Required+example",
137+
inputFiles: map[string][]string{
138+
"test10": {"./testdata/test10/markers.proto"},
139+
},
140+
wantFiles: []string{"test10/openapiv3.yaml"},
141+
},
112142
}
113143

114144
for _, tc := range testcases {

main.go

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
5656
disableKubeMarkers := false
5757

5858
var messagesWithEmptySchema []string
59+
var ignoredKubeMarkerSubstrings []string
5960

6061
p := extractParams(request.GetParameter())
6162
for k, v := range p {
@@ -147,6 +148,10 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
147148
default:
148149
return nil, fmt.Errorf("unknown value '%s' for disable_kube_markers", v)
149150
}
151+
} else if k == "ignored_kube_marker_substrings" {
152+
if len(v) > 0 {
153+
ignoredKubeMarkerSubstrings = strings.Split(v, "+")
154+
}
150155
} else {
151156
return nil, fmt.Errorf("unknown argument '%s' specified", k)
152157
}
@@ -184,6 +189,7 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
184189
protoOneof,
185190
intNative,
186191
disableKubeMarkers,
192+
ignoredKubeMarkerSubstrings,
187193
)
188194
return g.generateOutput(filesToGen)
189195
}

openapiGenerator.go

+39-13
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
"github.com/solo-io/protoc-gen-openapi/pkg/markers"
3535
"github.com/solo-io/protoc-gen-openapi/pkg/protomodel"
36+
"regexp"
3637
)
3738

3839
var descriptionExclusionMarkers = []string{"$hide_from_docs", "$hide", "@exclude"}
@@ -121,6 +122,10 @@ type openapiGenerator struct {
121122
// If set to true, kubebuilder markers and validations such as PreserveUnknownFields, MinItems, default, and all CEL rules will be omitted from the OpenAPI schema.
122123
// The Type and Required markers will be maintained.
123124
disableKubeMarkers bool
125+
126+
// when set, this list of substrings will be used to identify kubebuilder markers to ignore. When multiple are
127+
// supplied, this will function as a logical OR i.e. any rule which contains a provided substring will be ignored
128+
ignoredKubeMarkerSubstrings []string
124129
}
125130

126131
type DescriptionConfiguration struct {
@@ -143,25 +148,26 @@ func newOpenAPIGenerator(
143148
protoOneof bool,
144149
intNative bool,
145150
disableKubeMarkers bool,
151+
ignoredKubeMarkers []string,
146152
) *openapiGenerator {
147153
mRegistry, err := markers.NewRegistry()
148154
if err != nil {
149155
log.Panicf("error initializing marker registry: %v", err)
150156
}
151-
152157
return &openapiGenerator{
153-
model: model,
154-
perFile: perFile,
155-
singleFile: singleFile,
156-
yaml: yaml,
157-
useRef: useRef,
158-
descriptionConfiguration: descriptionConfiguration,
159-
enumAsIntOrString: enumAsIntOrString,
160-
customSchemasByMessageName: buildCustomSchemasByMessageName(messagesWithEmptySchema),
161-
protoOneof: protoOneof,
162-
intNative: intNative,
163-
markerRegistry: mRegistry,
164-
disableKubeMarkers: disableKubeMarkers,
158+
model: model,
159+
perFile: perFile,
160+
singleFile: singleFile,
161+
yaml: yaml,
162+
useRef: useRef,
163+
descriptionConfiguration: descriptionConfiguration,
164+
enumAsIntOrString: enumAsIntOrString,
165+
customSchemasByMessageName: buildCustomSchemasByMessageName(messagesWithEmptySchema),
166+
protoOneof: protoOneof,
167+
intNative: intNative,
168+
markerRegistry: mRegistry,
169+
disableKubeMarkers: disableKubeMarkers,
170+
ignoredKubeMarkerSubstrings: ignoredKubeMarkers,
165171
}
166172
}
167173

@@ -663,6 +669,13 @@ func (g *openapiGenerator) parseComments(desc protomodel.CoreDesc) (comments str
663669
c := strings.TrimSpace(desc.Location().GetLeadingComments())
664670
blocks := strings.Split(c, "\n\n")
665671

672+
var ignoredKubeMarkersRegexp *regexp.Regexp
673+
if len(g.ignoredKubeMarkerSubstrings) > 0 {
674+
ignoredKubeMarkersRegexp = regexp.MustCompile(
675+
fmt.Sprintf("(?:%s)", strings.Join(g.ignoredKubeMarkerSubstrings, "|")),
676+
)
677+
}
678+
666679
var sb strings.Builder
667680
for i, block := range blocks {
668681
if shouldNotRenderDesc(strings.TrimSpace(block)) {
@@ -681,7 +694,12 @@ func (g *openapiGenerator) parseComments(desc protomodel.CoreDesc) (comments str
681694
if shouldNotRenderDesc(l) {
682695
continue
683696
}
697+
684698
if strings.HasPrefix(l, markers.Kubebuilder) {
699+
if isIgnoredKubeMarker(ignoredKubeMarkersRegexp, l) {
700+
continue
701+
}
702+
685703
validationRules = append(validationRules, l)
686704
continue
687705
}
@@ -813,3 +831,11 @@ func (g *openapiGenerator) relativeName(desc protomodel.CoreDesc) string {
813831

814832
return desc.PackageDesc().Name + "." + typeName
815833
}
834+
835+
func isIgnoredKubeMarker(regexp *regexp.Regexp, l string) bool {
836+
if regexp == nil {
837+
return false
838+
}
839+
840+
return regexp.MatchString(l)
841+
}

testdata/golden/test10/openapiv3.yaml

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
components:
2+
schemas:
3+
test10.Msg:
4+
description: This is a top-level message.
5+
properties:
6+
a:
7+
exclusiveMaximum: true
8+
exclusiveMinimum: true
9+
format: int32
10+
maximum: 100
11+
minimum: 5
12+
multipleOf: 2
13+
type: integer
14+
x-kubernetes-validations:
15+
- message: must not equal 27
16+
rule: self != 27
17+
blist:
18+
items:
19+
type: string
20+
maxItems: 5
21+
minItems: 1
22+
type: array
23+
uniqueItems: true
24+
nested:
25+
maxProperties: 2
26+
minProperties: 1
27+
properties:
28+
a:
29+
pattern: ^[a-zA-Z0-9_]*$
30+
type: string
31+
b:
32+
enum:
33+
- Allow
34+
- Forbid
35+
- Replace
36+
type: string
37+
c:
38+
maxLength: 100
39+
minLength: 1
40+
type: string
41+
d:
42+
format: date-time
43+
type: string
44+
defaultValue:
45+
default: forty-two
46+
type: string
47+
embedded:
48+
nullable: true
49+
type: string
50+
x-kubernetes-embedded-resource: true
51+
intOrString:
52+
type: string
53+
x-kubernetes-int-or-string: true
54+
schemaless:
55+
description: Schemaless field
56+
type: object
57+
x-kubernetes-preserve-unknown-fields: true
58+
object:
59+
description: Should maintain valid Type marker and not enumerate subfields.
60+
type: object
61+
x-kubernetes-preserve-unknown-fields: true
62+
recursive:
63+
type: object
64+
x-kubernetes-preserve-unknown-fields: true
65+
val:
66+
x-kubernetes-preserve-unknown-fields: true
67+
type: object
68+
x-kubernetes-preserve-unknown-fields: true
69+
info:
70+
title: OpenAPI Spec for Solo APIs.
71+
version: ""
72+
openapi: 3.0.1
73+
paths: null

testdata/golden/test8/openapiv3.yaml

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
components:
2+
schemas:
3+
test8.Msg:
4+
description: This is a top-level message.
5+
properties:
6+
a:
7+
exclusiveMaximum: true
8+
exclusiveMinimum: true
9+
format: int32
10+
maximum: 100
11+
minimum: 5
12+
multipleOf: 2
13+
type: integer
14+
x-kubernetes-validations:
15+
- message: must not equal 27
16+
rule: self != 27
17+
blist:
18+
items:
19+
type: string
20+
maxItems: 5
21+
minItems: 1
22+
type: array
23+
uniqueItems: true
24+
nested:
25+
maxProperties: 2
26+
minProperties: 1
27+
properties:
28+
a:
29+
pattern: ^[a-zA-Z0-9_]*$
30+
type: string
31+
b:
32+
enum:
33+
- Allow
34+
- Forbid
35+
- Replace
36+
type: string
37+
c:
38+
maxLength: 100
39+
minLength: 1
40+
type: string
41+
d:
42+
format: date-time
43+
type: string
44+
defaultValue:
45+
default: forty-two
46+
example: forty-two
47+
type: string
48+
embedded:
49+
nullable: true
50+
type: string
51+
x-kubernetes-embedded-resource: true
52+
intOrString:
53+
type: string
54+
x-kubernetes-int-or-string: true
55+
schemaless:
56+
description: Schemaless field
57+
required:
58+
- a
59+
- b
60+
type: object
61+
x-kubernetes-preserve-unknown-fields: true
62+
object:
63+
description: Should maintain valid Type marker and not enumerate subfields.
64+
type: object
65+
x-kubernetes-preserve-unknown-fields: true
66+
recursive:
67+
type: object
68+
x-kubernetes-preserve-unknown-fields: true
69+
val:
70+
x-kubernetes-preserve-unknown-fields: true
71+
type: object
72+
x-kubernetes-preserve-unknown-fields: true
73+
info:
74+
title: OpenAPI Spec for Solo APIs.
75+
version: ""
76+
openapi: 3.0.1
77+
paths: null

0 commit comments

Comments
 (0)