Skip to content

Commit fa8d4e5

Browse files
Jeff McCormickjoelanford
andauthored
add selector flag to the scorecard subcommand (#2096)
* add dry-run and selector flags to the scorecard subcommand * make default selector flag value empty string * add selector implementation and remove dryrun which will be added later * remove dryrun from CHANGELOG since that is not in this PR * update scorecard doc to include selector flag * refactor to delete tests based on selector * remove weights in test suite as part of removing a test when using selector flag, update sdk scorecard docs to include selector flag and v1alpha2 output instead of v1alpha1 output * add validation for v1alpha2 to not allow external plugins, add test for this validation * Update internal/scorecard/config.go Co-Authored-By: Joe Lanford <[email protected]>
1 parent 12112ad commit fa8d4e5

File tree

11 files changed

+83
-38
lines changed

11 files changed

+83
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
- Added the Go version, OS, and architecture to the output of `operator-sdk version` ([#1863](https://github.com/operator-framework/operator-sdk/pull/1863))
3838
- 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))
3939
- 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)
40+
- 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)
4041

4142
### Changed
4243

cmd/operator-sdk/scorecard/cmd.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,16 @@ func NewCmd() *cobra.Command {
3737
scorecardCmd.Flags().String(scorecard.ConfigOpt, "", fmt.Sprintf("config file (default is '<project_dir>/%s'; the config file's extension and format can be .yaml, .json, or .toml)", scorecard.DefaultConfigFile))
3838
scorecardCmd.Flags().String(scplugins.KubeconfigOpt, "", "Path to kubeconfig of custom resource created in cluster")
3939
scorecardCmd.Flags().StringP(scorecard.OutputFormatOpt, "o", scorecard.TextOutputFormat, fmt.Sprintf("Output format for results. Valid values: %s, %s", scorecard.TextOutputFormat, scorecard.JSONOutputFormat))
40-
scorecardCmd.Flags().String(schelpers.VersionOpt, schelpers.DefaultScorecardVersion, fmt.Sprintf("scorecard version (tech preview version is '%s'", schelpers.LatestScorecardVersion))
40+
scorecardCmd.Flags().String(schelpers.VersionOpt, schelpers.DefaultScorecardVersion, "scorecard version. Valid values: v1alpha1, v1alpha2")
41+
scorecardCmd.Flags().StringP(scorecard.SelectorOpt, "l", "", "selector (label query) to filter tests on (only valid when version is v1alpha2)")
4142

4243
// TODO: make config file global and make this a top level flag
4344
viper.BindPFlag(scorecard.ConfigOpt, scorecardCmd.Flags().Lookup(scorecard.ConfigOpt))
4445

4546
viper.BindPFlag("scorecard."+scplugins.KubeconfigOpt, scorecardCmd.Flags().Lookup(scplugins.KubeconfigOpt))
4647
viper.BindPFlag("scorecard."+scorecard.OutputFormatOpt, scorecardCmd.Flags().Lookup(scorecard.OutputFormatOpt))
4748
viper.BindPFlag("scorecard."+schelpers.VersionOpt, scorecardCmd.Flags().Lookup(schelpers.VersionOpt))
49+
viper.BindPFlag("scorecard."+scorecard.SelectorOpt, scorecardCmd.Flags().Lookup(scorecard.SelectorOpt))
4850

4951
return scorecardCmd
5052
}

doc/sdk-cli-reference.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -420,28 +420,26 @@ Run scorecard tests on an operator
420420
* `-o, --output` string - Output format for results. Valid values: `human-readable` or `json` (default `human-readable`)
421421
* `proxy-image` string - Image name for scorecard proxy (default "quay.io/operator-framework/scorecard-proxy")
422422
* `proxy-pull-policy` string - Pull policy for scorecard proxy image (default "Always")
423+
* `selector` string - Selector (label query) to filter tests on (only valid when version is v1alpha2)
423424
* `-h, --help` - help for scorecard
424425
* `version` string - The scorecard version to run (default v1alpha1), the tech preview version is v1alpha2.
425426

426427
### Example
427428

428429
```console
429-
$ operator-sdk scorecard --cr-manifest deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml --csv-path deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml
430-
Basic Operator:
431-
Spec Block Exists: 1/1 points
432-
Status Block Exist: 1/1 points
433-
Operator actions are reflected in status: 1/1 points
434-
Writing into CRs has an effect: 1/1 points
435-
OLM Integration:
436-
Provided APIs have validation: 1/1
437-
Owned CRDs have resources listed: 1/1 points
438-
CRs have at least 1 example: 0/1 points
439-
Spec fields with descriptors: 1/1 points
440-
Status fields with descriptors: 0/1 points
441-
442-
Total Score: 84%
443-
SUGGESTION: Add an alm-examples annotation to your CSV to pass the CRs have at least 1 example test
444-
SUGGESTION: Add a status descriptor for nodes
430+
$ operator-sdk scorecard --cr-manifest deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml --csv-path deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml -o text
431+
basic:
432+
Writing into CRs has an effect : pass
433+
Spec Block Exists : pass
434+
Status Block Exists : pass
435+
olm:
436+
Spec fields with descriptors : pass
437+
Status fields with descriptors : pass
438+
Provided APIs have validation : fail
439+
Owned CRDs have resources listed : pass
440+
CRs have at least 1 example : pass
441+
SUGGESTION: Add CRD validation for Memcached/v1alpha1
442+
SUGGESTION: If it would be helpful to an end-user to understand or troubleshoot your CR, consider adding resources [deployments/v1 services/v1 configmaps/v1 memcacheds/v1alpha1 replicasets/v1] to the resources section for owned CRD Memcached
445443
```
446444

447445
## test

doc/test-framework/scorecard.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ While most configuration is done via a config file, there are a few important ar
106106
| `--config` | string | Path to config file (default `<project_dir>/.osdk-scorecard`; file type and extension can be any of `.yaml`, `.json`, or `.toml`). If a config file is not provided and a config file is not found at the default location, the scorecard will exit with an error. |
107107
| `--output`, `-o` | string | Output format. Valid options are: `text` and `json`. The default format is `text`, which is designed to be a simpler human readable format. The `json` format uses the JSON schema output format used for plugins defined later in this document. |
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. |
109-
| `--version` | string | The version of scorecard to run, v1alpha1 is the default, whereas v1alpha2 is the tech preview. |
109+
| `--version` | string | The version of scorecard to run, v1alpha2 is the default, valid values are v1alpha and v1alpha2. |
110+
| `--selector`, `-l` | string | The label selector to filter tests on, only valid in version v1alpha2. |
110111

111112
### Config File Options
112113

hack/tests/subcommand-scorecard.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CONFIG_PATH=".test-osdk-scorecard.yaml"
55
CONFIG_PATH_V1ALPHA1=".test-osdk-scorecard-v1alpha1.yaml"
66
CONFIG_PATH_DISABLE=".osdk-scorecard-disable.yaml"
77
CONFIG_PATH_INVALID=".osdk-scorecard-invalid.yaml"
8+
CONFIG_PATH_V1ALPHA2=".osdk-scorecard-v1alpha2.yaml"
89

910
set -ex
1011

@@ -14,10 +15,13 @@ set -ex
1415
# the test framework directory has all the manifests needed to run the cluster
1516
pushd test/test-framework
1617

18+
# test to see if scorecard fails when version is v1alpha2 and when external plugins are configured
19+
operator-sdk scorecard --version v1alpha2 --config "$CONFIG_PATH" |& grep '^.*error validating plugin config.*$'
20+
1721
# test to see if v1alpha2 is used from the command line
18-
commandoutput="$(operator-sdk scorecard --version v1alpha2 --config "$CONFIG_PATH" 2>&1)"
22+
commandoutput="$(operator-sdk scorecard --version v1alpha2 --config "$CONFIG_PATH_V1ALPHA2" 2>&1)"
1923
failCount=`echo $commandoutput | grep -o ": fail" | wc -l`
20-
expectedFailCount=7
24+
expectedFailCount=3
2125
if [ $failCount -ne $expectedFailCount ]
2226
then
2327
echo "expected fail count $expectedFailCount, got $failCount"

internal/scorecard/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"strings"
2020

21+
schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers"
2122
"gopkg.in/yaml.v2"
2223
)
2324

@@ -49,7 +50,7 @@ func (e externalPluginEnv) String() string {
4950
}
5051

5152
// validateConfig takes a viper config for a plugin and returns a nil error if valid or an error explaining why the config is invalid
52-
func validateConfig(config pluginConfig, idx int) error {
53+
func validateConfig(config pluginConfig, idx int, version string) error {
5354
// find plugin config type
5455
pluginType := ""
5556
if config.Basic != nil {
@@ -66,6 +67,9 @@ func validateConfig(config pluginConfig, idx int) error {
6667
return fmt.Errorf("plugin config can only contain one of: basic, olm, external")
6768
}
6869
pluginType = "external"
70+
if schelpers.IsV1alpha2(version) {
71+
return fmt.Errorf("revert to v1alpha1 to use external plugins: external plugins are not currently supported with v1alpha2")
72+
}
6973
}
7074
if pluginType == "" {
7175
marshalledConfig, err := yaml.Marshal(config)

internal/scorecard/helpers/test_definitions.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strings"
2121

2222
scapiv1alpha1 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha1"
23+
"k8s.io/apimachinery/pkg/labels"
2324
)
2425

2526
// Type Definitions
@@ -102,6 +103,19 @@ func (ts *TestSuite) TotalScore() (score int) {
102103
return int(floatScore * (100 / addedWeights))
103104
}
104105

106+
// ApplySelector apply label selectors removing tests that do not match
107+
func (ts *TestSuite) ApplySelector(selector labels.Selector) {
108+
for i := 0; i < len(ts.Tests); i++ {
109+
t := ts.Tests[i]
110+
if !selector.Matches(labels.Set(t.GetLabels())) {
111+
// Remove the test
112+
ts.Tests = append(ts.Tests[:i], ts.Tests[i+1:]...)
113+
delete(ts.Weights, t.GetName())
114+
i--
115+
}
116+
}
117+
}
118+
105119
// Run runs all Tests in a TestSuite
106120
func (ts *TestSuite) Run(ctx context.Context) {
107121
for _, test := range ts.Tests {

internal/scorecard/plugins/basic_tests.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func NewBasicTestSuite(conf BasicTestConfig) *schelpers.TestSuite {
100100
"Basic Tests",
101101
"Test suite that runs basic, functional operator tests",
102102
)
103+
103104
ts.AddTest(NewCheckSpecTest(conf), 1.5)
104105
ts.AddTest(NewCheckStatusTest(conf), 1)
105106
ts.AddTest(NewWritingIntoCRsHasEffectTest(conf), 1)

internal/scorecard/plugins/config.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"github.com/pkg/errors"
2121
v1 "k8s.io/api/core/v1"
22+
"k8s.io/apimachinery/pkg/labels"
2223
)
2324

2425
const (
@@ -39,18 +40,20 @@ const (
3940
)
4041

4142
type BasicAndOLMPluginConfig struct {
42-
Namespace string `mapstructure:"namespace"`
43-
Kubeconfig string `mapstructure:"kubeconfig"`
44-
InitTimeout int `mapstructure:"init-timeout"`
45-
OLMDeployed bool `mapstructure:"olm-deployed"`
46-
NamespacedManifest string `mapstructure:"namespaced-manifest"`
47-
GlobalManifest string `mapstructure:"global-manifest"`
48-
CRManifest []string `mapstructure:"cr-manifest"`
49-
CSVManifest string `mapstructure:"csv-path"`
50-
ProxyImage string `mapstructure:"proxy-image"`
51-
ProxyPullPolicy v1.PullPolicy `mapstructure:"proxy-pull-policy"`
52-
CRDsDir string `mapstructure:"crds-dir"`
53-
DeployDir string `mapstructure:"deploy-dir"`
43+
Namespace string `mapstructure:"namespace"`
44+
Kubeconfig string `mapstructure:"kubeconfig"`
45+
InitTimeout int `mapstructure:"init-timeout"`
46+
OLMDeployed bool `mapstructure:"olm-deployed"`
47+
NamespacedManifest string `mapstructure:"namespaced-manifest"`
48+
GlobalManifest string `mapstructure:"global-manifest"`
49+
CRManifest []string `mapstructure:"cr-manifest"`
50+
CSVManifest string `mapstructure:"csv-path"`
51+
ProxyImage string `mapstructure:"proxy-image"`
52+
ProxyPullPolicy v1.PullPolicy `mapstructure:"proxy-pull-policy"`
53+
CRDsDir string `mapstructure:"crds-dir"`
54+
DeployDir string `mapstructure:"deploy-dir"`
55+
Selector labels.Selector `mapstructure:"selector"`
56+
Version string `mapstructure:"version"`
5457
}
5558

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

internal/scorecard/plugins/plugin_runner.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
292292
ProxyPod: proxyPodGlobal,
293293
}
294294
basicTests := NewBasicTestSuite(conf)
295+
if schelpers.IsV1alpha2(config.Version) {
296+
basicTests.ApplySelector(config.Selector)
297+
}
295298
basicTests.Run(context.TODO())
296299
logs, err := ioutil.ReadAll(logReadWriter)
297300
if err != nil {
@@ -310,6 +313,9 @@ func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, lo
310313
ProxyPod: proxyPodGlobal,
311314
}
312315
olmTests := NewOLMTestSuite(conf)
316+
if schelpers.IsV1alpha2(config.Version) {
317+
olmTests.ApplySelector(config.Selector)
318+
}
313319
olmTests.Run(context.TODO())
314320
logs, err := ioutil.ReadAll(logReadWriter)
315321
if err != nil {

0 commit comments

Comments
 (0)