Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the user to provide a list of specific kube markers to be ignored #27

Merged
merged 14 commits into from
Aug 7, 2024
Merged
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ Other supported options are:
* 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.
* `disable_kube_markers`
* 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.
* `ignored_kube_marker_substrings`
* when set, this list of substrings will be used to identify kubebuilder markers to ignore. When multiple are
supplied, this will function as a logical OR i.e. any rule which contains a provided substring will be ignored
7 changes: 7 additions & 0 deletions changelog/v0.2.5/allow_ignored_kube_markers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
changelog:
- type: NEW_FEATURE
issueLink: https://github.com/solo-io/gloo-mesh-enterprise/issues/18119
resolvesIssue: false
description: |
Allows the user to define one or more kube markers to ignore. This is useful when using protos that contain
unsupported kubebuilder decorators.
30 changes: 30 additions & 0 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,36 @@ func TestOpenAPIGeneration(t *testing.T) {
},
wantFiles: []string{"test7/openapiv3.yaml"},
},
{
name: "Test no markers are ignored when ignored_kube_markers is zero length",
id: "test8",
perPackage: false,
genOpts: "yaml=true,single_file=true,proto_oneof=true,int_native=true,multiline_description=true,disable_kube_markers=false,ignored_kube_marker_substrings=",
inputFiles: map[string][]string{
"test8": {"./testdata/test8/markers.proto"},
},
wantFiles: []string{"test8/openapiv3.yaml"},
},
{
name: "Test ignored_kube_markers option ignores a single marker",
id: "test9",
perPackage: false,
genOpts: "yaml=true,single_file=true,proto_oneof=true,int_native=true,multiline_description=true,disable_kube_markers=false,ignored_kube_marker_substrings=Required",
inputFiles: map[string][]string{
"test9": {"./testdata/test9/markers.proto"},
},
wantFiles: []string{"test9/openapiv3.yaml"},
},
{
name: "Test ignored_kube_markers option ignores multiple markers",
id: "test10",
perPackage: false,
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",
jjamroga marked this conversation as resolved.
Show resolved Hide resolved
inputFiles: map[string][]string{
"test10": {"./testdata/test10/markers.proto"},
},
wantFiles: []string{"test10/openapiv3.yaml"},
},
}

for _, tc := range testcases {
Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
disableKubeMarkers := false

var messagesWithEmptySchema []string
var ignoredKubeMarkerSubstrings []string

p := extractParams(request.GetParameter())
for k, v := range p {
Expand Down Expand Up @@ -147,6 +148,10 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
default:
return nil, fmt.Errorf("unknown value '%s' for disable_kube_markers", v)
}
} else if k == "ignored_kube_marker_substrings" {
if len(v) > 0 {
ignoredKubeMarkerSubstrings = strings.Split(v, "+")
}
} else {
return nil, fmt.Errorf("unknown argument '%s' specified", k)
}
Expand Down Expand Up @@ -184,6 +189,7 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes
protoOneof,
intNative,
disableKubeMarkers,
ignoredKubeMarkerSubstrings,
)
return g.generateOutput(filesToGen)
}
Expand Down
52 changes: 39 additions & 13 deletions openapiGenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

"github.com/solo-io/protoc-gen-openapi/pkg/markers"
"github.com/solo-io/protoc-gen-openapi/pkg/protomodel"
"regexp"
)

var descriptionExclusionMarkers = []string{"$hide_from_docs", "$hide", "@exclude"}
Expand Down Expand Up @@ -121,6 +122,10 @@ type openapiGenerator struct {
// If 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.
disableKubeMarkers bool

// when set, this list of substrings will be used to identify kubebuilder markers to ignore. When multiple are
// supplied, this will function as a logical OR i.e. any rule which contains a provided substring will be ignored
ignoredKubeMarkerSubstrings []string
}

