-
Notifications
You must be signed in to change notification settings - Fork 40k
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
docs: kubectl command structure and generators conventions #17366
docs: kubectl command structure and generators conventions #17366
Conversation
Labelling this PR as size/M |
GCE e2e test build/test passed for commit e9a2702c0efa40bc87e2167e319dd6f7d4a17c77. |
GCE e2e test build/test passed for commit cb5fda55b851717949c88bfaba85c8228126b8ce. |
) | ||
|
||
// NewCmdMine implements the kubectl mine command. | ||
func NewCmdMine(f *cmdutil.Factory, out io.Writer) *cobra.Command { |
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 the intent to document what we have or what we want? Composition is easier if we pass the local and absolute names like we do downstream.
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 agree with you though I have mixed feelings about pushing for this pattern here. Downstream we use two different parent names (oc
and openshift cli
) while here it's only kubectl
. Is there any possibility in renaming/aliasing kubectl
in the future? @bgrant0607 @brendandburns If not, I don't want to enforce an unnecessary convention.
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 are we comparing with? Pointer to code examples?
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.
Most of our commands are passed an additional name which is the name of the parent command:
https://github.com/openshift/origin/blob/08471b135e168e77de716fee9e888fe78cf5f249/pkg/cmd/cli/cmd/process.go#L57
But we are gearing towards passing both the parent and child (active command) names:
https://github.com/openshift/origin/blob/08471b135e168e77de716fee9e888fe78cf5f249/pkg/cmd/cli/cmd/logs.go#L63
Having the command name as a separate constant is helpful when we want to suggest using this command in code elsewhere, for example:
https://github.com/openshift/origin/blob/08471b135e168e77de716fee9e888fe78cf5f249/pkg/cmd/cli/cmd/newapp.go#L295
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 we ever want to split kubectl into two or more commands, or make it easy to compose new commands with extension binaries, passing names makes a lot of sense.
|
||
For every command there should be a `NewCmd<CommandName>` function that creates the command and returns a pointer to a `cobra.Command`, which can later be added to other parent commands to compose the structure tree. There should also be a `<CommandName>Options` struct with a variable to every flag and argument declared by the command (and any other variable required for the command to run). This makes tests and mocking easier. The struct ideally exposes three functions: | ||
|
||
* `Complete`: Completes the struct variables with values that may not be directly provided, for example, by flags pointers. Here you will usually take the `args` slice and set the values as appropriate variables, instantiate configs or clients, 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.
This couples the business logic to the command framework, which I want to move away from -- #7311.
Complete should be separate from Validate and Run.
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 is that? It's possible that I don't understand you but it seems to me that this structure respects* the separation between business logic (the functionality that the user cares about when running the command?) and the command framework (all the machinery that needs to be in place for the command to work?).
[*] this is not 100% true because sometimes completing and validating may overlap eg. in interactive commands but still this structure is way better than a plain unit.
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.
We likely need multiple conventions:
- Interactive commands
- Builder-based generic commands, which should move into a more generic client library that has no dependency on cobra
- Generators
Thanks. Documenting the convention we desire would be useful. |
GCE e2e build/test failed for commit 5aad737af61ca4a7929b55f328a6609a303c8ae7. |
GCE e2e test build/test passed for commit 5aad737af61ca4a7929b55f328a6609a303c8ae7. |
GCE e2e build/test failed for commit 8f8cae2863d5723747582974a24b81d7fd025f1e. |
GCE e2e build/test failed for commit d4be40f6dd2874e50141d68e9e43faa3cae72fa1. |
I've updated the doc to pass the names down and updated kubectl logs to follow. PTAL |
Labelling this PR as size/L |
GCE e2e build/test failed for commit d4be40f6dd2874e50141d68e9e43faa3cae72fa1. |
There are also kubectl attach, kubectl annotate, kubectl exec, and almost kubectl convert that currently follow this pattern. |
GCE e2e build/test failed for commit a6a81f064eb3cb821c65f0722785b66b860cbf4b. |
GCE e2e build/test failed for commit 2c6a415b4c3cc353d07d2c7f1cb6ab7ac1fc1c19. |
GCE e2e build/test failed for commit 4d123c7a22755fdb3033e872b590f91d0d81d8cb. |
GCE e2e build/test failed for commit 41d7b953acc62351282f9b1971bd863e2728bbd3. |
@bgrant0607 I am taking a first pass at documenting generators (#17833) here. Mind having a look? |
Any comments from anyone else on @kubernetes/kubectl? @deads2k? @jlowdermilk? |
I like the I'm also ok with explaining what generators are, but I really don't want to encourage their use until we find something more structured. |
|
||
* `Complete`: Completes the struct variables with values that may not be directly provided, for example, by flags pointers. Here you will usually take the `args` slice and set the values as appropriate variables, instantiate configs or clients, etc. | ||
* `Validate`: performs validation and returns errors. | ||
* `Run<CommandName>`: runs the actual command, taking as assumption that the struct is complete with all required values to run, and they are 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.
It's not convention for Run
to be a function of the options struct. It's usually a plain function that takes an options struct, as you typically need to pass in the cobra.Command
as well.
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 not convention for Run to be a function of the options struct. It's usually a plain function that takes an options struct, as you typically need to pass in the cobra.Command as well.
I think that #7311 suggests moving towards the structure laid out here. Or at least something with a similar intent.
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 #7311 is slightly different; that suggests moving all business logic out of command definitions, which sounds like a good idea. Inside the command we still need access to the cobra.Command
, cmdutil.Factory
, cmdIn
, and cmdOut
, to parse flag values, access generators/printer, etc. Current convention is to do that in Run<CommandName>
, which can (and should) use a library to handle the business logic aside from parsing input and formatting output. e.g. rollingupdate
does this.
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 separation between Complete, Validate, and Run makes cleaner where the business logic will exist or most probably called from (Run), and where cobra, factory, etc. fall into (Complete). There are a bunch of commands in OpenShift (openshift/origin#3697) that already use this pattern and a couple of kubectl commands too
@@ -135,6 +133,151 @@ Updated: 8/27/2015 | |||
* Use "TYPE" for the particular flavor of resource type accepted by kubectl, rather than "RESOURCE" or "KIND" | |||
* Use "NAME" for resource names | |||
|
|||
## Command implementation conventions | |||
|
|||
For every command there should be a `NewCmd<CommandName>` function that creates the command and returns a pointer to a `cobra.Command`, which can later be added to other parent commands to compose the structure tree. There should also be a `<CommandName>Options` struct with a variable to every flag and argument declared by the command (and any other variable required for the command to run). This makes tests and mocking easier. The struct ideally exposes three functions: |
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.
Options in <CommandName>Options
struct name makes impression the struct contains only flags. I.e. all values that are passed down by cobra.Command
. As the paragraph goes on it says: "with a variable and every flag ... (and any other variable required for the command to run
)". Any other variable required for the command to run does not come under Options
. It is more configuration related. It would make more sense to use <CommandName>Config
instead. Options are part of configuration so as other variables like client, factory, mapper, 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.
I like this proposal. @deads2k ?
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 like this proposal. @deads2k ?
I'm fine with pretty much any name for it. I just want to make sure I end up with a value object containing all the information required to perform the action without any direct dependency on commands or flagsets.
GCE e2e test build/test passed for commit 0571a7b048017232ab792dd384cb1a3a1cbf6a6e. |
GCE e2e test build/test passed for commit facf120753dceb8195fdca3313429b343f6ae629. |
GCE e2e test build/test passed for commit ab3277ea739fc2fef14e6eb37ca58f5f40559dc2. |
@bgrant0607 updated this doc, ptal when you have time |
* NOUNs may be specified as TYPE name1 name2 ... or TYPE/name1 TYPE/name2; TYPE is omitted when only a single type is expected | ||
* Resource types are all lowercase, with no hyphens; both singular and plural forms are accepted | ||
* kubectl VERB NOUNs for commands that apply to multiple resource types. | ||
* NOUNs may be specified as TYPE name1 name2 ... or TYPE/name1 TYPE/name2; TYPE is omitted when only a single type is expected. |
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.
Or TYPE1,TYPE2,TYPE3/name1
Good enough for now, thanks. LGTM after adding the comma-separated form pointed out by @smarterclayton |
GCE e2e test build/test passed for commit 426ef93. |
@bgrant0607 @smarterclayton comments addressed |
LGTM |
@k8s-bot test this Tests are more than 48 hours old. Re-running tests. |
GCE e2e test build/test passed for commit 426ef93. |
@k8s-bot test this issue: #IGNORE Tests have been pending for 24 hours |
1 similar comment
@k8s-bot test this issue: #IGNORE Tests have been pending for 24 hours |
GCE e2e test build/test passed for commit 426ef93. |
GCE e2e test build/test passed for commit 426ef93. |
Automatic merge from submit-queue |
Auto commit by PR queue bot
Auto commit by PR queue bot
@smarterclayton @fabianofranz @kubernetes/kubectl