diff --git a/cmd/kube-score/main.go b/cmd/kube-score/main.go index a67d11b6..f83fcf22 100644 --- a/cmd/kube-score/main.go +++ b/cmd/kube-score/main.go @@ -105,6 +105,7 @@ func scoreFiles(binName string, args []string) error { outputVersion := fs.String("output-version", "", "Changes the version of the --output-format. The 'json' format has version 'v2' (default) and 'v1' (deprecated, will be removed in v1.7.0). The 'human' and 'ci' formats has only version 'v1' (default). If not explicitly set, the default version for that particular output format will be used.") optionalTests := fs.StringSlice("enable-optional-test", []string{}, "Enable an optional test, can be set multiple times") ignoreTests := fs.StringSlice("ignore-test", []string{}, "Disable a test, can be set multiple times") + ignoreNamespaces := fs.StringSlice("ignore-namespace", []string{}, "Disable test on specific namespace, 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") 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) @@ -154,6 +155,7 @@ Use "-" as filename to read from STDIN.`, execName(binName)) } ignoredTests := listToStructMap(ignoreTests) + ignoredNamespaces := listToStructMap(ignoreNamespaces) enabledOptionalTests := listToStructMap(optionalTests) kubeVer, err := config.ParseSemver(*kubernetesVersion) @@ -166,6 +168,7 @@ Use "-" as filename to read from STDIN.`, execName(binName)) VerboseOutput: *verboseOutput, IgnoreContainerCpuLimitRequirement: *ignoreContainerCpuLimit, IgnoreContainerMemoryLimitRequirement: *ignoreContainerMemoryLimit, + IgnoredNamespaces: ignoredNamespaces, IgnoredTests: ignoredTests, EnabledOptionalTests: enabledOptionalTests, UseIgnoreChecksAnnotation: !*disableIgnoreChecksAnnotation, diff --git a/config/config.go b/config/config.go index e5d2d3fd..3f7bb28d 100644 --- a/config/config.go +++ b/config/config.go @@ -14,6 +14,7 @@ type Configuration struct { VerboseOutput int IgnoreContainerCpuLimitRequirement bool IgnoreContainerMemoryLimitRequirement bool + IgnoredNamespaces map[string]struct{} IgnoredTests map[string]struct{} EnabledOptionalTests map[string]struct{} UseIgnoreChecksAnnotation bool diff --git a/score/score.go b/score/score.go index 007b31d3..900304ea 100644 --- a/score/score.go +++ b/score/score.go @@ -54,14 +54,14 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca for _, ingress := range allObjects.Ingresses() { o := newObject(ingress.GetTypeMeta(), ingress.GetObjectMeta()) for _, test := range allChecks.Ingresses() { - o.Add(test.Fn(ingress), test.Check, ingress) + o.Add(test.Fn(ingress), test.Check, ingress, cnf) } } for _, meta := range allObjects.Metas() { o := newObject(meta.TypeMeta, meta.ObjectMeta) for _, test := range allChecks.Metas() { - o.Add(test.Fn(meta), test.Check, meta) + o.Add(test.Fn(meta), test.Check, meta, cnf) } } @@ -72,7 +72,7 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca ObjectMeta: pod.Pod().ObjectMeta, Spec: pod.Pod().Spec, }, pod.Pod().TypeMeta) - o.Add(score, test.Check, pod) + o.Add(score, test.Check, pod, cnf) } } @@ -80,14 +80,14 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca o := newObject(podspecer.GetTypeMeta(), podspecer.GetObjectMeta()) for _, test := range allChecks.Pods() { score := test.Fn(podspecer.GetPodTemplateSpec(), podspecer.GetTypeMeta()) - o.Add(score, test.Check, podspecer) + o.Add(score, test.Check, podspecer, cnf) } } for _, service := range allObjects.Services() { o := newObject(service.Service().TypeMeta, service.Service().ObjectMeta) for _, test := range allChecks.Services() { - o.Add(test.Fn(service.Service()), test.Check, service) + o.Add(test.Fn(service.Service()), test.Check, service, cnf) } } @@ -98,7 +98,7 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca if err != nil { return nil, err } - o.Add(res, test.Check, statefulset) + o.Add(res, test.Check, statefulset, cnf) } } @@ -109,28 +109,28 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca if err != nil { return nil, err } - o.Add(res, test.Check, deployment) + o.Add(res, test.Check, deployment, cnf) } } for _, netpol := range allObjects.NetworkPolicies() { o := newObject(netpol.NetworkPolicy().TypeMeta, netpol.NetworkPolicy().ObjectMeta) for _, test := range allChecks.NetworkPolicies() { - o.Add(test.Fn(netpol.NetworkPolicy()), test.Check, netpol) + o.Add(test.Fn(netpol.NetworkPolicy()), test.Check, netpol, cnf) } } for _, cjob := range allObjects.CronJobs() { o := newObject(cjob.CronJob().TypeMeta, cjob.CronJob().ObjectMeta) for _, test := range allChecks.CronJobs() { - o.Add(test.Fn(cjob.CronJob()), test.Check, cjob) + o.Add(test.Fn(cjob.CronJob()), test.Check, cjob, cnf) } } for _, hpa := range allObjects.HorizontalPodAutoscalers() { o := newObject(hpa.GetTypeMeta(), hpa.GetObjectMeta()) for _, test := range allChecks.HorizontalPodAutoscalers() { - o.Add(test.Fn(hpa), test.Check, hpa) + o.Add(test.Fn(hpa), test.Check, hpa, cnf) } } diff --git a/score/security_test.go b/score/security_test.go index 1182fa5a..0868975a 100644 --- a/score/security_test.go +++ b/score/security_test.go @@ -266,3 +266,31 @@ func TestContainerSeccompAllGood(t *testing.T) { EnabledOptionalTests: structMap, }, "Container Seccomp Profile", scorecard.GradeAllOK) } + +func TestServiceIgnoreNamespace(t *testing.T) { + t.Parallel() + + structMap := make(map[string]struct{}) + structMap["site"] = struct{}{} + + s, err := testScore(config.Configuration{ + VerboseOutput: 0, + AllFiles: []ks.NamedReader{testFile("service-externalname.yaml")}, + IgnoredNamespaces: structMap, + }) + assert.Nil(t, err) + assert.Len(t, s, 1) + + tested := false + + for _, o := range s { + for _, c := range o.Checks { + if c.Check.ID == "service-targets-pod" { + assert.True(t, c.Skipped) + assert.Equal(t, scorecard.GradeAllOK, c.Grade) + tested = true + } + } + } + assert.True(t, tested) +} diff --git a/scorecard/scorecard.go b/scorecard/scorecard.go index 608508bb..06ac43ae 100644 --- a/scorecard/scorecard.go +++ b/scorecard/scorecard.go @@ -2,6 +2,7 @@ package scorecard import ( "fmt" + "github.com/zegl/kube-score/config" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "strings" @@ -89,7 +90,7 @@ func (so ScoredObject) HumanFriendlyRef() string { return s } -func (so *ScoredObject) Add(ts TestScore, check ks.Check, locationer ks.FileLocationer) { +func (so *ScoredObject) Add(ts TestScore, check ks.Check, locationer ks.FileLocationer, cnf config.Configuration) { ts.Check = check so.FileLocation = locationer.FileLocation() @@ -99,6 +100,11 @@ func (so *ScoredObject) Add(ts TestScore, check ks.Check, locationer ks.FileLoca ts.Comments = []TestScoreComment{{Summary: fmt.Sprintf("Skipped because %s is ignored", check.ID)}} } + if _, ok := cnf.IgnoredNamespaces[so.ObjectMeta.Namespace]; ok { + ts.Skipped = true + ts.Comments = []TestScoreComment{{Summary: fmt.Sprintf("Skipped because %s namespace is ignored", so.ObjectMeta.Namespace)}} + } + so.Checks = append(so.Checks, ts) }