Repository: zegl/kube-score (go)

Issue:
Ignore CronJob probes
**Describe the bug**
CronJobs are highly likely to not have a readiness or liveness concept as they are typically not hooked up to a service.  It likely makes sense to either ignore them for probes or to have them at a different level or an optional way to disable them.

**To Reproduce**

Have a cron job without a probe and run kube-score

**Expected behavior**
Ability to ignore missing cronjob probes


Pull request:
ignore probes on CronJobs and enhance pod checks to be aware of the r…
…oot type that created them

Resolves #108 

Patch:
diff --git a/.gitignore b/.gitignore
index 8d9db1d..5d8ce8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 /.idea
 /cmd/kube-score/dist/
 /cmd/kube-score/kube-score
+vendor
diff --git a/score/checks/checks.go b/score/checks/checks.go
index 61bd5b4..9d5e03f 100644
--- a/score/checks/checks.go
+++ b/score/checks/checks.go
@@ -1,6 +1,8 @@
 package checks
 
 import (
+	"strings"
+
 	ks "github.com/zegl/kube-score/domain"
 	"github.com/zegl/kube-score/scorecard"
 	appsv1 "k8s.io/api/apps/v1"
@@ -9,7 +11,6 @@ import (
 	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
 	networkingv1 "k8s.io/api/networking/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"strings"
 )
 
 func New() *Checks {
@@ -48,7 +49,7 @@ type MetaCheck struct {
 
 type PodCheck struct {
 	ks.Check
-	Fn func(corev1.PodTemplateSpec) scorecard.TestScore
+	Fn func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore
 }
 
 type ServiceCheck struct {
@@ -103,7 +104,7 @@ func (c *Checks) Metas() map[string]MetaCheck {
 	return c.metas
 }
 
-func (c *Checks) RegisterPodCheck(name, comment string, fn func(corev1.PodTemplateSpec) scorecard.TestScore) {
+func (c *Checks) RegisterPodCheck(name, comment string, fn func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore) {
 	ch := NewCheck(name, "Pod", comment)
 	c.all = append(c.all, ch)
 	c.pods[machineFriendlyName(name)] = PodCheck{ch, fn}
diff --git a/score/container/container.go b/score/container/container.go
index 2249ee5..a754450 100644
--- a/score/container/container.go
+++ b/score/container/container.go
@@ -1,11 +1,13 @@
 package container
 
 import (
+	"strings"
+
 	"github.com/zegl/kube-score/config"
 	"github.com/zegl/kube-score/score/checks"
 	"github.com/zegl/kube-score/scorecard"
 	corev1 "k8s.io/api/core/v1"
-	"strings"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func Register(allChecks *checks.Checks, cnf config.Configuration) {
@@ -16,8 +18,8 @@ func Register(allChecks *checks.Checks, cnf config.Configuration) {
 
 // containerResources makes sure that the container has resource requests and limits set
 // The check for a CPU limit requirement can be enabled via the requireCPULimit flag parameter
-func containerResources(requireCPULimit bool) func(corev1.PodTemplateSpec) scorecard.TestScore {
-	return func(podTemplate corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func containerResources(requireCPULimit bool) func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore {
+	return func(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
 		pod := podTemplate.Spec
 
 		allContainers := pod.InitContainers
@@ -61,7 +63,7 @@ func containerResources(requireCPULimit bool) func(corev1.PodTemplateSpec) score
 }
 
 // containerImageTag checks that no container is using the ":latest" tag
-func containerImageTag(podTemplate corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func containerImageTag(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
 	pod := podTemplate.Spec
 
 	allContainers := pod.InitContainers
@@ -87,7 +89,7 @@ func containerImageTag(podTemplate corev1.PodTemplateSpec) (score scorecard.Test
 }
 
 // containerImagePullPolicy checks if the containers ImagePullPolicy is set to PullAlways
-func containerImagePullPolicy(podTemplate corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func containerImagePullPolicy(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
 	pod := podTemplate.Spec
 
 	allContainers := pod.InitContainers
diff --git a/score/networkpolicy/networkpolicy.go b/score/networkpolicy/networkpolicy.go
index b7f9b80..6077c23 100644
--- a/score/networkpolicy/networkpolicy.go
+++ b/score/networkpolicy/networkpolicy.go
@@ -17,8 +17,8 @@ func Register(allChecks *checks.Checks, netpols ks.NetworkPolicies, pods ks.Pods
 
 // podHasNetworkPolicy returns a function that tests that all pods have matching NetworkPolicies
 // podHasNetworkPolicy takes a list of all defined NetworkPolicies as input
-func podHasNetworkPolicy(allNetpols []networkingv1.NetworkPolicy) func(spec corev1.PodTemplateSpec) scorecard.TestScore {
-	return func(podSpec corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func podHasNetworkPolicy(allNetpols []networkingv1.NetworkPolicy) func(spec corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) scorecard.TestScore {
+	return func(podSpec corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
 		hasMatchingEgressNetpol := false
 		hasMatchingIngressNetpol := false
 
diff --git a/score/probes/probes.go b/score/probes/probes.go
index 84ad26f..c7fa276 100644
--- a/score/probes/probes.go
+++ b/score/probes/probes.go
@@ -5,6 +5,7 @@ import (
 	"github.com/zegl/kube-score/score/checks"
 	"github.com/zegl/kube-score/scorecard"
 	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func Register(allChecks *checks.Checks, services ks.Services) {
@@ -16,8 +17,13 @@ func Register(allChecks *checks.Checks, services ks.Services) {
 // ReadinessProbes are not required if the pod is not targeted by a Service.
 //
 // containerProbes takes a slice of all defined Services as input.
-func containerProbes(allServices []corev1.Service) func(corev1.PodTemplateSpec) scorecard.TestScore {
-	return func(podTemplate corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func containerProbes(allServices []corev1.Service) func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore {
+	return func(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
+		if typeMeta.Kind == "CronJob" && typeMeta.GroupVersionKind().Group == "batch" {
+			score.Grade = scorecard.GradeAllOK
+			return score
+		}
+
 		allContainers := podTemplate.Spec.InitContainers
 		allContainers = append(allContainers, podTemplate.Spec.Containers...)
 
diff --git a/score/score.go b/score/score.go
index d22bad8..6d7b431 100644
--- a/score/score.go
+++ b/score/score.go
@@ -62,7 +62,7 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (scorecard.Scorecar
 			score := test.Fn(corev1.PodTemplateSpec{
 				ObjectMeta: pod.ObjectMeta,
 				Spec:       pod.Spec,
-			})
+			}, pod.TypeMeta)
 			o.Add(score, test.Check)
 		}
 	}
@@ -70,7 +70,7 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (scorecard.Scorecar
 	for _, podspecer := range allObjects.PodSpeccers() {
 		o := scoreCard.NewObject(podspecer.GetTypeMeta(), podspecer.GetObjectMeta())
 		for _, test := range allChecks.Pods() {
-			score := test.Fn(podspecer.GetPodTemplateSpec())
+			score := test.Fn(podspecer.GetPodTemplateSpec(), podspecer.GetTypeMeta())
 			o.Add(score, test.Check)
 		}
 	}
diff --git a/score/security/security.go b/score/security/security.go
index c498df4..9e0ffd6 100644
--- a/score/security/security.go
+++ b/score/security/security.go
@@ -4,6 +4,7 @@ import (
 	"github.com/zegl/kube-score/score/checks"
 	"github.com/zegl/kube-score/scorecard"
 	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func Register(allChecks *checks.Checks) {
@@ -11,7 +12,7 @@ func Register(allChecks *checks.Checks) {
 }
 
 // containerSecurityContext checks that the recommended securityPolicy options are set
-func containerSecurityContext(podTemplate corev1.PodTemplateSpec) (score scorecard.TestScore) {
+func containerSecurityContext(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) {
 	allContainers := podTemplate.Spec.InitContainers
 	allContainers = append(allContainers, podTemplate.Spec.Containers...)
 
@@ -36,7 +37,7 @@ func containerSecurityContext(podTemplate corev1.PodTemplateSpec) (score scoreca
 			score.AddComment(container.Name, "The container is privileged", "Set securityContext.privileged to false")
 		}
 
-		if sec.ReadOnlyRootFilesystem == nil || *sec.ReadOnlyRootFilesystem == false  {
+		if sec.ReadOnlyRootFilesystem == nil || *sec.ReadOnlyRootFilesystem == false {
 			hasWritableRootFS = true
 			score.AddComment(container.Name, "The pod has a container with a writable root filesystem", "Set securityContext.readOnlyRootFilesystem to true")
 		}


Test patch:
diff --git a/score/cronjob_test.go b/score/cronjob_test.go
index 0acedd8..39795d0 100644
--- a/score/cronjob_test.go
+++ b/score/cronjob_test.go
@@ -11,3 +11,7 @@ func TestCronJobHasDeadline(t *testing.T) {
 func TestCronJobNotHasDeadline(t *testing.T) {
 	testExpectedScore(t, "cronjob-deadline-not-set.yaml", "CronJob has deadline", 1)
 }
+
+func TestProbesPodCronMissingReady(t *testing.T) {
+	testExpectedScore(t, "cronjob-deadline-not-set.yaml", "Pod Probes", 10)
+}