diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40817857..2905dcec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
# x.x.x (next release)
* Added the ability to exempt a particular controller from a particular check.
+* Breaking changes in the config format.
+* Added support for finding the Owners, this will allow Polaris to work with types of Controllers it doesn't even know about.
# 0.6.0
* Fixed webhook support in Kubernetes 1.16
diff --git a/go.mod b/go.mod
index 0497f489..09acf22f 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
contrib.go.opencensus.io/exporter/ocagent v0.4.12
git.apache.org/thrift.git v0.12.0 // indirect
github.com/Azure/go-autorest v12.4.3+incompatible
+ github.com/Azure/go-autorest/autorest v0.10.0 // indirect
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30
github.com/beorn7/perks v1.0.0
github.com/census-instrumentation/opencensus-proto v0.2.1
@@ -68,7 +69,7 @@ require (
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe
go.opencensus.io v0.21.0
- go.uber.org/atomic v1.4.0
+ go.uber.org/atomic v1.6.0
go.uber.org/multierr v1.5.0
go.uber.org/zap v1.14.0
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915
@@ -78,13 +79,14 @@ require (
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292
golang.org/x/text v0.3.2
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
- golang.org/x/tools v0.0.0-20191219212307-145a1e401f50
+ golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40
google.golang.org/api v0.5.0
google.golang.org/appengine v1.6.0
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52
google.golang.org/grpc v1.20.1
gopkg.in/inf.v0 v0.9.1
- gopkg.in/yaml.v2 v2.2.2
+ gopkg.in/yaml.v2 v2.2.7
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
k8s.io/api v0.0.0-20181213150558-05914d821849
k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93
k8s.io/client-go v0.0.0-20181213151034-8d9ed539ba31
diff --git a/go.sum b/go.sum
index d5cc884a..97939e79 100644
--- a/go.sum
+++ b/go.sum
@@ -5,11 +5,28 @@ cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISt
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/Azure/go-autorest v1.1.1 h1:4G9tVCqooRY3vDTB2bA1Z01PlSALtnUbji0AfzthUSs=
github.com/Azure/go-autorest v12.0.0+incompatible h1:N+VqClcomLGD/sHb3smbSYYtNMgKpVV3Cd5r5i8z6bQ=
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v12.4.3+incompatible h1:tCkdkgLZqAk+43nZu3wda9n413Q2g+z7xp1wmjiJTPY=
github.com/Azure/go-autorest v12.4.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.0.0+incompatible h1:r/ug62X9o8vikt53/nkAPmFmzfSrCCAplPH7wa+mK0U=
+github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
+github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY=
+github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
+github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
+github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
+github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
+github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
+github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
+github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
+github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -375,6 +392,7 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -506,7 +524,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/config/schema.go b/pkg/config/schema.go
index 320a3843..2870eb2c 100644
--- a/pkg/config/schema.go
+++ b/pkg/config/schema.go
@@ -144,13 +144,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 SupportedController, isInit bool) bool {
+func (check SchemaCheck) IsActionable(target TargetKind, controllerType string, isInit bool) bool {
if check.Target != target {
return false
}
isIncluded := len(check.Controllers.Include) == 0
for _, inclusion := range check.Controllers.Include {
- if GetSupportedControllerFromString(inclusion) == controllerType {
+ if inclusion == controllerType {
isIncluded = true
break
}
@@ -159,7 +159,7 @@ func (check SchemaCheck) IsActionable(target TargetKind, controllerType Supporte
return false
}
for _, exclusion := range check.Controllers.Exclude {
- if GetSupportedControllerFromString(exclusion) == controllerType {
+ if exclusion == controllerType {
return false
}
}
diff --git a/pkg/dashboard/templates/dashboard.gohtml b/pkg/dashboard/templates/dashboard.gohtml
index 7b3e85cf..69a6f40b 100644
--- a/pkg/dashboard/templates/dashboard.gohtml
+++ b/pkg/dashboard/templates/dashboard.gohtml
@@ -43,6 +43,10 @@
Pods:
{{.AuditData.ClusterInfo.Pods}}
+
+ Controllers:
+ {{.AuditData.ClusterInfo.Controllers}}
+
Namespaces:
{{.AuditData.ClusterInfo.Namespaces}}
diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go
index 67a3be66..fc8d74f6 100644
--- a/pkg/kube/resources.go
+++ b/pkg/kube/resources.go
@@ -2,7 +2,6 @@ package kube
import (
"bytes"
- "encoding/json"
"io/ioutil"
"os"
"path/filepath"
@@ -10,33 +9,29 @@ import (
"strings"
"time"
+ "github.com/fairwindsops/polaris/pkg/validator/controllers"
"github.com/sirupsen/logrus"
- appsv1 "k8s.io/api/apps/v1"
- batchv1 "k8s.io/api/batch/v1"
- batchv1beta1 "k8s.io/api/batch/v1beta1"
+ "gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sYaml "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth" // Required for other auth providers like GKE.
+ "k8s.io/client-go/restmapper"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
// ResourceProvider contains k8s resources to be audited
type ResourceProvider struct {
- ServerVersion string
- CreationTime time.Time
- SourceName string
- SourceType string
- Nodes []corev1.Node
- Deployments []appsv1.Deployment
- StatefulSets []appsv1.StatefulSet
- DaemonSets []appsv1.DaemonSet
- Jobs []batchv1.Job
- CronJobs []batchv1beta1.CronJob
- ReplicationControllers []corev1.ReplicationController
- Namespaces []corev1.Namespace
- Pods []corev1.Pod
+ ServerVersion string
+ CreationTime time.Time
+ SourceName string
+ SourceType string
+ Nodes []corev1.Node
+ Namespaces []corev1.Namespace
+ Controllers []controllers.GenericController
}
type k8sResource struct {
@@ -54,18 +49,12 @@ func CreateResourceProvider(directory string) (*ResourceProvider, error) {
// CreateResourceProviderFromPath returns a new ResourceProvider using the YAML files in a directory
func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error) {
resources := ResourceProvider{
- ServerVersion: "unknown",
- SourceType: "Path",
- SourceName: directory,
- Nodes: []corev1.Node{},
- Deployments: []appsv1.Deployment{},
- StatefulSets: []appsv1.StatefulSet{},
- DaemonSets: []appsv1.DaemonSet{},
- Jobs: []batchv1.Job{},
- CronJobs: []batchv1beta1.CronJob{},
- ReplicationControllers: []corev1.ReplicationController{},
- Namespaces: []corev1.Namespace{},
- Pods: []corev1.Pod{},
+ ServerVersion: "unknown",
+ SourceType: "Path",
+ SourceName: directory,
+ Nodes: []corev1.Node{},
+ Namespaces: []corev1.Namespace{},
+ Controllers: []controllers.GenericController{},
}
addYaml := func(contents string) error {
@@ -114,44 +103,23 @@ func CreateResourceProviderFromCluster() (*ResourceProvider, error) {
logrus.Errorf("Error creating Kubernetes client: %v", err)
return nil, err
}
- return CreateResourceProviderFromAPI(api, kubeConf.Host)
+ dynamicInterface, err := dynamic.NewForConfig(kubeConf)
+ if err != nil {
+ logrus.Errorf("Error connecting to dynamic interface: %v", err)
+ return nil, err
+ }
+ return CreateResourceProviderFromAPI(api, kubeConf.Host, &dynamicInterface)
}
// CreateResourceProviderFromAPI creates a new ResourceProvider from an existing k8s interface
-func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string) (*ResourceProvider, error) {
+func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string, dynamic *dynamic.Interface) (*ResourceProvider, error) {
listOpts := metav1.ListOptions{}
serverVersion, err := kube.Discovery().ServerVersion()
if err != nil {
logrus.Errorf("Error fetching Cluster API version: %v", err)
return nil, err
}
- deploys, err := getDeployments(kube)
- if err != nil {
- return nil, err
- }
- statefulSets, err := getStatefulSets(kube)
- if err != nil {
- return nil, err
- }
- cronJobs, err := getCronJobs(kube)
- if err != nil {
- return nil, err
- }
- daemonSets, err := getDaemonSets(kube)
- if err != nil {
- return nil, err
- }
- jobs, err := kube.BatchV1().Jobs("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching Jobs: %v", err)
- return nil, err
- }
- replicationControllers, err := kube.CoreV1().ReplicationControllers("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching ReplicationControllers: %v", err)
- return nil, err
- }
nodes, err := kube.CoreV1().Nodes().List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Nodes: %v", err)
@@ -168,222 +136,103 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
return nil, err
}
+ resources, err := restmapper.GetAPIGroupResources(kube.Discovery())
+ if err != nil {
+ logrus.Errorf("Error getting API Group resources: %v", err)
+ return nil, err
+ }
+ restMapper := restmapper.NewDiscoveryRESTMapper(resources)
api := ResourceProvider{
- ServerVersion: serverVersion.Major + "." + serverVersion.Minor,
- SourceType: "Cluster",
- SourceName: clusterName,
- CreationTime: time.Now(),
- Deployments: deploys,
- StatefulSets: statefulSets,
- DaemonSets: daemonSets,
- CronJobs: cronJobs,
- Jobs: jobs.Items,
- ReplicationControllers: replicationControllers.Items,
- Nodes: nodes.Items,
- Namespaces: namespaces.Items,
- Pods: pods.Items,
+ ServerVersion: serverVersion.Major + "." + serverVersion.Minor,
+ SourceType: "Cluster",
+ SourceName: clusterName,
+ CreationTime: time.Now(),
+ Nodes: nodes.Items,
+ Namespaces: namespaces.Items,
+ Controllers: LoadControllers(pods.Items, dynamic, &restMapper),
}
return &api, nil
}
+// LoadControllers loads a list of controllers from the kubeResources Pods
+func LoadControllers(pods []corev1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) []controllers.GenericController {
+ interfaces := []controllers.GenericController{}
+ for _, pod := range pods {
+ interfaces = append(interfaces, controllers.NewGenericPodController(pod, dynamicClientPointer, restMapperPointer))
+ }
+ return deduplicateControllers(interfaces)
+}
+
+// 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 []controllers.GenericController) []controllers.GenericController {
+ controllerMap := make(map[string]controllers.GenericController)
+ for _, controller := range inputControllers {
+ key := controller.GetNamespace() + "/" + controller.GetKind() + "/" + controller.Name
+ oldController, ok := controllerMap[key]
+ if !ok || controller.CreatedTime.After(oldController.CreatedTime) {
+ controllerMap[key] = controller
+ }
+ }
+ results := make([]controllers.GenericController, 0)
+ for _, controller := range controllerMap {
+ results = append(results, controller)
+ }
+ return results
+}
+
+func getPodSpec(yaml map[string]interface{}) interface{} {
+ allowedChildren := []string{"jobTemplate", "spec", "template"}
+ for _, child := range allowedChildren {
+ if childYaml, ok := yaml[child]; ok {
+ return getPodSpec(childYaml.(map[string]interface{}))
+ }
+ }
+ return yaml
+}
+
func addResourceFromString(contents string, resources *ResourceProvider) error {
contentBytes := []byte(contents)
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
resource := k8sResource{}
err := decoder.Decode(&resource)
+ decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
+
if err != nil {
logrus.Errorf("Invalid YAML: %s", string(contents))
return err
}
- decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
- if resource.Kind == "Deployment" {
- controller := appsv1.Deployment{}
- err = decoder.Decode(&controller)
- resources.Deployments = append(resources.Deployments, controller)
- } else if resource.Kind == "StatefulSet" {
- controller := appsv1.StatefulSet{}
- err = decoder.Decode(&controller)
- resources.StatefulSets = append(resources.StatefulSets, controller)
- } else if resource.Kind == "DaemonSet" {
- controller := appsv1.DaemonSet{}
- err = decoder.Decode(&controller)
- resources.DaemonSets = append(resources.DaemonSets, controller)
- } else if resource.Kind == "Job" {
- controller := batchv1.Job{}
- err = decoder.Decode(&controller)
- resources.Jobs = append(resources.Jobs, controller)
- } else if resource.Kind == "CronJob" {
- controller := batchv1beta1.CronJob{}
- err = decoder.Decode(&controller)
- resources.CronJobs = append(resources.CronJobs, controller)
- } else if resource.Kind == "ReplicationController" {
- controller := corev1.ReplicationController{}
- err = decoder.Decode(&controller)
- resources.ReplicationControllers = append(resources.ReplicationControllers, controller)
- } else if resource.Kind == "Namespace" {
+ if resource.Kind == "Namespace" {
ns := corev1.Namespace{}
err = decoder.Decode(&ns)
resources.Namespaces = append(resources.Namespaces, ns)
} else if resource.Kind == "Pod" {
pod := corev1.Pod{}
err = decoder.Decode(&pod)
- resources.Pods = append(resources.Pods, pod)
+ resources.Controllers = append(resources.Controllers, controllers.NewGenericPodController(pod, nil, nil))
+ } 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"
+ finalDoc["spec"] = getPodSpec(yamlNode)
+ 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 := controllers.NewGenericPodController(pod, nil, nil)
+ newController.Kind = resource.Kind
+ resources.Controllers = append(resources.Controllers, newController)
}
- if err != nil {
- logrus.Errorf("Error parsing %s: %v", resource.Kind, err)
- return err
- }
- return nil
-}
-
-func getDeployments(kube kubernetes.Interface) ([]appsv1.Deployment, error) {
- listOpts := metav1.ListOptions{}
- deployList, err := kube.AppsV1().Deployments("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching Deployments: %v", err)
- return nil, err
- }
- deploys := deployList.Items
-
- oldDeploys := make([]interface{}, 0)
- deploysV1B1, err := kube.AppsV1beta1().Deployments("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching Deployments v1beta1: %v", err)
- return nil, err
- }
- for _, oldDeploy := range deploysV1B1.Items {
- oldDeploys = append(oldDeploys, oldDeploy)
- }
- deploysV1B2, err := kube.AppsV1beta2().Deployments("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching Deployments v1beta2: %v", err)
- return nil, err
- }
- for _, oldDeploy := range deploysV1B2.Items {
- oldDeploys = append(oldDeploys, oldDeploy)
- }
-
- for _, oldDeploy := range oldDeploys {
- str, err := json.Marshal(oldDeploy)
- if err != nil {
- logrus.Errorf("Error marshaling old deployment version: %v", err)
- return nil, err
- }
- deploy := appsv1.Deployment{}
- err = json.Unmarshal(str, &deploy)
- if err != nil {
- logrus.Errorf("Error unmarshaling old deployment version: %v", err)
- return nil, err
- }
- deploys = append(deploys, deploy)
- }
- return deploys, nil
-}
-
-func getStatefulSets(kube kubernetes.Interface) ([]appsv1.StatefulSet, error) {
- listOpts := metav1.ListOptions{}
- controllerList, err := kube.AppsV1().StatefulSets("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching StatefulSets: %v", err)
- return nil, err
- }
- controllers := controllerList.Items
-
- oldControllers := make([]interface{}, 0)
- controllersV1B1, err := kube.AppsV1beta1().StatefulSets("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching StatefulSets v1beta1: %v", err)
- return nil, err
- }
- for _, oldController := range controllersV1B1.Items {
- oldControllers = append(oldControllers, oldController)
- }
- controllersV1B2, err := kube.AppsV1beta2().StatefulSets("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching StatefulSets v1beta2: %v", err)
- return nil, err
- }
- for _, oldController := range controllersV1B2.Items {
- oldControllers = append(oldControllers, oldController)
- }
-
- for _, oldController := range oldControllers {
- str, err := json.Marshal(oldController)
- if err != nil {
- logrus.Errorf("Error marshaling old StatefulSet version: %v", err)
- return nil, err
- }
- controller := appsv1.StatefulSet{}
- err = json.Unmarshal(str, &controller)
- if err != nil {
- logrus.Errorf("Error unmarshaling old StatefulSet version: %v", err)
- return nil, err
- }
- controllers = append(controllers, controller)
- }
- return controllers, nil
-}
-
-func getDaemonSets(kube kubernetes.Interface) ([]appsv1.DaemonSet, error) {
- listOpts := metav1.ListOptions{}
- controllerList, err := kube.AppsV1().DaemonSets("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching DaemonSets: %v", err)
- return nil, err
- }
- controllers := controllerList.Items
-
- controllersV1B2, err := kube.AppsV1beta2().DaemonSets("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching DaemonSets v1beta2: %v", err)
- return nil, err
- }
-
- for _, oldController := range controllersV1B2.Items {
- str, err := json.Marshal(oldController)
- if err != nil {
- logrus.Errorf("Error marshaling old DaemonSet version: %v", err)
- return nil, err
- }
- controller := appsv1.DaemonSet{}
- err = json.Unmarshal(str, &controller)
- if err != nil {
- logrus.Errorf("Error unmarshaling old DaemonSet version: %v", err)
- return nil, err
- }
- controllers = append(controllers, controller)
- }
- return controllers, nil
-}
-
-func getCronJobs(kube kubernetes.Interface) ([]batchv1beta1.CronJob, error) {
- listOpts := metav1.ListOptions{}
- controllerList, err := kube.BatchV1beta1().CronJobs("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching CronJobs: %v", err)
- return nil, err
- }
- controllers := controllerList.Items
-
- controllersV2A1, err := kube.BatchV2alpha1().CronJobs("").List(listOpts)
- if err != nil {
- logrus.Errorf("Error fetching CronJobs v2alpha1: %v", err)
- return nil, err
- }
-
- for _, oldController := range controllersV2A1.Items {
- str, err := json.Marshal(oldController)
- if err != nil {
- logrus.Errorf("Error marshaling old CronJob version: %v", err)
- return nil, err
- }
- controller := batchv1beta1.CronJob{}
- err = json.Unmarshal(str, &controller)
- if err != nil {
- logrus.Errorf("Error unmarshaling old CronJob version: %v", err)
- return nil, err
- }
- controllers = append(controllers, controller)
- }
- return controllers, nil
+ return err
}
diff --git a/pkg/kube/resources_test.go b/pkg/kube/resources_test.go
index b3326ea4..742cc407 100644
--- a/pkg/kube/resources_test.go
+++ b/pkg/kube/resources_test.go
@@ -20,18 +20,16 @@ func TestGetResourcesFromPath(t *testing.T) {
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
- assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
- assert.Equal(t, "ubuntu", resources.Deployments[0].Spec.Template.Spec.Containers[0].Name)
-
- assert.Equal(t, 1, len(resources.StatefulSets), "Should have a stateful set")
- assert.Equal(t, "nginx", resources.StatefulSets[0].Spec.Template.Spec.Containers[0].Name)
-
assert.Equal(t, 1, len(resources.Namespaces), "Should have a namespace")
assert.Equal(t, "two", resources.Namespaces[0].ObjectMeta.Name)
- assert.Equal(t, 2, len(resources.Pods), "Should have two pods")
- assert.Equal(t, "", resources.Pods[0].ObjectMeta.Namespace, "Should have one pod in default namespace")
- assert.Equal(t, "two", resources.Pods[1].ObjectMeta.Namespace, "Should have one pod in namespace 'two'")
+ assert.Equal(t, 8, len(resources.Controllers), "Should have eight controllers")
+ namespaceCount := map[string]int{}
+ for _, controller := range resources.Controllers {
+ namespaceCount[controller.GetNamespace()]++
+ }
+ assert.Equal(t, 7, namespaceCount[""], "Should have seven controller in default namespace")
+ assert.Equal(t, 1, namespaceCount["two"], "Should have one controller in namespace 'two'")
}
func TestGetMultipleResourceFromSingleFile(t *testing.T) {
@@ -46,8 +44,8 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
- assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
- assert.Equal(t, "dashboard", resources.Deployments[0].Spec.Template.Spec.Containers[0].Name)
+ assert.Equal(t, 4, len(resources.Controllers), "Should have four controllers")
+ assert.Equal(t, "dashboard", resources.Controllers[0].PodSpec.Containers[0].Name)
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
@@ -60,9 +58,11 @@ func TestGetMultipleResourceFromBadFile(t *testing.T) {
}
func TestGetResourceFromAPI(t *testing.T) {
- k8s := test.SetupTestAPI()
+ k8s, dynamicInterface := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
- resources, err := CreateResourceProviderFromAPI(k8s, "test")
+ // TODO find a way to mock out the dynamic client
+ // and create fake pods in order to find all of the controllers.
+ resources, err := CreateResourceProviderFromAPI(k8s, "test", &dynamicInterface)
assert.Equal(t, nil, err, "Error should be nil")
assert.Equal(t, "Cluster", resources.SourceType, "Should have type Path")
@@ -70,9 +70,7 @@ func TestGetResourceFromAPI(t *testing.T) {
assert.IsType(t, time.Now(), resources.CreationTime, "Creation time should be set")
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
- assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
- assert.Equal(t, 1, len(resources.StatefulSets), "Should have a stateful set")
- assert.Equal(t, 0, len(resources.Pods), "Should have a pod")
+ assert.Equal(t, 1, len(resources.Controllers), "Should have 1 controller")
- assert.Equal(t, "", resources.Deployments[0].ObjectMeta.Name)
+ assert.Equal(t, "", resources.Controllers[0].ObjectMeta.Name)
}
diff --git a/pkg/validator/container.go b/pkg/validator/container.go
index 66f7f5a2..91add9ad 100644
--- a/pkg/validator/container.go
+++ b/pkg/validator/container.go
@@ -22,7 +22,7 @@ import (
)
// ValidateContainer validates a single container from a given controller
-func ValidateContainer(conf *config.Configuration, controller controllers.Interface, container *corev1.Container, isInit bool) (ContainerResult, error) {
+func ValidateContainer(conf *config.Configuration, controller controllers.GenericController, 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 controllers.Interf
}
// ValidateAllContainers validates both init and regular containers
-func ValidateAllContainers(conf *config.Configuration, controller controllers.Interface) ([]ContainerResult, error) {
+func ValidateAllContainers(conf *config.Configuration, controller controllers.GenericController) ([]ContainerResult, error) {
results := []ContainerResult{}
pod := controller.GetPodSpec()
for _, container := range pod.InitContainers {
diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go
index c298586b..f3f4d822 100644
--- a/pkg/validator/container_test.go
+++ b/pkg/validator/container_test.go
@@ -51,7 +51,7 @@ exemptions:
- foo
`
-func getEmptyController(name string) controllers.Interface {
+func getEmptyController(name string) controllers.GenericController {
return controllers.NewDeploymentController(appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@@ -66,7 +66,7 @@ func testValidate(t *testing.T, container *corev1.Container, resourceConf *strin
testValidateWithController(t, container, resourceConf, getEmptyController(controllerName), expectedErrors, expectedWarnings, expectedSuccesses)
}
-func testValidateWithController(t *testing.T, container *corev1.Container, resourceConf *string, controller controllers.Interface, expectedErrors []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
+func testValidateWithController(t *testing.T, container *corev1.Container, resourceConf *string, controller controllers.GenericController, expectedErrors []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
parsedConf, err := conf.Parse([]byte(*resourceConf))
assert.NoError(t, err, "Expected no error when parsing config")
@@ -1184,18 +1184,18 @@ func TestValidateResourcesEmptyContainerCPURequestsExempt(t *testing.T) {
}
expectedSuccesses := []ResultMessage{}
-
+
controller := controllers.NewDeploymentController(appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
- Annotations: map[string]string {
- "polaris.fairwinds.com/cpuRequestsMissing-exempt": "true", // Exempt this controller from cpuRequestsMissing
+ Annotations: map[string]string{
+ "polaris.fairwinds.com/cpuRequestsMissing-exempt": "true", // Exempt this controller from cpuRequestsMissing
"polaris.fairwinds.com/memoryRequestsMissing-exempt": "truthy", // Don't actually exempt this controller from memoryRequestsMissing
- } ,
+ },
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{},
},
})
testValidateWithController(t, &container, &resourceConfMinimal, controller, expectedErrors, expectedWarnings, expectedSuccesses)
-}
\ No newline at end of file
+}
diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go
index 9aa01ba6..b3d5ee5c 100644
--- a/pkg/validator/controller.go
+++ b/pkg/validator/controller.go
@@ -17,38 +17,36 @@ package validator
import (
"strings"
+ "github.com/sirupsen/logrus"
+
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
- "github.com/fairwindsops/polaris/pkg/validator/controllers"
controller "github.com/fairwindsops/polaris/pkg/validator/controllers"
)
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
// ValidateController validates a single controller, returns a ControllerResult.
-func ValidateController(conf *conf.Configuration, controller controller.Interface) (ControllerResult, error) {
+func ValidateController(conf *conf.Configuration, controller controller.GenericController) (ControllerResult, error) {
podResult, err := ValidatePod(conf, controller)
if err != nil {
return ControllerResult{}, err
}
result := ControllerResult{
- Kind: controller.GetKind().String(),
+ Kind: controller.GetKind(),
Name: controller.GetName(),
Namespace: controller.GetObjectMeta().Namespace,
Results: ResultSet{},
PodResult: podResult,
}
+
return result, nil
}
// ValidateControllers validates that each deployment conforms to the Polaris config,
// builds a list of ResourceResults organized by namespace.
func ValidateControllers(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) {
- var controllersToAudit []controller.Interface
- for _, supportedControllers := range config.ControllersToScan {
- loadedControllers, _ := controllers.LoadControllersByKind(supportedControllers, kubeResources)
- controllersToAudit = append(controllersToAudit, loadedControllers...)
- }
+ controllersToAudit := kubeResources.Controllers
results := []ControllerResult{}
for _, controller := range controllersToAudit {
@@ -57,14 +55,16 @@ func ValidateControllers(config *conf.Configuration, kubeResources *kube.Resourc
}
result, err := ValidateController(config, controller)
if err != nil {
+ logrus.Warn("An error occured validating controller:", err)
return nil, err
}
results = append(results, result)
}
+
return results, nil
}
-func hasExemptionAnnotation(ctrl controller.Interface) bool {
+func hasExemptionAnnotation(ctrl controller.GenericController) bool {
annot := ctrl.GetObjectMeta().Annotations
val := annot[exemptionAnnotationKey]
return strings.ToLower(val) == "true"
diff --git a/pkg/validator/controller_test.go b/pkg/validator/controller_test.go
index e8e832fd..a3335309 100644
--- a/pkg/validator/controller_test.go
+++ b/pkg/validator/controller_test.go
@@ -18,7 +18,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
conf "github.com/fairwindsops/polaris/pkg/config"
@@ -137,8 +136,10 @@ func TestControllerExemptions(t *testing.T) {
conf.Deployments,
},
}
+ newController := test.MockGenericController()
+ newController.Kind = "Deployment"
resources := &kube.ResourceProvider{
- Deployments: []appsv1.Deployment{test.MockDeploy()},
+ Controllers: []controller.GenericController{newController},
}
expectedSum := CountSummary{
@@ -154,7 +155,7 @@ func TestControllerExemptions(t *testing.T) {
assert.Equal(t, "Deployment", actualResults[0].Kind)
assert.EqualValues(t, expectedSum, actualResults[0].GetSummary())
- resources.Deployments[0].ObjectMeta.Annotations = map[string]string{
+ resources.Controllers[0].ObjectMeta.Annotations = map[string]string{
exemptionAnnotationKey: "true",
}
actualResults, err = ValidateControllers(&c, resources)
diff --git a/pkg/validator/controllers/cronjob.go b/pkg/validator/controllers/cronjob.go
index e55fc485..33a2c72e 100644
--- a/pkg/validator/controllers/cronjob.go
+++ b/pkg/validator/controllers/cronjob.go
@@ -2,42 +2,20 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
+ "github.com/sirupsen/logrus"
kubeAPIBatchV1beta1 "k8s.io/api/batch/v1beta1"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// CronJobController is an implementation of controller for deployments
-type CronJobController struct {
- GenericController
- K8SResource kubeAPIBatchV1beta1.CronJob
-}
-
-// GetPodTemplate returns the original template spec
-func (c CronJobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return &c.K8SResource.Spec.JobTemplate.Spec.Template
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (c CronJobController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &c.K8SResource.Spec.JobTemplate.Spec.Template.Spec
-}
-
-// GetKind returns the supportedcontroller enum type
-func (c CronJobController) GetKind() config.SupportedController {
- return config.CronJobs
-}
-
-// GetObjectMeta returns the metadata
-func (c CronJobController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return c.K8SResource.ObjectMeta
-}
-
// NewCronJobController builds a new controller interface for Deployments
-func NewCronJobController(originalDeploymentResource kubeAPIBatchV1beta1.CronJob) Interface {
- controller := CronJobController{}
- controller.Name = originalDeploymentResource.Name
- controller.Namespace = originalDeploymentResource.Namespace
- controller.K8SResource = originalDeploymentResource
+func NewCronJobController(originalResource kubeAPIBatchV1beta1.CronJob) GenericController {
+ controller := GenericController{}
+ controller.Name = originalResource.Name
+ controller.Namespace = originalResource.Namespace
+ controller.PodSpec = originalResource.Spec.JobTemplate.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.CronJobs.String()
+ if controller.Name == "" {
+ logrus.Warn("Name is missing from controller", originalResource.Namespace)
+ }
return controller
}
diff --git a/pkg/validator/controllers/daemonset.go b/pkg/validator/controllers/daemonset.go
index a3c67dad..c8ce6d3e 100644
--- a/pkg/validator/controllers/daemonset.go
+++ b/pkg/validator/controllers/daemonset.go
@@ -3,41 +3,15 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
kubeAPIAppsV1 "k8s.io/api/apps/v1"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// DaemonSetController is an implementation of controller for deployments
-type DaemonSetController struct {
- GenericController
- K8SResource kubeAPIAppsV1.DaemonSet
-}
-
-// GetPodTemplate returns the original template spec
-func (d DaemonSetController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return &d.K8SResource.Spec.Template
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (d DaemonSetController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &d.K8SResource.Spec.Template.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (d DaemonSetController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return d.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (d DaemonSetController) GetKind() config.SupportedController {
- return config.DaemonSets
-}
-
// NewDaemonSetController builds a new controller interface for Deployments
-func NewDaemonSetController(originalResource kubeAPIAppsV1.DaemonSet) Interface {
- controller := DaemonSetController{}
+func NewDaemonSetController(originalResource kubeAPIAppsV1.DaemonSet) GenericController {
+ controller := GenericController{}
controller.Name = originalResource.Name
controller.Namespace = originalResource.Namespace
- controller.K8SResource = originalResource
+ controller.PodSpec = originalResource.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.DaemonSets.String()
return controller
}
diff --git a/pkg/validator/controllers/deployment.go b/pkg/validator/controllers/deployment.go
index e02a60bd..2972d442 100644
--- a/pkg/validator/controllers/deployment.go
+++ b/pkg/validator/controllers/deployment.go
@@ -3,41 +3,15 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
kubeAPIAppsV1 "k8s.io/api/apps/v1"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// DeploymentController is an implementation of controller for deployments
-type DeploymentController struct {
- GenericController
- K8SResource kubeAPIAppsV1.Deployment
-}
-
-// GetPodTemplate returns the original template spec
-func (d DeploymentController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return &d.K8SResource.Spec.Template
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (d DeploymentController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &d.K8SResource.Spec.Template.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (d DeploymentController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return d.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (d DeploymentController) GetKind() config.SupportedController {
- return config.Deployments
-}
-
// NewDeploymentController builds a new controller interface for Deployments
-func NewDeploymentController(originalDeploymentResource kubeAPIAppsV1.Deployment) Interface {
- controller := DeploymentController{}
- controller.Name = originalDeploymentResource.Name
- controller.Namespace = originalDeploymentResource.Namespace
- controller.K8SResource = originalDeploymentResource
+func NewDeploymentController(originalResource kubeAPIAppsV1.Deployment) GenericController {
+ controller := GenericController{}
+ controller.Name = originalResource.Name
+ controller.Namespace = originalResource.Namespace
+ controller.PodSpec = originalResource.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.Deployments.String()
return controller
}
diff --git a/pkg/validator/controllers/generic.go b/pkg/validator/controllers/generic.go
new file mode 100644
index 00000000..3d78023c
--- /dev/null
+++ b/pkg/validator/controllers/generic.go
@@ -0,0 +1,91 @@
+package controllers
+
+import (
+ "time"
+
+ "github.com/sirupsen/logrus"
+ kubeAPICoreV1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
+ kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/client-go/dynamic"
+)
+
+// GenericController is a base implementation with some free methods for inherited structs
+type GenericController struct {
+ Name string
+ Namespace string
+ PodSpec kubeAPICoreV1.PodSpec
+ ObjectMeta kubeAPIMetaV1.ObjectMeta
+ Kind string
+ CreatedTime time.Time
+}
+
+// GetPodSpec returns the original kubernetes template pod spec
+func (g GenericController) GetPodSpec() *kubeAPICoreV1.PodSpec {
+ return &g.PodSpec
+}
+
+// GetObjectMeta returns the metadata
+func (g GenericController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
+ return g.ObjectMeta
+}
+
+// GetKind returns the supportedcontroller enum type
+func (g GenericController) GetKind() string {
+ return g.Kind
+}
+
+// GetName is inherited by all controllers using generic controller to get the name of the controller
+func (g GenericController) GetName() string {
+ return g.Name
+}
+
+// GetNamespace is inherited by all controllers using generic controller to get the namespace of the controller
+func (g GenericController) GetNamespace() string {
+ return g.Namespace
+}
+
+// NewGenericPodController builds a new controller interface for anytype of Pod
+func NewGenericPodController(originalResource kubeAPICoreV1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) GenericController {
+ controller := GenericController{}
+ controller.Name = originalResource.Name
+ controller.Namespace = originalResource.Namespace
+ controller.PodSpec = originalResource.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = "Pod"
+ controller.CreatedTime = controller.GetObjectMeta().CreationTimestamp.Time
+
+ owners := controller.GetObjectMeta().OwnerReferences
+ if dynamicClientPointer == nil || restMapperPointer == nil {
+ return controller
+ }
+ // If an owner exists then set the name to the controller.
+ // This allows us to handle CRDs creating Controllers or DeploymentConfigs in OpenShift.
+ for len(owners) > 0 {
+ if len(owners) > 1 {
+ logrus.Warn("More than 1 owner found")
+ }
+ firstOwner := owners[0]
+ controller.Kind = firstOwner.Kind
+ controller.Name = firstOwner.Name
+
+ dynamicClient := *dynamicClientPointer
+ restMapper := *restMapperPointer
+ fqKind := schema.FromAPIVersionAndKind(firstOwner.APIVersion, firstOwner.Kind)
+ mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
+ if err != nil {
+ logrus.Warnf("Error retrieving mapping %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
+ return controller
+ }
+ getParents, err := dynamicClient.Resource(mapping.Resource).Namespace(controller.GetObjectMeta().Namespace).Get(firstOwner.Name, kubeAPIMetaV1.GetOptions{})
+ if err != nil {
+ logrus.Warnf("Error retrieving parent object %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
+ return controller
+ }
+ owners = getParents.GetOwnerReferences()
+
+ }
+
+ return controller
+}
diff --git a/pkg/validator/controllers/interface.go b/pkg/validator/controllers/interface.go
deleted file mode 100644
index fbcca9aa..00000000
--- a/pkg/validator/controllers/interface.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package controllers
-
-import (
- "fmt"
-
- "github.com/fairwindsops/polaris/pkg/config"
- "github.com/fairwindsops/polaris/pkg/kube"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-// Interface is an interface for k8s controllers (e.g. Deployments and StatefulSets)
-type Interface interface {
- GetName() string
- GetNamespace() string
- GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec
- GetPodSpec() *kubeAPICoreV1.PodSpec
- GetKind() config.SupportedController
- GetObjectMeta() kubeAPIMetaV1.ObjectMeta
-}
-
-// GenericController is a base implementation with some free methods for inherited structs
-type GenericController struct {
- Name string
- Namespace string
-}
-
-// GetName is inherited by all controllers using generic controller to get the name of the controller
-func (g GenericController) GetName() string {
- return g.Name
-}
-
-// GetNamespace is inherited by all controllers using generic controller to get the namespace of the controller
-func (g GenericController) GetNamespace() string {
- return g.Namespace
-}
-
-// LoadControllersByKind loads a list of controllers from the kubeResources by detecting their type
-func LoadControllersByKind(controllerKind config.SupportedController, kubeResources *kube.ResourceProvider) ([]Interface, error) {
- interfaces := []Interface{}
- switch controllerKind {
- case config.Deployments:
- for _, deploy := range kubeResources.Deployments {
- interfaces = append(interfaces, NewDeploymentController(deploy))
- }
- case config.StatefulSets:
- for _, statefulSet := range kubeResources.StatefulSets {
- interfaces = append(interfaces, NewStatefulSetController(statefulSet))
- }
- case config.DaemonSets:
- for _, daemonSet := range kubeResources.DaemonSets {
- interfaces = append(interfaces, NewDaemonSetController(daemonSet))
- }
- case config.Jobs:
- for _, job := range kubeResources.Jobs {
- interfaces = append(interfaces, NewJobController(job))
- }
- case config.CronJobs:
- for _, cronJob := range kubeResources.CronJobs {
- interfaces = append(interfaces, NewCronJobController(cronJob))
- }
- case config.ReplicationControllers:
- for _, replicationController := range kubeResources.ReplicationControllers {
- interfaces = append(interfaces, NewReplicationControllerController(replicationController))
- }
- }
- if len(interfaces) > 0 {
- return interfaces, nil
- }
- return nil, fmt.Errorf("Controller type (%s) does not have a generator", controllerKind)
-}
diff --git a/pkg/validator/controllers/job.go b/pkg/validator/controllers/job.go
index 3efe181c..3377249f 100644
--- a/pkg/validator/controllers/job.go
+++ b/pkg/validator/controllers/job.go
@@ -3,41 +3,15 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
kubeAPIBatchV1 "k8s.io/api/batch/v1"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// JobController is an implementation of controller for deployments
-type JobController struct {
- GenericController
- K8SResource kubeAPIBatchV1.Job
-}
-
-// GetPodTemplate returns the original template spec
-func (j JobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return &j.K8SResource.Spec.Template
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (j JobController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &j.K8SResource.Spec.Template.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (j JobController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return j.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (j JobController) GetKind() config.SupportedController {
- return config.Jobs
-}
-
// NewJobController builds a new controller interface for Deployments
-func NewJobController(originalResource kubeAPIBatchV1.Job) Interface {
- controller := JobController{}
+func NewJobController(originalResource kubeAPIBatchV1.Job) GenericController {
+ controller := GenericController{}
controller.Name = originalResource.Name
controller.Namespace = originalResource.Namespace
- controller.K8SResource = originalResource
+ controller.PodSpec = originalResource.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.Jobs.String()
return controller
}
diff --git a/pkg/validator/controllers/naked-pod.go b/pkg/validator/controllers/naked-pod.go
index c5ab8e66..b96a76ee 100644
--- a/pkg/validator/controllers/naked-pod.go
+++ b/pkg/validator/controllers/naked-pod.go
@@ -3,40 +3,16 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// NakedPodController is an implementation of controller for deployments
-type NakedPodController struct {
- GenericController
- K8SResource kubeAPICoreV1.Pod
-}
-
-// GetPodTemplate returns the original template spec
-func (n NakedPodController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return nil
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (n NakedPodController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &n.K8SResource.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (n NakedPodController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return n.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (n NakedPodController) GetKind() config.SupportedController {
- return config.NakedPods
-}
-
// NewNakedPodController builds a new controller interface for NakedPods
-func NewNakedPodController(originalNakedPodResource kubeAPICoreV1.Pod) Interface {
- controller := NakedPodController{}
- controller.Name = originalNakedPodResource.Name
- controller.Namespace = originalNakedPodResource.Namespace
- controller.K8SResource = originalNakedPodResource
+func NewNakedPodController(originalResource kubeAPICoreV1.Pod) GenericController {
+ controller := GenericController{}
+ controller.Name = originalResource.Name
+ controller.Namespace = originalResource.Namespace
+ controller.PodSpec = originalResource.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.NakedPods.String()
+
return controller
}
diff --git a/pkg/validator/controllers/replicationcontroller.go b/pkg/validator/controllers/replicationcontroller.go
index cb510f88..1a9fa32f 100644
--- a/pkg/validator/controllers/replicationcontroller.go
+++ b/pkg/validator/controllers/replicationcontroller.go
@@ -3,43 +3,15 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// NOTE: Maybe this name of ReplicationController is duplicative but it's more explicit since
-// that's how kubernetes refers the the object.
-
-// ReplicationControllerController is an implementation of controller for deployments
-type ReplicationControllerController struct {
- GenericController
- K8SResource kubeAPICoreV1.ReplicationController
-}
-
-// GetPodTemplate returns the original template spec
-func (r ReplicationControllerController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return r.K8SResource.Spec.Template
-}
-
-// GetPodSpec returns the original kubernetes template pod spec
-func (r ReplicationControllerController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &r.K8SResource.Spec.Template.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (r ReplicationControllerController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return r.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (r ReplicationControllerController) GetKind() config.SupportedController {
- return config.ReplicationControllers
-}
-
// NewReplicationControllerController builds a new controller interface for Deployments
-func NewReplicationControllerController(originalResource kubeAPICoreV1.ReplicationController) Interface {
- controller := ReplicationControllerController{}
+func NewReplicationControllerController(originalResource kubeAPICoreV1.ReplicationController) GenericController {
+ controller := GenericController{}
controller.Name = originalResource.Name
controller.Namespace = originalResource.Namespace
- controller.K8SResource = originalResource
+ controller.PodSpec = originalResource.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.ReplicationControllers.String()
return controller
}
diff --git a/pkg/validator/controllers/statefulsets.go b/pkg/validator/controllers/statefulsets.go
index 4ac1f62b..fab6b865 100644
--- a/pkg/validator/controllers/statefulsets.go
+++ b/pkg/validator/controllers/statefulsets.go
@@ -2,42 +2,20 @@ package controllers
import (
"github.com/fairwindsops/polaris/pkg/config"
+ "github.com/sirupsen/logrus"
kubeAPIAppsV1 "k8s.io/api/apps/v1"
- kubeAPICoreV1 "k8s.io/api/core/v1"
- kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
-// StatefulSetController is an implementation of controller for deployments
-type StatefulSetController struct {
- GenericController
- K8SResource kubeAPIAppsV1.StatefulSet
-}
-
-// GetPodTemplate returns the kubernetes template spec
-func (s StatefulSetController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec {
- return &s.K8SResource.Spec.Template
-}
-
-// GetPodSpec returns the podspec from the original kubernetes resource
-func (s StatefulSetController) GetPodSpec() *kubeAPICoreV1.PodSpec {
- return &s.K8SResource.Spec.Template.Spec
-}
-
-// GetObjectMeta returns the metadata
-func (s StatefulSetController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
- return s.K8SResource.ObjectMeta
-}
-
-// GetKind returns the supportedcontroller enum type
-func (s StatefulSetController) GetKind() config.SupportedController {
- return config.StatefulSets
-}
-
// NewStatefulSetController builds a statefulset controller
-func NewStatefulSetController(originalResource kubeAPIAppsV1.StatefulSet) Interface {
- controller := StatefulSetController{}
+func NewStatefulSetController(originalResource kubeAPIAppsV1.StatefulSet) GenericController {
+ controller := GenericController{}
controller.Name = originalResource.Name
controller.Namespace = originalResource.Namespace
- controller.K8SResource = originalResource
+ controller.PodSpec = originalResource.Spec.Template.Spec
+ controller.ObjectMeta = originalResource.ObjectMeta
+ controller.Kind = config.StatefulSets.String()
+ if controller.Name == "" {
+ logrus.Warn("Name is missing from controller", originalResource.Namespace)
+ }
return controller
}
diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go
index 06eb6e93..1fe4b020 100644
--- a/pkg/validator/fullaudit.go
+++ b/pkg/validator/fullaudit.go
@@ -34,16 +34,11 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (
SourceName: kubeResources.SourceName,
DisplayName: displayName,
ClusterInfo: ClusterInfo{
- Version: kubeResources.ServerVersion,
- Nodes: len(kubeResources.Nodes),
- Pods: len(kubeResources.Pods),
- Namespaces: len(kubeResources.Namespaces),
- Deployments: len(kubeResources.Deployments),
- StatefulSets: len(kubeResources.StatefulSets),
- DaemonSets: len(kubeResources.DaemonSets),
- Jobs: len(kubeResources.Jobs),
- CronJobs: len(kubeResources.CronJobs),
- ReplicationControllers: len(kubeResources.ReplicationControllers),
+ Version: kubeResources.ServerVersion,
+ Nodes: len(kubeResources.Nodes),
+ Pods: len(kubeResources.Controllers), // TODO validate that this is still valuable
+ Namespaces: len(kubeResources.Namespaces),
+ Controllers: len(results),
},
Results: results,
}
diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go
index c5cdd7f8..0135ed30 100644
--- a/pkg/validator/fullaudit_test.go
+++ b/pkg/validator/fullaudit_test.go
@@ -10,10 +10,12 @@ import (
)
func TestGetTemplateData(t *testing.T) {
- k8s := test.SetupTestAPI()
+ k8s, dynamicClient := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
k8s = test.SetupAddExtraControllerVersions(k8s, "test-extra")
- resources, err := kube.CreateResourceProviderFromAPI(k8s, "test")
+ // TODO figure out how to mock out dynamic client.
+ // and add in pods for all controllers to fill out tests.
+ resources, err := kube.CreateResourceProviderFromAPI(k8s, "test", &dynamicClient)
assert.Equal(t, err, nil, "error should be nil")
c := conf.Configuration{
@@ -33,8 +35,8 @@ func TestGetTemplateData(t *testing.T) {
sum := CountSummary{
Successes: uint(0),
- Warnings: uint(9),
- Errors: uint(9),
+ Warnings: uint(1),
+ Errors: uint(1),
}
actualAudit, err := RunAudit(c, resources)
@@ -48,17 +50,7 @@ func TestGetTemplateData(t *testing.T) {
kind string
results int
}{
- {kind: "Deployment", results: 2},
- {kind: "Deployment", results: 2},
- {kind: "Deployment", results: 2},
- {kind: "StatefulSet", results: 2},
- {kind: "StatefulSet", results: 2},
- {kind: "StatefulSet", results: 2},
- {kind: "DaemonSet", results: 2},
- {kind: "DaemonSet", results: 2},
- {kind: "Job", results: 0},
- {kind: "CronJob", results: 0},
- {kind: "ReplicationController", results: 2},
+ {kind: "Pod", results: 2},
}
assert.Equal(t, len(expected), len(actualAudit.Results))
diff --git a/pkg/validator/output.go b/pkg/validator/output.go
index 49fc7626..845daaeb 100644
--- a/pkg/validator/output.go
+++ b/pkg/validator/output.go
@@ -15,6 +15,8 @@
package validator
import (
+ "time"
+
"github.com/fairwindsops/polaris/pkg/config"
)
@@ -36,16 +38,11 @@ type AuditData struct {
// ClusterInfo contains Polaris results as well as some high-level stats
type ClusterInfo struct {
- Version string
- Nodes int
- Pods int
- Namespaces int
- Deployments int
- StatefulSets int
- DaemonSets int
- Jobs int
- CronJobs int
- ReplicationControllers int
+ Version string
+ Nodes int
+ Pods int
+ Namespaces int
+ Controllers int
}
// ResultMessage is the result of a given check
@@ -62,11 +59,12 @@ type ResultSet map[string]ResultMessage
// ControllerResult provides results for a controller
type ControllerResult struct {
- Name string
- Namespace string
- Kind string
- Results ResultSet
- PodResult PodResult
+ Name string
+ Namespace string
+ Kind string
+ Results ResultSet
+ PodResult PodResult
+ CreatedTime time.Time
}
// PodResult provides a list of validation messages for each pod.
diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go
index 420ef39f..f6678191 100644
--- a/pkg/validator/pod.go
+++ b/pkg/validator/pod.go
@@ -20,12 +20,11 @@ import (
)
// ValidatePod validates that each pod conforms to the Polaris config, returns a ResourceResult.
-func ValidatePod(conf *config.Configuration, controller controllers.Interface) (PodResult, error) {
+func ValidatePod(conf *config.Configuration, controller controllers.GenericController) (PodResult, error) {
podResults, err := applyPodSchemaChecks(conf, controller)
if err != nil {
return PodResult{}, err
}
-
pRes := PodResult{
Results: podResults,
ContainerResults: []ContainerResult{},
@@ -35,6 +34,5 @@ func ValidatePod(conf *config.Configuration, controller controllers.Interface) (
if err != nil {
return pRes, err
}
-
return pRes, nil
}
diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go
index a0e72b12..1c2139ec 100644
--- a/pkg/validator/pod_test.go
+++ b/pkg/validator/pod_test.go
@@ -36,7 +36,7 @@ func TestValidatePod(t *testing.T) {
},
}
- k8s := test.SetupTestAPI()
+ k8s, _ := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
p := test.MockPod()
deployment := controllers.NewDeploymentController(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: p}})
@@ -73,7 +73,7 @@ func TestInvalidIPCPod(t *testing.T) {
},
}
- k8s := test.SetupTestAPI()
+ k8s, _ := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
p := test.MockPod()
p.Spec.HostIPC = true
@@ -110,7 +110,7 @@ func TestInvalidNeworkPod(t *testing.T) {
},
}
- k8s := test.SetupTestAPI()
+ k8s, _ := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
p := test.MockPod()
p.Spec.HostNetwork = true
@@ -148,7 +148,7 @@ func TestInvalidPIDPod(t *testing.T) {
},
}
- k8s := test.SetupTestAPI()
+ k8s, _ := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
p := test.MockPod()
p.Spec.HostPID = true
@@ -192,7 +192,7 @@ func TestExemption(t *testing.T) {
},
}
- k8s := test.SetupTestAPI()
+ k8s, _ := test.SetupTestAPI()
k8s = test.SetupAddControllers(k8s, "test")
p := test.MockPod()
p.Spec.HostIPC = true
diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go
index 1d5033f5..89f8c872 100644
--- a/pkg/validator/schema.go
+++ b/pkg/validator/schema.go
@@ -74,7 +74,7 @@ func parseCheck(rawBytes []byte) (config.SchemaCheck, error) {
}
}
-func resolveCheck(conf *config.Configuration, checkID string, controller controllers.Interface, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) {
+func resolveCheck(conf *config.Configuration, checkID string, controller controllers.GenericController, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) {
check, ok := conf.CustomChecks[checkID]
if !ok {
check, ok = builtInChecks[checkID]
@@ -110,7 +110,7 @@ func getExemptKey(checkID string) string {
return fmt.Sprintf("polaris.fairwinds.com/%s-exempt", checkID)
}
-func applyPodSchemaChecks(conf *config.Configuration, controller controllers.Interface) (ResultSet, error) {
+func applyPodSchemaChecks(conf *config.Configuration, controller controllers.GenericController) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objectAnnotations := controller.GetObjectMeta().Annotations
@@ -120,9 +120,7 @@ func applyPodSchemaChecks(conf *config.Configuration, controller controllers.Int
continue
}
check, err := resolveCheck(conf, checkID, controller, config.TargetPod, false)
- if err != nil {
- return nil, err
- }
+
if err != nil {
return nil, err
} else if check == nil {
@@ -137,7 +135,7 @@ func applyPodSchemaChecks(conf *config.Configuration, controller controllers.Int
return results, nil
}
-func applyContainerSchemaChecks(conf *config.Configuration, controller controllers.Interface, container *corev1.Container, isInit bool) (ResultSet, error) {
+func applyContainerSchemaChecks(conf *config.Configuration, controller controllers.GenericController, container *corev1.Container, isInit bool) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objectAnnotations := controller.GetObjectMeta().Annotations
diff --git a/pkg/validator/summary.go b/pkg/validator/summary.go
index 6801de8a..19e5198e 100644
--- a/pkg/validator/summary.go
+++ b/pkg/validator/summary.go
@@ -17,6 +17,9 @@ type CountSummaryByCategory map[string]CountSummary
// GetScore returns an overall score in [0, 100] for the CountSummary
func (cs CountSummary) GetScore() uint {
total := (cs.Successes * 2) + cs.Warnings + (cs.Errors * 2)
+ if total == 0 {
+ return 0 // Prevent divide by 0.
+ }
return uint((float64(cs.Successes*2) / float64(total)) * 100)
}
diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go
index f93064a3..b395ae56 100644
--- a/pkg/webhook/validator.go
+++ b/pkg/webhook/validator.go
@@ -98,7 +98,7 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons
podResult, err = validator.ValidatePod(&v.Config, nakedPod)
}
} else {
- var controller controllers.Interface
+ var controller controllers.GenericController
if yes := v.Config.CheckIfKindIsConfiguredForValidation(req.AdmissionRequest.Kind.Kind); !yes {
logrus.Warnf("Skipping, kind (%s) isn't something we are configured to scan", req.AdmissionRequest.Kind.Kind)
return admission.ValidationResponse(true, fmt.Sprintf("Skipping: (%s) isn't something we're configured to scan.", req.AdmissionRequest.Kind.Kind))
diff --git a/test/fixtures.go b/test/fixtures.go
index 303b0613..7a5ad47c 100644
--- a/test/fixtures.go
+++ b/test/fixtures.go
@@ -1,12 +1,16 @@
package test
import (
+ "github.com/fairwindsops/polaris/pkg/validator/controllers"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/dynamic"
+ dynamicFake "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
)
@@ -32,6 +36,20 @@ func MockPod() corev1.PodTemplateSpec {
return p
}
+// MockGenericController creates a generic controller object for testing.
+func MockGenericController() controllers.GenericController {
+ return controllers.GenericController{
+ PodSpec: MockPod().Spec,
+ }
+}
+
+// MockNakedPod creates a pod object.
+func MockNakedPod() corev1.Pod {
+ return corev1.Pod{
+ Spec: MockPod().Spec,
+ }
+}
+
// MockDeploy creates a Deployment object.
func MockDeploy() appsv1.Deployment {
p := MockPod()
@@ -96,8 +114,10 @@ func MockReplicationController() corev1.ReplicationController {
}
// SetupTestAPI creates a test kube API struct.
-func SetupTestAPI() kubernetes.Interface {
- return fake.NewSimpleClientset()
+func SetupTestAPI() (kubernetes.Interface, dynamic.Interface) {
+ scheme := runtime.NewScheme()
+
+ return fake.NewSimpleClientset(), dynamicFake.NewSimpleDynamicClient(scheme)
}
// SetupAddControllers creates mock controllers and adds them to the test clientset.
@@ -132,6 +152,11 @@ func SetupAddControllers(k kubernetes.Interface, namespace string) kubernetes.In
panic(err)
}
+ p1 := MockNakedPod()
+ if _, err := k.CoreV1().Pods(namespace).Create(&p1); err != nil {
+ panic(err)
+ }
+
return k
}