type DescriptionConfiguration struct {
Expand All @@ -143,25 +148,26 @@ func newOpenAPIGenerator(
protoOneof bool,
intNative bool,
disableKubeMarkers bool,
ignoredKubeMarkers []string,
) *openapiGenerator {
mRegistry, err := markers.NewRegistry()
if err != nil {
log.Panicf("error initializing marker registry: %v", err)
}

return &openapiGenerator{
model: model,
perFile: perFile,
singleFile: singleFile,
yaml: yaml,
useRef: useRef,
descriptionConfiguration: descriptionConfiguration,
enumAsIntOrString: enumAsIntOrString,
customSchemasByMessageName: buildCustomSchemasByMessageName(messagesWithEmptySchema),
protoOneof: protoOneof,
intNative: intNative,
markerRegistry: mRegistry,
disableKubeMarkers: disableKubeMarkers,
model: model,
perFile: perFile,
singleFile: singleFile,
yaml: yaml,
useRef: useRef,
descriptionConfiguration: descriptionConfiguration,
enumAsIntOrString: enumAsIntOrString,
customSchemasByMessageName: buildCustomSchemasByMessageName(messagesWithEmptySchema),
protoOneof: protoOneof,
intNative: intNative,
markerRegistry: mRegistry,
disableKubeMarkers: disableKubeMarkers,
ignoredKubeMarkerSubstrings: ignoredKubeMarkers,
}
}

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

var ignoredKubeMarkersRegexp *regexp.Regexp
if len(g.ignoredKubeMarkerSubstrings) > 0 {
ignoredKubeMarkersRegexp = regexp.MustCompile(
fmt.Sprintf("(?:%s)", strings.Join(g.ignoredKubeMarkerSubstrings, "|")),
)
}

var sb strings.Builder
for i, block := range blocks {
if shouldNotRenderDesc(strings.TrimSpace(block)) {
Expand All @@ -681,7 +694,12 @@ func (g *openapiGenerator) parseComments(desc protomodel.CoreDesc) (comments str
if shouldNotRenderDesc(l) {
continue
}

if strings.HasPrefix(l, markers.Kubebuilder) {
if isIgnoredKubeMarker(ignoredKubeMarkersRegexp, l) {
continue
}

validationRules = append(validationRules, l)
continue
}
Expand Down Expand Up @@ -813,3 +831,11 @@ func (g *openapiGenerator) relativeName(desc protomodel.CoreDesc) string {

return desc.PackageDesc().Name + "." + typeName
}

func isIgnoredKubeMarker(regexp *regexp.Regexp, l string) bool {
if regexp == nil {
return false
}

return regexp.MatchString(l)
}
73 changes: 73 additions & 0 deletions testdata/golden/test10/openapiv3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
components:
schemas:
test10.Msg:
description: This is a top-level message.
properties:
a:
exclusiveMaximum: true
exclusiveMinimum: true
format: int32
maximum: 100
minimum: 5
multipleOf: 2
type: integer
x-kubernetes-validations:
- message: must not equal 27
rule: self != 27
blist:
items:
type: string
maxItems: 5
minItems: 1
type: array
uniqueItems: true
nested:
maxProperties: 2
minProperties: 1
properties:
a:
pattern: ^[a-zA-Z0-9_]*$
type: string
b:
enum:
- Allow
- Forbid
- Replace
type: string
c:
maxLength: 100
minLength: 1
type: string
d:
format: date-time
type: string
defaultValue:
default: forty-two
type: string
embedded:
nullable: true
type: string
x-kubernetes-embedded-resource: true
intOrString:
type: string
x-kubernetes-int-or-string: true
schemaless:
description: Schemaless field
type: object
x-kubernetes-preserve-unknown-fields: true
object:
description: Should maintain valid Type marker and not enumerate subfields.
type: object
x-kubernetes-preserve-unknown-fields: true
recursive:
type: object
x-kubernetes-preserve-unknown-fields: true
val:
x-kubernetes-preserve-unknown-fields: true
type: object
x-kubernetes-preserve-unknown-fields: true
info:
title: OpenAPI Spec for Solo APIs.
version: ""
openapi: 3.0.1
paths: null
77 changes: 77 additions & 0 deletions testdata/golden/test8/openapiv3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
components:
schemas:
test8.Msg:
description: This is a top-level message.
properties:
a:
exclusiveMaximum: true
exclusiveMinimum: true
format: int32
maximum: 100
minimum: 5
multipleOf: 2
type: integer
x-kubernetes-validations:
- message: must not equal 27
rule: self != 27
blist:
items:
type: string
maxItems: 5
minItems: 1
type: array
uniqueItems: true
nested:
maxProperties: 2
minProperties: 1
properties:
a:
pattern: ^[a-zA-Z0-9_]*$
type: string
b:
enum:
- Allow
- Forbid
- Replace
type: string
c:
maxLength: 100
minLength: 1
type: string
d:
format: date-time
type: string
defaultValue:
default: forty-two
example: forty-two
type: string
embedded:
nullable: true
type: string
x-kubernetes-embedded-resource: true
intOrString:
type: string
x-kubernetes-int-or-string: true
schemaless:
description: Schemaless field
required:
- a
- b
type: object
x-kubernetes-preserve-unknown-fields: true
object:
description: Should maintain valid Type marker and not enumerate subfields.
type: object
x-kubernetes-preserve-unknown-fields: true
recursive:
type: object
x-kubernetes-preserve-unknown-fields: true
val:
x-kubernetes-preserve-unknown-fields: true
type: object
x-kubernetes-preserve-unknown-fields: true
info:
title: OpenAPI Spec for Solo APIs.
version: ""
openapi: 3.0.1
paths: null
Loading
Loading