Skip to content

V1alpha2 api - scorecard output changes for v1alpha2 version #1995

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

Merged
merged 34 commits into from
Oct 16, 2019
Merged

V1alpha2 api - scorecard output changes for v1alpha2 version #1995

merged 34 commits into from
Oct 16, 2019

Conversation

jmccormick2001
Copy link
Contributor

Description of the change:

This changes adds a new output format to the scorecard subcommand when you specify
a version=v1alpha2 in the configuration or command line. The following shows the v1alpha1 output on the left and the v1alpha2 format on the right:

Screenshot from 2019-09-30 13-53-02

Motivation for the change:

These changes are part of the larger effort of creating a new v1alpha2 format for the
scorecard. Additional work remains and would be added in with separate PRs. This
PR mostly impacts the output format that an end user sees in the text format along with
changes to the json format specifically for v1alpha2.

@openshift-ci-robot openshift-ci-robot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Sep 30, 2019
@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-helm

Copy link
Member

@joelanford joelanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some initial comments/questions. Looking forward to trying this out.

fmt.Printf("\t%-35s: ", result.Name)

if result.State == scapiv1alpha1.PassState {
fmt.Printf(passColor, scapiv1alpha1.PassState)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to check if the process is running in a terminal before using color. That way it'll be easier to read in CI if using text-formatted output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, will add that new dependency and the code looks simple enough to add a check for a terminal, if no terminal is found then no colorization will be done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a check for the terminal, if its not a terminal then no colorization is performed.

passColor = "\033[1;32m%s\033[0m\n"
)

func printPluginOutputs(pluginOutputs []scapiv1alpha1.ScorecardOutput) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is it possible/would it make sense to declare an interface for output formats that has separate implementations in thev1alpha1 and v1alpha2 packages (possibly by ScorecardOutputList) so that they are decoupled (making it easier to remove v1alpha1 later). Maybe something like:

type ScorecardFormatter interface {
    MarshalText() (string, error)
    MarshalJSON() ([]byte, error)
}

Then in the main scorecard runner function, we could do all the version and format checks and make the appropriate conversions and format the outputs. That way we can limit the places we have logic that cares about format and version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a shot at this today, creating the interface and the v1/v2 implementations of that interface....seems better in the long term for sure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize, but looking at your new implementation (which looks great!), I realize that the ScorecardOutputList seems to be for a list of separate scorecard runs? If so, I don't think we have a use case for that type right now (we could probably remove it from v1alpha2).

I was envisioning that whatever type captures the results of a single run would be the one that implements this interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I was wondering about the usefulness of the OutputList, I couldn't see it being used anywhere but was unsure if that was there for some specific future purpose. I'll remove it today and replace it with the ScorecardOutput type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed in the latest commit.

@@ -139,6 +139,15 @@ func UpdateState(res scapiv1alpha1.ScorecardTestResult) scapiv1alpha1.ScorecardT
if res.State == scapiv1alpha1.ErrorState {
return res
}

if IsV1alpha2() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move this logic to the ConvertTestResultV1ToV2 function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, will move it.


const (
// UnsetState is the default state for a TestResult. It must be updated by UpdateState or by the Test.
UnsetState State = "unset"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the need for this to be "unset" vs. just "", but I see it was that way in v1alpha1. Maybe worth changing since "" is the default value of a string?

Do we use UnsetState?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove that from v1alpha2, I could not find it being used anywhere either.


// ScorecardSuiteResult contains the combined results of a suite of tests.
// +k8s:openapi-gen=true
type ScorecardSuiteResult struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to continue with the idea of a suite in v1alpha2 (e.g. "Basic", "OLM", etc.), or would a flat list of tests be sufficient given that we're planning to introduce labels for test selection, which would support a suite: "olm" label, for example?

@dmesser thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To expand on this, I feel like v1alpha2 types are probably more complex than is really necessary.

At the end of the day, what I think we want to output is:

  1. A list of test results
  2. The logs from the overall run, if failure or verbose or JSON (which I think is similar to how go test behaves).

I think for each test result we would output:

  1. Name
  2. State (pass/fail/error)
  3. Description, if verbose or JSON
  4. Labels, if verbose or JSON (I know we're not to the label work yet, but it might be good to go ahead and add this to the type)
  5. Errors, if any
  6. Suggestions, if any

Copy link
Contributor

@dmesser dmesser Oct 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would also need to capture the overall outcome of the scorecard run. Is it overall failed vs. pass etc. Otherwise agree with @joelanford

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to hold off on doing any label work in this PR since its pretty large already, I've added the overall outcome to the v1alpha2 api based on this comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmccormick2001 Since we're making v1alpha2 the default as of this PR, I think we should try to get it's type definition right, so we don't have to go back and change it when we get to the rest of the labels features. I'd suggest doing three things in this PR:

  1. Add a labels field to v1alpha2.ScorecardTestResult as a placeholder (we won't populate them yet, however)
  2. Remove the suite layer from v1alpha2 entirely
  3. Make sure the JSON output format spec contains everything we'll need to finish the epic. If we aren't confident we can do this piece yet, I would hesitate to make v1alpha2 the default.

@dmesser What do you think is sufficient to capture the overall output of the scorecard run? Do you think the log and a simple pass/fail/error state field? Anything else?

Basically, would we be alright with a top-level output struct that contains?

type ScorecardOutput struct {
    Results []ScorecardTestResult
    State string // pass, fail, or error
    Log string
}

type ScorecardTestResult struct {
    Name string
    State string
    Description string
    Labels map[string]string
    Errors []error
    Suggestions []string
}

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, I was going to add the labels field in the upcoming label story work but could do that in this PR if you want. I think the notion of the 'suite' was not clear in my mind how it would be implemented or if it totally removed as a concept, but rather it just becomes another label in the map that is populated.

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-subcommand

1 similar comment
@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-subcommand

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-ansible
/test e2e-aws-helm
/test e2e-aws-subcommand

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-helm
/test e2e-aws-ansible
/test e2e-aws-subcommand

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-ansible
/test e2e-aws-helm
/test e2e-aws-subcommand

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-subcommand

Copy link
Member

@joelanford joelanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more nits :)

CHANGELOG.md Outdated
@@ -51,6 +52,7 @@
- [`pkg/test.FrameworkClient`](https://github.com/operator-framework/operator-sdk/blob/master/pkg/test/client.go#L33) methods `List()` and `Delete()` have new signatures corresponding to the homonymous methods of `sigs.k8s.io/controller-runtime/pkg/client.Client`. ([#1876](https://github.com/operator-framework/operator-sdk/pull/1876))
- CRD file names were previously of the form `<group>_<version>_<kind>_crd.yaml`. Now that CRD manifest `spec.version` is deprecated in favor of `spec.versions`, i.e. multiple versions can be specified in one CRD, CRD file names have the form `<full group>_<resource>_crd.yaml`. `<full group>` is the full group name of your CRD while `<group>` is the last subdomain of `<full group>`, ex. `foo.bar.com` vs `foo`. `<resource>` is the plural lower-case CRD Kind found at `spec.names.plural`. ([#1876](https://github.com/operator-framework/operator-sdk/pull/1876))
- Upgrade Python version from `2.7` to `3.6`, Ansible version from `2.8.0` to `~=2.8` and ansible-runner from `1.2` to `1.3.4` in the Ansible based images. ([#1947](https://github.com/operator-framework/operator-sdk/pull/1947))
- Made the default scorecard version v1alpha2 which is new for this release and could break users that were parsing the older scorecard output (v1alpha1). Users can still specify version v1alpha1 on the scorecard configuration to use the older style for some period of time till v1alpha1 is removed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Made the default scorecard version v1alpha2 which is new for this release and could break users that were parsing the older scorecard output (v1alpha1). Users can still specify version v1alpha1 on the scorecard configuration to use the older style for some period of time till v1alpha1 is removed.
- Made the default scorecard version `v1alpha2` which is new for this release and could break users that were parsing the older scorecard output (`v1alpha1`). Users can still specify version `v1alpha1` on the scorecard configuration to use the older style for some period of time until `v1alpha1` is removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated with your suggested change.

failCount=`echo $commandoutput | grep -o ": fail" | wc -l`
if [ $failCount -ne 7 ]
then
echo "expected fail count not equal to output"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: define $expectedFailCount and improve the failure text a bit?

Suggested change
echo "expected fail count not equal to output"
echo "expected fail count $expectedFailCount, got $failCount"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, made the change.

func IsLatestVersion() bool {
if viper.Sub("scorecard").GetString(VersionOpt) == LatestScorecardVersion {
func IsV1alpha2(version string) bool {
if version == v1alpha2 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This could be simplified to return version == v1alpha2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, made the change.

}

// produce text output
if scViper.GetString(OutputFormatOpt) == TextOutputFormat {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is out of scope for this PR, but I'm not a huge fan of the global variables used throughout scorecard (e.g. scViper) because they cause non-obvious side effects if you're looking at the call site of the functions that use the global variables.

In a follow-on, I think it would be worth it to refactor things so that viper is used only in the cmd/operator-sdk/scorecard package to parse the config from flags, env, config files, etc. and then populate a scorecard struct with the resulting config, so that we don't have to constantly interrogate a global viper instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, I could write up a new Jira story for refactoring that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@jmccormick2001 jmccormick2001 Oct 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://jira.coreos.com/browse/OSDK-587
another one that falls into the refactoring camp that would be an improvement.

@jmccormick2001
Copy link
Contributor Author

@joelanford @dmesser based on the comments, I'll be removing the suite layer from the v1alpha2 API and including that set of changes into this PR.

@jmccormick2001
Copy link
Contributor Author

/retest

Copy link
Member

@joelanford joelanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully last round of changes. :)

In a follow-up, I think we'll want to re-work the scorecard config file format a bit to handle:

  1. Always requiring a bundle directory and pulling required manifests from it.
  2. Probably removing the duplication between the basic and olm configs since they're already basically identical.
  3. Fail if external plugins are defined for v1alpha2 since the longer term plan is to support Job-based tests provided in the bundle.

Ideally this follow-up would happen before we release v0.12.0 so we don't have a release that has the partial v1alpha2 changes.

Comment on lines 31 to 34
// RequiredTestsPassed occurs when all required tests pass
RequiredTestsPassedState State = "All required tests passed."
// RequiredTestsFailed occurs when one of the required tests fails
RequiredTestsFailedState State = "A required test has failed."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we're not planning any special handling for required tests (right?), I think we can remove these and the code that uses them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://jira.coreos.com/browse/OSDK-607

added that story to capture reworking the config file format for v1alpha2

Comment on lines 43 to 47
requiredTestStatus := fmt.Sprintf(passColor, RequiredTestsPassedState)

if s.FailedRequiredTests > 0 {
requiredTestStatus = fmt.Sprintf(failColor, RequiredTestsFailedState)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we remove this as well? Since we plan to have the exit code reflect the state of pass/fail for the entire run, I'm not sure we still need a message at the end to that effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

Comment on lines 49 to 78
var currentSuite string
for _, result := range s.Results {
if currentSuite != result.Labels["suite"] {
sb.WriteString(fmt.Sprintf("%s:\n", result.Labels["suite"]))
currentSuite = result.Labels["suite"]
}
sb.WriteString(fmt.Sprintf("\t%-35s: ", result.Name))

if result.State == PassState {
sb.WriteString(fmt.Sprintf(passColor, PassState))
} else {
sb.WriteString(fmt.Sprintf(failColor, FailState))
}
}

sb.WriteString(fmt.Sprintf(requiredTestStatus))

for _, result := range s.Results {
for _, suggestion := range result.Suggestions {
// 33 is yellow (specifically, the same shade of yellow that logrus uses for warnings)
sb.WriteString(fmt.Sprintf("\x1b[%dmSUGGESTION:\x1b[0m %s\n", 33, suggestion))
}
}

for _, result := range s.Results {
for _, err := range result.Errors {
// 31 is red (specifically, the same shade of red that logrus uses for errors)
sb.WriteString(fmt.Sprintf("\x1b[%dmERROR:\x1b[0m %s\n", 31, err))
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about going ahead and implementing the non-verbose text output I suggested here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mind if I do that in a separate story?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that should be fine. Ideally we'll have the output formats done before our next release so we don't have to change them. I think it's probably okay to change the text format since it's meant for humans.

JSON is the one we definitely want to get right before the next release since that's meant to be machine-readable and changes might break pipelines. I think JSON is good to go, but I'll give it one more run with these latest changes to confirm.

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-ansible

2 similar comments
@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-ansible

@jmccormick2001
Copy link
Contributor Author

/test e2e-aws-ansible

@@ -20,6 +20,7 @@
- Ansible based operators now gather and serve metrics about each custom resource on port 8686 of the metrics service. ([#1723](https://github.com/operator-framework/operator-sdk/pull/1723))
- Added the Go version, OS, and architecture to the output of `operator-sdk version` ([#1863](https://github.com/operator-framework/operator-sdk/pull/1863))
- Added support for `ppc64le-linux` for the `operator-sdk` binary and the Helm operator base image. ([#1533](https://github.com/operator-framework/operator-sdk/pull/1533))
- Added new `--version` flag to the `operator-sdk scorecard` command to support a new output format for the scorecard. ([#1916](https://github.com/operator-framework/operator-sdk/pull/1916)
Copy link
Contributor

@camilamacedo86 camilamacedo86 Oct 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked that k8s uses the nomenclature --experimental-x for this kind of impl. WDYT about we adopted the same? As for example --experimental-version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's meant more for enabling or configuring experimental features. In this case, this will control core features of the scorecard (output formats, support for external plugins, exit code handling, label selection, etc.).

So I think we should leave --version as the flag name.

Copy link
Member

@joelanford joelanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

In the follow-on when we're working on labels, I'd suggest changing the suite labels from "Basic Tests" and "OLM Tests" to "basic" and "olm"

@jmccormick2001 jmccormick2001 merged commit b964dd6 into operator-framework:master Oct 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants