-
Notifications
You must be signed in to change notification settings - Fork 63
Adding the API generator #12
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
Conversation
ddebroy
left a comment
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.
This is looking great! Just a few comments.
client/api/README.md
Outdated
| CSI-proxy's API is a GRPC, versioned API. | ||
|
|
||
| The server exposes a number of API groups, all independent of each other. Additionally, each API group has one or more versions. Each version in each group listens for GRPC messages on a Windows named pipe of the form `\\.\\pipe\\csi-proxy-<api_group_name>-<version>` (e.g. `\\.\\pipe\\csi-proxy-filesystem-v2alpha1` or `\\.\\pipe\\csi-proxy-iscsi-v1`). | ||
| The server exposes a number of API groups, all independent of each other. Additionally, each API group has one or more versions. Each version in each group listens for GRPC messages on a Windows named pipe of the form `\\.\\pipe\\csi-proxy-<api_group_name>-<version>` (e.g. `\\.\\pipe\\csi-proxy-file_system-v2alpha1` or `\\.\\pipe\\csi-proxy-iscsi-v1`). |
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.
How about keeping the API groups single word as much as possible - like filesystem, volume, disk, smb and iscsi?
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.
FileSystem seemed cleaner than Filesystem in the places where we need camel case, so that's why I changed this - but happy to change back if you prefer.
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.
My preference would be filesystem.
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.
👍
client/utils.go
Outdated
| // CsiProxyNamedPipePrefix is the prefix for the named pipes the proxy creates. | ||
| // The suffix will be the API group and version, | ||
| // e.g. "\\.\\pipe\\csi-proxy-iscsi-v1", "\\.\\pipe\\csi-proxy-filesystem-v2alpha1", etc. | ||
| // e.g. "\\.\\pipe\\csi-proxy-iscsi-v1", "\\.\\pipe\\csi-proxy-file_system-v2alpha1", etc. |
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.
Let's keep this filesystem
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.
👍
|
|
||
| const ( | ||
| // csiProxyRootPath is the CSI-proxy go library's root path. | ||
| csiProxyRootPath = "github.com/kubernetes-csi/csi-proxy" |
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.
Will it be possible to make csiProxyRootPath configurable through a parameter? Can be done in a separate PR later.
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.
Sure, though I'm not sure what the use case would be?
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 was thinking it will make name changes easier to handle in the future. Looks like k/k hardcodes this as well in their generators: https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/staging/src/k8s.io/code-generator/cmd/conversion-gen/generators/conversion.go#L439-L440. So fine for now.
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.
But even then, that parameter presumably would have to be hardcoded somewhere in this repo? :)
Keeping as is for now anyway, as agreed.
cmd/csi-api-gen/generators/api.go
Outdated
| tagName = "csi-proxy-gen" | ||
|
|
||
| // headerComment is the comment on the first line of every generated file. | ||
| headerComment = "// Code generated by csi-api-gen. DO NOT EDIT.\n\n" |
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.
How about calling the utility csi-proxy-api-gen? It is not generating CSI APIs, so csi-api-gen does not sound right.
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.
Fair enough, changing :)
cmd/csi-api-gen/generators/api.go
Outdated
| // * +csi-proxy-gen=clientBasePkg:<pkg_path> to set the base output directory | ||
| // for generated client files - defaults to github.com/kubernetes-csi/csi-proxy/client/groups | ||
| tagMarker = "+" | ||
| tagName = "csi-proxy-gen" |
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.
Based on comment below, I think this (as well as the comments above) should read csi-proxy-api-gen.
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.
👍
| unsafe "unsafe" | ||
|
|
||
| pb "github.com/kubernetes-csi/csi-proxy/integrationtests/apigroups/api/dummy/v1" | ||
| v1 "github.com/kubernetes-csi/csi-proxy/integrationtests/apigroups/api/dummy/v1" |
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.
The v1 in the alias seems unnecessary given the structure of the imports.
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.
It is, but that's what what gengo spits out - not sure why. Would need to go look why it does that, and change it; or just leave it as it - generated code does tend to have some quirks?
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 think it is okay for now.
| return nil | ||
| } | ||
|
|
||
| // Convert_internal_ComputeDoubleRequest_To_v1_ComputeDoubleRequest is an autogenerated conversion function. |
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.
Is Convert_internal_*Request_To_vX_*Request here mainly for completeness? I assume this conversion path will never be invoked i.e. in the Request path, Convert_vX_*Request_To_internal_*Request is what is mainly needed.
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 absolutely right.
However, I couldn't think of a great way to tell the generic conversion generator which conversion functions you actually cared about. Best I could think of would be registering a callback with it to check if a given conversion pair is of interest - but it does add a decent amount of complexity for not much again.
If you feel strongly against having unused funcs though, can work it into code-generator.
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 think their presence is fine. I was wondering if they will be invoked in any scenario.
| func convert_internal_ComputeDoubleResponse_To_pb_ComputeDoubleResponse(in *internal.ComputeDoubleResponse, out *pb.ComputeDoubleResponse) error { | ||
| return autoconvert_internal_ComputeDoubleResponse_To_pb_ComputeDoubleResponse(in, out) | ||
| // Convert_v1_ComputeDoubleResponse_To_internal_ComputeDoubleResponse is an autogenerated conversion function. | ||
| func Convert_v1_ComputeDoubleResponse_To_internal_ComputeDoubleResponse(in *v1.ComputeDoubleResponse, out *internal.ComputeDoubleResponse) error { |
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.
Is Convert_vX_*Response_To_internal_*Response here mainly for completeness? I assume this conversion path will never be invoked i.e. in the Response path, Convert_internal_*Response_To_vX_*Response is what is mainly needed.
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.
Same answer :)
|
cc @jingxu97 @peterhornyak |
| @@ -0,0 +1,74 @@ | |||
| package generators | |||
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.
is this file auto generated?
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.
No. It contains the generator that generates api_group_generated.go files, hence the name.
|
@ddebroy : amended as requested, PTAL, thanks! :) |
| } | ||
| } | ||
|
|
||
| // isError returns true iff type t is the built-in type "error" |
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.
maybe spell out iff to if and only if
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.
👍
|
|
||
| // isError returns true iff type t is the built-in type "error" | ||
| func isError(t *types.Type) bool { | ||
| return t.Kind == types.Interface && t.Name.Name == "error" && t.Name.Package == "" |
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.
The t.Name.Package must be empty string? What does it mean if it is not empty?
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.
It means that the type belongs to a package; that's not the case for built-in types.
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 it make sense to name it IsBuildinError to be more specific?
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.
Absolutely, changed, thanks :)
|
|
||
| // buildAPIGroupDefinitionFromDocComment looks for a +csi-proxy-api-gen comment in the package's | ||
| // doc.go file, and if it finds one build the corresponding API definition. | ||
| func buildAPIGroupDefinitionFromDocComment(pkgPath string, pkg *types.Package, groups map[string]*groupDefinition) bool { |
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.
the passing pkgPath is just for logging purpose in the function?
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.
Just for logging.
| } | ||
| } | ||
|
|
||
| klog.V(5).Infof("Found API group %q", definition.name) |
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.
Here we assume we always find one api group? Is that possible that the returned commentTags are all just empty string? Is that possible that name or value is empty string? Is that valid?
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 there is a csi-proxy-api-gen tag in a package's doc.go file, then it must be an API group, regardless of which additional custom options it has (or doesn't have). So yes, past the
if len(commentTags) == 0 {
return false
}
check on line 160, we know we've found an API group.
| continue | ||
| } | ||
|
|
||
| if buildAPIGroupDefinitionFromDocComment(pkgPath, pkg, groups) { |
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.
so it is not possible to have group definition in both subdirectories of client/api and doc.go file ?
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.
It's absolutely possible. If a group definition is in the canonical client/api/ location and has a doc.go file with csi-proxy-api-gen tags, then it's processed according to the tags' options (and if there are no custom options then the tag is redundant with it being in the canonical location)
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 am trying to check the continue logic here means if buildAPIGroupDefinitionFromDocComment returns true, the rest will be skipped?
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.
Yes, it will, since the groupDefinition for it will already have been built.
| func packages(context *generator.Context, arguments *args.GeneratorArgs) (pkgs generator.Packages) { | ||
| // find API group definitions | ||
| groups := findAPIGroupDefinitions(context) | ||
| klog.V(5).Infof("Found API groups: %v", groups) |
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.
all your log level is 5 or above?
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.
It's all debugging logging.
| for _, filePath := range toRemove { | ||
| filePath = path.Join(outputBase, filePath) | ||
| if err := os.RemoveAll(filePath); err != nil { | ||
| klog.Fatalf("unable to remove %q: %v", filePath, err) |
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 want to retry or just fail immediately?
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'm not sure what error modes could justify retrying later?
Also, the general philosophy here (and also for all of k8s' code generation, as far as I can tell) is that since this is not a service, and is just meant to be run manually (or in tests) from time to time, it's okay to fail early.
| removeGeneratedFiles(group, arguments.OutputBase) | ||
| pkgs = append(pkgs, packagesForGroup(group, arguments.OutputBase)...) | ||
| } | ||
| return |
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.
So it just returns nothing?
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.
Nope, it's returning pkgs
7cd45af to
c54f852
Compare
| // are up-to-date (i.e. consistent with the current generator). | ||
|
|
||
| // TestNewAPIGroup tests that bootstraping a new group works as intended. | ||
| func TestNewAPIGroup(t *testing.T) { |
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.
This test case is having a doc.go? Maybe it is not that important, just want to check is there other test cases that are interesting to us?
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.
Yes it does have one. Testing API groups from canonical locations will be done by re-generating all auto-generated files as part of all builds.
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.
Can we name the cmd tool directory (cmd/csi-api-gen/) csi-proxy-api-gen as well please? Thanks for updating the name elsewhere in the code/comments.
ddebroy
left a comment
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.
Just a few nits. Otherwise looking good.
| @@ -0,0 +1,11 @@ | |||
| package main | |||
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.
Can we name the cmd tool directory (cmd/csi-api-gen/ above) csi-proxy-api-gen as well please? Thanks for updating the name elsewhere in the code/comments.
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.
👍
cmd/csi-api-gen/generators/api.go
Outdated
| if err := os.MkdirAll(path.Dir(docFilePath), os.ModePerm); err != nil { | ||
| klog.Fatalf("Unable to create directory %q: %v", path.Dir(docFilePath), err) | ||
| } | ||
| contents := fmt.Sprintf("// Temporary code generated by conversion-gen. DO NOT COMMIT.\n// Should be removed automatically, but please remove manually otherwise.\n\npackage %s\n", vsn.Name) |
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.
Should this say conversion-gen or csi-proxy-api-gen?
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.
👍
| // validateServerCallback checks that server callbacks have the expected shape, i.e.: | ||
| // * all versioned (i.e. in the same package) parameter should be pointers | ||
| // * return values should all be pointers, except for the last one, that must be an error | ||
| // These assumptions are |
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.
nit: check the comment sentence
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.
But they... are!
Changed, thanks :)
The API generator generates all the boilerplate files the API needs, as described in https://github.com/kubernetes-csi/csi-proxy/tree/master/client/api#detailed-breakdown-of-generated-files Added a few tests, though the main test will be checking that auto-generated files are in sync with the rest of the code & proto files; that will be done in automated builds in a future patch (very soon). Signed-off-by: Jean Rouge <rougej+github@gmail.com>
ddebroy
left a comment
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.
/lgtm
/approve
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: ddebroy, wk8 The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
What type of PR is this?
/kind feature
What this PR does / why we need it:
This patch adds the API generator that generates all the boilerplate files the API needs, as described in
https://github.com/kubernetes-csi/csi-proxy/tree/master/client/api#detailed-breakdown-of-generated-files
Added a few tests, though the main test will be checking that auto-generated files are in sync with the rest of the code & proto files; that will be done in automated builds in a future patch (very soon).
Which issue(s) this PR fixes:
Fixes #8
Does this PR introduce a user-facing change?: