Skip to content

Commit 289ee29

Browse files
author
Jeff McCormick
authored
scorecard - Add list flag (#2137)
* add initial list flag logic to scorecard * fix the list flag - v1alpha1 check * refactor text output and list flag logic * clean up code, add quotes around labels * slight refactor of applying selectors when listing * add test for label and selector flags for scorecard * change test selector to use suite=basic
1 parent c0335ab commit 289ee29

File tree

11 files changed

+144
-16
lines changed

11 files changed

+144
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
- 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))
5353
- 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)
5454
- Added new `--selector` flag to the `operator-sdk scorecard` command to support filtering scorecard tests based on labels added to each test. ([#1916](https://github.com/operator-framework/operator-sdk/pull/1916)
55+
- Added new `--list` flag to the `operator-sdk scorecard` command to support listing scorecard tests that would be executed based on selector filters. ([#1916](https://github.com/operator-framework/operator-sdk/pull/1916)
5556

5657
### Changed
5758

cmd/operator-sdk/scorecard/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func NewCmd() *cobra.Command {
3939
scorecardCmd.Flags().StringP(scorecard.OutputFormatOpt, "o", scorecard.TextOutputFormat, fmt.Sprintf("Output format for results. Valid values: %s, %s", scorecard.TextOutputFormat, scorecard.JSONOutputFormat))
4040
scorecardCmd.Flags().String(schelpers.VersionOpt, schelpers.DefaultScorecardVersion, "scorecard version. Valid values: v1alpha1, v1alpha2")
4141
scorecardCmd.Flags().StringP(scorecard.SelectorOpt, "l", "", "selector (label query) to filter tests on (only valid when version is v1alpha2)")
42+
scorecardCmd.Flags().BoolP(scorecard.ListOpt, "L", false, "If true, only print the test names that would be run based on selector filtering (only valid when version is v1alpha2)")
4243

4344
// TODO: make config file global and make this a top level flag
4445
viper.BindPFlag(scorecard.ConfigOpt, scorecardCmd.Flags().Lookup(scorecard.ConfigOpt))
@@ -47,6 +48,7 @@ func NewCmd() *cobra.Command {
4748
viper.BindPFlag("scorecard."+scorecard.OutputFormatOpt, scorecardCmd.Flags().Lookup(scorecard.OutputFormatOpt))
4849
viper.BindPFlag("scorecard."+schelpers.VersionOpt, scorecardCmd.Flags().Lookup(schelpers.VersionOpt))
4950
viper.BindPFlag("scorecard."+scorecard.SelectorOpt, scorecardCmd.Flags().Lookup(scorecard.SelectorOpt))
51+
viper.BindPFlag("scorecard."+scorecard.ListOpt, scorecardCmd.Flags().Lookup(scorecard.ListOpt))
5052

5153
return scorecardCmd
5254
}

doc/test-framework/scorecard.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ While most configuration is done via a config file, there are a few important ar
108108
| `--kubeconfig`, `-o` | string | path to kubeconfig. It sets the kubeconfig internally for internal plugins and sets the `KUBECONFIG` env var to the provided value for external plugins. If an external plugin specifically sets the `KUBECONFIG` env var, the kubeconfig from the specified env var will be used for that plugin instead. |
109109
| `--version` | string | The version of scorecard to run, v1alpha2 is the default, valid values are v1alpha and v1alpha2. |
110110
| `--selector`, `-l` | string | The label selector to filter tests on, only valid in version v1alpha2. |
111+
| `--list`, `-L` | bool | If true, only print the test names that would be run based on selector filtering, only valid in version v1alpha2. |
111112

112113
### Config File Options
113114

hack/tests/subcommand-scorecard.sh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,34 @@ operator-sdk scorecard --version v1alpha2 --config "$CONFIG_PATH" |& grep '^.*er
2020

2121
# test to see if v1alpha2 is used from the command line
2222
commandoutput="$(operator-sdk scorecard --version v1alpha2 --config "$CONFIG_PATH_V1ALPHA2" 2>&1)"
23-
failCount=`echo $commandoutput | grep -o ": fail" | wc -l`
23+
failCount=`echo $commandoutput | grep -o "fail" | wc -l`
2424
expectedFailCount=3
2525
if [ $failCount -ne $expectedFailCount ]
2626
then
2727
echo "expected fail count $expectedFailCount, got $failCount"
2828
exit 1
2929
fi
3030

31+
# test to see if list flag work
32+
commandoutput="$(operator-sdk scorecard --version v1alpha2 --list --selector=suite=basic --config "$CONFIG_PATH_V1ALPHA2" 2>&1)"
33+
labelCount=`echo $commandoutput | grep -o "Label" | wc -l`
34+
expectedLabelCount=3
35+
if [ $labelCount -ne $expectedLabelCount ]
36+
then
37+
echo "expected label count $expectedLabelCount, got $labelCount"
38+
exit 1
39+
fi
40+
41+
# test to see if selector flags work
42+
commandoutput="$(operator-sdk scorecard --version v1alpha2 --selector=suite=basic --config "$CONFIG_PATH_V1ALPHA2" 2>&1)"
43+
labelCount=`echo $commandoutput | grep -o "Label" | wc -l`
44+
expectedLabelCount=3
45+
if [ $labelCount -ne $expectedLabelCount ]
46+
then
47+
echo "expected label count $expectedLabelCount, got $labelCount"
48+
exit 1
49+
fi
50+
3151
# test to see if version in config file allows v1alpha1 to be specified
3252
commandoutput="$(operator-sdk scorecard --config "$CONFIG_PATH_V1ALPHA1" 2>&1)"
3353
echo $commandoutput | grep "Total Score: 67%"

internal/scorecard/interfaces.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
)
3131

3232
type Plugin interface {
33+
List() scapiv1alpha1.ScorecardOutput
3334
Run() scapiv1alpha1.ScorecardOutput
3435
}
3536

@@ -42,6 +43,10 @@ type basicOrOLMPlugin struct {
4243
config scplugins.BasicAndOLMPluginConfig
4344
}
4445

46+
func (p externalPlugin) List() scapiv1alpha1.ScorecardOutput {
47+
return scapiv1alpha1.ScorecardOutput{}
48+
}
49+
4550
func (p externalPlugin) Run() scapiv1alpha1.ScorecardOutput {
4651
cmd := exec.Command(p.config.Command, p.config.Args...)
4752
for _, env := range p.config.Env {
@@ -84,6 +89,15 @@ var olmTestsPlugin = basicOrOLMPlugin{
8489
pluginType: scplugins.OLMIntegration,
8590
}
8691

92+
func (p basicOrOLMPlugin) List() scapiv1alpha1.ScorecardOutput {
93+
res, err := scplugins.ListInternalPlugin(p.pluginType, p.config)
94+
if err != nil {
95+
log.Errorf("%v", err)
96+
return scapiv1alpha1.ScorecardOutput{}
97+
}
98+
return res
99+
}
100+
87101
func (p basicOrOLMPlugin) Run() scapiv1alpha1.ScorecardOutput {
88102
pluginLogs := &bytes.Buffer{}
89103
res, err := scplugins.RunInternalPlugin(p.pluginType, p.config, pluginLogs)

internal/scorecard/plugins/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type BasicAndOLMPluginConfig struct {
5454
DeployDir string `mapstructure:"deploy-dir"`
5555
Selector labels.Selector `mapstructure:"selector"`
5656
Version string `mapstructure:"version"`
57+
ListOpt bool `mapstructure:"list"`
5758
}
5859

5960
func validateScorecardPluginFlags(config BasicAndOLMPluginConfig, pluginType PluginType) error {

internal/scorecard/plugins/plugin_runner.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/sirupsen/logrus"
3939
v1 "k8s.io/api/core/v1"
4040
extscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
41+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
4142
"k8s.io/apimachinery/pkg/runtime"
4243
"k8s.io/apimachinery/pkg/runtime/schema"
4344
"k8s.io/apimachinery/pkg/runtime/serializer"
@@ -267,6 +268,7 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
267268
logReadWriter := &bytes.Buffer{}
268269
log.SetOutput(logReadWriter)
269270
log.Printf("Running for cr: %s", cr)
271+
var obj *unstructured.Unstructured
270272
if !config.OLMDeployed {
271273
if err := createFromYAMLFile(config.Namespace, config.GlobalManifest, config.ProxyImage, config.ProxyPullPolicy); err != nil {
272274
return scapiv1alpha1.ScorecardOutput{}, fmt.Errorf("failed to create global resources: %v", err)
@@ -278,7 +280,7 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
278280
if err := createFromYAMLFile(config.Namespace, cr, config.ProxyImage, config.ProxyPullPolicy); err != nil {
279281
return scapiv1alpha1.ScorecardOutput{}, fmt.Errorf("failed to create cr resource: %v", err)
280282
}
281-
obj, err := yamlToUnstructured(config.Namespace, cr)
283+
obj, err = yamlToUnstructured(config.Namespace, cr)
282284
if err != nil {
283285
return scapiv1alpha1.ScorecardOutput{}, fmt.Errorf("failed to decode custom resource manifest into object: %s", err)
284286
}
@@ -295,6 +297,7 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
295297
if schelpers.IsV1alpha2(config.Version) {
296298
basicTests.ApplySelector(config.Selector)
297299
}
300+
298301
basicTests.Run(context.TODO())
299302
logs, err := ioutil.ReadAll(logReadWriter)
300303
if err != nil {
@@ -316,6 +319,7 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
316319
if schelpers.IsV1alpha2(config.Version) {
317320
olmTests.ApplySelector(config.Selector)
318321
}
322+
319323
olmTests.Run(context.TODO())
320324
logs, err := ioutil.ReadAll(logReadWriter)
321325
if err != nil {
@@ -346,3 +350,50 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
346350
}
347351
return output, nil
348352
}
353+
354+
func ListInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig) (scapiv1alpha1.ScorecardOutput, error) {
355+
var suites []schelpers.TestSuite
356+
357+
switch pluginType {
358+
case BasicOperator:
359+
conf := BasicTestConfig{}
360+
basicTests := NewBasicTestSuite(conf)
361+
362+
if schelpers.IsV1alpha2(config.Version) {
363+
basicTests.ApplySelector(config.Selector)
364+
}
365+
366+
basicTests.TestResults = make([]schelpers.TestResult, 0)
367+
for i := 0; i < len(basicTests.Tests); i++ {
368+
result := schelpers.TestResult{}
369+
result.Test = basicTests.Tests[i]
370+
result.Suggestions = make([]string, 0)
371+
result.Errors = make([]error, 0)
372+
basicTests.TestResults = append(basicTests.TestResults, result)
373+
}
374+
suites = append(suites, *basicTests)
375+
case OLMIntegration:
376+
conf := OLMTestConfig{}
377+
olmTests := NewOLMTestSuite(conf)
378+
379+
if schelpers.IsV1alpha2(config.Version) {
380+
olmTests.ApplySelector(config.Selector)
381+
}
382+
383+
olmTests.TestResults = make([]schelpers.TestResult, 0)
384+
for i := 0; i < len(olmTests.Tests); i++ {
385+
result := schelpers.TestResult{}
386+
result.Test = olmTests.Tests[i]
387+
result.Suggestions = make([]string, 0)
388+
result.Errors = make([]error, 0)
389+
olmTests.TestResults = append(olmTests.TestResults, result)
390+
}
391+
suites = append(suites, *olmTests)
392+
}
393+
suites, err := schelpers.MergeSuites(suites)
394+
if err != nil {
395+
return scapiv1alpha1.ScorecardOutput{}, fmt.Errorf("failed to merge test suite results: %v", err)
396+
}
397+
output := schelpers.TestSuitesToScorecardOutput(suites, "")
398+
return output, nil
399+
}

internal/scorecard/scorecard.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const (
4343
JSONOutputFormat = "json"
4444
TextOutputFormat = "text"
4545
SelectorOpt = "selector"
46+
ListOpt = "list"
4647
)
4748

4849
// make a global logger for scorecard
@@ -64,6 +65,7 @@ func getPlugins(version string, selector labels.Selector) ([]Plugin, error) {
6465
if scViper.IsSet(scplugins.KubeconfigOpt) {
6566
kubeconfig = scViper.GetString(scplugins.KubeconfigOpt)
6667
}
68+
6769
// Add plugins from config
6870
var plugins []Plugin
6971
configs := []pluginConfig{}
@@ -80,12 +82,14 @@ func getPlugins(version string, selector labels.Selector) ([]Plugin, error) {
8082
pluginConfig := plugin.Basic
8183
pluginConfig.Version = version
8284
pluginConfig.Selector = selector
85+
pluginConfig.ListOpt = scViper.GetBool(ListOpt)
8386
setConfigDefaults(pluginConfig, kubeconfig)
8487
newPlugin = basicOrOLMPlugin{pluginType: scplugins.BasicOperator, config: *pluginConfig}
8588
} else if plugin.Olm != nil {
8689
pluginConfig := plugin.Olm
8790
pluginConfig.Version = version
8891
pluginConfig.Selector = selector
92+
pluginConfig.ListOpt = scViper.GetBool(ListOpt)
8993
setConfigDefaults(pluginConfig, kubeconfig)
9094
newPlugin = basicOrOLMPlugin{pluginType: scplugins.OLMIntegration, config: *pluginConfig}
9195
} else {
@@ -122,7 +126,11 @@ func ScorecardTests(cmd *cobra.Command, args []string) error {
122126

123127
var pluginOutputs []scapiv1alpha1.ScorecardOutput
124128
for _, plugin := range plugins {
125-
pluginOutputs = append(pluginOutputs, plugin.Run())
129+
if scViper.GetBool(ListOpt) {
130+
pluginOutputs = append(pluginOutputs, plugin.List())
131+
} else {
132+
pluginOutputs = append(pluginOutputs, plugin.Run())
133+
}
126134
}
127135

128136
// Update the state for the tests
@@ -187,7 +195,17 @@ func validateScorecardConfig() error {
187195
return fmt.Errorf("invalid output format (%s); valid values: %s, %s", outputFormat, TextOutputFormat, JSONOutputFormat)
188196
}
189197

190-
return schelpers.ValidateVersion(scViper.GetString(schelpers.VersionOpt))
198+
version := scViper.GetString(schelpers.VersionOpt)
199+
err := schelpers.ValidateVersion(version)
200+
if err != nil {
201+
return err
202+
}
203+
204+
if !schelpers.IsV1alpha2(version) && scViper.GetBool(ListOpt) {
205+
return fmt.Errorf("list flag is not supported on v1alpha1")
206+
}
207+
208+
return nil
191209

192210
}
193211

@@ -198,6 +216,7 @@ func makeSCViper() {
198216
scViper.Set(scplugins.KubeconfigOpt, viper.GetString("scorecard."+scplugins.KubeconfigOpt))
199217
scViper.Set(schelpers.VersionOpt, viper.GetString("scorecard."+schelpers.VersionOpt))
200218
scViper.Set(SelectorOpt, viper.GetString("scorecard."+SelectorOpt))
219+
scViper.Set(ListOpt, viper.GetString("scorecard."+ListOpt))
201220

202221
}
203222

internal/scorecard/scorecard_output.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers"
2121
scapi "github.com/operator-framework/operator-sdk/pkg/apis/scorecard"
2222
scapiv1alpha1 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha1"
23+
scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2"
2324
"io/ioutil"
2425
)
2526

@@ -34,6 +35,12 @@ func printPluginOutputs(version string, pluginOutputs []scapiv1alpha1.ScorecardO
3435

3536
if schelpers.IsV1alpha2(version) {
3637
list = scapi.ConvertScorecardOutputV1ToV2(list.(scapiv1alpha1.ScorecardOutput))
38+
if scViper.GetBool(ListOpt) {
39+
scorecardOutput := list.(scapiv1alpha2.ScorecardOutput)
40+
for i := 0; i < len(scorecardOutput.Results); i++ {
41+
scorecardOutput.Results[i].State = scapiv1alpha2.NotRunState
42+
}
43+
}
3744
}
3845

3946
// produce text output

pkg/apis/scorecard/v1alpha2/formatter.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ const (
3030
func (s ScorecardOutput) MarshalText() (string, error) {
3131
var sb strings.Builder
3232

33-
failColor := "\033[1;" + redColor + "m%s\033[0m\n"
34-
passColor := "\033[1;" + greenColor + "m%s\033[0m\n"
33+
failColor := ": \033[1;" + redColor + "m%s\033[0m\n"
34+
passColor := ": \033[1;" + greenColor + "m%s\033[0m\n"
3535

3636
// turn off colorization if not in a terminal
3737
if !isatty.IsTerminal(os.Stdout.Fd()) &&
@@ -46,27 +46,37 @@ func (s ScorecardOutput) MarshalText() (string, error) {
4646
sb.WriteString(fmt.Sprintf("%s:\n", result.Labels["suite"]))
4747
currentSuite = result.Labels["suite"]
4848
}
49-
sb.WriteString(fmt.Sprintf("\t%-35s: ", result.Name))
49+
sb.WriteString(fmt.Sprintf("\t%-35s ", result.Name))
5050

5151
if result.State == PassState {
5252
sb.WriteString(fmt.Sprintf(passColor, PassState))
53-
} else {
53+
} else if result.State == FailState {
5454
sb.WriteString(fmt.Sprintf(failColor, FailState))
55+
} else {
56+
sb.WriteString(fmt.Sprintf("\n"))
5557
}
56-
}
5758

58-
for _, result := range s.Results {
59-
for _, suggestion := range result.Suggestions {
59+
sb.WriteString("\tLabels: \n")
60+
for labelKey, labelValue := range result.Labels {
61+
sb.WriteString(fmt.Sprintf("\t\t%q:%q\n", labelKey, labelValue))
62+
}
63+
64+
if len(result.Suggestions) > 0 {
6065
// 33 is yellow (specifically, the same shade of yellow that logrus uses for warnings)
61-
sb.WriteString(fmt.Sprintf("\x1b[%dmSUGGESTION:\x1b[0m %s\n", 33, suggestion))
66+
sb.WriteString(fmt.Sprintf("\t\x1b[%dmSuggestions:\x1b[0m \n", 33))
67+
}
68+
for _, suggestion := range result.Suggestions {
69+
sb.WriteString(fmt.Sprintf("\t\t%s\n", suggestion))
6270
}
63-
}
6471

65-
for _, result := range s.Results {
66-
for _, err := range result.Errors {
72+
if len(result.Errors) > 0 {
6773
// 31 is red (specifically, the same shade of red that logrus uses for errors)
68-
sb.WriteString(fmt.Sprintf("\x1b[%dmERROR:\x1b[0m %s\n", 31, err))
74+
sb.WriteString(fmt.Sprintf("\t\x1b[%dmErrors:\x1b[0m \n", 31))
75+
}
76+
for _, err := range result.Errors {
77+
sb.WriteString(fmt.Sprintf("\t\t%s\n", err))
6978
}
79+
sb.WriteString(fmt.Sprintf("\n"))
7080
}
7181

7282
return sb.String(), nil

pkg/apis/scorecard/v1alpha2/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
type State string
2323

2424
const (
25+
// NotRun occurs when a user specifies the --list flag
26+
NotRunState State = ""
2527
// PassState occurs when a Test's ExpectedPoints == MaximumPoints.
2628
PassState State = "pass"
2729
// FailState occurs when a Test's ExpectedPoints == 0.

0 commit comments

Comments
 (0)