From baa0bf739cdd4a599e40170a75ca718d7c70612f Mon Sep 17 00:00:00 2001 From: Varsha Varadarajan Date: Fri, 14 Jun 2019 15:22:17 -0400 Subject: [PATCH] Add node name check: Checks for pods which use node name in the node selector. --- checks/all/all.go | 1 + checks/doks/node_name_pod_selector.go | 45 ++++++++++++++ checks/doks/node_name_pod_selector_test.go | 71 ++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 checks/doks/node_name_pod_selector.go create mode 100644 checks/doks/node_name_pod_selector_test.go diff --git a/checks/all/all.go b/checks/all/all.go index 0821e8f..e835ee8 100644 --- a/checks/all/all.go +++ b/checks/all/all.go @@ -4,5 +4,6 @@ package all import ( _ "github.com/digitalocean/clusterlint/checks/basic" + _ "github.com/digitalocean/clusterlint/checks/doks" _ "github.com/digitalocean/clusterlint/checks/noop" ) diff --git a/checks/doks/node_name_pod_selector.go b/checks/doks/node_name_pod_selector.go new file mode 100644 index 0000000..090969e --- /dev/null +++ b/checks/doks/node_name_pod_selector.go @@ -0,0 +1,45 @@ +package doks + +import ( + "fmt" + + "github.com/digitalocean/clusterlint/checks" + "github.com/digitalocean/clusterlint/kube" + corev1 "k8s.io/api/core/v1" +) + +func init() { + checks.Register(&podSelectorCheck{}) +} + +type podSelectorCheck struct{} + +// Name returns a unique name for this check. +func (nc *podSelectorCheck) Name() string { + return "node-name-pod-selector" +} + +// Groups returns a list of group names this check should be part of. +func (nc *podSelectorCheck) Groups() []string { + return []string{"doks"} +} + +// Description returns a detailed human-readable description of what this check +// does. +func (nc *podSelectorCheck) Description() string { + return "Checks if there are pods which use kubernetes.io/hostname label in the node selector." +} + +// 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 (nc *podSelectorCheck) Run(objects *kube.Objects) (warnings []error, errors []error, err error) { + var e []error + for _, pod := range objects.Pods.Items { + nodeSelectorMap := pod.Spec.NodeSelector + if _, ok := nodeSelectorMap[corev1.LabelHostname]; ok { + e = append(e, fmt.Errorf("pod '%s' in namespace '%s' uses the node name for node selector", pod.GetName(), pod.GetNamespace())) + } + } + return nil, e, nil +} diff --git a/checks/doks/node_name_pod_selector_test.go b/checks/doks/node_name_pod_selector_test.go new file mode 100644 index 0000000..61ab94d --- /dev/null +++ b/checks/doks/node_name_pod_selector_test.go @@ -0,0 +1,71 @@ +package doks + +import ( + "fmt" + "testing" + + "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 TestGroup(t *testing.T) { + podSelectorCheck := podSelectorCheck{} + assert.Equal(t, []string{"doks"}, podSelectorCheck.Groups()) +} + +func TestNodeNameError(t *testing.T) { + scenarios := []struct { + name string + arg *kube.Objects + expected []error + }{ + { + name: "no node name selector", + arg: empty(), + expected: nil, + }, + { + name: "node name used in node selector", + arg: invalidPod(), + expected: errors(), + }, + } + + podSelectorCheck := podSelectorCheck{} + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + _, e, _ := podSelectorCheck.Run(scenario.arg) + assert.ElementsMatch(t, scenario.expected, e) + }) + } +} + +func empty() *kube.Objects { + objs := &kube.Objects{ + Pods: &corev1.PodList{}, + } + return objs +} + +func invalidPod() *kube.Objects { + objs := empty() + objs.Pods = &corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"}, + Spec: corev1.PodSpec{NodeSelector: map[string]string{corev1.LabelHostname: "foo"}}, + }, + }, + } + return objs +} + +func errors() []error { + e := []error{ + fmt.Errorf("pod 'pod_foo' in namespace 'k8s' uses the node name for node selector"), + } + return e +}