-
Notifications
You must be signed in to change notification settings - Fork 1.8k
internal/pkg/scorecard: implement new scorecard config system #1641
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
internal/pkg/scorecard: implement new scorecard config system #1641
Conversation
Based on ./doc/proposals/improved-scorecard-config.md
|
Documentation PR is now open: #1648 |
Co-Authored-By: Ish Shah <ish@ishshah.me>
theishshah
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, let's wait for another set of eyes before merging
estroz
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.
Looks good on a first pass. A few questions and suggestions.
| if err != nil { | ||
| return err | ||
| } | ||
| log.Warn("Could not load config file; using flags") |
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.
Now users always need a config file? Can they not run only their plugins with global CLI args? If a config is always required, mark --config as required.
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 quite sure. It might make sense to make a slight behavior change here. Currently, if you don't provide a config file, the scorecard will attempt to run all plugins. It will fail for the olm and basic plugins (as it did before due to missing cr-manifest; I intentionally kept that behavior for this PR), but for external plugins that don't need extra configuration it should work.
It might make sense to run all external plugins without configuration and ignore the internal plugins if a config file is not provided, but I feel like that might kinda hide the internal plugins from the users a bit, which is not ideal. This might need further discussion.
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.
IMO we should run only external plugins if no configuration file is provided, but log an info statement before doing so. Something similar to:
No internal plugin configuration detected. Skipping Basic and OLM Plugins and running all external plugins.
@joelanford @hasbro17 @lilic @shawn-hurley @theishshah WDYT about handling this case?
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.
Yeah, I think that skipping them and logging an info statement would be a better behavior
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 seems like the options are:
- Skip internal plugins and log a message that they were skipped
- Fail/do nothing without a config file. Don't even run external plugins.
- If we don't find a config file, initialize one and attempt to use sane defaults, even for the internal plugins. Would this be possible using CR manifests from the
deploy/crdsdirectory? Perhaps we need full support for multiple CRs first?
I kind of prefer option 2 to force the user to be explicit about what to run. Or option 3 if we think the defaulting is possible/sane.
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.
AFAIK option 3 could work only if we assume all CR's in deploy/crds that also have accompanying CRD's should be tested, which is a bad idea. I agree we need to be explicit about what is being run.
Users should still be able to run internal plugins without a config, as they can do now, which is why I'm against option 2 and in favor of 1.
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.
AFAIK option 3 could work only if we assume all CR's in deploy/crds that also have accompanying CRD's should be tested, which is a bad idea. I agree we need to be explicit about what is being run.
That's a good point. I know we don't do this anywhere else, but I know kubebuilder does - we could use interactive prompts in that case to have the user answer yes/no questions about including a CR in the config file. I'm not personally sold on that, but just throwing it out there.
Users should still be able to run internal plugins without a config, as they can do now, which is why I'm against option 2 and in favor of 1.
With option 1, aren't we saying that a config file has to be present to run internal plugins? Otherwise, they'll be skipped, a message will be logged, and the results will not contain anything about them?
For interactive use cases, I think option 1 is fine. But for non-interactive use cases, it seems like that message will get buried and results that don't include the basic and OLM tests may be misleading. I guess it depends on how the thing interpreting the JSON results is implemented.
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.
Yeah I'm backtracking on my opinion of option 1. To align our UX I'm now thinking we should go with option 2, and scaffold a scorecard config when running operator-sdk new.
695c7d9 to
c089232
Compare
internal/pkg/scorecard/interfaces.go
Outdated
| description := fmt.Sprintf("Plugin with file name `%s` failed", filepath.Base(p.binPath)) | ||
| if err := os.Chdir(filepath.Join(rootDir, scViper.GetString(PluginDirOpt))); err != nil { | ||
| name := fmt.Sprintf("Failed Plugin: %s", p.name) | ||
| description := fmt.Sprintf("Plugin with file name `%s` failed", filepath.Base(p.config.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.
Probably out of scope for this PR since we're not changing it here, but it seems odd to me that we're changing the name and description of the test based on the test outcome.
It seems like those should always stay the same, since there are other indicators of the outcome (e.g. "error": 1 and "log": "failed to chdir into scorecard plugin directory...")
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 a new problem since we define a name for the plugins with this config design. However, the plugin also returns a name field, and in a successful run, we return the name that the plugin gave us, not the name from the config. This is a bit confusing, because this means we only ever use the config provided name during a failure, where we specify the name. I'm not quite sure how we should handle the name specified in the config file, or if we should even have one at all 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.
Hmm that's an interesting question. These end up being the suite names right? So going through various options:
- Plugin provides name in output, config name field is removed
- Plugin fails to execute or does not return name in output => use basename of plugin executable
- Plugin returns name in output => use name provided by plugin
- Plugin provides name, but can be overridden by config
- Plugin fails to execute or does not return name in output => use config name if present, fallback to basename of plugin executable
- Plugin returns name in output => use config name if present, fallback to name provided by plugin
- Plugin name is always basename of plugin executable
- Plugin name is always provided by config
None of these are perfect IMO.
Option 1 gives control to the plugin author. Plugin authors could do something weird, but generally I'd imagine this would be static or based on flags/input. This property is nice because shared plugins will generally result in the same name when run against different projects, which makes it easier to compare apples-to-apples.
Option 2 is probably my least favorite option, since its the \_(ツ)_/ of the group.
Option 3 and 4 are nice because duplicates can be found without executing the plugins, and they're very straightforward. Not sure if/how we handle duplicate names? Con: the same plugin might be renamed or configured with a different name, making comparison across projects a no-go.
I think I lean towards option 1. Thoughts? Any other options I'm not thinking of?
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.
A plugin could return multiple suites. This would mean that there would be multiple names (each suite has a name), so changing the name(s) of the suite using the scorecard config can get messy.
Option 1 is good, but there is the issue that people may want to run the same plugin with different arguments or environment. If we only print the command's basename, a user may have difficulty identifying which test failed. By also providing the name that a user provided in their config, it makes it a lot easier to identify which plugin/configuration failed.
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 could go with option 1 and append an md5 digest of all arguments/environment data to the plugin name, so you can run the same plugin with different configurations.
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.
A plugin could return multiple suites. This would mean that there would be multiple names (each suite has a name), so changing the name(s) of the suite using the scorecard config can get messy.
Maybe having a plugin return multiple suites is worth revisiting in scorecard 2.0. In the current output, is it possible to know which suites came from which plugin? If plugins ran only one suite, the plugin name would be the suite name, which seems like a decent way to handle plugins.
Either way, that isn't the case now. So in the meantime, it sounds like we're in agreement on not allowing user-overrideable test names?
Option 1 is good, but there is the issue that people may want to run the same plugin with different arguments or environment. If we only print the command's basename, a user may have difficulty identifying which test failed. By also providing the name that a user provided in their config, it makes it a lot easier to identify which plugin/configuration failed.
True, but it sounds like the only time the basename would be used is when the plugin fails. In that case, can we populate the log output with the full path, args, env of the failed plugin, along with whatever the stdout/err was.
| } | ||
|
|
||
| type internalPlugin struct { | ||
| type basicOrOLMPlugin struct { |
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.
Also probably out of scope for this PR since the API is what matters most, but did you consider defining separate basicPlugin and olmPlugin structs such that each could have its own Run() implementation?
It might be cleaner to extract the shared code from basicOrOLMPlugin.Run() and plugins.RunInternalPlugin into separate functions or packages so that we wouldn't have to support both plugin types with one 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.
Yeah, we discussed separating the Run functions in another PR. They are very similar and would only differ by about 5-10 lines out of a very large function. They also have all the same configuration fields (and they both use all of them except for csv-path in the basic plugins). Splitting up the Run function into multiple smaller functions would be good to make it clearer as to what's actually going on in the function and is something we definitely should do, but we still would essentially be running the same functions for both plugins.
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 isn't to say that no future internal plugins would be separate. For the proposed YAML-defined scorecard tests, we would have a completely separate Run: #1241
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 right. Sorry I'm harping on it 🙂
They also have all the same configuration fields (and they both use all of them except for csv-path in the basic plugins)
For this we could use struct embedding where the common fields go in a basicPlugin struct and an olmPlugin struct embeds basicPlugin and adds csv-path.
Splitting up the Run function into multiple smaller functions would be good to make it clearer as to what's actually going on in the function and is something we definitely should do
Yeah that's pretty much what I'm suggesting.
but we still would essentially be running the same functions for both plugins.
Which is totally fine. I'm basically saying that code that runs based on a pluginType conditional could be extracted into the plugin's run function, and all other code could be shared in smaller split-out functions.
And again, probably not in scope for this PR, but something like this would be good to do in a follow-up depending on direction from CVP requirements.
d1c494d to
39f08b7
Compare
|
/test e2e-aws-subcommand |
|
/test e2e-aws-go |
joelanford
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.
Looks pretty close. Hopefully just the one last question.
estroz
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.
Needs a changelog entry, otherwise LGTM.
Also there should be a follow-up PR that scaffolds a scorecard config on operator-sdk new as per #1641 (comment).
joelanford
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.
Agree with @estroz. LGTM after CHANGELOG update.
|
Updated CHANGELOG. Will work on updating the docs PR now (#1648) |
|
/test e2e-aws-ansible |
1 similar comment
|
/test e2e-aws-ansible |
|
/retest |
|
/test e2e-aws-ansible |
|
/retest |
Description of the change: Implement new file based configuration system for the operator-sdk scorecard as detailed in the proposal made via PR #1558. It is a large PR, but a large majority of the new/changed lines are from 3 new scorecard subcommand tests and the related JSON assets and configs for the tests, as well as replacing all usages of
viperin the internal plugins to a config struct built by unmarshalling the user provided configs. Documentation will be added in a separate PR that should be ready soonMotivation for the change: Implement PR #1558