diff --git a/checks/basic/bare_pods.go b/checks/basic/bare_pods.go new file mode 100644 index 0000000..3df93a0 --- /dev/null +++ b/checks/basic/bare_pods.go @@ -0,0 +1,70 @@ +/* +Copyright 2019 DigitalOcean + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package basic + +import ( + "fmt" + + "github.com/digitalocean/clusterlint/checks" + "github.com/digitalocean/clusterlint/kube" +) + +func init() { + checks.Register(&barePodCheck{}) +} + +type barePodCheck struct{} + +// Name returns a unique name for this check. +func (b *barePodCheck) Name() string { + return "bare-pods" +} + +// Groups returns a list of group names this check should be part of. +func (b *barePodCheck) Groups() []string { + return []string{"basic"} +} + +// Description returns a detailed human-readable description of what this check +// does. +func (b *barePodCheck) Description() string { + return "Check if there are bare pods in the cluster" +} + +// Run runs this check on a set of Kubernetes objects. It can return warnings +// (low-priority problems) and errors (high-priority problems) as well as an +// error value indicating that the check failed to run. +func (b *barePodCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) { + var diagnostics []checks.Diagnostic + for _, pod := range objects.Pods.Items { + pod := pod + fmt.Println(pod.ObjectMeta.OwnerReferences) + if len(pod.ObjectMeta.OwnerReferences) == 0 { + d := checks.Diagnostic{ + Check: b.Name(), + Severity: checks.Error, + Message: fmt.Sprintf("Avoid using bare pods in clusters"), + Kind: checks.Pod, + Object: &pod.ObjectMeta, + Owners: pod.ObjectMeta.GetOwnerReferences(), + } + diagnostics = append(diagnostics, d) + } + } + + return diagnostics, nil +} diff --git a/checks/basic/bare_pods_test.go b/checks/basic/bare_pods_test.go new file mode 100644 index 0000000..6e1688e --- /dev/null +++ b/checks/basic/bare_pods_test.go @@ -0,0 +1,107 @@ +package basic + +import ( + "testing" + + "github.com/digitalocean/clusterlint/checks" + "github.com/digitalocean/clusterlint/kube" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBarePodCheckMeta(t *testing.T) { + barePodCheck := barePodCheck{} + assert.Equal(t, "bare-pods", barePodCheck.Name()) + assert.Equal(t, []string{"basic"}, barePodCheck.Groups()) + assert.NotEmpty(t, barePodCheck.Description()) +} + +func TestBarePodCheckRegistration(t *testing.T) { + barePodCheck := &barePodCheck{} + check, err := checks.Get(barePodCheck.Name()) + assert.NoError(t, err, "Should not fail registration") + assert.Equal(t, check, barePodCheck) +} + +func TestBarePodError(t *testing.T) { + tests := []struct { + name string + objs *kube.Objects + expected []checks.Diagnostic + }{ + { + name: "no pods", + objs: &kube.Objects{Pods: &corev1.PodList{}}, + expected: nil, + }, + { + name: "pod has owner ref", + objs: initRefs(initPod()), + expected: nil, + }, + { + name: "multiple pods with owner refs", + objs: initRefs(initMultiplePods()), + expected: nil, + }, + { + name: "pod has no owner ref", + objs: initPod(), + expected: []checks.Diagnostic{ + { + Severity: "error", + Check: "bare-pods", + Kind: checks.Pod, + Message: "Avoid using bare pods in clusters", + Object: GetObjectMeta(), + Owners: nil, + }, + }, + }, + { + name: "multiple pods with no owner ref", + objs: initMultiplePods(), + expected: []checks.Diagnostic{ + { + Severity: "error", + Check: "bare-pods", + Kind: checks.Pod, + Message: "Avoid using bare pods in clusters", + Object: &metav1.ObjectMeta{Name: "pod_1", Namespace: "k8s"}, + Owners: nil, + }, + { + Severity: "error", + Check: "bare-pods", + Kind: checks.Pod, + Message: "Avoid using bare pods in clusters", + Object: &metav1.ObjectMeta{Name: "pod_2", Namespace: "k8s"}, + Owners: nil, + }, + }, + }, + } + + barePodCheck := &barePodCheck{} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + d, err := barePodCheck.Run(test.objs) + assert.NoError(t, err) + assert.ElementsMatch(t, test.expected, d) + }) + } +} + +func initRefs(objs *kube.Objects) *kube.Objects { + for index, _ := range objs.Pods.Items { + objs.Pods.Items[index].ObjectMeta.OwnerReferences = []metav1.OwnerReference{ + { + Name: "Deployment", + APIVersion: "apps/v1", + }, + } + } + return objs +} diff --git a/checks/basic/helper_test.go b/checks/basic/helper_test.go index 3dc7b08..13c891d 100644 --- a/checks/basic/helper_test.go +++ b/checks/basic/helper_test.go @@ -37,6 +37,24 @@ func initPod() *kube.Objects { return objs } +func initMultiplePods() *kube.Objects { + objs := &kube.Objects{ + Pods: &corev1.PodList{ + Items: []corev1.Pod{ + { + TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, + ObjectMeta: metav1.ObjectMeta{Name: "pod_1", Namespace: "k8s"}, + }, + { + TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, + ObjectMeta: metav1.ObjectMeta{Name: "pod_2", Namespace: "k8s"}, + }, + }, + }, + } + return objs +} + func GetObjectMeta() *metav1.ObjectMeta { objs := initPod() return &objs.Pods.Items[0].ObjectMeta