Repository: FairwindsOps/polaris
Pull request #342 (2020-06-10 20:50:15)

Add priority class check, some test infra


Patch:
diff --git a/checks/priorityClassNotSet.yaml b/checks/priorityClassNotSet.yaml
new file mode 100644
index 00000000..3516a3fe
--- /dev/null
+++ b/checks/priorityClassNotSet.yaml
@@ -0,0 +1,9 @@
+successMessage: Priority class has been set
+failureMessage: Priority class should be set
+category: Security
+target: Pod
+schema:
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  required:
+  - priorityClassName
diff --git a/examples/config-full.yaml b/examples/config-full.yaml
index d9bd6cbc..ad3af46c 100644
--- a/examples/config-full.yaml
+++ b/examples/config-full.yaml
@@ -1,6 +1,7 @@
 checks:
   # reliability
   multipleReplicasForDeployment: warning
+  priorityClassNotSet: warning
   # resources
   cpuRequestsMissing: warning
   cpuLimitsMissing: warning
diff --git a/examples/config.yaml b/examples/config.yaml
index 32c4ec2e..ec68b6b6 100644
--- a/examples/config.yaml
+++ b/examples/config.yaml
@@ -1,6 +1,7 @@
 checks:
   # reliability
   multipleReplicasForDeployment: ignore
+  priorityClassNotSet: ignore
   # resources
   cpuRequestsMissing: warning
   cpuLimitsMissing: warning
diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go
index 1d6930b7..88c311d8 100644
--- a/pkg/kube/resources.go
+++ b/pkg/kube/resources.go
@@ -241,6 +241,40 @@ func addResourcesFromYaml(contents string, resources *ResourceProvider) error {
 	return nil
 }
 
+// GetWorkloadFromBytes parses a GenericWorkload
+func GetWorkloadFromBytes(contentBytes []byte) (*GenericWorkload, error) {
+	yamlNode := make(map[string]interface{})
+	err := yaml.Unmarshal(contentBytes, &yamlNode)
+	if err != nil {
+		logrus.Errorf("Invalid YAML: %s", string(contentBytes))
+		return nil, err
+	}
+	finalDoc := make(map[string]interface{})
+	finalDoc["metadata"] = yamlNode["metadata"]
+	finalDoc["apiVersion"] = "v1"
+	finalDoc["kind"] = "Pod"
+	podSpec := GetPodSpec(yamlNode)
+	if podSpec == nil {
+		return nil, nil
+	}
+	finalDoc["spec"] = podSpec
+	marshaledYaml, err := yaml.Marshal(finalDoc)
+	if err != nil {
+		logrus.Errorf("Could not marshal yaml: %v", err)
+		return nil, err
+	}
+	decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(marshaledYaml), 1000)
+	pod := corev1.Pod{}
+	err = decoder.Decode(&pod)
+	newController, err := NewGenericWorkloadFromPod(pod, yamlNode)
+
+	if err != nil {
+		return nil, err
+	}
+	newController.Kind = yamlNode["kind"].(string)
+	return &newController, nil
+}
+
 func addResourceFromString(contents string, resources *ResourceProvider) error {
 	contentBytes := []byte(contents)
 	decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
@@ -268,36 +302,11 @@ func addResourceFromString(contents string, resources *ResourceProvider) error {
 		}
 		resources.Controllers = append(resources.Controllers, workload)
 	} else {
-		yamlNode := make(map[string]interface{})
-		err = yaml.Unmarshal(contentBytes, &yamlNode)
-		if err != nil {
-			logrus.Errorf("Invalid YAML: %s", string(contents))
-			return err
-		}
-		finalDoc := make(map[string]interface{})
-		finalDoc["metadata"] = yamlNode["metadata"]
-		finalDoc["apiVersion"] = "v1"
-		finalDoc["kind"] = "Pod"
-		podSpec := GetPodSpec(yamlNode)
-		if podSpec == nil {
-			return nil
-		}
-		finalDoc["spec"] = podSpec
-		marshaledYaml, err := yaml.Marshal(finalDoc)
-		if err != nil {
-			logrus.Errorf("Could not marshal yaml: %v", err)
-			return err
-		}
-		decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(marshaledYaml), 1000)
-		pod := corev1.Pod{}
-		err = decoder.Decode(&pod)
-		newController, err := NewGenericWorkloadFromPod(pod, yamlNode)
-
-		if err != nil {
+		newController, err := GetWorkloadFromBytes(contentBytes)
+		if err != nil || newController == nil {
 			return err
 		}
-		newController.Kind = resource.Kind
-		resources.Controllers = append(resources.Controllers, newController)
+		resources.Controllers = append(resources.Controllers, *newController)
 	}
 	return err
 }
diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go
index 7be791d8..567f5833 100644
--- a/pkg/validator/schema.go
+++ b/pkg/validator/schema.go
@@ -43,6 +43,7 @@ var (
 		"privilegeEscalationAllowed",
 		"dangerousCapabilities",
 		"insecureCapabilities",
+		"priorityClassNotSet",
 	}
 )
 


Test patch:
diff --git a/test/checks/priorityClassNotSet/failure.yaml b/test/checks/priorityClassNotSet/failure.yaml
new file mode 100644
index 00000000..3cae90e0
--- /dev/null
+++ b/test/checks/priorityClassNotSet/failure.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: nginx
+  labels:
+    env: test
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    imagePullPolicy: IfNotPresent
diff --git a/test/checks/priorityClassNotSet/success.yaml b/test/checks/priorityClassNotSet/success.yaml
new file mode 100644
index 00000000..e78ab066
--- /dev/null
+++ b/test/checks/priorityClassNotSet/success.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: nginx
+  labels:
+    env: test
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    imagePullPolicy: IfNotPresent
+  priorityClassName: high-priority
diff --git a/test/checks_test.go b/test/checks_test.go
new file mode 100644
index 00000000..a7d46c85
--- /dev/null
+++ b/test/checks_test.go
@@ -0,0 +1,72 @@
+package test
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/fairwindsops/polaris/pkg/config"
+	"github.com/fairwindsops/polaris/pkg/kube"
+	"github.com/fairwindsops/polaris/pkg/validator"
+)
+
+var testCases = []testCase{}
+
+type testCase struct {
+	check   string
+	input   []byte
+	failure bool
+}
+
+func init() {
+	_, baseDir, _, _ := runtime.Caller(0)
+	baseDir = filepath.Dir(baseDir) + "/checks"
+	dirs, err := ioutil.ReadDir(baseDir)
+	if err != nil {
+		panic(err)
+	}
+	for _, dir := range dirs {
+		check := dir.Name()
+		checkDir := baseDir + "/" + check
+		cases, err := ioutil.ReadDir(checkDir)
+		if err != nil {
+			panic(err)
+		}
+		for _, tc := range cases {
+			body, err := ioutil.ReadFile(checkDir + "/" + tc.Name())
+			if err != nil {
+				panic(err)
+			}
+			testCases = append(testCases, testCase{
+				check:   check,
+				input:   body,
+				failure: strings.Contains(tc.Name(), "failure"),
+			})
+		}
+	}
+}
+
+func TestChecks(t *testing.T) {
+	for _, tc := range testCases {
+		workload, err := kube.GetWorkloadFromBytes(tc.input)
+		assert.NoError(t, err)
+		c, err := config.Parse([]byte("checks:\n  " + tc.check + ": danger"))
+		assert.NoError(t, err)
+		result, err := validator.ValidateController(&c, *workload)
+		assert.NoError(t, err)
+		summary := result.GetSummary()
+		if tc.failure {
+			message := "Check " + tc.check + " passed unexpectedly"
+			assert.Equal(t, uint(0), summary.Successes, message)
+			assert.Equal(t, uint(1), summary.Dangers, message)
+		} else {
+			message := "Check " + tc.check + " failed unexpectedly"
+			assert.Equal(t, uint(1), summary.Successes, message)
+			assert.Equal(t, uint(0), summary.Dangers, message)
+		}
+	}
+}