refactor a bunch

This commit is contained in:
Robert Brennan
2021-03-17 21:06:17 +00:00
parent 5b8c1b28a9
commit 32e2f71ed8
18 changed files with 340 additions and 330 deletions
+5 -3
View File
@@ -2,10 +2,12 @@ package config
import (
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// IsActionable determines whether a check is actionable given the current configuration
func (conf Configuration) IsActionable(ruleID, namespace, controllerName, containerName string) bool {
func (conf Configuration) IsActionable(ruleID string, objMeta metav1.Object, containerName string) bool {
if severity, ok := conf.Checks[ruleID]; !ok || !severity.IsActionable() {
return false
}
@@ -13,7 +15,7 @@ func (conf Configuration) IsActionable(ruleID, namespace, controllerName, contai
return true
}
for _, exemption := range conf.Exemptions {
if exemption.Namespace != "" && exemption.Namespace != namespace {
if exemption.Namespace != "" && exemption.Namespace != objMeta.GetNamespace() {
continue
}
@@ -27,7 +29,7 @@ func (conf Configuration) IsActionable(ruleID, namespace, controllerName, contai
}
if len(exemption.Rules) == 0 || checkIfRuleMatches {
if !isExemptionCheckMatched(exemption.ControllerNames, controllerName) {
if !isExemptionCheckMatched(exemption.ControllerNames, objMeta.GetName()) {
continue
}
if isExemptionCheckMatched(exemption.ContainerNames, containerName) {
+63 -49
View File
@@ -18,6 +18,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
var confContainerTest = `
@@ -68,26 +71,37 @@ exemptions:
- namespace: polaris
`
func createMeta(name, namespace string) metav1.Object {
unst := unstructured.Unstructured{}
obj, err := meta.Accessor(&unst)
if err != nil {
panic(err)
}
obj.SetName(name)
obj.SetNamespace(namespace)
return obj
}
func TestNamespaceExemptionForSpecifiedRules(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "", "")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", ""), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller1", "container11")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller1"), "container11")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "", "container11")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", ""), "container11")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller1", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller1"), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("pullPolicyNotAlways", "prometheus", "controller1", "")
actionable = parsedConf.IsActionable("pullPolicyNotAlways", createMeta("prometheus", "controller1"), "")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "")
assert.True(t, actionable)
}
@@ -95,19 +109,19 @@ func TestNamespaceExemptionForAllRules(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "", "")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", ""), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "controller1", "container11")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", "controller1"), "container11")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "", "container11")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", ""), "container11")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "controller1", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", "controller1"), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("pullPolicyNotAlways", "polaris", "controller1", "")
actionable = parsedConf.IsActionable("pullPolicyNotAlways", createMeta("polaris", "controller1"), "")
assert.False(t, actionable)
}
@@ -115,28 +129,28 @@ func TestControllerExemption(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller2", "")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller2"), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller2", "container21")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller2"), "container21")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller2", "container21")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller2"), "container21")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller2", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller2"), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller3", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller3"), "")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller3", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller3"), "")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller3", "container31")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller3"), "container31")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller4", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller4"), "")
assert.True(t, actionable)
}
@@ -144,22 +158,22 @@ func TestOnlyContainerExemption(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container41")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container41")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container42")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container42")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller4", "container41")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller4"), "container41")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container41")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container41")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller4", "container41")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller4"), "container41")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container51")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container51")
assert.True(t, actionable)
}
@@ -167,25 +181,25 @@ func TestNamespaceAndContainerExemption(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container51")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container51")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("priorityClassNotSet", "kube-system", "", "container51")
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("kube-system", ""), "container51")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller5", "container51")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller5"), "container51")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller5", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller5"), "")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "insights-agent", "", "container51")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("insights-agent", ""), "container51")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container51")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container51")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller5", "container51")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller5"), "container51")
assert.True(t, actionable)
}
@@ -193,25 +207,25 @@ func TestControllerAndContainerExemption(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller6", "container61")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller6"), "container61")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("priorityClassNotSet", "", "controller6", "container61")
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("", "controller6"), "container61")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "container61")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "container61")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller7", "container61")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller7"), "container61")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container61")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container61")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container61")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container61")
assert.True(t, actionable)
}
@@ -219,33 +233,33 @@ func TestContainerExemption(t *testing.T) {
parsedConf, err := Parse([]byte(confContainerTest))
assert.NoError(t, err)
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container71")
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container71")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container71")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container71")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller7", "container71")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller7"), "container71")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "container71")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "container71")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "insights-agent", "controller7", "container71")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("insights-agent", "controller7"), "container71")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "container71")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "container71")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "container61")
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "container61")
assert.True(t, actionable)
actionable = parsedConf.IsActionable("priorityClassNotSet", "kube-system", "controller7", "container71")
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("kube-system", "controller7"), "container71")
assert.False(t, actionable)
actionable = parsedConf.IsActionable("pullPolicyNotAlways", "kube-system", "controller8", "container71")
actionable = parsedConf.IsActionable("pullPolicyNotAlways", createMeta("kube-system", "controller8"), "container71")
assert.True(t, actionable)
}
+5 -4
View File
@@ -3,6 +3,7 @@ package config
import (
"encoding/json"
"fmt"
"strings"
"github.com/qri-io/jsonschema"
corev1 "k8s.io/api/core/v1"
@@ -158,13 +159,13 @@ func (check SchemaCheck) CheckObject(obj interface{}) (bool, error) {
}
// IsActionable decides if this check applies to a particular target
func (check SchemaCheck) IsActionable(target TargetKind, controllerType string, isInit bool) bool {
if check.Target != target {
func (check SchemaCheck) IsActionable(target TargetKind, kind string, isInit bool) bool {
if check.Target != target && string(check.Target) != kind && !strings.HasSuffix(string(check.Target), "/"+kind) {
return false
}
isIncluded := len(check.Controllers.Include) == 0
for _, inclusion := range check.Controllers.Include {
if inclusion == controllerType {
if inclusion == kind {
isIncluded = true
break
}
@@ -173,7 +174,7 @@ func (check SchemaCheck) IsActionable(target TargetKind, controllerType string,
return false
}
for _, exclusion := range check.Controllers.Exclude {
if exclusion == controllerType {
if exclusion == kind {
return false
}
}
+42 -65
View File
@@ -1,7 +1,6 @@
package kube
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -13,22 +12,23 @@ import (
kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
k8sYaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
)
// GenericWorkload is a base implementation with some free methods for inherited structs
type GenericWorkload struct {
// GenericResource is a base implementation with some free methods for inherited structs
type GenericResource struct {
Kind string
PodSpec kubeAPICoreV1.PodSpec
ObjectMeta kubeAPIMetaV1.Object
Resource unstructured.Unstructured
PodSpec *kubeAPICoreV1.PodSpec
OriginalObjectJSON []byte
}
// NewGenericWorkloadFromUnstructured creates a workload from an unstructured.Unstructured
func NewGenericWorkloadFromUnstructured(kind string, unst *unstructured.Unstructured) (GenericWorkload, error) {
workload := GenericWorkload{
Kind: kind,
// NewGenericResourceFromUnstructured creates a workload from an unstructured.Unstructured
func NewGenericResourceFromUnstructured(unst *unstructured.Unstructured) (GenericResource, error) {
workload := GenericResource{
Kind: unst.GetKind(),
Resource: *unst,
}
objMeta, err := meta.Accessor(unst)
@@ -49,25 +49,27 @@ func NewGenericWorkloadFromUnstructured(kind string, unst *unstructured.Unstruct
return workload, err
}
podSpecMap := GetPodSpec(m)
b, err = json.Marshal(podSpecMap)
if err != nil {
return workload, err
if podSpecMap != nil {
b, err = json.Marshal(podSpecMap)
if err != nil {
return workload, err
}
podSpec := kubeAPICoreV1.PodSpec{}
err = json.Unmarshal(b, &podSpec)
if err != nil {
return workload, err
}
workload.PodSpec = &podSpec
}
podSpec := kubeAPICoreV1.PodSpec{}
err = json.Unmarshal(b, &podSpec)
if err != nil {
return workload, err
}
workload.PodSpec = podSpec
return workload, nil
}
// NewGenericWorkloadFromPod builds a new workload for a given Pod without looking at parents
func NewGenericWorkloadFromPod(podResource kubeAPICoreV1.Pod, originalObject interface{}) (GenericWorkload, error) {
workload := GenericWorkload{
// NewGenericResourceFromPod builds a new workload for a given Pod without looking at parents
func NewGenericResourceFromPod(podResource kubeAPICoreV1.Pod, originalObject interface{}) (GenericResource, error) {
workload := GenericResource{
Kind: "Pod",
PodSpec: podResource.Spec,
PodSpec: &podResource.Spec,
ObjectMeta: podResource.ObjectMeta.GetObjectMeta(),
}
if originalObject != nil {
@@ -77,13 +79,12 @@ func NewGenericWorkloadFromPod(podResource kubeAPICoreV1.Pod, originalObject int
}
workload.OriginalObjectJSON = bytes
var unst unstructured.Unstructured
err = json.Unmarshal(bytes, &unst.Object)
err = json.Unmarshal(bytes, &workload.Resource.Object)
if err != nil {
logrus.Error("Couldn't marshal JSON for pod ", err)
return workload, err
}
objMeta, err := meta.Accessor(&unst)
objMeta, err := meta.Accessor(&workload.Resource)
if err != nil {
logrus.Error("Couldn't create meta accessor for unstructred ", err)
return workload, err
@@ -93,20 +94,30 @@ func NewGenericWorkloadFromPod(podResource kubeAPICoreV1.Pod, originalObject int
return workload, nil
}
// NewGenericWorkload builds a new workload for a given Pod
func NewGenericWorkload(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient *dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericWorkload, error) {
workload, err := newGenericWorkload(ctx, podResource, dynamicClient, restMapper, objectCache)
// NewGenericResourceFromBytes parses a generic kubernetes resource
func NewGenericResourceFromBytes(contentBytes []byte) (GenericResource, error) {
unst := unstructured.Unstructured{}
err := yaml.Unmarshal(contentBytes, &unst.Object)
if err != nil {
return GenericResource{}, err
}
return NewGenericResourceFromUnstructured(&unst)
}
// NewGenericResource builds a new workload for a given Pod
func NewGenericResource(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient *dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
workload, err := newGenericResource(ctx, podResource, dynamicClient, restMapper, objectCache)
if err != nil {
return workload, err
}
if len(workload.OriginalObjectJSON) == 0 {
return NewGenericWorkloadFromPod(podResource, podResource)
return NewGenericResourceFromPod(podResource, podResource)
}
return workload, err
}
func newGenericWorkload(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient *dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericWorkload, error) {
workload, err := NewGenericWorkloadFromPod(podResource, nil)
func newGenericResource(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient *dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
workload, err := NewGenericResourceFromPod(podResource, nil)
if err != nil {
return workload, err
}
@@ -207,37 +218,3 @@ func GetPodSpec(yaml map[string]interface{}) interface{} {
}
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 := kubeAPICoreV1.Pod{}
err = decoder.Decode(&pod)
newController, err := NewGenericWorkloadFromPod(pod, yamlNode)
if err != nil {
return nil, err
}
newController.Kind = yamlNode["kind"].(string)
return &newController, nil
}
+46 -48
View File
@@ -31,14 +31,14 @@ import (
// ResourceProvider contains k8s resources to be audited
type ResourceProvider struct {
ServerVersion string
CreationTime time.Time
SourceName string
SourceType string
Nodes []corev1.Node
Namespaces []corev1.Namespace
Controllers []GenericWorkload
ArbitraryKinds map[string]*unstructured.Unstructured
ServerVersion string
CreationTime time.Time
SourceName string
SourceType string
Nodes []corev1.Node
Namespaces []corev1.Namespace
Controllers []GenericResource
OtherKinds map[string][]GenericResource
}
type k8sResource struct {
@@ -50,7 +50,7 @@ var podSpecFields = []string{"jobTemplate", "spec", "template"}
// CreateResourceProvider returns a new ResourceProvider object to interact with k8s resources
func CreateResourceProvider(ctx context.Context, directory, workload string, c conf.Configuration) (*ResourceProvider, error) {
if workload != "" {
return CreateResourceProviderFromWorkload(ctx, workload)
return CreateResourceProviderFromResource(ctx, workload)
}
if directory != "" {
return CreateResourceProviderFromPath(directory)
@@ -58,8 +58,8 @@ func CreateResourceProvider(ctx context.Context, directory, workload string, c c
return CreateResourceProviderFromCluster(ctx, c)
}
// CreateResourceProviderFromWorkload creates a new ResourceProvider that just contains one workload
func CreateResourceProviderFromWorkload(ctx context.Context, workload string) (*ResourceProvider, error) {
// CreateResourceProviderFromResource creates a new ResourceProvider that just contains one workload
func CreateResourceProviderFromResource(ctx context.Context, workload string) (*ResourceProvider, error) {
kubeConf, configError := config.GetConfig()
if configError != nil {
logrus.Errorf("Error fetching KubeConfig: %v", configError)
@@ -77,7 +77,7 @@ func CreateResourceProviderFromWorkload(ctx context.Context, workload string) (*
}
resources := ResourceProvider{
ServerVersion: serverVersion.Major + "." + serverVersion.Minor,
SourceType: "Workload",
SourceType: "Resource",
SourceName: workload,
CreationTime: time.Now(),
Nodes: []corev1.Node{},
@@ -109,13 +109,13 @@ func CreateResourceProviderFromWorkload(ctx context.Context, workload string) (*
logrus.Errorf("Could not find workload %s: %v", workload, err)
return nil, err
}
workloadObj, err := NewGenericWorkloadFromUnstructured(kind, obj)
workloadObj, err := NewGenericResourceFromUnstructured(obj)
if err != nil {
logrus.Errorf("Could not parse workload %s: %v", workload, err)
return nil, err
}
resources.Controllers = []GenericWorkload{workloadObj}
resources.Controllers = []GenericResource{workloadObj}
return &resources, nil
}
@@ -127,7 +127,7 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
SourceName: directory,
Nodes: []corev1.Node{},
Namespaces: []corev1.Namespace{},
Controllers: []GenericWorkload{},
Controllers: []GenericResource{},
}
if directory == "-" {
@@ -218,7 +218,7 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
}
}
var arbitraryObjects map[string]*unstructured.Unstructured
otherObjects := map[string][]GenericResource{}
for _, kind := range additionalKinds {
groupKind := schema.ParseGroupKind(string(kind))
mapping, err := (restMapper).RESTMapping(groupKind)
@@ -233,7 +233,14 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
return nil, err
}
for _, obj := range objects.Items {
arbitraryObjects[string(kind)] = &obj
res, err := NewGenericResourceFromUnstructured(&obj)
if err != nil {
return nil, err
}
if _, ok := otherObjects[res.Kind]; !ok {
otherObjects[res.Kind] = make([]GenericResource, 0)
}
otherObjects[res.Kind] = append(otherObjects[res.Kind], res)
}
}
@@ -246,21 +253,21 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
}
api := ResourceProvider{
ServerVersion: serverVersion.Major + "." + serverVersion.Minor,
SourceType: "Cluster",
SourceName: clusterName,
CreationTime: time.Now(),
Nodes: nodes.Items,
Namespaces: namespaces.Items,
Controllers: controllers,
ArbitraryKinds: arbitraryObjects,
ServerVersion: serverVersion.Major + "." + serverVersion.Minor,
SourceType: "Cluster",
SourceName: clusterName,
CreationTime: time.Now(),
Nodes: nodes.Items,
Namespaces: namespaces.Items,
Controllers: controllers,
OtherKinds: otherObjects,
}
return &api, nil
}
// LoadControllers loads a list of controllers from the kubeResources Pods
func LoadControllers(ctx context.Context, pods []corev1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) ([]GenericWorkload, error) {
interfaces := []GenericWorkload{}
func LoadControllers(ctx context.Context, pods []corev1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) ([]GenericResource, error) {
interfaces := []GenericResource{}
deduped := map[string]corev1.Pod{}
for _, pod := range pods {
owners := pod.ObjectMeta.OwnerReferences
@@ -271,7 +278,7 @@ func LoadControllers(ctx context.Context, pods []corev1.Pod, dynamicClientPointe
deduped[pod.ObjectMeta.Namespace+"/"+owners[0].Kind+"/"+owners[0].Name] = pod
}
for _, pod := range deduped {
workload, err := NewGenericWorkload(ctx, pod, dynamicClientPointer, restMapperPointer, objectCache)
workload, err := NewGenericResource(ctx, pod, dynamicClientPointer, restMapperPointer, objectCache)
if err != nil {
return nil, err
}
@@ -282,8 +289,8 @@ func LoadControllers(ctx context.Context, pods []corev1.Pod, dynamicClientPointe
// Because the controllers with an Owner take on the name of the Owner, this eliminates any duplicates.
// In cases like CronJobs older children can hang around, so this takes the most recent.
func deduplicateControllers(inputControllers []GenericWorkload) []GenericWorkload {
controllerMap := make(map[string]GenericWorkload)
func deduplicateControllers(inputControllers []GenericResource) []GenericResource {
controllerMap := make(map[string]GenericResource)
for _, controller := range inputControllers {
key := controller.ObjectMeta.GetNamespace() + "/" + controller.Kind + "/" + controller.ObjectMeta.GetName()
oldController, ok := controllerMap[key]
@@ -291,7 +298,7 @@ func deduplicateControllers(inputControllers []GenericWorkload) []GenericWorkloa
controllerMap[key] = controller
}
}
results := make([]GenericWorkload, 0)
results := make([]GenericResource, 0)
for _, controller := range controllerMap {
results = append(results, controller)
}
@@ -346,32 +353,23 @@ func addResourceFromString(contents string, resources *ResourceProvider) error {
if err != nil {
return err
}
workload, err := NewGenericWorkloadFromPod(pod, pod)
workload, err := NewGenericResourceFromPod(pod, pod)
if err != nil {
return err
}
resources.Controllers = append(resources.Controllers, workload)
} else {
newController, err := GetWorkloadFromBytes(contentBytes)
newResource, err := NewGenericResourceFromBytes(contentBytes)
if err != nil {
return err
}
if newController != nil {
resources.Controllers = append(resources.Controllers, *newController)
} else if resource.Kind != "" {
unst := unstructured.Unstructured{}
err = decoder.Decode(&unst)
if err != nil {
fmt.Println(resource.Kind)
return err
}
if resources.ArbitraryKinds == nil {
resources.ArbitraryKinds = map[string]*unstructured.Unstructured{
resource.Kind: &unst,
}
} else {
resources.ArbitraryKinds[resource.Kind] = &unst
if newResource.PodSpec != nil {
resources.Controllers = append(resources.Controllers, newResource)
} else {
if _, ok := resources.OtherKinds[resource.Kind]; !ok {
resources.OtherKinds[resource.Kind] = make([]GenericResource, 0)
}
resources.OtherKinds[resource.Kind] = append(resources.OtherKinds[resource.Kind], newResource)
}
}
return err
+1 -1
View File
@@ -72,7 +72,7 @@ func TestAddResourcesFromReader(t *testing.T) {
SourceName: "-",
Nodes: []corev1.Node{},
Namespaces: []corev1.Namespace{},
Controllers: []GenericWorkload{},
Controllers: []GenericResource{},
}
err = addResourcesFromReader(reader, resources)
assert.NoError(t, err)
+4 -3
View File
@@ -18,21 +18,22 @@ import (
"testing"
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func TestValidateArbitraryKind(t *testing.T) {
func TestValidateOtherKind(t *testing.T) {
c := conf.Configuration{
Checks: map[string]conf.Severity{
"pdbDisruptionsAllowedGreaterThanZero": conf.SeverityWarning,
},
}
pdb := unstructured.Unstructured{}
res, err := kube.NewGenericResourceFromUnstructured(&pdb)
var actualResult Result
actualResult, err := ValidateArbitraryKind(&c, &pdb)
actualResult, err := ValidateOtherKind(&c, res)
if err != nil {
panic(err)
}
+2 -2
View File
@@ -22,7 +22,7 @@ import (
)
// ValidateContainer validates a single container from a given controller
func ValidateContainer(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ContainerResult, error) {
func ValidateContainer(conf *config.Configuration, controller kube.GenericResource, container *corev1.Container, isInit bool) (ContainerResult, error) {
results, err := applyContainerSchemaChecks(conf, controller, container, isInit)
if err != nil {
return ContainerResult{}, err
@@ -37,7 +37,7 @@ func ValidateContainer(conf *config.Configuration, controller kube.GenericWorklo
}
// ValidateAllContainers validates both init and regular containers
func ValidateAllContainers(conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) {
func ValidateAllContainers(conf *config.Configuration, controller kube.GenericResource) ([]ContainerResult, error) {
results := []ContainerResult{}
pod := controller.PodSpec
for _, container := range pod.InitContainers {
+6 -6
View File
@@ -50,8 +50,8 @@ exemptions:
- foo
`
func getEmptyWorkload(t *testing.T, name string) kube.GenericWorkload {
workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{
func getEmptyWorkload(t *testing.T, name string) kube.GenericResource {
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
@@ -64,7 +64,7 @@ func testValidate(t *testing.T, container *corev1.Container, resourceConf *strin
testValidateWithWorkload(t, container, resourceConf, getEmptyWorkload(t, controllerName), expectedDangers, expectedWarnings, expectedSuccesses)
}
func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourceConf *string, workload kube.GenericWorkload, expectedDangers []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourceConf *string, workload kube.GenericResource, expectedDangers []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
parsedConf, err := conf.Parse([]byte(*resourceConf))
assert.NoError(t, err, "Expected no error when parsing config")
@@ -921,7 +921,7 @@ func TestValidateSecurity(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{Spec: *tt.pod}, nil)
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
assert.NoError(t, err)
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, workload, tt.container, false)
if err != nil {
@@ -1066,7 +1066,7 @@ func TestValidateRunAsRoot(t *testing.T) {
}
for idx, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{Spec: *tt.pod}, nil)
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
assert.NoError(t, err)
results, err := applyContainerSchemaChecks(&config, workload, tt.container, false)
if err != nil {
@@ -1168,7 +1168,7 @@ func TestValidateResourcesEmptyContainerCPURequestsExempt(t *testing.T) {
expectedSuccesses := []ResultMessage{}
workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Annotations: map[string]string{
+2 -2
View File
@@ -22,14 +22,14 @@ import (
)
// ValidateController validates a single controller, returns a Result.
func ValidateController(conf *conf.Configuration, controller kube.GenericWorkload) (Result, error) {
func ValidateController(conf *conf.Configuration, controller kube.GenericResource) (Result, error) {
podResult, err := ValidatePod(conf, controller)
if err != nil {
return Result{}, err
}
var controllerResult ResultSet
controllerResult, err = applyControllerSchemaChecks(conf, controller)
controllerResult, err = applyTopLevelSchemaChecks(conf, controller)
if err != nil {
return Result{}, err
}
+6 -6
View File
@@ -34,7 +34,7 @@ func TestValidateController(t *testing.T) {
"hostPIDSet": conf.SeverityDanger,
},
}
deployment, err := kube.NewGenericWorkloadFromPod(test.MockPod(), nil)
deployment, err := kube.NewGenericResourceFromPod(test.MockPod(), nil)
assert.NoError(t, err)
deployment.Kind = "Deployment"
expectedSum := CountSummary{
@@ -126,7 +126,7 @@ func TestSkipHealthChecks(t *testing.T) {
}
pod := test.MockPod()
pod.Spec.InitContainers = []corev1.Container{test.MockContainer("test")}
deployment, err := kube.NewGenericWorkloadFromPod(pod, nil)
deployment, err := kube.NewGenericResourceFromPod(pod, nil)
assert.NoError(t, err)
deployment.Kind = "Deployment"
expectedSum := CountSummary{
@@ -149,7 +149,7 @@ func TestSkipHealthChecks(t *testing.T) {
assert.EqualValues(t, ResultSet{}, actualResult.PodResult.ContainerResults[0].Results)
assert.EqualValues(t, expectedResults, actualResult.PodResult.ContainerResults[1].Results)
job, err := kube.NewGenericWorkloadFromPod(test.MockPod(), nil)
job, err := kube.NewGenericResourceFromPod(test.MockPod(), nil)
assert.NoError(t, err)
job.Kind = "Job"
expectedSum = CountSummary{
@@ -167,7 +167,7 @@ func TestSkipHealthChecks(t *testing.T) {
assert.EqualValues(t, expectedSum, actualResult.GetSummary())
assert.EqualValues(t, expectedResults, actualResult.PodResult.ContainerResults[0].Results)
cronjob, err := kube.NewGenericWorkloadFromPod(test.MockPod(), nil)
cronjob, err := kube.NewGenericResourceFromPod(test.MockPod(), nil)
assert.NoError(t, err)
cronjob.Kind = "CronJob"
expectedSum = CountSummary{
@@ -207,11 +207,11 @@ func TestControllerExemptions(t *testing.T) {
pod := test.MockPod()
pod.ObjectMeta.Namespace = "foo"
workload, err := kube.NewGenericWorkloadFromPod(pod, nil)
workload, err := kube.NewGenericResourceFromPod(pod, nil)
assert.NoError(t, err)
workload.Kind = "Deployment"
resources := &kube.ResourceProvider{
Controllers: []kube.GenericWorkload{workload},
Controllers: []kube.GenericResource{workload},
}
actualResults, err = ValidateControllers(&c, resources)
+1 -1
View File
@@ -28,7 +28,7 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider, o
}
controllerCount := len(results)
arbitraryResults, err := ValidateArbitraryKinds(&config, kubeResources)
arbitraryResults, err := ValidateOtherKinds(&config, kubeResources)
if err != nil {
return AuditData{}, err
}
@@ -17,34 +17,34 @@ package validator
import (
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// ValidateArbitraryKinds validates all the unstructured objects in a ResourceProvider
func ValidateArbitraryKinds(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]Result, error) {
// ValidateOtherKinds validates all the unstructured objects in a ResourceProvider
func ValidateOtherKinds(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]Result, error) {
var results []Result
for _, arb := range kubeResources.ArbitraryKinds {
result, err := ValidateArbitraryKind(config, arb)
if err != nil {
return []Result{}, err
for _, resources := range kubeResources.OtherKinds {
for _, res := range resources {
result, err := ValidateOtherKind(config, res)
if err != nil {
return []Result{}, err
}
results = append(results, result)
}
results = append(results, result)
}
return results, nil
}
// ValidateArbitraryKind validates a single unstructured object
func ValidateArbitraryKind(config *conf.Configuration, arb *unstructured.Unstructured) (Result, error) {
results, err := applyArbitrarySchemaChecks(config, arb)
// ValidateOtherKind validates a single unstructured object
func ValidateOtherKind(config *conf.Configuration, res kube.GenericResource) (Result, error) {
results, err := applyTopLevelSchemaChecks(config, res)
if err != nil {
return Result{}, err
}
result := Result{
Kind: arb.GetKind(),
Name: arb.GetName(),
Namespace: arb.GetNamespace(),
Kind: res.Kind,
Name: res.ObjectMeta.GetName(),
Namespace: res.ObjectMeta.GetNamespace(),
Results: results,
}
+1 -1
View File
@@ -20,7 +20,7 @@ import (
)
// ValidatePod validates that each pod conforms to the Polaris config, returns a ResourceResult.
func ValidatePod(conf *config.Configuration, controller kube.GenericWorkload) (PodResult, error) {
func ValidatePod(conf *config.Configuration, controller kube.GenericResource) (PodResult, error) {
podResults, err := applyPodSchemaChecks(conf, controller)
if err != nil {
return PodResult{}, err
+5 -5
View File
@@ -36,7 +36,7 @@ func TestValidatePod(t *testing.T) {
}
p := test.MockPod()
deployment, err := kube.NewGenericWorkloadFromPod(p, nil)
deployment, err := kube.NewGenericResourceFromPod(p, nil)
assert.NoError(t, err)
expectedSum := CountSummary{
Successes: uint(4),
@@ -73,7 +73,7 @@ func TestInvalidIPCPod(t *testing.T) {
p := test.MockPod()
p.Spec.HostIPC = true
workload, err := kube.NewGenericWorkloadFromPod(p, nil)
workload, err := kube.NewGenericResourceFromPod(p, nil)
assert.NoError(t, err)
expectedSum := CountSummary{
Successes: uint(3),
@@ -109,7 +109,7 @@ func TestInvalidNetworkPod(t *testing.T) {
p := test.MockPod()
p.Spec.HostNetwork = true
workload, err := kube.NewGenericWorkloadFromPod(p, nil)
workload, err := kube.NewGenericResourceFromPod(p, nil)
assert.NoError(t, err)
expectedSum := CountSummary{
Successes: uint(3),
@@ -146,7 +146,7 @@ func TestInvalidPIDPod(t *testing.T) {
p := test.MockPod()
p.Spec.HostPID = true
workload, err := kube.NewGenericWorkloadFromPod(p, nil)
workload, err := kube.NewGenericResourceFromPod(p, nil)
assert.NoError(t, err)
expectedSum := CountSummary{
Successes: uint(3),
@@ -192,7 +192,7 @@ func TestExemption(t *testing.T) {
p.ObjectMeta = metav1.ObjectMeta{
Name: "foo",
}
workload, err := kube.NewGenericWorkloadFromPod(p, nil)
workload, err := kube.NewGenericResourceFromPod(p, nil)
assert.NoError(t, err)
expectedSum := CountSummary{
Successes: uint(3),
+127 -114
View File
@@ -9,9 +9,7 @@ import (
"github.com/gobuffalo/packr/v2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
"github.com/fairwindsops/polaris/pkg/config"
@@ -53,6 +51,21 @@ var (
}
)
type schemaTestCase struct {
Target config.TargetKind
Resource kube.GenericResource
IsInitContianer bool
Container *corev1.Container
}
func (test schemaTestCase) getName() string {
name := fmt.Sprintf("%s/%s/%s", test.Resource.Kind, test.Resource.ObjectMeta.GetNamespace(), test.Resource.ObjectMeta.GetName())
if test.Container != nil {
name += "/" + test.Container.Name
}
return name + ": " + string(test.Target)
}
func init() {
schemaBox = packr.New("Schemas", "../../checks")
for _, checkID := range checkOrder {
@@ -83,7 +96,10 @@ func parseCheck(rawBytes []byte) (config.SchemaCheck, error) {
}
}
func resolveCheck(conf *config.Configuration, checkID, kind string, target config.TargetKind, meta metaV1.Object, containerName string, isInitContainer bool) (*config.SchemaCheck, error) {
func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCase) (*config.SchemaCheck, error) {
if !conf.DisallowExemptions && hasExemptionAnnotation(test.Resource.ObjectMeta, checkID) {
return nil, nil
}
check, ok := conf.CustomChecks[checkID]
if !ok {
check, ok = builtInChecks[checkID]
@@ -92,12 +108,14 @@ func resolveCheck(conf *config.Configuration, checkID, kind string, target confi
return nil, fmt.Errorf("Check %s not found", checkID)
}
namespace := meta.GetNamespace()
name := meta.GetName()
if !conf.IsActionable(check.ID, namespace, name, containerName) {
containerName := ""
if test.Container != nil {
containerName = test.Container.Name
}
if !conf.IsActionable(check.ID, test.Resource.ObjectMeta, containerName) {
return nil, nil
}
if !check.IsActionable(target, kind, isInitContainer) {
if !check.IsActionable(test.Target, test.Resource.Kind, test.IsInitContianer) {
return nil, nil
}
return &check, nil
@@ -121,8 +139,8 @@ func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bo
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
const exemptionAnnotationPattern = "polaris.fairwinds.com/%s-exempt"
func hasExemptionAnnotation(ctrl kube.GenericWorkload, checkID string) bool {
annot := ctrl.ObjectMeta.GetAnnotations()
func hasExemptionAnnotation(objMeta metaV1.Object, checkID string) bool {
annot := objMeta.GetAnnotations()
val := annot[exemptionAnnotationKey]
if strings.ToLower(val) == "true" {
return true
@@ -135,129 +153,124 @@ func hasExemptionAnnotation(ctrl kube.GenericWorkload, checkID string) bool {
return false
}
func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) {
func ApplyAllSchemaChecks(conf *config.Configuration, resource kube.GenericResource) (Result, error) {
finalResult := Result{
Kind: resource.Kind,
Name: resource.ObjectMeta.GetName(),
Namespace: resource.ObjectMeta.GetNamespace(),
}
resultSet, err := applyTopLevelSchemaChecks(conf, resource)
if err != nil {
return finalResult, err
}
finalResult.Results = resultSet
if resource.PodSpec != nil {
podRS, err := applyPodSchemaChecks(conf, resource)
if err != nil {
return finalResult, err
}
podRes := PodResult{
Results: podRS,
ContainerResults: []ContainerResult{},
}
for _, container := range resource.PodSpec.InitContainers {
results, err := applyContainerSchemaChecks(conf, resource, &container, true)
if err != nil {
return finalResult, err
}
cRes := ContainerResult{
Name: container.Name,
Results: results,
}
podRes.ContainerResults = append(podRes.ContainerResults, cRes)
}
for _, container := range resource.PodSpec.Containers {
results, err := applyContainerSchemaChecks(conf, resource, &container, false)
if err != nil {
return finalResult, err
}
cRes := ContainerResult{
Name: container.Name,
Results: results,
}
podRes.ContainerResults = append(podRes.ContainerResults, cRes)
}
finalResult.PodResult = &podRes
}
return finalResult, nil
}
func applyTopLevelSchemaChecks(conf *config.Configuration, res kube.GenericResource) (ResultSet, error) {
test := schemaTestCase{
Target: config.TargetController,
Resource: res,
}
return applySchemaChecks(conf, test)
}
func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericResource) (ResultSet, error) {
test := schemaTestCase{
Target: config.TargetPod,
Resource: controller,
}
return applySchemaChecks(conf, test)
}
func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) {
test := schemaTestCase{
Target: config.TargetContainer,
Resource: controller,
Container: container,
}
return applySchemaChecks(conf, test)
}
func applySchemaChecks(conf *config.Configuration, test schemaTestCase) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
for _, checkID := range checkIDs {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetPod, controller.ObjectMeta, "", false)
result, err := applySchemaCheck(conf, checkID, test)
if err != nil {
return nil, err
} else if check == nil {
continue
return results, err
}
passes, err := check.CheckPod(&controller.PodSpec)
if err != nil {
return nil, err
if result != nil {
results[checkID] = *result
}
results[check.ID] = makeResult(conf, check, passes)
}
return results, nil
}
func applyControllerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
for _, checkID := range checkIDs {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetController, controller.ObjectMeta, "", false)
if err != nil {
return nil, err
} else if check == nil {
continue
}
passes, err := check.CheckController(controller.OriginalObjectJSON)
if err != nil {
return nil, err
}
results[check.ID] = makeResult(conf, check, passes)
func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTestCase) (*ResultMessage, error) {
check, err := resolveCheck(conf, checkID, test)
if err != nil {
return nil, err
} else if check == nil {
return nil, nil
}
return results, nil
}
func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
for _, checkID := range checkIDs {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetContainer, controller.ObjectMeta, container.Name, isInit)
if err != nil {
return nil, err
} else if check == nil {
continue
}
var passes bool
if check.SchemaTarget == config.TargetPod {
podCopy := controller.PodSpec
var passes bool
if check.SchemaTarget != "" {
if check.SchemaTarget == config.TargetPod && check.Target == config.TargetContainer {
podCopy := *test.Resource.PodSpec
podCopy.InitContainers = []corev1.Container{}
podCopy.Containers = []corev1.Container{*container}
podCopy.Containers = []corev1.Container{*test.Container}
passes, err = check.CheckPod(&podCopy)
} else {
passes, err = check.CheckContainer(container)
return nil, fmt.Errorf("Unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget)
}
if err != nil {
return nil, err
}
results[check.ID] = makeResult(conf, check, passes)
} else if check.Target == config.TargetPod {
passes, err = check.CheckPod(test.Resource.PodSpec)
} else if check.Target == config.TargetContainer {
passes, err = check.CheckContainer(test.Container)
} else {
passes, err = check.CheckObject(test.Resource.Resource.Object)
}
return results, nil
}
func applyOtherSchemaChecks(conf *config.Configuration, unst *unstructured.Unstructured) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objMeta, err := meta.Accessor(unst)
if err != nil {
return results, err
return nil, err
}
for _, checkID := range checkIDs {
check, err := resolveCheck(conf, checkID, unst.GetKind(), "", objMeta, "", false)
if err != nil {
return nil, err
} else if check == nil {
continue
}
passes, err := check.CheckObject(unst)
if err != nil {
return nil, err
}
results[check.ID] = makeResult(conf, check, passes)
}
return results, nil
}
func applyArbitrarySchemaChecks(conf *config.Configuration, unst *unstructured.Unstructured) (ResultSet, error) {
results := ResultSet{}
objMeta, err := meta.Accessor(unst)
if err != nil {
return results, err
}
checkIDs := getSortedKeys(conf.Checks)
for _, checkID := range checkIDs {
check, err := resolveCheck(conf, checkID, unst.GetKind(), "", objMeta, "", false)
if err != nil {
return nil, err
} else if check == nil {
continue
}
passes, err := check.CheckObject(unst)
if err != nil {
return nil, err
}
results[check.ID] = makeResult(conf, check, passes)
}
return results, nil
result := makeResult(conf, check, passes)
return &result, nil
}
func getSortedKeys(m map[string]config.Severity) []string {
+1 -1
View File
@@ -99,7 +99,7 @@ func (v *Validator) handleInternal(req admission.Request) (*validator.PodResult,
} else {
pod, originalObject, err = GetObjectFromRawRequest(req.Object.Raw)
}
controller, err := kube.NewGenericWorkloadFromPod(pod, originalObject)
controller, err := kube.NewGenericResourceFromPod(pod, originalObject)
if err != nil {
return nil, err
}
+8 -4
View File
@@ -1,6 +1,7 @@
package test
import (
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
@@ -52,14 +53,17 @@ func init() {
func TestChecks(t *testing.T) {
for _, tc := range testCases {
workload, err := kube.GetWorkloadFromBytes(tc.input)
assert.NoError(t, err)
res, err := kube.NewGenericResourceFromBytes(tc.input)
if err != nil {
fmt.Println("error parsing", string(tc.input))
panic(err)
}
c, err := config.Parse([]byte("checks:\n " + tc.check + ": danger"))
assert.NoError(t, err)
var result validator.Result
result, err = validator.ValidateController(&c, *workload)
result, err := validator.ApplyAllSchemaChecks(&c, res)
assert.NoError(t, err)
summary := result.GetSummary()
fmt.Println(tc.check, summary)
if tc.failure {
message := "Check " + tc.check + " passed unexpectedly"
assert.Equal(t, uint(0), summary.Successes, message)