-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
feat(cli): Add Plugin Support to the Argo CD CLI #20074
base: master
Are you sure you want to change the base?
Conversation
🔴 Preview Environment stopped on BunnyshellSee: Environment Details | Pipeline Logs Available commands (reply to this comment):
|
✅ Preview Environment created on Bunnyshell but will not be auto-deployedSee: Environment Details Available commands (reply to this comment):
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #20074 +/- ##
==========================================
+ Coverage 55.89% 59.73% +3.84%
==========================================
Files 343 344 +1
Lines 57327 57442 +115
==========================================
+ Hits 32044 34315 +2271
+ Misses 22643 20367 -2276
- Partials 2640 2760 +120 ☔ View full report in Codecov by Sentry. |
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.
Please consider adding a new documentation page explaining how to write plugins and how to install and consume them.
92967cd
to
4a260dd
Compare
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.
plugin_test.go
is kept in separate package because keeping it in the util will create circular dependency.
Update: Resolved
0e8f338
to
c314e43
Compare
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.
@nitishfy Great progress. I think this PR needs to be reviewed by someone from the security sig as I see some potential risks in the code. I added comments to the security issues that I identified at first but a more security driven review would be important in this case.
cc @jannfis @crenshaw-dev
652887c
to
fea85cd
Compare
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 are not using the cmd.Find()
anymore.
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.
Please check my 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.
PTAL.
Also, I think I'll have to restructure the entire tests for |
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.
Great progress. Tks @nitishfy!
There are a few more open items to be addressed in this PR before we merge.
Please take a look.
fc285bd
to
9e1e532
Compare
cmd/argocd/commands/plugin.go
Outdated
log.Errorf("Error: %v\n", pluginErr) | ||
return pluginErr |
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 not log error before returning it. Let the caller handle the error when you are returning it.
return fmt.Errorf("Error: %v\n", pluginErr)
@nitishfy my personal opinion is still that we should still have some E2E tests for this (i.e. running tests that exercises the actual CLI). |
// set by the cobra.OnInitialize() was never executed because cmd.Execute() | ||
// gave us a non-nil error. | ||
initConfig() | ||
log.SetLevel(log.DebugLevel) |
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.
Why setting debug logs? it should respect the cmdutil.LogLevel
provided in the command arguments.
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.
sorry the previous comment got deleted...
the log level needs to be setup manually here since the initConfig()
set by the cobra.OnInitialize()
was never executed because cmd.Execute()
gave us a non-nil error.
If you look at the unit tests that have been written, we're not testing a specific function actually. All it does is callout the similar logic that would go inside an E2E test. I just believe that's redundant testing for the similar thing again. |
// unknown command error or any other. | ||
if err != nil { | ||
pluginHandler := cli.NewDefaultPluginHandler([]string{"argocd"}) | ||
pluginErr := pluginHandler.HandleCommandExecutionError(err, isArgocdCLI, os.Args) |
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 command execution error should probably receive the command object in parameter. This way, the handler can use functions such as cmd.UsageString()
and cmd.ErrPrefix()
to handle the behavior disabled by SilenceUsage
and SilenceErrors
.
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 don't need that because the behaviour disabled by SilenceUsage
and SilenceErrors
is getting handled here:
if isArgocdCLI && strings.Contains(err.Error(), "unknown command") {
log.Debug("command does not exist, looking for a plugin...")
pluginPath, pluginErr := h.handlePluginCommand(args[1:])
// IMP: If a plugin doesn't exist, the returned path will be empty along with nil error
// This means the command is neither a normal Argo CD Command nor a plugin.
if pluginErr != nil {
// If plugin handling fails, report the plugin error and exit
return fmt.Errorf("Error: %w\n", pluginErr)
} else if pluginPath == "" {
log.Errorf("Error: %v\nRun 'argocd --help' for usage.\n", err)
return err
}
} else {
// If it's any other error (not an unknown command), report it directly and exit
log.Errorf("Error: %v\n", err)
return err
}
I believe once the cobra PR to check for unknown command gets merged, we can refactor this codebase to an extent to make it better.
Well, I disagree with that. We would at the very least still need to check that the CLI parses flags correctly from the command line. |
6ebff61
to
697195a
Compare
Signed-off-by: nitishfy <[email protected]> modify mkdocs.yaml Signed-off-by: nitishfy <[email protected]> add unit test Signed-off-by: nitishfy <[email protected]> add more tests Signed-off-by: nitishfy <[email protected]> modify docs a bit Signed-off-by: nitishfy <[email protected]> update docs Signed-off-by: nitishfy <[email protected]> fix merge conflicts Signed-off-by: nitishfy <[email protected]> fix go.mod Signed-off-by: nitishfy <[email protected]> add line Signed-off-by: nitishfy <[email protected]> fix go.mod Signed-off-by: nitishfy <[email protected]> fix lint checks Signed-off-by: nitishfy <[email protected]> fix failing tests Signed-off-by: nitishfy <[email protected]> fix lint checks Signed-off-by: nitishfy <[email protected]> add e2e tests Signed-off-by: nitishfy <[email protected]> add one more e2e test Signed-off-by: nitishfy <[email protected]>
697195a
to
1687909
Compare
Signed-off-by: nitishfy <[email protected]>
01d3ea5
to
8c84127
Compare
All the E2E tests have been added now. |
Signed-off-by: nitishfy <[email protected]>
5275c03
to
819c558
Compare
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.
Minor nits, otherwise LGTM
Signed-off-by: nitishfy <[email protected]>
Signed-off-by: nitishfy <[email protected]>
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.
PTAL.
// set by the cobra.OnInitialize() was never executed because cmd.Execute() | ||
// gave us a non-nil error. | ||
initConfig() | ||
log.SetLevel(log.DebugLevel) |
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.
sorry the previous comment got deleted...
the log level needs to be setup manually here since the initConfig()
set by the cobra.OnInitialize()
was never executed because cmd.Execute()
gave us a non-nil error.
// unknown command error or any other. | ||
if err != nil { | ||
pluginHandler := cli.NewDefaultPluginHandler([]string{"argocd"}) | ||
pluginErr := pluginHandler.HandleCommandExecutionError(err, isArgocdCLI, os.Args) |
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 don't need that because the behaviour disabled by SilenceUsage
and SilenceErrors
is getting handled here:
if isArgocdCLI && strings.Contains(err.Error(), "unknown command") {
log.Debug("command does not exist, looking for a plugin...")
pluginPath, pluginErr := h.handlePluginCommand(args[1:])
// IMP: If a plugin doesn't exist, the returned path will be empty along with nil error
// This means the command is neither a normal Argo CD Command nor a plugin.
if pluginErr != nil {
// If plugin handling fails, report the plugin error and exit
return fmt.Errorf("Error: %w\n", pluginErr)
} else if pluginPath == "" {
log.Errorf("Error: %v\nRun 'argocd --help' for usage.\n", err)
return err
}
} else {
// If it's any other error (not an unknown command), report it directly and exit
log.Errorf("Error: %v\n", err)
return err
}
I believe once the cobra PR to check for unknown command gets merged, we can refactor this codebase to an extent to make it better.
cc @agaudreault |
Update: There's a little issue related to flag parsing that needs to be worked upon. Rn, all the global level flags won't be parsed when you are running your plugin. This is happening because cobra internally doesn't parse the flags when a command is not found. This also means that if a user has set its own flags in a binary that matches with Argo CD global flags, the default value of the Argo CD global flags will be used no matter what (why? because flag parsing never happens by cobra for an unknown command). For normal Argo CD commands, the flag parsing and everything else remains same as this is how it used to be. What should be a better approach to handle this? Should we add this in a limitation section and tell users about not to use duplicate flags as argocd global flags? Do we even want a user to set it's own flags that overrides argocd global flag values?
|
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 should be a better approach to handle this? Should we add this in a limitation section and tell users about not to use duplicate flags as argocd global flags? Do we even want a user to set it's own flags that overrides argocd global flag values?
@nitishfy In my view, we need to guarantee to plugin writers 2 capabilities:
- The plugin doesn't have to authenticate with Argo CD API. Plugin authors can directly invoke the Argo CD API and the current context will be used.
- While some global flags don't make sense to be used with plugins, some others do:
--argocd-context
,--auth-token
,--logformat
,--loglevel
are some (but not the only ones) that makes sense to be available when executing plugins. However I don't think that it is the plugin responsibility to handle those flags. We would need to pre-process the flags before invoking the plugin itself.
That being said, it seems that there is more work required in order to get the plugin feature ready. However I am not opposed if other maintainers are ok with moving forward clearly documenting the current limitations and marking the feature as alpha.
Could we not have a dummy (no-op) command for the error path? We invoke the dummy command that will parse the flags, specifically the flags we would like to still keep (we can use the |
Ref: #19624
Checklist: