Repository: zegl/kube-score (go)

Issue:
Add flag --run-test strings
This is a proposal of a feature request.

Currently, there is only the option `--ignore-test` to select the tests you want to exclude.
I would like to request to have the opposite option, so whenever this flag `--run-test strings` is enabled, just check the given test(s), leaving out the rest of the tests.


Pull request description:
Add new --all-default-optional flag to enable all optional tests. 
```
RELNOTE: New score command line flag --all-default-optional. When set to true all optional tests are enabled. Using this flag with --ignore-test will throw an error. 
```

Fixes #509

Patch:
diff --git a/cmd/kube-score/main.go b/cmd/kube-score/main.go
index 80901ec..6d105c9 100644
--- a/cmd/kube-score/main.go
+++ b/cmd/kube-score/main.go
@@ -13,8 +13,6 @@ import (
 
 	"github.com/mattn/go-isatty"
 	flag "github.com/spf13/pflag"
-	"golang.org/x/term"
-
 	"github.com/zegl/kube-score/config"
 	ks "github.com/zegl/kube-score/domain"
 	"github.com/zegl/kube-score/parser"
@@ -24,6 +22,7 @@ import (
 	"github.com/zegl/kube-score/renderer/sarif"
 	"github.com/zegl/kube-score/score"
 	"github.com/zegl/kube-score/scorecard"
+	"golang.org/x/term"
 )
 
 func main() {
@@ -112,6 +111,7 @@ func scoreFiles(binName string, args []string) error {
 	ignoreTests := fs.StringSlice("ignore-test", []string{}, "Disable a test, can be set multiple times")
 	disableIgnoreChecksAnnotation := fs.Bool("disable-ignore-checks-annotations", false, "Set to true to disable the effect of the 'kube-score/ignore' annotations")
 	disableOptionalChecksAnnotation := fs.Bool("disable-optional-checks-annotations", false, "Set to true to disable the effect of the 'kube-score/enable' annotations")
+	allDefaultOptional := fs.Bool("all-default-optional", false, "Set to true to enable all tests")
 	kubernetesVersion := fs.String("kubernetes-version", "v1.18", "Setting the kubernetes-version will affect the checks ran against the manifests. Set this to the version of Kubernetes that you're using in production for the best results.")
 	setDefault(fs, binName, "score", false)
 
@@ -169,6 +169,20 @@ Use "-" as filename to read from STDIN.`, execName(binName))
 		allFilePointers = append(allFilePointers, namedReader{Reader: fp, name: filename})
 	}
 
+	if len(*ignoreTests) > 0 && *allDefaultOptional {
+		return errors.New("Invalid argument combination. --all-default-optional and --ignore-tests cannot be used together")
+	}
+
+	if *allDefaultOptional {
+		var addOptionalChecks []string
+		for _, c := range score.RegisterAllChecks(parser.Empty(), config.Configuration{}).All() {
+			if c.Optional {
+				addOptionalChecks = append(addOptionalChecks, c.ID)
+			}
+		}
+		optionalTests = &addOptionalChecks
+	}
+
 	ignoredTests := listToStructMap(ignoreTests)
 	enabledOptionalTests := listToStructMap(optionalTests)
 
diff --git a/go.mod b/go.mod
index c2de0a2..2eacc5a 100644
--- a/go.mod
+++ b/go.mod
@@ -4,13 +4,13 @@ require (
 	github.com/eidolon/wordwrap v0.0.0-20161011182207-e0f54129b8bb
 	github.com/fatih/color v1.15.0
 	github.com/google/go-cmp v0.5.9
-	github.com/mattn/go-isatty v0.0.18
+	github.com/mattn/go-isatty v0.0.19
 	github.com/spf13/pflag v1.0.5
-	github.com/stretchr/testify v1.8.2
+	github.com/stretchr/testify v1.8.4
 	golang.org/x/term v0.8.0
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/api v0.27.1
-	k8s.io/apimachinery v0.27.1
+	k8s.io/api v0.27.3
+	k8s.io/apimachinery v0.27.3
 )
 
 require (
diff --git a/go.sum b/go.sum
index 4a2c79c..07f75ec 100644
--- a/go.sum
+++ b/go.sum
@@ -24,8 +24,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -37,13 +37,9 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -88,13 +84,12 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0=
-k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E=
-k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc=
-k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM=
+k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y=
+k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg=
+k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM=
+k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
 k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
 k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
diff --git a/scorecard/enabled.go b/scorecard/enabled.go
index 5f2f5c8..1476698 100644
--- a/scorecard/enabled.go
+++ b/scorecard/enabled.go
@@ -13,8 +13,12 @@ func (so *ScoredObject) isEnabled(check ks.Check, annotations, childAnnotations
 			if v == key {
 				return true
 			}
-			if _, ok := impliedIgnoreAnnotations[v]; ok {
-				return true
+			if vals, ok := impliedIgnoreAnnotations[v]; ok {
+				for i := range vals {
+					if vals[i] == key {
+						return true
+					}
+				}
 			}
 		}
 		return false


Test patch:
diff --git a/score/score_test.go b/score/score_test.go
index 296c37d..4bcfdd6 100644
--- a/score/score_test.go
+++ b/score/score_test.go
@@ -515,3 +515,36 @@ func TestPodEnvDuplicated(t *testing.T) {
 	diff := cmp.Diff(expected, actual)
 	assert.Empty(t, diff)
 }
+
+func TestMultipleIgnoreAnnotations(t *testing.T) {
+	t.Parallel()
+	s, err := testScore(config.Configuration{
+		VerboseOutput:             0,
+		AllFiles:                  []ks.NamedReader{testFile("kube-score-ignore-annotations.yaml")},
+		UseIgnoreChecksAnnotation: true,
+	})
+	assert.Nil(t, err)
+	assert.Len(t, s, 1)
+
+	tested := false
+	skipped := false
+
+	for _, o := range s {
+		for _, c := range o.Checks {
+			// implied by the ignore container-resources annotation
+			if c.Check.ID == "container-ephemeral-storage-request-and-limit" {
+				assert.True(t, c.Skipped)
+				skipped = true
+			}
+			// a default check
+			if c.Check.ID == "container-image-pull-policy" {
+				assert.False(t, c.Skipped)
+				assert.Equal(t, scorecard.GradeCritical, c.Grade)
+				tested = true
+			}
+			assert.Equal(t, "kube-score-ignore-annotations", o.ObjectMeta.Name)
+		}
+	}
+	assert.True(t, tested)
+	assert.True(t, skipped)
+}
diff --git a/score/testdata/kube-score-ignore-annotations.yaml b/score/testdata/kube-score-ignore-annotations.yaml
new file mode 100644
index 0000000..e930062
--- /dev/null
+++ b/score/testdata/kube-score-ignore-annotations.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: kube-score-ignore-annotations
+  annotations:
+    "kube-score/ignore": container-security-context-readonlyrootfilesystem,pod-networkpolicy,container-security-context-user-group-id,pod-probes,container-resources
+spec:
+  containers:
+  - name: foobar
+    image: foo/bar:123
+    resources:
+      limits:
+        cpu: 200m
+        memory: 1Gi
+        ephemeral-storage: 2Gi
+      requests:
+        cpu: 200m