diff --git a/score/checks/checks.go b/score/checks/checks.go index 20a709c3..758f8299 100644 --- a/score/checks/checks.go +++ b/score/checks/checks.go @@ -3,14 +3,12 @@ package checks import ( "strings" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/zegl/kube-score/config" ks "github.com/zegl/kube-score/domain" "github.com/zegl/kube-score/scorecard" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" ) func New(cnf config.Configuration) *Checks { @@ -19,7 +17,7 @@ func New(cnf config.Configuration) *Checks { all: make([]ks.Check, 0), metas: make(map[string]GenCheck[ks.BothMeta]), - pods: make(map[string]PodCheck), + pods: make(map[string]GenCheck[ks.PodSpecer]), services: make(map[string]GenCheck[corev1.Service]), statefulsets: make(map[string]GenCheck[appsv1.StatefulSet]), deployments: make(map[string]GenCheck[appsv1.Deployment]), @@ -53,12 +51,6 @@ type MetaCheck struct { Fn MetaCheckFn } -type PodCheckFn = func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore -type PodCheck struct { - ks.Check - Fn PodCheckFn -} - type CheckFunc[T any] func(T) (scorecard.TestScore, error) type GenCheck[T any] struct { @@ -69,7 +61,7 @@ type GenCheck[T any] struct { type Checks struct { all []ks.Check metas map[string]GenCheck[ks.BothMeta] - pods map[string]PodCheck + pods map[string]GenCheck[ks.PodSpecer] services map[string]GenCheck[corev1.Service] statefulsets map[string]GenCheck[appsv1.StatefulSet] deployments map[string]GenCheck[appsv1.Deployment] @@ -110,46 +102,34 @@ func (c *Checks) Metas() map[string]GenCheck[ks.BothMeta] { return c.metas } -func (c *Checks) RegisterPodCheck(name, comment string, fn PodCheckFn) { - ch := NewCheck(name, "Pod", comment, false) - c.registerPodCheck(PodCheck{ch, fn}) +func reg[T any](c *Checks, targetType, name, comment string, optional bool, fn CheckFunc[T], mp map[string]GenCheck[T]) { + ch := NewCheck(name, targetType, comment, optional) + check := GenCheck[T]{Check: ch, Fn: fn} + c.all = append(c.all, check.Check) + if !c.isEnabled(check.Check) { + return + } + mp[machineFriendlyName(ch.Name)] = check } -func (c *Checks) RegisterOptionalPodCheck(name, comment string, fn PodCheckFn) { - ch := NewCheck(name, "Pod", comment, true) - c.registerPodCheck(PodCheck{ch, fn}) +func (c *Checks) RegisterPodCheck(name, comment string, fn CheckFunc[ks.PodSpecer]) { + reg(c, "Pod", name, comment, false, fn, c.pods) } -func (c *Checks) registerPodCheck(ch PodCheck) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.pods[machineFriendlyName(ch.Name)] = ch +func (c *Checks) RegisterOptionalPodCheck(name, comment string, fn CheckFunc[ks.PodSpecer]) { + reg(c, "Pod", name, comment, true, fn, c.pods) } -func (c *Checks) Pods() map[string]PodCheck { +func (c *Checks) Pods() map[string]GenCheck[ks.PodSpecer] { return c.pods } func (c *Checks) RegisterHorizontalPodAutoscalerCheck(name, comment string, fn CheckFunc[ks.HpaTargeter]) { - ch := NewCheck(name, "HorizontalPodAutoscaler", comment, false) - c.registerHorizontalPodAutoscalerCheck(GenCheck[ks.HpaTargeter]{ch, fn}) + reg(c, "HorizontalPodAutoscaler", name, comment, false, fn, c.horizontalPodAutoscalers) } func (c *Checks) RegisterOptionalHorizontalPodAutoscalerCheck(name, comment string, fn CheckFunc[ks.HpaTargeter]) { - ch := NewCheck(name, "HorizontalPodAutoscaler", comment, true) - c.registerHorizontalPodAutoscalerCheck(GenCheck[ks.HpaTargeter]{ch, fn}) -} - -func (c *Checks) registerHorizontalPodAutoscalerCheck(ch GenCheck[ks.HpaTargeter]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.horizontalPodAutoscalers[machineFriendlyName(ch.Name)] = ch + reg(c, "HorizontalPodAutoscaler", name, comment, true, fn, c.horizontalPodAutoscalers) } func (c *Checks) HorizontalPodAutoscalers() map[string]GenCheck[ks.HpaTargeter] { @@ -157,22 +137,11 @@ func (c *Checks) HorizontalPodAutoscalers() map[string]GenCheck[ks.HpaTargeter] } func (c *Checks) RegisterCronJobCheck(name, comment string, fn CheckFunc[ks.CronJob]) { - ch := NewCheck(name, "CronJob", comment, false) - c.registerCronJobCheck(GenCheck[ks.CronJob]{ch, fn}) + reg(c, "CronJob", name, comment, false, fn, c.cronjobs) } func (c *Checks) RegisterOptionalCronJobCheck(name, comment string, fn CheckFunc[ks.CronJob]) { - ch := NewCheck(name, "CronJob", comment, true) - c.registerCronJobCheck(GenCheck[ks.CronJob]{ch, fn}) -} - -func (c *Checks) registerCronJobCheck(ch GenCheck[ks.CronJob]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.cronjobs[machineFriendlyName(ch.Name)] = ch + reg(c, "CronJob", name, comment, true, fn, c.cronjobs) } func (c *Checks) CronJobs() map[string]GenCheck[ks.CronJob] { @@ -180,22 +149,11 @@ func (c *Checks) CronJobs() map[string]GenCheck[ks.CronJob] { } func (c *Checks) RegisterStatefulSetCheck(name, comment string, fn CheckFunc[appsv1.StatefulSet]) { - ch := NewCheck(name, "StatefulSet", comment, false) - c.registerStatefulSetCheck(GenCheck[appsv1.StatefulSet]{ch, fn}) + reg(c, "StatefulSet", name, comment, false, fn, c.statefulsets) } func (c *Checks) RegisterOptionalStatefulSetCheck(name, comment string, fn CheckFunc[appsv1.StatefulSet]) { - ch := NewCheck(name, "StatefulSet", comment, true) - c.registerStatefulSetCheck(GenCheck[appsv1.StatefulSet]{ch, fn}) -} - -func (c *Checks) registerStatefulSetCheck(ch GenCheck[appsv1.StatefulSet]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.statefulsets[machineFriendlyName(ch.Name)] = ch + reg(c, "StatefulSet", name, comment, true, fn, c.statefulsets) } func (c *Checks) StatefulSets() map[string]GenCheck[appsv1.StatefulSet] { @@ -203,22 +161,11 @@ func (c *Checks) StatefulSets() map[string]GenCheck[appsv1.StatefulSet] { } func (c *Checks) RegisterDeploymentCheck(name, comment string, fn CheckFunc[appsv1.Deployment]) { - ch := NewCheck(name, "Deployment", comment, false) - c.registerDeploymentCheck(GenCheck[appsv1.Deployment]{ch, fn}) + reg(c, "Deployment", name, comment, false, fn, c.deployments) } func (c *Checks) RegisterOptionalDeploymentCheck(name, comment string, fn CheckFunc[appsv1.Deployment]) { - ch := NewCheck(name, "Deployment", comment, true) - c.registerDeploymentCheck(GenCheck[appsv1.Deployment]{ch, fn}) -} - -func (c *Checks) registerDeploymentCheck(ch GenCheck[appsv1.Deployment]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.deployments[machineFriendlyName(ch.Name)] = ch + reg(c, "Deployment", name, comment, true, fn, c.deployments) } func (c *Checks) Deployments() map[string]GenCheck[appsv1.Deployment] { @@ -226,22 +173,11 @@ func (c *Checks) Deployments() map[string]GenCheck[appsv1.Deployment] { } func (c *Checks) RegisterIngressCheck(name, comment string, fn CheckFunc[ks.Ingress]) { - ch := NewCheck(name, "Ingress", comment, false) - c.registerIngressCheck(GenCheck[ks.Ingress]{ch, fn}) + reg(c, "Ingress", name, comment, false, fn, c.ingresses) } func (c *Checks) RegisterOptionalIngressCheck(name, comment string, fn CheckFunc[ks.Ingress]) { - ch := NewCheck(name, "Ingress", comment, true) - c.registerIngressCheck(GenCheck[ks.Ingress]{ch, fn}) -} - -func (c *Checks) registerIngressCheck(ch GenCheck[ks.Ingress]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.ingresses[machineFriendlyName(ch.Name)] = ch + reg(c, "Ingress", name, comment, true, fn, c.ingresses) } func (c *Checks) Ingresses() map[string]GenCheck[ks.Ingress] { @@ -249,22 +185,11 @@ func (c *Checks) Ingresses() map[string]GenCheck[ks.Ingress] { } func (c *Checks) RegisterNetworkPolicyCheck(name, comment string, fn CheckFunc[networkingv1.NetworkPolicy]) { - ch := NewCheck(name, "NetworkPolicy", comment, false) - c.registerNetworkPolicyCheck(GenCheck[networkingv1.NetworkPolicy]{ch, fn}) + reg(c, "NetworkPolicy", name, comment, false, fn, c.networkpolicies) } func (c *Checks) RegisterOptionalNetworkPolicyCheck(name, comment string, fn CheckFunc[networkingv1.NetworkPolicy]) { - ch := NewCheck(name, "NetworkPolicy", comment, true) - c.registerNetworkPolicyCheck(GenCheck[networkingv1.NetworkPolicy]{ch, fn}) -} - -func (c *Checks) registerNetworkPolicyCheck(ch GenCheck[networkingv1.NetworkPolicy]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.networkpolicies[machineFriendlyName(ch.Name)] = ch + reg(c, "NetworkPolicy", name, comment, true, fn, c.networkpolicies) } func (c *Checks) NetworkPolicies() map[string]GenCheck[networkingv1.NetworkPolicy] { @@ -272,18 +197,7 @@ func (c *Checks) NetworkPolicies() map[string]GenCheck[networkingv1.NetworkPolic } func (c *Checks) RegisterPodDisruptionBudgetCheck(name, comment string, fn CheckFunc[ks.PodDisruptionBudget]) { - ch := NewCheck(name, "PodDisruptionBudget", comment, false) - c.registerPodDisruptionBudgetCheck(GenCheck[ks.PodDisruptionBudget]{ch, fn}) -} - -func (c *Checks) registerPodDisruptionBudgetCheck(ch GenCheck[ks.PodDisruptionBudget]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - - c.poddisruptionbudgets[machineFriendlyName(ch.Name)] = ch + reg(c, "PodDisruptionBudget", name, comment, false, fn, c.poddisruptionbudgets) } func (c *Checks) PodDisruptionBudgets() map[string]GenCheck[ks.PodDisruptionBudget] { @@ -291,22 +205,11 @@ func (c *Checks) PodDisruptionBudgets() map[string]GenCheck[ks.PodDisruptionBudg } func (c *Checks) RegisterServiceCheck(name, comment string, fn CheckFunc[corev1.Service]) { - ch := NewCheck(name, "Service", comment, false) - c.registerServiceCheck(GenCheck[corev1.Service]{ch, fn}) + reg(c, "Service", name, comment, false, fn, c.services) } func (c *Checks) RegisterOptionalServiceCheck(name, comment string, fn CheckFunc[corev1.Service]) { - ch := NewCheck(name, "Service", comment, true) - c.registerServiceCheck(GenCheck[corev1.Service]{ch, fn}) -} - -func (c *Checks) registerServiceCheck(ch GenCheck[corev1.Service]) { - c.all = append(c.all, ch.Check) - - if !c.isEnabled(ch.Check) { - return - } - c.services[machineFriendlyName(ch.Name)] = ch + reg(c, "Service", name, comment, true, fn, c.services) } func (c *Checks) Services() map[string]GenCheck[corev1.Service] { diff --git a/score/container/container.go b/score/container/container.go index b6125980..5b30c92a 100644 --- a/score/container/container.go +++ b/score/container/container.go @@ -5,10 +5,10 @@ import ( "strings" "github.com/zegl/kube-score/config" + ks "github.com/zegl/kube-score/domain" "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, cnf config.Configuration) { @@ -26,9 +26,9 @@ 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, requireMemoryLimit bool) func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore { - return func(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func containerResources(requireCPULimit bool, requireMemoryLimit bool) func(ks.PodSpecer) (scorecard.TestScore, error) { + return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) @@ -72,9 +72,9 @@ func containerResources(requireCPULimit bool, requireMemoryLimit bool) func(core } // containerResourceRequestsEqualLimits checks that all containers have equal requests and limits for CPU and memory resources -func containerResourceRequestsEqualLimits(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - cpuScore := containerCPURequestsEqualLimits(podTemplate, typeMeta) - memoryScore := containerMemoryRequestsEqualLimits(podTemplate, typeMeta) +func containerResourceRequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + cpuScore, _ := containerCPURequestsEqualLimits(ps) + memoryScore, _ := containerMemoryRequestsEqualLimits(ps) score.Grade = scorecard.GradeAllOK if cpuScore.Grade == scorecard.GradeCritical { @@ -86,12 +86,12 @@ func containerResourceRequestsEqualLimits(podTemplate corev1.PodTemplateSpec, ty score.Comments = append(score.Comments, memoryScore.Comments...) } - return score + return } // containerCPURequestsEqualLimits checks that all containers have equal requests and limits for CPU resources -func containerCPURequestsEqualLimits(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func containerCPURequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) @@ -117,8 +117,8 @@ func containerCPURequestsEqualLimits(podTemplate corev1.PodTemplateSpec, typeMet } // containerMemoryRequestsEqualLimits checks that all containers have equal requests and limits for memory resources -func containerMemoryRequestsEqualLimits(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func containerMemoryRequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) @@ -144,8 +144,8 @@ func containerMemoryRequestsEqualLimits(podTemplate corev1.PodTemplateSpec, type } // containerImageTag checks that no container is using the ":latest" tag -func containerImageTag(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func containerImageTag(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) @@ -170,8 +170,8 @@ func containerImageTag(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeM } // containerImagePullPolicy checks if the containers ImagePullPolicy is set to PullAlways -func containerImagePullPolicy(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func containerImagePullPolicy(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) @@ -209,10 +209,9 @@ func containerTag(image string) string { return "" } -func containerStorageEphemeralRequestAndLimit(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) +func containerStorageEphemeralRequestAndLimit(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) score.Grade = scorecard.GradeAllOK @@ -231,10 +230,9 @@ func containerStorageEphemeralRequestAndLimit(podTemplate corev1.PodTemplateSpec return } -func containerStorageEphemeralRequestEqualsLimit(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) +func containerStorageEphemeralRequestEqualsLimit(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) score.Grade = scorecard.GradeAllOK @@ -254,12 +252,12 @@ func containerStorageEphemeralRequestEqualsLimit(podTemplate corev1.PodTemplateS // List of ports to expose from the container. This is primarily informational. Not specifying a port here // does not prevent it from being exposed. Specifying it does not expose the port outside the cluster; that -// requires a Service object. However misspecifying elements of this optional Container -func containerPortsCheck(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { +// requires a Service object. +func containerPortsCheck(ps ks.PodSpecer) (score scorecard.TestScore, err error) { const maxPortNameLength = 15 - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) score.Grade = scorecard.GradeAllOK @@ -289,8 +287,8 @@ func containerPortsCheck(podTemplate corev1.PodTemplateSpec, typeMeta metav1.Typ } // environmentVariableKeyDuplication checks that no duplicated environment variable keys. -func environmentVariableKeyDuplication(podTemplate corev1.PodTemplateSpec, _ metav1.TypeMeta) (score scorecard.TestScore) { - pod := podTemplate.Spec +func environmentVariableKeyDuplication(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + pod := ps.GetPodTemplateSpec().Spec allContainers := pod.InitContainers allContainers = append(allContainers, pod.Containers...) diff --git a/score/container/container_test.go b/score/container/container_test.go index 91322a6c..5629bf41 100644 --- a/score/container/container_test.go +++ b/score/container/container_test.go @@ -3,6 +3,7 @@ package container import ( "testing" + ks "github.com/zegl/kube-score/domain" "k8s.io/apimachinery/pkg/api/resource" "github.com/stretchr/testify/assert" @@ -12,29 +13,53 @@ import ( "github.com/zegl/kube-score/scorecard" ) +type podSpeccer struct { + typeMeta metav1.TypeMeta + objectMeta metav1.ObjectMeta + spec corev1.PodTemplateSpec +} + +func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta { + return p.typeMeta +} + +func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta { + return p.objectMeta +} + +func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec { + return p.spec +} + +func (p *podSpeccer) FileLocation() ks.FileLocation { + return ks.FileLocation{} +} + func TestOkAllTheSameContainerResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerResourceRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + s, _ := containerResourceRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -42,55 +67,57 @@ func TestOkAllTheSameContainerResourceRequestsEqualLimits(t *testing.T) { func TestOkMultipleContainersContainerResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerResourceRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + s, _ := containerResourceRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, - }, - { - Name: "foo2", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + { + Name: "foo2", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -98,27 +125,29 @@ func TestOkMultipleContainersContainerResourceRequestsEqualLimits(t *testing.T) func TestOkSameQuantityContainerResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerResourceRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1000m"), - "memory": resource.MustParse("0.25Gi"), + s, _ := containerResourceRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1000m"), + "memory": resource.MustParse("0.25Gi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -126,27 +155,28 @@ func TestOkSameQuantityContainerResourceRequestsEqualLimits(t *testing.T) { func TestFailBothContainerResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerResourceRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("2"), - "memory": resource.MustParse("512Mi"), + s, _ := containerResourceRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("2"), + "memory": resource.MustParse("512Mi"), + }, }, }, }, }, }, - }, - metav1.TypeMeta{}) + }) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 2) @@ -160,42 +190,44 @@ func TestFailBothContainerResourceRequestsEqualLimits(t *testing.T) { func TestFailCpuInitContainerResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerResourceRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "init", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("2"), - "memory": resource.MustParse("256Mi"), + s, _ := containerResourceRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "init", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("2"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 1) @@ -206,25 +238,27 @@ func TestFailCpuInitContainerResourceRequestsEqualLimits(t *testing.T) { func TestOkAllCPURequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerCPURequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), + s, _ := containerCPURequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -232,49 +266,51 @@ func TestOkAllCPURequestsEqualLimits(t *testing.T) { func TestOkMultipleContainersContainerCPURequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerCPURequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), + s, _ := containerCPURequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, }, }, - }, - { - Name: "foo2", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), + { + Name: "foo2", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -282,25 +318,27 @@ func TestOkMultipleContainersContainerCPURequestsEqualLimits(t *testing.T) { func TestOkSameQuantityContainerCPURequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerCPURequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1000m"), + s, _ := containerCPURequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1000m"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -308,27 +346,29 @@ func TestOkSameQuantityContainerCPURequestsEqualLimits(t *testing.T) { func TestFailContainerCPURequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerCPURequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("2"), - "memory": resource.MustParse("512Mi"), + s, _ := containerCPURequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("2"), + "memory": resource.MustParse("512Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 1) @@ -340,40 +380,42 @@ func TestFailContainerCPURequestsEqualLimits(t *testing.T) { func TestFailInitContainerCPURequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerCPURequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "init", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("2"), + s, _ := containerCPURequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "init", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("2"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 1) @@ -384,25 +426,27 @@ func TestFailInitContainerCPURequestsEqualLimits(t *testing.T) { func TestOkContainerMemoryResourceRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerMemoryRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), + s, _ := containerMemoryRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -410,49 +454,51 @@ func TestOkContainerMemoryResourceRequestsEqualLimits(t *testing.T) { func TestOkMultipleContainersContainerMemoryRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerMemoryRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), + s, _ := containerMemoryRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, }, }, - }, - { - Name: "foo2", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), + { + Name: "foo2", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -460,25 +506,27 @@ func TestOkMultipleContainersContainerMemoryRequestsEqualLimits(t *testing.T) { func TestOkSameQuantityContainerMemoryRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerMemoryRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("0.25Gi"), + s, _ := containerMemoryRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("0.25Gi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeAllOK, s.Grade) assert.Len(t, s.Comments, 0) @@ -486,27 +534,29 @@ func TestOkSameQuantityContainerMemoryRequestsEqualLimits(t *testing.T) { func TestFailContainerMemoryRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerMemoryRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("2"), - "memory": resource.MustParse("512Mi"), + s, _ := containerMemoryRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("2"), + "memory": resource.MustParse("512Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 1) @@ -517,40 +567,42 @@ func TestFailContainerMemoryRequestsEqualLimits(t *testing.T) { func TestFailInitContainerMemoryRequestsEqualLimits(t *testing.T) { t.Parallel() - s := containerMemoryRequestsEqualLimits( - corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{ - { - Name: "init", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "memory": resource.MustParse("512Mi"), + s, _ := containerMemoryRequestsEqualLimits( + &podSpeccer{ + spec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "init", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "memory": resource.MustParse("512Mi"), + }, }, }, }, - }, - Containers: []corev1.Container{ - { - Name: "foo", - Resources: corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), - }, - Limits: map[corev1.ResourceName]resource.Quantity{ - "cpu": resource.MustParse("1"), - "memory": resource.MustParse("256Mi"), + Containers: []corev1.Container{ + { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": resource.MustParse("1"), + "memory": resource.MustParse("256Mi"), + }, }, }, }, }, }, }, - metav1.TypeMeta{}) + ) assert.Equal(t, scorecard.GradeCritical, s.Grade) assert.Len(t, s.Comments, 1) diff --git a/score/networkpolicy/networkpolicy.go b/score/networkpolicy/networkpolicy.go index 2a8bfbab..f5630a11 100644 --- a/score/networkpolicy/networkpolicy.go +++ b/score/networkpolicy/networkpolicy.go @@ -1,7 +1,6 @@ package networkpolicy import ( - corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,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 []ks.NetworkPolicy) func(spec corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) scorecard.TestScore { - return func(podSpec corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { +func podHasNetworkPolicy(allNetpols []ks.NetworkPolicy) func(ks.PodSpecer) (scorecard.TestScore, error) { + return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) { hasMatchingEgressNetpol := false hasMatchingIngressNetpol := false @@ -27,12 +26,12 @@ func podHasNetworkPolicy(allNetpols []ks.NetworkPolicy) func(spec corev1.PodTemp netPol := n.NetworkPolicy() // Make sure that the pod and networkpolicy is in the same namespace - if podSpec.Namespace != netPol.Namespace { + if ps.GetPodTemplateSpec().Namespace != netPol.Namespace { continue } if selector, err := metav1.LabelSelectorAsSelector(&netPol.Spec.PodSelector); err == nil { - if selector.Matches(internal.MapLabels(podSpec.Labels)) { + if selector.Matches(internal.MapLabels(ps.GetPodTemplateSpec().Labels)) { // Documentation of PolicyTypes // diff --git a/score/networkpolicy/networkpolicy_test.go b/score/networkpolicy/networkpolicy_test.go index 46721ab7..a0ae76a1 100644 --- a/score/networkpolicy/networkpolicy_test.go +++ b/score/networkpolicy/networkpolicy_test.go @@ -102,7 +102,8 @@ func TestPodHasNetworkPolicy(t *testing.T) { } fn := podHasNetworkPolicy([]domain.NetworkPolicy{np{Obj: pol}}) - score := fn(corev1.PodTemplateSpec{ObjectMeta: pod.ObjectMeta, Spec: pod.Spec}, pod.TypeMeta) + spec := corev1.PodTemplateSpec{ObjectMeta: pod.ObjectMeta, Spec: pod.Spec} + score, _ := fn(&podSpeccer{spec: spec}) assert.Equal(t, tc.expected, score.Grade, "caseID = %d", caseID) } } @@ -118,3 +119,25 @@ func (n np) NetworkPolicy() v1.NetworkPolicy { func (np) FileLocation() domain.FileLocation { return domain.FileLocation{} } + +type podSpeccer struct { + typeMeta metav1.TypeMeta + objectMeta metav1.ObjectMeta + spec corev1.PodTemplateSpec +} + +func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta { + return p.typeMeta +} + +func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta { + return p.objectMeta +} + +func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec { + return p.spec +} + +func (p *podSpeccer) FileLocation() domain.FileLocation { + return domain.FileLocation{} +} diff --git a/score/probes/probes.go b/score/probes/probes.go index a6831012..f5ede4c2 100644 --- a/score/probes/probes.go +++ b/score/probes/probes.go @@ -1,13 +1,11 @@ package probes import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ks "github.com/zegl/kube-score/domain" "github.com/zegl/kube-score/score/checks" "github.com/zegl/kube-score/score/internal" "github.com/zegl/kube-score/scorecard" + corev1 "k8s.io/api/core/v1" ) func Register(allChecks *checks.Checks, services ks.Services) { @@ -19,13 +17,15 @@ 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 []ks.Service) func(corev1.PodTemplateSpec, metav1.TypeMeta) scorecard.TestScore { - return func(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { +func containerProbes(allServices []ks.Service) func(ks.PodSpecer) (scorecard.TestScore, error) { + return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + typeMeta := ps.GetTypeMeta() if typeMeta.Kind == "CronJob" && typeMeta.GroupVersionKind().Group == "batch" || typeMeta.Kind == "Job" && typeMeta.GroupVersionKind().Group == "batch" { score.Grade = scorecard.GradeAllOK - return score + return score, nil } + podTemplate := ps.GetPodTemplateSpec() allContainers := podTemplate.Spec.InitContainers allContainers = append(allContainers, podTemplate.Spec.Containers...) @@ -94,13 +94,13 @@ func containerProbes(allServices []ks.Service) func(corev1.PodTemplateSpec, meta "Using the same probe for liveness and readiness is very likely dangerous. Generally it's better to avoid the livenessProbe than re-using the readinessProbe.", "https://github.com/zegl/kube-score/blob/master/README_PROBES.md", ) - return score + return score, nil } if !isTargetedByService { score.Grade = scorecard.GradeAllOK score.AddComment("", "The pod is not targeted by a service, skipping probe checks.", "") - return score + return score, nil } if !hasReadinessProbe { @@ -111,7 +111,7 @@ func containerProbes(allServices []ks.Service) func(corev1.PodTemplateSpec, meta "It's also used during rollouts, and can prevent downtime if a new version of the application is failing.", "https://github.com/zegl/kube-score/blob/master/README_PROBES.md", ) - return score + return score, nil } if !hasLivenessProbe { @@ -121,12 +121,12 @@ func containerProbes(allServices []ks.Service) func(corev1.PodTemplateSpec, meta "It's only recommended to setup a livenessProbe if you really need one.", "https://github.com/zegl/kube-score/blob/master/README_PROBES.md", ) - return score + return score, nil } score.Grade = scorecard.GradeAllOK - return score + return score, nil } } diff --git a/score/score.go b/score/score.go index c8fafcc1..8f538a44 100644 --- a/score/score.go +++ b/score/score.go @@ -41,6 +41,28 @@ func RegisterAllChecks(allObjects ks.AllTypes, cnf config.Configuration) *checks return allChecks } +type podSpeccer struct { + typeMeta metav1.TypeMeta + objectMeta metav1.ObjectMeta + spec corev1.PodTemplateSpec +} + +func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta { + return p.typeMeta +} + +func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta { + return p.objectMeta +} + +func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec { + return p.spec +} + +func (p *podSpeccer) FileLocation() ks.FileLocation { + return ks.FileLocation{} +} + // Score runs a pre-configured list of tests against the files defined in the configuration, and returns a scorecard. // Additional configuration and tuning parameters can be provided via the config. func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scorecard, error) { @@ -76,10 +98,17 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca for _, pod := range allObjects.Pods() { o := newObject(pod.Pod().TypeMeta, pod.Pod().ObjectMeta) for _, test := range allChecks.Pods() { - score := test.Fn(corev1.PodTemplateSpec{ + + podTemplateSpec := corev1.PodTemplateSpec{ ObjectMeta: pod.Pod().ObjectMeta, Spec: pod.Pod().Spec, - }, pod.Pod().TypeMeta) + } + + score, _ := test.Fn(&podSpeccer{ + typeMeta: pod.Pod().TypeMeta, + objectMeta: pod.Pod().ObjectMeta, + spec: podTemplateSpec, + }) o.Add(score, test.Check, pod, pod.Pod().ObjectMeta.Annotations) } } @@ -87,7 +116,7 @@ func Score(allObjects ks.AllTypes, cnf config.Configuration) (*scorecard.Scoreca for _, podspecer := range allObjects.PodSpeccers() { o := newObject(podspecer.GetTypeMeta(), podspecer.GetObjectMeta()) for _, test := range allChecks.Pods() { - score := test.Fn(podspecer.GetPodTemplateSpec(), podspecer.GetTypeMeta()) + score, _ := test.Fn(podspecer) o.Add(score, test.Check, podspecer, podspecer.GetObjectMeta().Annotations, podspecer.GetPodTemplateSpec().Annotations, diff --git a/score/security/security.go b/score/security/security.go index 9ed5504f..4262a8d5 100644 --- a/score/security/security.go +++ b/score/security/security.go @@ -1,11 +1,10 @@ package security import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - + ks "github.com/zegl/kube-score/domain" "github.com/zegl/kube-score/score/checks" "github.com/zegl/kube-score/scorecard" + corev1 "k8s.io/api/core/v1" ) func Register(allChecks *checks.Checks) { @@ -17,9 +16,9 @@ func Register(allChecks *checks.Checks) { } // containerSecurityContextReadOnlyRootFilesystem checks for pods using writeable root filesystems -func containerSecurityContextReadOnlyRootFilesystem(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) +func containerSecurityContextReadOnlyRootFilesystem(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) noContextSet := false hasWritableRootFS := false @@ -47,9 +46,9 @@ func containerSecurityContextReadOnlyRootFilesystem(podTemplate corev1.PodTempla } // containerSecurityContextPrivileged checks for privileged containers -func containerSecurityContextPrivileged(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) +func containerSecurityContextPrivileged(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) hasPrivileged := false for _, container := range allContainers { if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged { @@ -66,10 +65,10 @@ func containerSecurityContextPrivileged(podTemplate corev1.PodTemplateSpec, type } // containerSecurityContextUserGroupID checks that the user and group are valid ( > 10000) in the security context -func containerSecurityContextUserGroupID(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - allContainers := podTemplate.Spec.InitContainers - allContainers = append(allContainers, podTemplate.Spec.Containers...) - podSecurityContext := podTemplate.Spec.SecurityContext +func containerSecurityContextUserGroupID(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + allContainers := ps.GetPodTemplateSpec().Spec.InitContainers + allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...) + podSecurityContext := ps.GetPodTemplateSpec().Spec.SecurityContext noContextSet := false hasLowUserID := false hasLowGroupID := false @@ -110,9 +109,9 @@ func containerSecurityContextUserGroupID(podTemplate corev1.PodTemplateSpec, typ return } -// podSeccompProfile checks if the any Seccommp profile is configured for the pod -func podSeccompProfile(podTemplate corev1.PodTemplateSpec, typeMeta metav1.TypeMeta) (score scorecard.TestScore) { - metadata := podTemplate.ObjectMeta +// podSeccompProfile checks that a Seccommp profile is configured for the pod +func podSeccompProfile(ps ks.PodSpecer) (score scorecard.TestScore, err error) { + metadata := ps.GetPodTemplateSpec().ObjectMeta seccompAnnotated := false if metadata.Annotations != nil {