-
Notifications
You must be signed in to change notification settings - Fork 446
🌱 crd gen: handle type any #1252
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
base: main
Are you sure you want to change the base?
Conversation
Welcome @brekelj1! |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: brekelj1 The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Hi @brekelj1. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
779349b
to
2ebad8e
Compare
pkg/crd/testdata2/go.mod
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a whole new testdata to be able to test this change? testdata
is a well known path, I'm not sure if you can just create a testdata2
and have it have the same grace/ignoring that is done for testdata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. Changed tests to use existing testdata directory to follow existing approach.
pkg/crd/gen_integration_test.go
Outdated
@@ -171,6 +171,44 @@ var _ = Describe("CRD Generation proper defaulting", func() { | |||
}) | |||
}) | |||
|
|||
var _ = Describe("CRD Generation with any", func() { | |||
It("Works", func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need a separate suite or can it be integrated into the existing testdata?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I introduced a separate Describe
because the original Describe
said "CRD Generation proper defaulting" which doesn't quite fit what is being tested.
Having said that "CRD Generation proper defaulting" was already testing many more behaviours than proper defaulting. I merged the test into the existing structure.
type: object | ||
spec: | ||
properties: | ||
bar: {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean from an API perspective? What will the API server do when it sees an empty schema for bar
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applying CRD to a cluster gives this error:
The CustomResourceDefinition "foos.example.com" is invalid: spec.validation.openAPIV3Schema.properties[spec].properties[bar].type: Required value: must not be empty for specified object fields
I haven't been able to find a reference in docs/code, but I think we can conclude these kind of schemas are invalid in K8S world.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this officially states type
is required here.
Ref: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
A structural schema is an OpenAPI v3.0 validation schema which:
- specifies a non-empty type (via type in OpenAPI) for the root, for each specified field of an object node (via properties or additionalProperties in OpenAPI) and for each item in an array node (via items in OpenAPI), with the exception of:
1a. a node with x-kubernetes-int-or-string: true
1b. a node with x-kubernetes-preserve-unknown-fields: true
...
It appears at the moment that we are generating a schema with the I'm not sure if returning an empty schema is correct here, I don't know how the API server reacts to that /hold we need to explore what the correct behaviour is here |
I updated the approach to always error on interface types (instead of also allowing empty interfaces): #1252 (comment) Also made updates for the other comments. @JoelSpeed please rereview |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the generator errors for this type, do we still expect it to spit out a yaml file? Not sure I was expecting to see a yaml output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1
This is how the existing CRD generator behaves for similar kinds of errors. For example, this existing code follows the same approach for error handling on interface types:
ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("unsupported AST kind %T", expr), rawType))
// NB(directxman12): we explicitly don't handle interfaces
return &apiext.JSONSchemaProps{}
I think reassessing the failure behaviour should be left out of scope of this pull request, in favour of more holistically revisiting that in another pull request.
2
More to the point: I'm not sure how much of an issue outputting a YAML file is when there are errors like this, since the user can see those errors in the output anyway (did anyone over the years raise issues about this?), and even if the user overlooked the errors the CRD will be rejected by the API server, so that would incentivise the user to look closer anyway.
It also looks like func main
/controller-gen exits with non-zero code when errors are added to *"pkg/loader".Package
's (which makes it more difficult for users to overlook):
- https://github.com/kubernetes-sigs/controller-tools/blob/main/cmd/controller-gen/main.go#L187
- https://github.com/kubernetes-sigs/controller-tools/blob/main/pkg/genall/genall.go#L282
3
I added this YAML file in this PR since the existing "should have deterministic output" test load everything in testdata/gen
as ./...
to test group-kinds are sorted/deterministic. This also includes iface_types.go
.
/lgtm @sbueringer @alvaroaleman I think this is a step in the right direction for avoiding panics when we come across Any concerns? |
LGTM label has been added. Git tree hash: c98381fff1ecc0b9ab610c6ae079b545f433ae78
|
/ok-to-test |
/assign I'll take a look |
@@ -294,6 +294,10 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiext.JSONSchema | |||
Format: fmt, | |||
} | |||
} | |||
if _, isInterface := typeInfo.(*types.Interface); isInterface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does types.Interface only capture any? Or do we actually want to block all interface types even if they would work?
What about changing the logic below to simply not panic on the type cast?
typeInfoWithObjMethod, ok := typeInfo.(interface{ Obj() *types.TypeName })
if ok {
typeNameInfo := typeInfoWithObjMethod.Obj()
pkg := typeNameInfo.Pkg()
pkgPath := loader.NonVendorPath(pkg.Path())
if pkg == ctx.pkg.Types {
pkgPath = ""
}
ctx.requestSchema(pkgPath, typeNameInfo.Name())
link := TypeRefLink(pkgPath, typeNameInfo.Name())
return &apiext.JSONSchemaProps{
Ref: &link,
}
}
Then we can still handle various cases below that
Handle Go1.18's
any
type in CRD generator.Fixes #1251.