-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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 plugin system #1379
internal/pkg/scorecard: implement plugin system #1379
Conversation
bea1688
to
014b2d6
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.
LGTM, I would suggest completing the helper function method in the TODO
internal/pkg/scorecard/scorecard.go
Outdated
} | ||
for _, file := range files { | ||
cmd := exec.Command("bash", "-c", "./bin/"+file.Name()) | ||
output, err := cmd.CombinedOutput() |
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.
In this case, it might make sense to capture stdout and stderr separately. We'd parse the stdout as JSON and maybe include the stderr in a log?
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.
Where exactly would we put the log? We provide a per-suite log field for plugins to use so they shouldn't be outputting anything to stdout or stderr except the JSON or potentially fatal errors. I assume we could add it into the main scorecard log (in the final ScorecardOutput
), but that could get messy pretty quickly
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 that's a good question. Wdyt of checking if result.Log
is empty. If so, set result.Log
to the stderr buffer. If not, log a warning that would be seen in the human-readable output?
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.
That sounds reasonable. I'll do that.
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.
Actually, there's a problem with that. A single plugin can have more than 1 suite. If that's the case, we won't be able to simply add the stderr to a log field.
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 way I'll handle this for now is using log.Warn
for the stderr output, which will print it nicely in the human-readable output or add it to the main log for the json output.
Co-Authored-By: AlexNPavel <[email protected]>
for _, suite := range suites { | ||
for idx, res := range suite.TestResults { | ||
suite.TestResults[idx] = UpdateState(res) | ||
for _, suite := range pluginOutputs { |
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 for inside another shows not be the best solution. Note the Big O here. Could it not cause performance issues? Could it not be improved to achieve a better performance?
numSuites := 0 | ||
for _, plugin := range pluginOutputs { | ||
for _, suite := range plugin.Results { | ||
fmt.Printf("%s:\n", suite.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.
The same point of attention in the impl. The complexity of this impl.
for _, suite := range plugin.Results { | ||
for _, result := range suite.Tests { | ||
for _, suggestion := range result.Suggestions { | ||
// 33 is yellow (specifically, the same shade of yellow that logrus uses for warnings) |
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 loop the inside of another loop 4 times. Is not possible to solve this need in a more performative way?
for _, err := range result.Errors { | ||
// 31 is red (specifically, the same shade of red that logrus uses for errors) | ||
fmt.Printf("\x1b[%dmERROR:\x1b[0m %s\n", 31, err) | ||
for _, plugin := range pluginOutputs { |
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 loop the inside of another loop 4 times. Is not possible to solve this need in a more performative way?
@camilamacedo86 BigO for these loops is irrelevant to the runtime of the scorecard. Much of the time spent in the scorecard will be on the actual plugins themselves (which includes the Basic and OLM tests), which will likely be waiting on networking. The plugins should be optimized if they are compute heavy. However, the time spent in the loops you mentioned will be on the order of milliseconds (excluding the print syscall time, which we would still have) even for runs with a lot of output and plugins. Those extra couple of milliseconds spent in the loops are irrelevant to the overall runtime of the scorecard and any optimizations there would sacrifice clarity. |
IMHO shows that it could be changed/improved. Now it shows irrelevant, however, in it could grow and the issues started to be faced. The CI tests take too long already. Also, could not the code be more understandable and in this way easier to keep maintained and be improved without these loops inside of loops? Note that the following exactly the same loop inside of a loop is made many times. for _, plugin := range pluginOutputs {
for _, suite := range plugin.Results { You make a great work here, it is just a suggestion. Please, feel free to move forward as you decide |
I think that if we want to make this better with fewer loops we would need to redesign the structs. I think that something we could do in this PR or as a follow up to make this better is:
If that is too much, at the very least lets combine the two different for loops, by creating the output in two string buffers and then combining.
/cc @camilamacedo86 @AlexNPavel BTW I personally feel this is acceptable to do in a follow-up. |
@shawn-hurley: GitHub didn't allow me to request PR reviews from the following users: camilamacedo86, AlexNPavel. Note that only operator-framework members and repo collaborators can review this PR, and authors cannot review their own PRs. In response to this:
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. |
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.
👍 on @shawn-hurley's comment about further improvements. I also think we could address them in a follow-on PR.
A few more nits and comments.
@@ -147,7 +135,7 @@ func TestResultToScorecardTestResult(tr TestResult) scapiv1alpha1.ScorecardTestR | |||
} | |||
|
|||
// UpdateState updates the state of a TestResult. | |||
func UpdateState(res TestResult) TestResult { | |||
func UpdateState(res scapiv1alpha1.ScorecardTestResult) scapiv1alpha1.ScorecardTestResult { |
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 have a handful of exported helper functions for the scapiv1alpha1
types in this package. I'd suggest moving them into the scapiv1alpha1
package, possibly making them methods on the scapiv1alpha1
structs (where that makes sense), and also possibly unexporting them if we think they'll only be used on the SDK side of the plugin interface.
This could be in a follow-on PR. Since we're going to reorganize our built-in scorecard tests as plugins, I'd be interested in your thoughts on a design for a plugin library that would make it easy to write a plugin in go.
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'll make a PR to expose some of these. I was initially intending to expose some of this with the JSON PR, but it wasn't quite ready. Now that we have the plugin support in place, we can probably expose the helpers.
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
suite.PartialPass++ | ||
case scapiv1alpha1.FailState: | ||
suite.Fail++ | ||
} |
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 should probably have a default case to catch and log any unknown state.
Even though it doesn't seem like we'll end up in an unknown state, it's still good practice in case we change it in the future.
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'll do this in a future PR. The issue is that there is no way to really handle this cleanly right now. One way to fix this would be to add another field to the SuiteResults of invalid
. That way we can indicate to users that a plugin returned an invalid state. Another option (which is what I want to do in a future PR) is to change the state from whatever incorrect state it was in to error
and add a string to the errors array indicating that the scorecard marked this test as failed due to an invalid state.
Technically though, due to the way the helpers are set up (we calculate the state using the earned vs max points unless the state is error), the only situation where this could occur (currently) would be if the earned points are higher than the max. I'd be fine marking that as an error.
Looks good overall. Just some small nits. |
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.
One last nit: We should update the CLI reference guide for the flags. But you can leave that out unless you have a separate PR for doc changes.
@hasbro17 I am going to make a PR with all the doc changes today |
Description of the change: Implement scorecard plugins
Motivation for the change: This PR implements support for scorecard plugins as described in this proposal: https://github.com/operator-framework/operator-sdk/blob/master/doc/proposals/scorecard-plugin-system.md
The Basic and OLM test suites will be split out into separate plugins after this PR is merged.