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
Fix prefixed global flags for kubectl plugins #94290
Conversation
Hi @cmurphy. Thanks for your PR. I'm waiting for a kubernetes 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/test-infra repository. |
/ok-to-test |
8e1e0a2
to
8c03f80
Compare
@cmurphy I think you need to run |
This is a tricky issue to solve completely and has been discussed several times before. I think this PR would help though, by relocating as much as possible after the plugin name. This would relocate global flags, and even non-global flags with no arg or that do not have a space before the arg. There is still an edge case where if you provide a non-global flag with a space before the arg, the plugin it will fail.... ie. I think this PR is an improvement though and fixes most cases. /lgtm |
@brianpursley commented on Aug 31, 2020, 7:28 AM PDT:
Thanks for the review and the background information!
I think this is an oversight on my part, I don't think it should work this way. I think whether the flag-before-the-command has an argument or not, it should be treated the same way, and in this case I think if it is not a recognized global flag it should just be rejected because the "unknown command myplugin" is more confusing than an unrecognized flag. I'll try to fix this.
I should mention (and will add in a comment) that the logic and function names are largely inspired by similar functionality in cobra - I would have reused it more directly if any of it was exported. |
8c03f80
to
0b81f5a
Compare
New changes are detected. LGTM label has been removed. |
pkg/kubectl/cmd/cmd.go
Outdated
case strings.HasPrefix(flag, "--"): | ||
return fs.Lookup(flag[2:]) | ||
case strings.HasPrefix(flag, "-"): | ||
return fs.ShorthandLookup(flag[1:2]) |
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 OK?
My concern is if someone accidentally does -
when they meant --
and if this only takes the first character after the -
then it could cause the wrong flag to be used instead of failing.
For example, in kubectl run
there is an --image
flag and a -i
flag which is shorthand for --stdin
.
In this case it probably would not cause a bad problem because the command would fail, but if I used -image
instead of --image
, I think the code above would interpret -image
as -i
, right?
What if it just does this?
return fs.ShorthandLookup(flag[1:])
Or return an error if the length of the single-dash flag is not 1.
Just an idea
EDIT: Or just return null if a single-dash flag is more than 1 character in length might work too
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.
Thanks for catching that, I handled it in the original version and mistakenly removed it
Looks pretty good I think. Just one minor comment. Flag parsing is a little hard to imagine given all the scenarios, but I like that you have tests to cover them. |
Looks good. Can you squash your commits into a single commit? |
533e411
to
ee15e43
Compare
@brianpursley commented on Sep 4, 2020, 10:30 AM PDT:
done |
/assign @seans3 |
note to other reviewers. I know we have discussed this topic before on several occasions (I linked above in a previous comment). The reason I think this PR is fine because it improves the handling of global flags before the plugin name, which helps in the majority of the problem cases where people have aliased kubectl with a flag included. It still cannot handle non-global flags before the plugin name, but I think that is just going to have to remain a known limitation. |
/assign @soltysh |
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.
@cmurphy I greatly appreciate your effort and time you spend putting this PR together but as mentioned in #92343 and several other places, which @brianpursley also mentioned sig-cli decided (see https://docs.google.com/document/d/1r0YElcXt6G5mOWxwZiXgGu_X6he3F--wKwg-9UBc29I/edit#heading=h.ji0nwpg7dyuz) that we're not going to re-schuffle flags but rather we'll put a warning in place. I've spent decent amount of time carefully examining your PR today and I was still able to find issues with global flags. Namely passing --v=2
, -v=2
or -v 2
which works just fine with kubectl
does not b/c -v
is not registered as global flags. We could theoretically have a list of exceptions but I'm worried we'd be playing never-ending catch up game with that.
My proposal for this PR and the overall problem are as follows. For this PR I'd like to see the current test case fixed (see my comment below about comparing counts and not actual arguments passed to plugins) and a new test which will run plugin commands with no flags (1), make sure they get routed to, then run with flags after the commands and make sure they get routed to (2), then run with flags before the commands and make sure it errors (3).
Secondly, in the long run I think we should look into cobra flag parsing mechanism and check what we could do to make the flag parsing more predictable.
/hold
pkg/kubectl/cmd/cmd_test.go
Outdated
name: "test that a plugin executable is found with flags before arg", | ||
args: []string{"kubectl", "--kubeconfig", "bar.conf", "foo"}, | ||
expectPlugin: "plugin/testdata/kubectl-foo", | ||
expectPluginArgs: []string{"--kubeconfig", "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.
bar.conf
?
pkg/kubectl/cmd/cmd_test.go
Outdated
name: "test that a plugin executable is found with flags with '=' before arg", | ||
args: []string{"kubectl", "--kubeconfig=bar.conf", "foo"}, | ||
expectPlugin: "plugin/testdata/kubectl-foo", | ||
expectPluginArgs: []string{"--kubeconfig=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.
--kubeconfig=bar.conf
?
pkg/kubectl/cmd/cmd_test.go
Outdated
}, | ||
{ | ||
name: "test that a plugin executable is found with short flags with '=' before arg", | ||
args: []string{"kubectl", "-n=default", "foo"}, |
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 looks wrong, shorthand does not pass argument with =
, so this should be an 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.
Oh my, I just checked this code and the test only verifies the length of args not their actual contents, see
kubernetes/pkg/kubectl/cmd/cmd_test.go
Line 106 in 1a645c2
if len(pluginsHandler.withArgs) != len(test.expectPluginArgs) { |
Thank you for the thorough review, @soltysh !
Could you clarify here - why is
A plugin may still not respect -v even though it is a global flag, but it now gives an error message about the flag, the same as it would if the flag came at the end:
There was an issue with the unit tests where the registered name of the flag became Regarding the concern about being able to tell the difference between a flag that has an argument and a flag without an argument, I think I have covered that as well:
What case of flags did I miss where this is doing the wrong thing? Or is the last case in this example what's not acceptable? I support just emitting an error as in the the other PR, that is certainly simpler and fixes the essence of the problem. I just want to make sure I understand where I went wrong here so I can learn :)
Happy to turn this PR into fixing the tests
This part I think should either wait for #92343 to merge, or perhaps just be part of that PR? Thanks again for your time! |
IIRC the main issues is that kubernetes/cmd/kubectl/kubectl.go Line 46 in 6dddea5
kubernetes/vendor/k8s.io/klog/v2/klog.go Line 433 in 6dddea5
I can't remember exactly, but when I checked this last time not all of the ones you mentioned worked as I expect them. I'd need to give it another try.
yeah - fixing tests is always nice seen, the more tests the better for us.
That merged by now. |
Ensure the unit tests compare the actual plugin arguments rather than just the number of arguments, and add a test using a plugin with no flags provided.
ee15e43
to
1283b84
Compare
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: brianpursley, cmurphy 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 |
Removed the changes to the plugin flag handling, fixed the unit tests to do a real comparison of the argument list and added a test for when no flag is provided to the plugin. I couldn't test the error case, because the plugin simply does an immediate exit 1 instead of returning, so the unit tests aren't able to handle it safely:
Moreover, the error handling introduced in #92343 doesn't work if there is only one prefixed flag and one command in the plugin name:
|
/retest |
What type of PR is this?
/kind bug
What this PR does / why we need it:
For normal kubectl commands, global flags can be used anywhere within
the command line:
However, without this patch, when calling a plugin as a subcommand, the
flag can only work if it comes after the plugin subcommand:
This is an inconsistency that can lead to the user confusion regarding
whether they have installed the plugin correctly and so is an unfriendly
user experience. This patch corrects the issue for plugin handling by
relocating persistent flags to the end of the argument list before
attempting to search for the plugin binary.
Which issue(s) this PR fixes:
Fixes #93432
Special notes for your reviewer:
Does this PR introduce a user-facing change?:
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: