Skip to content

Commit

Permalink
scorecard: use TestList for output, exit 1 on test failure (#3427)
Browse files Browse the repository at this point in the history
  • Loading branch information
joelanford authored Jul 15, 2020
1 parent fe330c1 commit 4d8960c
Show file tree
Hide file tree
Showing 19 changed files with 307 additions and 248 deletions.
15 changes: 15 additions & 0 deletions changelog/fragments/scorecard-output-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# entries is a list of entries to include in
# release notes and/or the migration guide
entries:
- description: >
Changed scorecard text and json output to use a `v1alpha3.TestList`
instead of aggregating all test results under a single
`v1alpha3.Test` and set exit status to 1 when a test fails.
kind: "change"
breaking: true
migration:
header: Alpha scorecard output API updates
body: |
Update any scripts interpretting the scorecard output to
understand the v1alpha3 TestList format.
43 changes: 27 additions & 16 deletions cmd/operator-sdk/alpha/scorecard/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,20 @@ If the argument holds an image tag, it must be present remotely.`,
return scorecardCmd
}

func (c *scorecardCmd) printOutput(output v1alpha3.Test) error {
func (c *scorecardCmd) printOutput(output v1alpha3.TestList) error {
switch c.outputFormat {
case "text":
if len(output.Status.Results) == 0 {
if len(output.Items) == 0 {
fmt.Println("0 tests selected")
return nil
}
o, err := output.MarshalText()
if err != nil {
fmt.Println(err.Error())
return err
for _, test := range output.Items {
fmt.Println(test.MarshalText())
}
fmt.Printf("%s\n", o)
case "json":
bytes, err := json.MarshalIndent(output, "", " ")
if err != nil {
fmt.Println(err.Error())
return err
return fmt.Errorf("marshal json error: %v", err)
}
fmt.Printf("%s\n", string(bytes))
default:
Expand Down Expand Up @@ -154,12 +150,9 @@ func (c *scorecardCmd) run() (err error) {
return fmt.Errorf("could not parse selector %w", err)
}

var scorecardTest v1alpha3.Test
var scorecardTests v1alpha3.TestList
if c.list {
scorecardTest, err = o.ListTests()
if err != nil {
return fmt.Errorf("error listing tests %w", err)
}
scorecardTests = o.List()
} else {
runner := scorecard.PodTestRunner{
ServiceAccount: c.serviceAccount,
Expand All @@ -178,13 +171,31 @@ func (c *scorecardCmd) run() (err error) {
ctx, cancel := context.WithTimeout(context.Background(), c.waitTime)
defer cancel()

scorecardTest, err = o.RunTests(ctx)
scorecardTests, err = o.Run(ctx)
if err != nil {
return fmt.Errorf("error running tests %w", err)
}
}

return c.printOutput(scorecardTest)
if err := c.printOutput(scorecardTests); err != nil {
log.Fatal(err)
}

if hasFailingTest(scorecardTests) {
os.Exit(1)
}
return nil
}

func hasFailingTest(list v1alpha3.TestList) bool {
for _, t := range list.Items {
for _, r := range t.Status.Results {
if r.State != v1alpha3.PassState {
return true
}
}
}
return false
}

func (c *scorecardCmd) validate(args []string) error {
Expand Down
12 changes: 6 additions & 6 deletions internal/scorecard/alpha/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ const (
)

type Test struct {
Name string `yaml:"name"` // The container test name
Image string `yaml:"image"` // The container image name
// An list of commands and arguments passed to the test image
Entrypoint []string `yaml:"entrypoint,omitempty"`
Labels map[string]string `yaml:"labels"` // User defined labels used to filter tests
Description string `yaml:"description"` // User readable test description
// Image is the name of the testimage
Image string `json:"image"`
// Entrypoint is list of commands and arguments passed to the test image
Entrypoint []string `json:"entrypoint,omitempty"`
// Labels that further describe the test and enable selection
Labels map[string]string `json:"labels,omitempty"`
}

// Config represents the set of test configurations which scorecard
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
tests:
- name: "customtest1"
image: quay.io/username/custom-scorecard-tests:dev
entrypoint:
- image: quay.io/username/custom-scorecard-tests:dev
entrypoint:
- custom-scorecard-tests
- customtest1
labels:
suite: custom
test: customtest1
description: an ISV custom test
- name: "customtest2"
entrypoint:
- image: quay.io/username/custom-scorecard-tests:dev
entrypoint:
- custom-scorecard-tests
- customtest2
image: quay.io/username/custom-scorecard-tests:dev
labels:
suite: custom
test: customtest2
description: an ISV custom test
38 changes: 16 additions & 22 deletions internal/scorecard/alpha/formatting.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,41 @@ import (
"context"
"encoding/json"

"github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha3"
v1 "k8s.io/api/core/v1"

"github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha3"
)

// getTestResult fetches the test pod log and converts it into
// Test format
func (r PodTestRunner) getTestStatus(ctx context.Context, p *v1.Pod, test Test) (output *v1alpha3.TestStatus) {

func (r PodTestRunner) getTestStatus(ctx context.Context, p *v1.Pod) (output *v1alpha3.TestStatus) {
logBytes, err := getPodLog(ctx, r.Client, p)
if err != nil {
return testStatusError(err, test)
return convertErrorToStatus(err, string(logBytes))
}
// marshal pod log into TestResult
err = json.Unmarshal(logBytes, &output)
if err != nil {
return testStatusError(err, test)
return convertErrorToStatus(err, string(logBytes))
}
return output
}

// ListTests lists the scorecard tests as configured that would be
// List lists the scorecard tests as configured that would be
// run based on user selection
func (o Scorecard) ListTests() (output v1alpha3.Test, err error) {
func (o Scorecard) List() v1alpha3.TestList {
output := v1alpha3.NewTestList()
tests := o.selectTests()
if len(tests) == 0 {
return output, err
}

for _, test := range tests {
output.Status.Results = append(output.Status.Results, v1alpha3.TestResult{Name: test.Name})
item := v1alpha3.NewTest()
item.Spec = v1alpha3.TestSpec{
Image: test.Image,
Entrypoint: test.Entrypoint,
Labels: test.Labels,
}
output.Items = append(output.Items, item)
}

return output, err
}

func testStatusError(err error, test Test) *v1alpha3.TestStatus {
r := v1alpha3.TestResult{}
r.Name = test.Name
r.State = v1alpha3.FailState
r.Errors = []string{err.Error()}
return &v1alpha3.TestStatus{
Results: []v1alpha3.TestResult{r},
}
return output
}
17 changes: 3 additions & 14 deletions internal/scorecard/alpha/formatting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"path/filepath"
"testing"

"github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha3"
"k8s.io/apimachinery/pkg/labels"
)

Expand All @@ -27,10 +26,9 @@ func TestList(t *testing.T) {
cases := []struct {
bundlePathValue string
selector string
wantError bool
resultCount int
}{
{"testdata/bundle", "suite=basic", false, 1},
{"testdata/bundle", "suite=basic", 1},
}

for _, c := range cases {
Expand All @@ -50,17 +48,8 @@ func TestList(t *testing.T) {
}
runner.BundlePath = c.bundlePathValue
o.TestRunner = &runner
var output v1alpha3.Test
output, err = o.ListTests()
if err == nil && c.wantError {
t.Fatalf("Wanted error but got no error")
} else if err != nil {
if !c.wantError {
t.Fatalf("Wanted result but got error: %v", err)
}
return
}
actualResultCount := len(output.Status.Results)
output := o.List()
actualResultCount := len(output.Items)
if c.resultCount != actualResultCount {
t.Fatalf("Wanted result count %d but got : %d", c.resultCount, actualResultCount)
}
Expand Down
30 changes: 8 additions & 22 deletions internal/scorecard/alpha/labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,64 +66,50 @@ func TestEmptySelector(t *testing.T) {
}

const testConfig = `tests:
- name: "customtest1"
image: quay.io/someuser/customtest1:v0.0.1
- image: quay.io/someuser/customtest1:v0.0.1
entrypoint:
- custom-test
labels:
suite: custom
test: customtest1
description: an ISV custom test that does...
- name: "customtest2"
image: quay.io/someuser/customtest2:v0.0.1
- image: quay.io/someuser/customtest2:v0.0.1
entrypoint:
- custom-test
labels:
suite: custom
test: customtest2
description: an ISV custom test that does...
- name: "basic-check-spec"
image: quay.io/redhat/basictests:v0.0.1
- image: quay.io/redhat/basictests:v0.0.1
entrypoint:
- scorecard-test
- basic-check-spec
labels:
suite: basic
test: basic-check-spec-test
description: check the spec test
- name: "basic-check-status"
image: quay.io/redhat/basictests:v0.0.1
- image: quay.io/redhat/basictests:v0.0.1
entrypoint:
- scorecard-test
- basic-check-status
labels:
suite: basic
test: basic-check-status-test
description: check the status test
- name: "olm-bundle-validation"
image: quay.io/redhat/olmtests:v0.0.1
- image: quay.io/redhat/olmtests:v0.0.1
entrypoint:
- scorecard-test
- olm-bundle-validation
labels:
suite: olm
test: olm-bundle-validation-test
description: validate the bundle test
- name: "olm-crds-have-validation"
image: quay.io/redhat/olmtests:v0.0.1
- image: quay.io/redhat/olmtests:v0.0.1
entrypoint:
- scorecard-test
- olm-crds-have-validation
labels:
suite: olm
test: olm-crds-have-validation-test
description: CRDs have validation
- name: "kuttl-tests"
image: quay.io/redhat/kuttltests:v0.0.1
- image: quay.io/redhat/kuttltests:v0.0.1
labels:
suite: kuttl
entrypoint:
- kuttl-test
- olm-status-descriptors
description: Kuttl tests
`
`
11 changes: 6 additions & 5 deletions internal/scorecard/alpha/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import (
"testing"
"time"

"github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha3"
"k8s.io/apimachinery/pkg/labels"

"github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha3"
)

func TestRunTests(t *testing.T) {
Expand Down Expand Up @@ -79,11 +80,11 @@ func TestRunTests(t *testing.T) {

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(7*time.Second))
defer cancel()
var scorecardOutput v1alpha3.Test
scorecardOutput, err = o.RunTests(ctx)
var scorecardOutput v1alpha3.TestList
scorecardOutput, err = o.Run(ctx)

if scorecardOutput.Status.Results[0].State != c.expectedState {
t.Fatalf("Wanted state %v, got %v", c.expectedState, scorecardOutput.Status.Results[0].State)
if scorecardOutput.Items[0].Status.Results[0].State != c.expectedState {
t.Fatalf("Wanted state %v, got %v", c.expectedState, scorecardOutput.Items[0].Status.Results[0].State)
}

if err == nil && c.wantError {
Expand Down
Loading

0 comments on commit 4d8960c

Please sign in to comment.