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 797c77a0..d8413823 100644 --- a/pkg/kube/resources.go +++ b/pkg/kube/resources.go @@ -21,7 +21,6 @@ import ( "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/rest" "k8s.io/client-go/restmapper" "sigs.k8s.io/controller-runtime/pkg/client/config" ) @@ -120,11 +119,16 @@ func CreateResourceProviderFromCluster() (*ResourceProvider, error) { logrus.Errorf("Error creating Kubernetes client: %v", err) return nil, err } - return CreateResourceProviderFromAPI(api, kubeConf.Host, kubeConf) + 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, kubeConf *rest.Config) (*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 { @@ -151,11 +155,7 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string logrus.Errorf("Error fetching Pods: %v", err) return nil, err } - dynamicInterface, err := dynamic.NewForConfig(kubeConf) - if err != nil { - logrus.Errorf("Error connecting to dynamic interface: %v", err) - return nil, err - } + resources, err := restmapper.GetAPIGroupResources(kube.Discovery()) if err != nil { logrus.Errorf("Error getting API Group resources: %v", err) @@ -172,7 +172,7 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string Nodes: nodes.Items, Namespaces: namespaces.Items, Pods: pods.Items, - DynamicClient: &dynamicInterface, + DynamicClient: dynamic, RestMapper: &restMapper, } return &api, nil diff --git a/pkg/kube/resources_test.go b/pkg/kube/resources_test.go index b3326ea4..ee9ccb08 100644 --- a/pkg/kube/resources_test.go +++ b/pkg/kube/resources_test.go @@ -62,7 +62,7 @@ func TestGetMultipleResourceFromBadFile(t *testing.T) { func TestGetResourceFromAPI(t *testing.T) { k8s := test.SetupTestAPI() k8s = test.SetupAddControllers(k8s, "test") - resources, err := CreateResourceProviderFromAPI(k8s, "test") + resources, err := CreateResourceProviderFromAPI(k8s, "test", nil) assert.Equal(t, nil, err, "Error should be nil") assert.Equal(t, "Cluster", resources.SourceType, "Should have type Path") @@ -70,9 +70,6 @@ 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, "", resources.Deployments[0].ObjectMeta.Name) } diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index dd2c2683..dccd44a0 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -103,11 +103,9 @@ func deduplicateControllers(controllerResults []ControllerResult) []ControllerRe // builds a list of ResourceResults organized by namespace. func ValidateControllers(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) { var controllersToAudit []controller.Interface - loadedControllers, err := controllers.LoadControllersByKind(conf.NakedPods, kubeResources) - if err != nil { - logrus.Warn(err) - } + loadedControllers := controllers.LoadControllers(kubeResources) controllersToAudit = append(controllersToAudit, loadedControllers...) + results := []ControllerResult{} for _, controller := range controllersToAudit { if !config.DisallowExemptions && hasExemptionAnnotation(controller) { @@ -115,10 +113,12 @@ func ValidateControllers(config *conf.Configuration, kubeResources *kube.Resourc } result, err := ValidateController(config, controller, kubeResources) if err != nil { + logrus.Warn("An error occured validating controller:", err) return nil, err } results = append(results, result) } + return deduplicateControllers(results), nil } diff --git a/pkg/validator/controller_test.go b/pkg/validator/controller_test.go index e8e832fd..548c2f4f 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" @@ -46,7 +45,7 @@ func TestValidateController(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "error", Category: "Security"}, } - actualResult, err := ValidateController(&c, deployment) + actualResult, err := ValidateController(&c, deployment, &kube.ResourceProvider{}) if err != nil { panic(err) } @@ -84,7 +83,7 @@ func TestSkipHealthChecks(t *testing.T) { "readinessProbeMissing": {ID: "readinessProbeMissing", Message: "Readiness probe should be configured", Success: false, Severity: "error", Category: "Health Checks"}, "livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Health Checks"}, } - actualResult, err := ValidateController(&c, deployment) + actualResult, err := ValidateController(&c, deployment, &kube.ResourceProvider{}) if err != nil { panic(err) } @@ -101,7 +100,7 @@ func TestSkipHealthChecks(t *testing.T) { Errors: uint(0), } expectedResults = ResultSet{} - actualResult, err = ValidateController(&c, job) + actualResult, err = ValidateController(&c, job, &kube.ResourceProvider{}) if err != nil { panic(err) } @@ -117,7 +116,7 @@ func TestSkipHealthChecks(t *testing.T) { Errors: uint(0), } expectedResults = ResultSet{} - actualResult, err = ValidateController(&c, cronjob) + actualResult, err = ValidateController(&c, cronjob, &kube.ResourceProvider{}) if err != nil { panic(err) } @@ -138,7 +137,7 @@ func TestControllerExemptions(t *testing.T) { }, } resources := &kube.ResourceProvider{ - Deployments: []appsv1.Deployment{test.MockDeploy()}, + Pods: []corev1.Pod{test.MockNakedPod()}, } expectedSum := CountSummary{ @@ -151,10 +150,10 @@ func TestControllerExemptions(t *testing.T) { panic(err) } assert.Equal(t, 1, len(actualResults)) - assert.Equal(t, "Deployment", actualResults[0].Kind) + assert.Equal(t, "NakedPod", actualResults[0].Kind) assert.EqualValues(t, expectedSum, actualResults[0].GetSummary()) - resources.Deployments[0].ObjectMeta.Annotations = map[string]string{ + resources.Pods[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..885ae359 100644 --- a/pkg/validator/controllers/cronjob.go +++ b/pkg/validator/controllers/cronjob.go @@ -13,11 +13,6 @@ type CronJobController struct { 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 diff --git a/pkg/validator/controllers/daemonset.go b/pkg/validator/controllers/daemonset.go index a3c67dad..96d8f7d5 100644 --- a/pkg/validator/controllers/daemonset.go +++ b/pkg/validator/controllers/daemonset.go @@ -13,11 +13,6 @@ type DaemonSetController struct { 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 diff --git a/pkg/validator/controllers/deployment.go b/pkg/validator/controllers/deployment.go index e02a60bd..a2d4d5e9 100644 --- a/pkg/validator/controllers/deployment.go +++ b/pkg/validator/controllers/deployment.go @@ -13,11 +13,6 @@ type DeploymentController struct { 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 diff --git a/pkg/validator/controllers/interface.go b/pkg/validator/controllers/interface.go index f9ee30a7..9b3f6d7e 100644 --- a/pkg/validator/controllers/interface.go +++ b/pkg/validator/controllers/interface.go @@ -1,8 +1,6 @@ package controllers import ( - "fmt" - "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/kube" kubeAPICoreV1 "k8s.io/api/core/v1" @@ -13,7 +11,6 @@ import ( type Interface interface { GetName() string GetNamespace() string - GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec GetPodSpec() *kubeAPICoreV1.PodSpec GetKind() config.SupportedController GetObjectMeta() kubeAPIMetaV1.ObjectMeta @@ -35,41 +32,11 @@ 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) { +// LoadControllers loads a list of controllers from the kubeResources Pods +func LoadControllers(kubeResources *kube.ResourceProvider) []Interface { interfaces := []Interface{} - switch controllerKind { - case config.NakedPods: - for _, pod := range kubeResources.Pods { - interfaces = append(interfaces, NewNakedPodController(pod)) - } - 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)) - } + for _, pod := range kubeResources.Pods { + interfaces = append(interfaces, NewNakedPodController(pod)) } - if len(interfaces) > 0 { - return interfaces, nil - } - return nil, fmt.Errorf("Controller type (%s) does not have a generator", controllerKind) + return interfaces } diff --git a/pkg/validator/controllers/job.go b/pkg/validator/controllers/job.go index 3efe181c..699f8588 100644 --- a/pkg/validator/controllers/job.go +++ b/pkg/validator/controllers/job.go @@ -13,11 +13,6 @@ type JobController struct { 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 diff --git a/pkg/validator/controllers/naked-pod.go b/pkg/validator/controllers/naked-pod.go index c5ab8e66..6086f15a 100644 --- a/pkg/validator/controllers/naked-pod.go +++ b/pkg/validator/controllers/naked-pod.go @@ -12,11 +12,6 @@ type NakedPodController struct { 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 diff --git a/pkg/validator/controllers/replicationcontroller.go b/pkg/validator/controllers/replicationcontroller.go index cb510f88..43cf110e 100644 --- a/pkg/validator/controllers/replicationcontroller.go +++ b/pkg/validator/controllers/replicationcontroller.go @@ -15,11 +15,6 @@ type ReplicationControllerController struct { 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 diff --git a/pkg/validator/controllers/statefulsets.go b/pkg/validator/controllers/statefulsets.go index 4ac1f62b..e49598f1 100644 --- a/pkg/validator/controllers/statefulsets.go +++ b/pkg/validator/controllers/statefulsets.go @@ -13,11 +13,6 @@ type StatefulSetController struct { 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 diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go index 06eb6e93..7c3e8ed4 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.Pods), + 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..3b3750d4 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -13,7 +13,7 @@ func TestGetTemplateData(t *testing.T) { k8s := test.SetupTestAPI() k8s = test.SetupAddControllers(k8s, "test") k8s = test.SetupAddExtraControllerVersions(k8s, "test-extra") - resources, err := kube.CreateResourceProviderFromAPI(k8s, "test") + resources, err := kube.CreateResourceProviderFromAPI(k8s, "test", nil) assert.Equal(t, err, nil, "error should be nil") c := conf.Configuration{ @@ -33,8 +33,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 +48,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: "NakedPod", results: 2}, } assert.Equal(t, len(expected), len(actualAudit.Results)) diff --git a/pkg/validator/output.go b/pkg/validator/output.go index 9df68e38..845daaeb 100644 --- a/pkg/validator/output.go +++ b/pkg/validator/output.go @@ -15,8 +15,9 @@ package validator import ( - "github.com/fairwindsops/polaris/pkg/config" "time" + + "github.com/fairwindsops/polaris/pkg/config" ) const ( @@ -37,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 diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index 420ef39f..32d9e29e 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -25,7 +25,6 @@ func ValidatePod(conf *config.Configuration, controller controllers.Interface) ( 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/test/fixtures.go b/test/fixtures.go index 303b0613..272ab38c 100644 --- a/test/fixtures.go +++ b/test/fixtures.go @@ -32,6 +32,13 @@ func MockPod() corev1.PodTemplateSpec { return p } +// MockNakedPod created a pod object. +func MockNakedPod() corev1.Pod { + return corev1.Pod{ + Spec: MockPod().Spec, + } +} + // MockDeploy creates a Deployment object. func MockDeploy() appsv1.Deployment { p := MockPod() @@ -132,6 +139,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 }