From eb3741c0b9f42275047aed4d17e5d50d2949a242 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Sat, 6 Jul 2019 07:47:06 -0600 Subject: [PATCH 01/28] Additions - Refactored the way controllers work to be an interface - Added configurable controllers to include in scans - Added daemonsets, jobs and cronjobs in scans Explicitly did not add ReplicationControllers and ReplicaSets since they are either deprecated or controlled by deployments (generally). - Added tests to supportedController scanner --- README.md | 14 +++ examples/config.yaml | 6 ++ main.go | 21 ++++- pkg/config/config.go | 13 +-- pkg/config/config_test.go | 9 +- pkg/config/supportedcontrollers.go | 105 ++++++++++++++++++++++ pkg/config/supportedcontrollers_test.go | 105 ++++++++++++++++++++++ pkg/kube/resources.go | 38 ++++++++ pkg/validator/controller.go | 85 ++++++------------ pkg/validator/controllers/cronjob.go | 37 ++++++++ pkg/validator/controllers/daemonset.go | 37 ++++++++ pkg/validator/controllers/deployment.go | 37 ++++++++ pkg/validator/controllers/interface.go | 65 ++++++++++++++ pkg/validator/controllers/job.go | 37 ++++++++ pkg/validator/controllers/statefulsets.go | 37 ++++++++ pkg/validator/fullaudit.go | 6 ++ pkg/validator/fullaudit_test.go | 4 + pkg/validator/types.go | 6 ++ pkg/webhook/validator.go | 24 +++-- 19 files changed, 609 insertions(+), 77 deletions(-) create mode 100644 pkg/config/supportedcontrollers.go create mode 100644 pkg/config/supportedcontrollers_test.go create mode 100644 pkg/validator/controllers/cronjob.go create mode 100644 pkg/validator/controllers/daemonset.go create mode 100644 pkg/validator/controllers/deployment.go create mode 100644 pkg/validator/controllers/interface.go create mode 100644 pkg/validator/controllers/job.go create mode 100644 pkg/validator/controllers/statefulsets.go diff --git a/README.md b/README.md index 9273f7be0..b47e59dfb 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,20 @@ rather than a cluster: polaris --audit --audit-path ./deploy/ ``` +##### Exit Codes for Audits +
+
Exit 0
+
Successful exit code
+
Exit 1
+
Could not run audit, or application had a failure while running.
+
Exit 2
+
Unused
+
Exit 3
+
Exiting due to `--set-exit-code-on-error` being set and at least one error was found after an audit.
+
Edit 4 +
Exiting due to `--set-exit-code-below-score` being set and the audit resulted in a score less than the minimum score value.
+
+ ##### Running with CI/CD You can integrate Polaris into CI/CD for repositories containing infrastructure-as-code. For example, to fail if polaris detects *any* error-level issues, or if the score drops below 90%: diff --git a/examples/config.yaml b/examples/config.yaml index ee26eeb9d..232b5b663 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -41,3 +41,9 @@ security: - SYS_CHROOT - KILL - AUDIT_WRITE +controllers_to_scan: + - Deployments + - StatefulSets + - DaemonSets + - CronJobs + - Jobs diff --git a/main.go b/main.go index 637118b7a..6edc55c9e 100644 --- a/main.go +++ b/main.go @@ -46,6 +46,8 @@ const ( ) func main() { + // Load CLI Flags + // TODO: Split up global flags vs dashboard/webhook/audit specific flags dashboard := flag.Bool("dashboard", false, "Runs the webserver for Polaris dashboard.") webhook := flag.Bool("webhook", false, "Runs the webhook webserver.") audit := flag.Bool("audit", false, "Runs a one-time audit.") @@ -67,11 +69,13 @@ func main() { flag.Parse() + // if version is specified anywhere, print and exit if *version { fmt.Printf("Polaris version %s\n", Version) os.Exit(0) } + // Set logging level parsedLevel, err := logrus.ParseLevel(*logLevel) if err != nil { logrus.Errorf("log-level flag has invalid value %s", *logLevel) @@ -79,25 +83,32 @@ func main() { logrus.SetLevel(parsedLevel) } + // Parse the config file c, err := conf.ParseFile(*configPath) - if *displayName != "" { - c.DisplayName = *displayName - } if err != nil { logrus.Errorf("Error parsing config at %s: %v", *configPath, err) os.Exit(1) } + // Override display name on reports if defined in CLI flags + if *displayName != "" { + c.DisplayName = *displayName + } + + // default to run as audit if no "run-mode" is defined if !*dashboard && !*webhook && !*audit { *audit = true } + // perform the action for the desired "run-mode" if *webhook { startWebhookServer(c, *disableWebhookConfigInstaller, *webhookPort) } else if *dashboard { startDashboardServer(c, *auditPath, *dashboardPort, *dashboardBasePath) } else if *audit { auditData := runAndReportAudit(c, *auditPath, *auditOutputFile, *auditOutputURL, *auditOutputFormat) + + // exit code 3 if any errors in the audit else if score is under desired minimum, exit 4 if *setExitCode && auditData.ClusterSummary.Results.Totals.Errors > 0 { logrus.Infof("%d errors found in audit", auditData.ClusterSummary.Results.Totals.Errors) os.Exit(3) @@ -190,6 +201,7 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool } func runAndReportAudit(c conf.Configuration, auditPath string, outputFile string, outputURL string, outputFormat string) validator.AuditData { + // Create a kubernetes client resource provider k, err := kube.CreateResourceProvider(auditPath) if err != nil { logrus.Errorf("Error fetching Kubernetes resources %v", err) @@ -198,7 +210,8 @@ func runAndReportAudit(c conf.Configuration, auditPath string, outputFile string auditData, err := validator.RunAudit(c, k) if err != nil { - panic(err) + logrus.Errorf("Error while running audit on resources: %v", err) + os.Exit(1) } var outputBytes []byte diff --git a/pkg/config/config.go b/pkg/config/config.go index 18f268e42..1acdff6b6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -28,12 +28,13 @@ import ( // Configuration contains all of the config for the validation checks. type Configuration struct { - DisplayName string `json:"displayName"` - Resources Resources `json:"resources"` - HealthChecks HealthChecks `json:"healthChecks"` - Images Images `json:"images"` - Networking Networking `json:"networking"` - Security Security `json:"security"` + DisplayName string `json:"displayName"` + Resources Resources `json:"resources"` + HealthChecks HealthChecks `json:"healthChecks"` + Images Images `json:"images"` + Networking Networking `json:"networking"` + Security Security `json:"security"` + ControllersToScan []SupportedController `json:"controllers_to_scan"` } // Resources contains config for resource requests and limits. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 23720459a..273de649b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -53,6 +53,9 @@ resources: warning: below: 300M above: 4G +controllers_to_scan: + - Deployments + - StatefulSets ` var resourceConfJSON1 = `{ @@ -97,7 +100,8 @@ var resourceConfJSON1 = `{ "above": "4G" } } - } + }, + "controllers_to_scan": ["Deployments", "StatefulSets"] }` func TestParseError(t *testing.T) { @@ -144,4 +148,7 @@ func testParsedConfig(t *testing.T, config *Configuration) { assert.Equal(t, int64(6000), memLimits.Error.Above.ScaledValue(resource.Mega)) assert.Equal(t, int64(300), memLimits.Warning.Below.ScaledValue(resource.Mega)) assert.Equal(t, int64(4000), memLimits.Warning.Above.ScaledValue(resource.Mega)) + + controllersToScan := config.ControllersToScan + assert.ElementsMatch(t, []SupportedController{Deployments, StatefulSets}, controllersToScan) } diff --git a/pkg/config/supportedcontrollers.go b/pkg/config/supportedcontrollers.go new file mode 100644 index 000000000..410c34e41 --- /dev/null +++ b/pkg/config/supportedcontrollers.go @@ -0,0 +1,105 @@ +package config + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +const ( + // Unsupported is the default enum for non-defined controller types + Unsupported SupportedController = iota + // Deployments are a supported controller for scanning pod specs + Deployments + // StatefulSets are a supported controller for scanning pod specs + StatefulSets + // DaemonSets are a supported controller for scanning pod specs + DaemonSets + // Jobs are a supported controller for scanning pod specs + Jobs + // CronJobs are a supported controller for scanning pod specs + CronJobs +) + +// ControllerStrings are strongly ordered to match the SupportedController enum +var ControllerStrings = []string{ + "Unsupported", + "Deployments", + "StatefulSets", + "DaemonSets", + "Jobs", + "CronJobs", +} + +// stringLookupForSupportedControllers is the list of lowercase singular and plural strings for string to enum lookup +var stringLookupForSupportedControllers = map[string]SupportedController{ + "deployment": Deployments, + "deployments": Deployments, + "statefulset": StatefulSets, + "statefulsets": StatefulSets, + "daemonset": DaemonSets, + "daemonsets": DaemonSets, + "job": Jobs, + "jobs": Jobs, + "cronjob": CronJobs, + "cronjobs": CronJobs, +} + +// SupportedController is a constant item of a controller that is supported for scanning pod specs +type SupportedController int + +// String returns the string name for a given SupportedController enum +func (s SupportedController) String() string { + return ControllerStrings[s] +} + +// MarshalJSON manages writing the enum into json data or error on unsupported value +func (s SupportedController) MarshalJSON() ([]byte, error) { + if s == Unsupported { + return []byte{}, fmt.Errorf("Unsupported is not a valid Supported Controller") + } + buffer := bytes.NewBufferString(`"`) + buffer.WriteString(s.String()) + buffer.WriteString(`"`) + return buffer.Bytes(), nil +} + +// UnmarshalJSON handles reading json data into enum +func (s *SupportedController) UnmarshalJSON(b []byte) error { + var j string + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + + *s, err = GetSupportedControllerFromString(j) + if err != nil { + return err + } + return nil +} + +// GetSupportedControllerFromString fuzzy matches a string with a SupportedController Enum +func GetSupportedControllerFromString(str string) (SupportedController, error) { + lowerStr := strings.ToLower(str) + if stringLookupForSupportedControllers[lowerStr] == Unsupported { + return 0, fmt.Errorf("Value ('%v') in configuration was not found in Supported Controllers: (%v)", str, strings.Join(ControllerStrings, ",")) + } + return stringLookupForSupportedControllers[lowerStr], nil +} + +// CheckIfKindIsConfiguredForValidation takes a kind (in string format) and checks if Polaris is configured to scan this type of controller +func (c Configuration) CheckIfKindIsConfiguredForValidation(kind string) bool { + controller, err := GetSupportedControllerFromString(kind) + // if no errors then we found the kind in supported controller types + if err == nil { + // see if the kind exists in the controllers to scan config + for _, controllerToScan := range c.ControllersToScan { + if controller == controllerToScan { + return true + } + } + } + return false +} diff --git a/pkg/config/supportedcontrollers_test.go b/pkg/config/supportedcontrollers_test.go new file mode 100644 index 000000000..e573fa657 --- /dev/null +++ b/pkg/config/supportedcontrollers_test.go @@ -0,0 +1,105 @@ +// Copyright 2019 ReactiveOps +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "encoding/json" + "fmt" + "testing" +) + +type checkMarshal struct { + Controllers []SupportedController `json:"controllers"` +} + +func TestUnmarshalSupportedControllers(t *testing.T) { + for idx, controllerString := range ControllerStrings { + // Check taking all strings and convert them into enums + object := checkMarshal{} + jsonBytes := []byte(fmt.Sprintf(`{"controllers":["%v"]}`, controllerString)) + err := json.Unmarshal(jsonBytes, &object) + if idx == 0 { + if err == nil { + // Assure the first element always should NOT unmarshal + t.Errorf("Expected the first element (%s) to fail json unmarshal. First element in this array should always be 'Unsupported'", controllerString) + } + } else if err != nil { + t.Errorf("Could not unmarshal json (%s) to a Supported controller; Received (%v)", jsonBytes, err) + } + } + + badJSON := []byte(`{"controllers":[{"not":"valid_structure"}]}`) + err := json.Unmarshal(badJSON, &checkMarshal{}) + if err == nil { + t.Error("expected invalid schema json to fail unmarshal") + } +} + +func TestMarshalSupportedControllers(t *testing.T) { + for idx, controllerString := range ControllerStrings { + controllerType, err := GetSupportedControllerFromString(controllerString) + if idx == 0 { + if err == nil { + t.Errorf("Expected first element (%s) to fail as a non-valid supported controller. Reserved for 'Unsupported'", controllerString) + } + } else if err != nil { + t.Errorf("Unable to take the configured string (%s) and convert into Enum; Error: (%s)", controllerString, err) + } + + object := checkMarshal{ + Controllers: []SupportedController{controllerType}, + } + _, err = json.Marshal(object) + if idx == 0 { + if err == nil { + t.Errorf("Expected (%s) to throw an error. Reserving the first element in the enum to be an invalid config", controllerString) + } + } else if err != nil { + t.Errorf("Could not write json output for element (%s); Received Error: (%s)", controllerString, err) + } + } +} + +func TestCheckIfControllerKindIsConfiguredForValidation(t *testing.T) { + config := Configuration{} + for _, controllerString := range ControllerStrings[1:] { + controllerEnum, err := GetSupportedControllerFromString(controllerString) + if err != nil { + t.Errorf("Expected controller string (%s) to be convertable into enum: (%s)", controllerString, err) + } + config.ControllersToScan = append(config.ControllersToScan, controllerEnum) + } + + validControllerKinds := []string{ + "deployment", + "statefulset", + } + + invalidControllerKinds := []string{ + "nonExistent", + } + + for _, kind := range validControllerKinds { + if ok := config.CheckIfKindIsConfiguredForValidation(kind); !ok { + t.Errorf("Kind (%s) expected to be valid for configuration.", kind) + } + } + + for _, kind := range invalidControllerKinds { + if ok := config.CheckIfKindIsConfiguredForValidation(kind); ok { + t.Errorf("Kind (%s) should not be a valid controller to check", kind) + } + } +} diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go index 0b5d40a91..674449e67 100644 --- a/pkg/kube/resources.go +++ b/pkg/kube/resources.go @@ -11,6 +11,8 @@ import ( "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sYaml "k8s.io/apimachinery/pkg/util/yaml" @@ -28,6 +30,9 @@ type ResourceProvider struct { Nodes []corev1.Node Deployments []appsv1.Deployment StatefulSets []appsv1.StatefulSet + DaemonSets []appsv1.DaemonSet + Jobs []batchv1.Job + CronJobs []batchv1beta1.CronJob Namespaces []corev1.Namespace Pods []corev1.Pod } @@ -53,6 +58,9 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error) Nodes: []corev1.Node{}, Deployments: []appsv1.Deployment{}, StatefulSets: []appsv1.StatefulSet{}, + DaemonSets: []appsv1.DaemonSet{}, + Jobs: []batchv1.Job{}, + CronJobs: []batchv1beta1.CronJob{}, Namespaces: []corev1.Namespace{}, Pods: []corev1.Pod{}, } @@ -124,6 +132,21 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string logrus.Errorf("Error fetching StatefulSets%v", err) return nil, err } + daemonSets, err := kube.AppsV1().DaemonSets("").List(listOpts) + if err != nil { + logrus.Errorf("Error fetching DaemonSets %v", err) + return nil, err + } + jobs, err := kube.BatchV1().Jobs("").List(listOpts) + if err != nil { + logrus.Errorf("Error fetching Jobs %v", err) + return nil, err + } + cronJobs, err := kube.BatchV1beta1().CronJobs("").List(listOpts) + if err != nil { + logrus.Errorf("Error fetching CronJobs %v", err) + return nil, err + } nodes, err := kube.CoreV1().Nodes().List(listOpts) if err != nil { logrus.Errorf("Error fetching Nodes %v", err) @@ -147,6 +170,9 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string CreationTime: time.Now(), Deployments: deploys.Items, StatefulSets: statefulSets.Items, + DaemonSets: daemonSets.Items, + Jobs: jobs.Items, + CronJobs: cronJobs.Items, Nodes: nodes.Items, Namespaces: namespaces.Items, Pods: pods.Items, @@ -173,6 +199,18 @@ func addResourceFromString(contents string, resources *ResourceProvider) error { dep := appsv1.StatefulSet{} err = decoder.Decode(&dep) resources.StatefulSets = append(resources.StatefulSets, dep) + } else if resource.Kind == "DaemonSet" { + dep := appsv1.DaemonSet{} + err = decoder.Decode(&dep) + resources.DaemonSets = append(resources.DaemonSets, dep) + } else if resource.Kind == "Job" { + dep := batchv1.Job{} + err = decoder.Decode(&dep) + resources.Jobs = append(resources.Jobs, dep) + } else if resource.Kind == "Job" { + dep := batchv1beta1.CronJob{} + err = decoder.Decode(&dep) + resources.CronJobs = append(resources.CronJobs, dep) } else if resource.Kind == "Namespace" { ns := corev1.Namespace{} err = decoder.Decode(&ns) diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 67f791ba4..1c72eed9b 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -17,80 +17,45 @@ package validator import ( conf "github.com/reactiveops/polaris/pkg/config" "github.com/reactiveops/polaris/pkg/kube" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" + "github.com/reactiveops/polaris/pkg/validator/controllers" + controller "github.com/reactiveops/polaris/pkg/validator/controllers" ) -// ControllerSpec is a generic type for k8s controller specs -type ControllerSpec struct { - Template corev1.PodTemplateSpec -} - -// Controller is a generic type for k8s controllers (e.g. Deployments and StatefulSets) -type Controller struct { - Type string - Name string - Namespace string - Spec ControllerSpec -} - // ValidateController validates a single controller, returns a ControllerResult. -func ValidateController(conf conf.Configuration, controller Controller) ControllerResult { - pod := controller.Spec.Template.Spec - podResult := ValidatePod(conf, &pod) +func ValidateController(conf conf.Configuration, controller controller.Interface) ControllerResult { + pod := controller.GetPodSpec() + podResult := ValidatePod(conf, pod) return ControllerResult{ - Type: controller.Type, - Name: controller.Name, + Type: controller.GetType().String(), + Name: controller.GetName(), PodResult: podResult, } } // ValidateControllers validates that each deployment conforms to the Polaris config, -// returns a list of ResourceResults organized by namespace. +// builds a list of ResourceResults organized by namespace. func ValidateControllers(config conf.Configuration, kubeResources *kube.ResourceProvider, nsResults *NamespacedResults) { - controllers := []Controller{} - for _, deploy := range kubeResources.Deployments { - controllers = append(controllers, ControllerFromDeployment(deploy)) - } - for _, deploy := range kubeResources.StatefulSets { - controllers = append(controllers, ControllerFromStatefulSet(deploy)) + var controllersToAudit []controller.Interface + for _, supportedControllers := range config.ControllersToScan { + loadedControllers, _ := controllers.LoadControllersByType(supportedControllers, kubeResources) + controllersToAudit = append(controllersToAudit, loadedControllers...) } - for _, controller := range controllers { + + for _, controller := range controllersToAudit { controllerResult := ValidateController(config, controller) - nsResult := nsResults.getNamespaceResult(controller.Namespace) + nsResult := nsResults.getNamespaceResult(controller.GetNamespace()) nsResult.Summary.appendResults(*controllerResult.PodResult.Summary) - if controller.Type == "Deployment" { - nsResult.DeploymentResults = append(nsResult.DeploymentResults, controllerResult) - } else if controller.Type == "StatefulSet" { + switch controller.GetType() { + case conf.StatefulSets: nsResult.StatefulSetResults = append(nsResult.StatefulSetResults, controllerResult) + case conf.DaemonSets: + nsResult.DaemonSetResults = append(nsResult.DaemonSetResults, controllerResult) + case conf.Deployments: + nsResult.DeploymentResults = append(nsResult.DeploymentResults, controllerResult) + case conf.Jobs: + nsResult.JobResults = append(nsResult.JobResults, controllerResult) + case conf.CronJobs: + nsResult.CronJobResults = append(nsResult.CronJobResults, controllerResult) } } } - -// ControllerFrom* functions are 100% boilerplate - -// ControllerFromDeployment creates a controller -func ControllerFromDeployment(c appsv1.Deployment) Controller { - spec := ControllerSpec{ - Template: c.Spec.Template, - } - return Controller{ - Type: "Deployment", - Name: c.Name, - Namespace: c.Namespace, - Spec: spec, - } -} - -// ControllerFromStatefulSet creates a controller -func ControllerFromStatefulSet(c appsv1.StatefulSet) Controller { - spec := ControllerSpec{ - Template: c.Spec.Template, - } - return Controller{ - Type: "StatefulSet", - Name: c.Name, - Namespace: c.Namespace, - Spec: spec, - } -} diff --git a/pkg/validator/controllers/cronjob.go b/pkg/validator/controllers/cronjob.go new file mode 100644 index 000000000..ec695a74a --- /dev/null +++ b/pkg/validator/controllers/cronjob.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPIBatchV1beta1 "k8s.io/api/batch/v1beta1" + kubeAPICoreV1 "k8s.io/api/core/v1" +) + +// CronJobController is an implementation of controller for deployments +type CronJobController struct { + GenericController + K8SResource kubeAPIBatchV1beta1.CronJob +} + +// GetPodTemplate returns the original template spec +func (d CronJobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { + return &d.K8SResource.Spec.JobTemplate.Spec.Template +} + +// GetPodSpec returns the original kubernetes template pod spec +func (d CronJobController) GetPodSpec() *kubeAPICoreV1.PodSpec { + return &d.K8SResource.Spec.JobTemplate.Spec.Template.Spec +} + +// GetType returns the supportedcontroller enum type +func (d CronJobController) GetType() config.SupportedController { + return config.CronJobs +} + +// 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 + return controller +} diff --git a/pkg/validator/controllers/daemonset.go b/pkg/validator/controllers/daemonset.go new file mode 100644 index 000000000..16b7bb5ab --- /dev/null +++ b/pkg/validator/controllers/daemonset.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPIAppsV1 "k8s.io/api/apps/v1" + kubeAPICoreV1 "k8s.io/api/core/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 +} + +// GetType returns the supportedcontroller enum type +func (d DaemonSetController) GetType() config.SupportedController { + return config.DaemonSets +} + +// NewDaemonSetController builds a new controller interface for Deployments +func NewDaemonSetController(originalDeploymentResource kubeAPIAppsV1.DaemonSet) Interface { + controller := DaemonSetController{} + controller.Name = originalDeploymentResource.Name + controller.Namespace = originalDeploymentResource.Namespace + controller.K8SResource = originalDeploymentResource + return controller +} diff --git a/pkg/validator/controllers/deployment.go b/pkg/validator/controllers/deployment.go new file mode 100644 index 000000000..5de2ef4cf --- /dev/null +++ b/pkg/validator/controllers/deployment.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPIAppsV1 "k8s.io/api/apps/v1" + kubeAPICoreV1 "k8s.io/api/core/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 +} + +// GetType returns the supportedcontroller enum type +func (d DeploymentController) GetType() 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 + return controller +} diff --git a/pkg/validator/controllers/interface.go b/pkg/validator/controllers/interface.go new file mode 100644 index 000000000..44d1bcf56 --- /dev/null +++ b/pkg/validator/controllers/interface.go @@ -0,0 +1,65 @@ +package controllers + +import ( + "fmt" + + "github.com/reactiveops/polaris/pkg/config" + "github.com/reactiveops/polaris/pkg/kube" + kubeAPICoreV1 "k8s.io/api/core/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 + GetType() config.SupportedController +} + +// 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 +} + +// LoadControllersByType loads a list of controllers from the kubeResources by detecting their type +func LoadControllersByType(controllerType config.SupportedController, kubeResources *kube.ResourceProvider) ([]Interface, error) { + interfaces := []Interface{} + switch controllerType { + 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)) + } + } + if len(interfaces) > 0 { + return interfaces, nil + } + return nil, fmt.Errorf("Controller type (%s) does not have a generator", controllerType) +} diff --git a/pkg/validator/controllers/job.go b/pkg/validator/controllers/job.go new file mode 100644 index 000000000..7d206b402 --- /dev/null +++ b/pkg/validator/controllers/job.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPIBatchV1 "k8s.io/api/batch/v1" + kubeAPICoreV1 "k8s.io/api/core/v1" +) + +// JobController is an implementation of controller for deployments +type JobController struct { + GenericController + K8SResource kubeAPIBatchV1.Job +} + +// GetPodTemplate returns the original template spec +func (d JobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { + return &d.K8SResource.Spec.Template +} + +// GetPodSpec returns the original kubernetes template pod spec +func (d JobController) GetPodSpec() *kubeAPICoreV1.PodSpec { + return &d.K8SResource.Spec.Template.Spec +} + +// GetType returns the supportedcontroller enum type +func (d JobController) GetType() config.SupportedController { + return config.Jobs +} + +// NewJobController builds a new controller interface for Deployments +func NewJobController(originalDeploymentResource kubeAPIBatchV1.Job) Interface { + controller := JobController{} + controller.Name = originalDeploymentResource.Name + controller.Namespace = originalDeploymentResource.Namespace + controller.K8SResource = originalDeploymentResource + return controller +} diff --git a/pkg/validator/controllers/statefulsets.go b/pkg/validator/controllers/statefulsets.go new file mode 100644 index 000000000..a5a1869fe --- /dev/null +++ b/pkg/validator/controllers/statefulsets.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPIAppsV1 "k8s.io/api/apps/v1" + kubeAPICoreV1 "k8s.io/api/core/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 +} + +// GetType returns the supportedcontroller enum type +func (s StatefulSetController) GetType() config.SupportedController { + return config.StatefulSets +} + +// NewStatefulSetController builds a statefulset controller +func NewStatefulSetController(originalStatefulSetResource kubeAPIAppsV1.StatefulSet) Interface { + controller := StatefulSetController{} + controller.Name = originalStatefulSetResource.Name + controller.Namespace = originalStatefulSetResource.Namespace + controller.K8SResource = originalStatefulSetResource + return controller +} diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go index e484d8346..4b0246002 100644 --- a/pkg/validator/fullaudit.go +++ b/pkg/validator/fullaudit.go @@ -21,6 +21,9 @@ type ClusterSummary struct { Namespaces int Deployments int StatefulSets int + DaemonSets int + Jobs int + CronJobs int Score uint } @@ -70,6 +73,9 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) ( Namespaces: len(kubeResources.Namespaces), Deployments: len(kubeResources.Deployments), StatefulSets: len(kubeResources.StatefulSets), + DaemonSets: len(kubeResources.DaemonSets), + Jobs: len(kubeResources.Jobs), + CronJobs: len(kubeResources.CronJobs), Results: clusterResults, Score: clusterResults.Totals.GetScore(), }, diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go index d8b9951a1..48b7daa2e 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -20,6 +20,10 @@ func TestGetTemplateData(t *testing.T) { ReadinessProbeMissing: conf.SeverityError, LivenessProbeMissing: conf.SeverityWarning, }, + ControllersToScan: []conf.SupportedController{ + conf.Deployments, + conf.StatefulSets, + }, } sum := ResultSummary{ diff --git a/pkg/validator/types.go b/pkg/validator/types.go index f0fc0ca7e..2db244714 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -36,6 +36,9 @@ type NamespaceResult struct { Summary *ResultSummary DeploymentResults []ControllerResult StatefulSetResults []ControllerResult + DaemonSetResults []ControllerResult + JobResults []ControllerResult + CronJobResults []ControllerResult } // NamespacedResults is a mapping of namespace name to the validation results. @@ -49,6 +52,9 @@ func (nsResults NamespacedResults) getNamespaceResult(nsName string) *NamespaceR Summary: &ResultSummary{}, DeploymentResults: []ControllerResult{}, StatefulSetResults: []ControllerResult{}, + DaemonSetResults: []ControllerResult{}, + JobResults: []ControllerResult{}, + CronJobResults: []ControllerResult{}, } nsResults[nsName] = nsResult default: diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index b46d71fe5..0f2df4a01 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -22,6 +22,7 @@ import ( conf "github.com/reactiveops/polaris/pkg/config" validator "github.com/reactiveops/polaris/pkg/validator" + "github.com/reactiveops/polaris/pkg/validator/controllers" "github.com/sirupsen/logrus" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" @@ -92,16 +93,27 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons err = v.decoder.Decode(req, &pod) podResult = validator.ValidatePod(v.Config, &pod.Spec) } else { - var controller validator.Controller - switch req.AdmissionRequest.Kind.Kind { - case "Deployment": + var controller controllers.Interface + 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) + // FIXME: Should we be returning an OK response as skipped? + return admission.ErrorResponse(http.StatusBadRequest, err) + } + controllerType, err := conf.GetSupportedControllerFromString(req.AdmissionRequest.Kind.Kind) + if err != nil { + msg := fmt.Errorf("Unexpected error occurred. Expected Kind to be a supported type (%s)", req.AdmissionRequest.Kind.Kind) + logrus.Error(msg) + return admission.ErrorResponse(http.StatusInternalServerError, err) + } + switch controllerType { + case conf.Deployments: deploy := appsv1.Deployment{} err = v.decoder.Decode(req, &deploy) - controller = validator.ControllerFromDeployment(deploy) - case "StatefulSet": + controller = controllers.NewDeploymentController(deploy) + case conf.StatefulSets: statefulSet := appsv1.StatefulSet{} err = v.decoder.Decode(req, &statefulSet) - controller = validator.ControllerFromStatefulSet(statefulSet) + controller = controllers.NewStatefulSetController(statefulSet) } controllerResult := validator.ValidateController(v.Config, controller) podResult = controllerResult.PodResult From bbec13011220a35dd3ab7c7c00eed9c9ffd5d017 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Mon, 15 Jul 2019 20:07:58 -0600 Subject: [PATCH 02/28] Added additionally supported Controllers to test config --- pkg/config/config_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 273de649b..d7577cb53 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -56,6 +56,9 @@ resources: controllers_to_scan: - Deployments - StatefulSets + - Jobs + - CronJobs + - DaemonSets ` var resourceConfJSON1 = `{ @@ -101,7 +104,7 @@ var resourceConfJSON1 = `{ } } }, - "controllers_to_scan": ["Deployments", "StatefulSets"] + "controllers_to_scan": ["Deployments", "StatefulSets", "Jobs", "CronJobs", "DaemonSets"] }` func TestParseError(t *testing.T) { @@ -150,5 +153,5 @@ func testParsedConfig(t *testing.T, config *Configuration) { assert.Equal(t, int64(4000), memLimits.Warning.Above.ScaledValue(resource.Mega)) controllersToScan := config.ControllersToScan - assert.ElementsMatch(t, []SupportedController{Deployments, StatefulSets}, controllersToScan) + assert.ElementsMatch(t, []SupportedController{Deployments, StatefulSets, Jobs, CronJobs, DaemonSets}, controllersToScan) } From b92d7c7267b0e4ce916b29cbcb31791a96aa0a79 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 13:57:31 -0600 Subject: [PATCH 03/28] Added docs pages per request --- README.md | 168 ++---------------- .../health-checks.md | 0 docs/{ => check-documentation}/images.md | 0 docs/{ => check-documentation}/networking.md | 0 docs/{ => check-documentation}/resources.md | 0 .../security-capabilities.md | 0 docs/{ => check-documentation}/security.md | 0 docs/exit-codes.md | 13 ++ docs/usage.md | 145 +++++++++++++++ 9 files changed, 168 insertions(+), 158 deletions(-) rename docs/{ => check-documentation}/health-checks.md (100%) rename docs/{ => check-documentation}/images.md (100%) rename docs/{ => check-documentation}/networking.md (100%) rename docs/{ => check-documentation}/resources.md (100%) rename docs/{ => check-documentation}/security-capabilities.md (100%) rename docs/{ => check-documentation}/security.md (100%) create mode 100644 docs/exit-codes.md create mode 100644 docs/usage.md diff --git a/README.md b/README.md index b47e59dfb..fda6f3130 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ problems in the future. Polaris can be run in a few different modes: **Want to learn more?** ReactiveOps holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@reactiveops.com` -## Quickstart +# Quickstart Dashboard ``` kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/dashboard.yaml @@ -32,6 +32,9 @@ kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 ``` With the port forwarding in place, you can open http://localhost:8080 in your browser to view the dashboard. +* * * + +# Components ## Dashboard The Polaris dashboard is a way to get a simple visual overview of the current state of your Kubernetes deployments as well as a roadmap for what can be improved. The dashboard provides a cluster wide overview as well as breaking out results by category, namespace, and deployment. @@ -42,176 +45,25 @@ The Polaris dashboard is a way to get a simple visual overview of the current st Our default standards in Polaris are rather high, so don’t be surprised if your score is lower than you might expect. A key goal for Polaris was to set a high standard and aim for great configuration by default. If the defaults we’ve included are too strict, it’s easy to adjust the configuration as part of the deployment configuration to better suit your workloads. + ## Webhook Polaris includes experimental support for an optional validating webhook. This accepts the same configuration as the dashboard, and can run the same validations. This webhook will reject any deployments that trigger a validation error. This is indicative of the greater goal of Polaris, not just to encourage better configuration through dashboard visibility, but to actually enforce it with this webhook. *Although we are working towards greater stability and better test coverage, we do not currently consider this webhook component production ready.* Unfortunately we have not found a way to display warnings as part of `kubectl` output unless we are rejecting a deployment altogether. That means that any checks with a severity of `warning` will still pass webhook validation, and the only evidence of that warning will either be in the Polaris dashboard or the Polaris webhook logs. -## Installation and Usage -Polaris can be installed on your cluster using kubectl or Helm. It can also -be run as a local binary, which will use your kubeconfig to connect to the cluster -or run against local YAML files. - -### kubectl -#### Dashboard -``` -kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/dashboard.yaml -kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 -``` - -#### Webhook -``` -kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/webhook.yaml -``` - -### Helm -Start by adding the ReactiveOps Helm repo: -``` -helm repo add reactiveops-stable https://charts.reactiveops.com/stable -``` - -#### Dashboard -``` -helm upgrade --install polaris reactiveops-stable/polaris --namespace polaris -kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 -``` - -#### Webhook -``` -helm upgrade --install polaris reactiveops-stable/polaris --namespace polaris \ - --set webhook.enable=true --set dashboard.enable=false -``` - -### Local Binary -#### Installation -Binary releases are available on the [releases page](https://github.com/reactiveops/polaris/releases) or can be installed with [Homebrew](https://brew.sh/): -``` -brew tap reactiveops/tap -brew install reactiveops/tap/polaris -polaris --version -``` - -You can run `polaris --help` to see a full list of options. - -#### Dashboard -The dashboard can be run on your local machine, without installing anything on the cluster. -Polaris will use your local kubeconfig to connect to the cluster. - -``` -polaris --dashboard --dashboard-port 8080 -``` - -#### Audits -You can also run audits on the command line and see the output as JSON, YAML, or a raw score: -``` -polaris --audit --output-format yaml > report.yaml -polaris --audit --output-format score -# 92 -``` - -Both the dashboard and audits can run against a local directory or YAML file -rather than a cluster: -``` -polaris --audit --audit-path ./deploy/ -``` - -##### Exit Codes for Audits -
-
Exit 0
-
Successful exit code
-
Exit 1
-
Could not run audit, or application had a failure while running.
-
Exit 2
-
Unused
-
Exit 3
-
Exiting due to `--set-exit-code-on-error` being set and at least one error was found after an audit.
-
Edit 4 -
Exiting due to `--set-exit-code-below-score` being set and the audit resulted in a score less than the minimum score value.
-
- -##### Running with CI/CD -You can integrate Polaris into CI/CD for repositories containing infrastructure-as-code. -For example, to fail if polaris detects *any* error-level issues, or if the score drops below 90%: -```bash -polaris --audit --audit-path ./deploy/ \ - --set-exit-code-on-error \ - --set-exit-code-below-score 90 -``` - -## Configuration - -Polaris supports a wide range of validations covering a number of Kubernetes best practices. Here's a sample configuration file that includes all currently supported checks. The [default configuration](https://github.com/reactiveops/polaris/blob/master/examples/config.yaml) contains a number of those checks. This repository also includes a sample [full configuration file](https://github.com/reactiveops/polaris/blob/master/examples/config-full.yaml) that enables all available checks. - -Each check can be assigned a `severity`. Only checks with a severity of `error` or `warning` will be validated. The results of these validations are visible on the dashboard. In the case of the validating webhook, only failures with a severity of `error` will result in a change being rejected. - -Polaris validation checks fall into several different categories: - -- [Health Checks](docs/health-checks.md) -- [Images](docs/images.md) -- [Networking](docs/networking.md) -- [Resources](docs/resources.md) -- [Security](docs/security.md) - -## CLI Options - -``` -# high-level flags --version - Prints the version of Polaris --config string - Location of Polaris configuration file --kubeconfig string - Path to a kubeconfig. Only required if out-of-cluster. --log-level string - Logrus log level (default "info") --master string - The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - -# dashboard flags --dashboard - Runs the webserver for Polaris dashboard. --dashboard-base-path string - Path on which the dashboard is served (default "/") --dashboard-port int - Port for the dashboard webserver (default 8080) --display-name string - An optional identifier for the audit - -# audit flags --audit - Runs a one-time audit. --audit-path string - If specified, audits one or more YAML files instead of a cluster --output-file string - Destination file for audit results --output-format string - Output format for results - json, yaml, or score (default "json") --output-url string - Destination URL to send audit results --set-exit-code-below-score int - When running with --audit, set an exit code of 4 when the score is below this threshold (1-100) --set-exit-code-on-error - When running with --audit, set an exit code of 3 when the audit contains error-level issues. - -# webhook flags --webhook - Runs the webhook webserver. --webhook-port int - Port for the webhook webserver (default 9876) --disable-webhook-config-installer - disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping -``` +# Usage Documentation +See the [Usage Guide](/docs/usage.md) in the docs folder. -## Contributing +# Contributing PRs welcome! Check out the [Contributing Guidelines](CONTRIBUTING.md), [Code of Conduct](CODE_OF_CONDUCT.md), and [Roadmap](ROADMAP.md) for more information. -## Further Information +# Further Information A history of changes to this project can be viewed in the [Changelog](CHANGELOG.md) If you'd like to learn more about Polaris, or if you'd like to speak with a Kubernetes expert, you can contact `info@reactiveops.com` or [visit our website](https://reactiveops.com) -## License +# License Apache License 2.0 diff --git a/docs/health-checks.md b/docs/check-documentation/health-checks.md similarity index 100% rename from docs/health-checks.md rename to docs/check-documentation/health-checks.md diff --git a/docs/images.md b/docs/check-documentation/images.md similarity index 100% rename from docs/images.md rename to docs/check-documentation/images.md diff --git a/docs/networking.md b/docs/check-documentation/networking.md similarity index 100% rename from docs/networking.md rename to docs/check-documentation/networking.md diff --git a/docs/resources.md b/docs/check-documentation/resources.md similarity index 100% rename from docs/resources.md rename to docs/check-documentation/resources.md diff --git a/docs/security-capabilities.md b/docs/check-documentation/security-capabilities.md similarity index 100% rename from docs/security-capabilities.md rename to docs/check-documentation/security-capabilities.md diff --git a/docs/security.md b/docs/check-documentation/security.md similarity index 100% rename from docs/security.md rename to docs/check-documentation/security.md diff --git a/docs/exit-codes.md b/docs/exit-codes.md new file mode 100644 index 000000000..0b260c5f6 --- /dev/null +++ b/docs/exit-codes.md @@ -0,0 +1,13 @@ +# Polaris Exit Codes for Audit Runs +
+
Exit 0
+
Successful exit code
+
Exit 1
+
Could not run audit, or application had a failure while running.
+
Exit 2
+
Unused
+
Exit 3
+
Exiting due to `--set-exit-code-on-error` being set and at least one error was found after an audit.
+
Edit 4 +
Exiting due to `--set-exit-code-below-score` being set and the audit resulted in a score less than the minimum score value.
+
diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 000000000..2f8c19aef --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,145 @@ +# Installation and Usage +Polaris can be installed on your cluster using kubectl or Helm. It can also +be run as a local binary, which will use your kubeconfig to connect to the cluster +or run against local YAML files. + +## Configuration + +Polaris supports a wide range of validations covering a number of Kubernetes best practices. Here's a sample configuration file that includes all currently supported checks. The [default configuration](https://github.com/reactiveops/polaris/blob/master/examples/config.yaml) contains a number of those checks. This repository also includes a sample [full configuration file](https://github.com/reactiveops/polaris/blob/master/examples/config-full.yaml) that enables all available checks. + +Each check can be assigned a `severity`. Only checks with a severity of `error` or `warning` will be validated. The results of these validations are visible on the dashboard. In the case of the validating webhook, only failures with a severity of `error` will result in a change being rejected. + +Polaris validation checks fall into several different categories: + +- [Health Checks](check-documentation/health-checks.md) +- [Images](check-documentation/images.md) +- [Networking](check-documentation/networking.md) +- [Resources](check-documentation/resources.md) +- [Security](check-documentation/security.md) + +## CLI Options + +``` +# high-level flags +-version + Prints the version of Polaris +-config string + Location of Polaris configuration file +-kubeconfig string + Path to a kubeconfig. Only required if out-of-cluster. +-log-level string + Logrus log level (default "info") +-master string + The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. + +# dashboard flags +-dashboard + Runs the webserver for Polaris dashboard. +-dashboard-base-path string + Path on which the dashboard is served (default "/") +-dashboard-port int + Port for the dashboard webserver (default 8080) +-display-name string + An optional identifier for the audit + +# audit flags +-audit + Runs a one-time audit. +-audit-path string + If specified, audits one or more YAML files instead of a cluster +-output-file string + Destination file for audit results +-output-format string + Output format for results - json, yaml, or score (default "json") +-output-url string + Destination URL to send audit results +-set-exit-code-below-score int + When running with --audit, set an exit code of 4 when the score is below this threshold (1-100) +-set-exit-code-on-error + When running with --audit, set an exit code of 3 when the audit contains error-level issues. + +# webhook flags +-webhook + Runs the webhook webserver. +-webhook-port int + Port for the webhook webserver (default 9876) +-disable-webhook-config-installer + disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping +``` + +# Installing +There are several ways to install and use Polaris. Below outline ways to install using `kubectl`, `helm` and `local binary`. + +## kubectl +### Dashboard +``` +kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/dashboard.yaml +kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 +``` + +### Webhook +``` +kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/webhook.yaml +``` + +## Helm +Start by adding the ReactiveOps Helm repo: +``` +helm repo add reactiveops-stable https://charts.reactiveops.com/stable +``` + +### Dashboard +``` +helm upgrade --install polaris reactiveops-stable/polaris --namespace polaris +kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 +``` + +### Webhook +``` +helm upgrade --install polaris reactiveops-stable/polaris --namespace polaris \ + --set webhook.enable=true --set dashboard.enable=false +``` + +## Local Binary +### Installation +Binary releases are available on the [releases page](https://github.com/reactiveops/polaris/releases) or can be installed with [Homebrew](https://brew.sh/): +``` +brew tap reactiveops/tap +brew install reactiveops/tap/polaris +polaris --version +``` + +You can run `polaris --help` to see a full list of options. + +### Dashboard +The dashboard can be run on your local machine, without installing anything on the cluster. +Polaris will use your local kubeconfig to connect to the cluster. + +``` +polaris --dashboard --dashboard-port 8080 +``` + +### Audits +You can also run audits on the command line and see the output as JSON, YAML, or a raw score: +``` +polaris --audit --output-format yaml > report.yaml +polaris --audit --output-format score +# 92 +``` + +Both the dashboard and audits can run against a local directory or YAML file +rather than a cluster: +``` +polaris --audit --audit-path ./deploy/ +``` + +### Running with CI/CD +You can integrate Polaris into CI/CD for repositories containing infrastructure-as-code. +For example, to fail if polaris detects *any* error-level issues, or if the score drops below 90%: +```bash +polaris --audit --audit-path ./deploy/ \ + --set-exit-code-on-error \ + --set-exit-code-below-score 90 +``` + +For more on exit code meanings, see [exit-code docs](exit-codes.md). From 18b9f6db683b257cbf82f97ec1dff179578adfe5 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 14:02:11 -0600 Subject: [PATCH 04/28] Copyright rename --- LICENSE | 2 +- main.go | 2 +- pkg/config/config.go | 2 +- pkg/config/config_test.go | 2 +- pkg/config/severity.go | 2 +- pkg/config/supportedcontrollers_test.go | 2 +- pkg/dashboard/dashboard.go | 2 +- pkg/dashboard/helpers.go | 2 +- pkg/validator/container.go | 2 +- pkg/validator/container_test.go | 2 +- pkg/validator/controller.go | 2 +- pkg/validator/pod.go | 2 +- pkg/validator/pod_test.go | 2 +- pkg/validator/resource.go | 2 +- pkg/validator/types.go | 2 +- pkg/webhook/validator.go | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/LICENSE b/LICENSE index 394f00f7e..5466f7785 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 ReactiveOps + Copyright 2019 Fairwinds Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/main.go b/main.go index addf7f6b1..9279f7cbd 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/config.go b/pkg/config/config.go index 1acdff6b6..ed37bb457 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index d7577cb53..142da1af4 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/severity.go b/pkg/config/severity.go index ca61f2532..cc419e783 100644 --- a/pkg/config/severity.go +++ b/pkg/config/severity.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/supportedcontrollers_test.go b/pkg/config/supportedcontrollers_test.go index e573fa657..f94b75b0b 100644 --- a/pkg/config/supportedcontrollers_test.go +++ b/pkg/config/supportedcontrollers_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index 7e33c6a88..a5d606ff3 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dashboard/helpers.go b/pkg/dashboard/helpers.go index 2165369cc..bdebb92a8 100644 --- a/pkg/dashboard/helpers.go +++ b/pkg/dashboard/helpers.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/container.go b/pkg/validator/container.go index 65ec9f61e..8fca1275f 100644 --- a/pkg/validator/container.go +++ b/pkg/validator/container.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index 6f3995556..ed5c2bbdc 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 1c72eed9b..e8548f19d 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index 2c4e33e5f..e0cdb38bd 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index 87de9be27..76cb93ecb 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/resource.go b/pkg/validator/resource.go index 3ac533005..284f868f1 100644 --- a/pkg/validator/resource.go +++ b/pkg/validator/resource.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/types.go b/pkg/validator/types.go index 2db244714..200654464 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index 0f2df4a01..415fdcb0e 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -1,4 +1,4 @@ -// Copyright 2019 ReactiveOps +// Copyright 2019 Fairwinds // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 8d3c08efe66139288ddbc6183f17cd6b3a589058 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 14:08:18 -0600 Subject: [PATCH 05/28] Adjusted office hours name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20ccfe91c..679eaa397 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ problems in the future. Polaris can be run in a few different modes: - An experimental validating webhook that can prevent any future deployments that do not live up to a configured standard. - A command-line audit that can be incorporated into your CI/CD pipeline -**Want to learn more?** ReactiveOps holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@fairwinds.com` +**Want to learn more?** FairWinds holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@fairwinds.com` # Quickstart Dashboard From 824d2100c822646894f8d7dae77b6d7faa0d9d11 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 16:56:27 -0600 Subject: [PATCH 06/28] Adjustments - Added `ReplicationController` type controllers to the supported list - Adjusted logic for failed YAML parsing to bubble up errors - Added better logic for calculating summaries on cluster wide results - Relocated responsibilities for counting types into validators vs spreading it around more packages - Fixed bug where cronjob parsing was using wrong KIND - Added fixtures for mocking new controller types - Added example yamls to test scanning files - Added functions to NamespacedResult(s) to reduce code complexity deep set iterations - Refactored how results get added to namespacedresults so adding more later is easier - Minor signature changes for interface implementing structs for controllers --- CHANGELOG.md | 3 + deploy/dashboard.yaml | 1 - examples/config.yaml | 1 + pkg/config/supportedcontrollers.go | 25 +++-- pkg/kube/resources.go | 89 ++++++++++-------- pkg/kube/resources_test.go | 10 +- pkg/kube/test_files/test_1/cron_job.yaml | 16 ++++ pkg/kube/test_files/test_1/daemon_set.yaml | 20 ++++ pkg/kube/test_files/test_1/job.yaml | 12 +++ .../test_1/replication_controller.yaml | 19 ++++ pkg/kube/test_files/test_3/bad.yaml | 5 + pkg/validator/controller.go | 14 +-- pkg/validator/controllers/cronjob.go | 10 +- pkg/validator/controllers/daemonset.go | 8 +- pkg/validator/controllers/interface.go | 4 + pkg/validator/controllers/job.go | 18 ++-- .../controllers/replicationcontroller.go | 39 ++++++++ pkg/validator/controllers/statefulsets.go | 8 +- pkg/validator/fullaudit.go | 55 ++++++----- pkg/validator/fullaudit_test.go | 17 ++-- pkg/validator/types.go | 92 ++++++++++++++++--- test/fixtures.go | 67 +++++++++++++- 22 files changed, 395 insertions(+), 138 deletions(-) create mode 100644 pkg/kube/test_files/test_1/cron_job.yaml create mode 100644 pkg/kube/test_files/test_1/daemon_set.yaml create mode 100644 pkg/kube/test_files/test_1/job.yaml create mode 100644 pkg/kube/test_files/test_1/replication_controller.yaml create mode 100644 pkg/kube/test_files/test_3/bad.yaml create mode 100644 pkg/validator/controllers/replicationcontroller.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d17ef679..3aa489fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # x.x.x (next release) +# 0.4.0 +* Added additional Pod Controllers to scan PodSpec (`jobs`, `cronjobs`, `daemonsets`, `replicationcontrollers`) + # 0.3.1 * Changed dashboard branding to refer to new org name Fairwinds diff --git a/deploy/dashboard.yaml b/deploy/dashboard.yaml index c4c748e32..80e1e3398 100644 --- a/deploy/dashboard.yaml +++ b/deploy/dashboard.yaml @@ -219,4 +219,3 @@ spec: --- # Source: polaris/templates/webhook.service.yaml - diff --git a/examples/config.yaml b/examples/config.yaml index 232b5b663..cb9e077bd 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -47,3 +47,4 @@ controllers_to_scan: - DaemonSets - CronJobs - Jobs + - ReplicationControllers diff --git a/pkg/config/supportedcontrollers.go b/pkg/config/supportedcontrollers.go index 410c34e41..a50c6d8a7 100644 --- a/pkg/config/supportedcontrollers.go +++ b/pkg/config/supportedcontrollers.go @@ -20,6 +20,8 @@ const ( Jobs // CronJobs are a supported controller for scanning pod specs CronJobs + // ReplicationControllers are supported controllers for scanning pod specs + ReplicationControllers ) // ControllerStrings are strongly ordered to match the SupportedController enum @@ -30,20 +32,23 @@ var ControllerStrings = []string{ "DaemonSets", "Jobs", "CronJobs", + "ReplicationController", } // stringLookupForSupportedControllers is the list of lowercase singular and plural strings for string to enum lookup var stringLookupForSupportedControllers = map[string]SupportedController{ - "deployment": Deployments, - "deployments": Deployments, - "statefulset": StatefulSets, - "statefulsets": StatefulSets, - "daemonset": DaemonSets, - "daemonsets": DaemonSets, - "job": Jobs, - "jobs": Jobs, - "cronjob": CronJobs, - "cronjobs": CronJobs, + "deployment": Deployments, + "deployments": Deployments, + "statefulset": StatefulSets, + "statefulsets": StatefulSets, + "daemonset": DaemonSets, + "daemonsets": DaemonSets, + "job": Jobs, + "jobs": Jobs, + "cronjob": CronJobs, + "cronjobs": CronJobs, + "replicationcontroller": ReplicationControllers, + "replicationcontrollers": ReplicationControllers, } // SupportedController is a constant item of a controller that is supported for scanning pod specs diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go index 674449e67..7e2d1bfea 100644 --- a/pkg/kube/resources.go +++ b/pkg/kube/resources.go @@ -23,18 +23,19 @@ import ( // 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 - Namespaces []corev1.Namespace - Pods []corev1.Pod + 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 } type k8sResource struct { @@ -52,17 +53,18 @@ 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{}, - Namespaces: []corev1.Namespace{}, - Pods: []corev1.Pod{}, + 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{}, } addYaml := func(contents string) error { @@ -85,7 +87,7 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error) } err = addYaml(spec) if err != nil { - logrus.Errorf("Error parsing YAML %v", err) + logrus.Errorf("Error parsing YAML: (%v)", err) return err } } @@ -147,6 +149,11 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string logrus.Errorf("Error fetching CronJobs %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) @@ -164,18 +171,19 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string } api := ResourceProvider{ - ServerVersion: serverVersion.Major + "." + serverVersion.Minor, - SourceType: "Cluster", - SourceName: clusterName, - CreationTime: time.Now(), - Deployments: deploys.Items, - StatefulSets: statefulSets.Items, - DaemonSets: daemonSets.Items, - Jobs: jobs.Items, - CronJobs: cronJobs.Items, - Nodes: nodes.Items, - Namespaces: namespaces.Items, - Pods: pods.Items, + ServerVersion: serverVersion.Major + "." + serverVersion.Minor, + SourceType: "Cluster", + SourceName: clusterName, + CreationTime: time.Now(), + Deployments: deploys.Items, + StatefulSets: statefulSets.Items, + DaemonSets: daemonSets.Items, + Jobs: jobs.Items, + CronJobs: cronJobs.Items, + ReplicationControllers: replicationControllers.Items, + Nodes: nodes.Items, + Namespaces: namespaces.Items, + Pods: pods.Items, } return &api, nil } @@ -186,9 +194,8 @@ func addResourceFromString(contents string, resources *ResourceProvider) error { resource := k8sResource{} err := decoder.Decode(&resource) if err != nil { - // TODO: should we panic if the YAML is bad? logrus.Errorf("Invalid YAML: %s", string(contents)) - return nil + return err } decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000) if resource.Kind == "Deployment" { @@ -207,10 +214,14 @@ func addResourceFromString(contents string, resources *ResourceProvider) error { dep := batchv1.Job{} err = decoder.Decode(&dep) resources.Jobs = append(resources.Jobs, dep) - } else if resource.Kind == "Job" { + } else if resource.Kind == "CronJob" { dep := batchv1beta1.CronJob{} err = decoder.Decode(&dep) resources.CronJobs = append(resources.CronJobs, dep) + } else if resource.Kind == "ReplicationController" { + dep := corev1.ReplicationController{} + err = decoder.Decode(&dep) + resources.ReplicationControllers = append(resources.ReplicationControllers, dep) } else if resource.Kind == "Namespace" { ns := corev1.Namespace{} err = decoder.Decode(&ns) diff --git a/pkg/kube/resources_test.go b/pkg/kube/resources_test.go index f57a9f3cd..e5fa09b6f 100644 --- a/pkg/kube/resources_test.go +++ b/pkg/kube/resources_test.go @@ -1,10 +1,11 @@ package kube import ( - "github.com/reactiveops/polaris/test" - "github.com/stretchr/testify/assert" "testing" "time" + + "github.com/reactiveops/polaris/test" + "github.com/stretchr/testify/assert" ) func TestGetResourcesFromPath(t *testing.T) { @@ -53,6 +54,11 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) { assert.Equal(t, "polaris-2", resources.Namespaces[1].ObjectMeta.Name) } +func TestGetMultipleResourceFromBadFile(t *testing.T) { + _, err := CreateResourceProviderFromPath("./test_files/test_3") + assert.NotEqual(t, nil, err, "CreateResource From Path should fail with bad yaml") +} + func TestGetResourceFromAPI(t *testing.T) { k8s := test.SetupTestAPI() k8s = test.SetupAddControllers(k8s, "test") diff --git a/pkg/kube/test_files/test_1/cron_job.yaml b/pkg/kube/test_files/test_1/cron_job.yaml new file mode 100644 index 000000000..0b0b14db3 --- /dev/null +++ b/pkg/kube/test_files/test_1/cron_job.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: test +spec: + schedule: "*/1 * * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: test + image: busybox + args: + - whoami + restartPolicy: OnFailure diff --git a/pkg/kube/test_files/test_1/daemon_set.yaml b/pkg/kube/test_files/test_1/daemon_set.yaml new file mode 100644 index 000000000..65ab67dc3 --- /dev/null +++ b/pkg/kube/test_files/test_1/daemon_set.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: test + labels: + k8s-app: test +spec: + selector: + matchLabels: + name: test + template: + metadata: + labels: + name: test + spec: + containers: + - name: test + image: busybox + command: ["whoami"] + resources: {} diff --git a/pkg/kube/test_files/test_1/job.yaml b/pkg/kube/test_files/test_1/job.yaml new file mode 100644 index 000000000..7dac5ec1b --- /dev/null +++ b/pkg/kube/test_files/test_1/job.yaml @@ -0,0 +1,12 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: test +spec: + template: + spec: + containers: + - name: test + image: alpine + command: ["whoami"] + restartPolicy: Never diff --git a/pkg/kube/test_files/test_1/replication_controller.yaml b/pkg/kube/test_files/test_1/replication_controller.yaml new file mode 100644 index 000000000..c93b09df8 --- /dev/null +++ b/pkg/kube/test_files/test_1/replication_controller.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + name: test +spec: + replicas: 1 + selector: + app: test + template: + metadata: + name: test + labels: + app: test + spec: + containers: + - name: test + image: busybox + command: ["tail"] + args: ["-f", "/dev/null"] diff --git a/pkg/kube/test_files/test_3/bad.yaml b/pkg/kube/test_files/test_3/bad.yaml new file mode 100644 index 000000000..89348a87e --- /dev/null +++ b/pkg/kube/test_files/test_3/bad.yaml @@ -0,0 +1,5 @@ +-- +# Source: polaris/templates/dashboard.deployment.yaml +apiVersion: extensions/v1beta1 +kind: Deployment +:::: diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index e8548f19d..c4c694e3a 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -19,6 +19,7 @@ import ( "github.com/reactiveops/polaris/pkg/kube" "github.com/reactiveops/polaris/pkg/validator/controllers" controller "github.com/reactiveops/polaris/pkg/validator/controllers" + "github.com/sirupsen/logrus" ) // ValidateController validates a single controller, returns a ControllerResult. @@ -45,17 +46,8 @@ func ValidateControllers(config conf.Configuration, kubeResources *kube.Resource controllerResult := ValidateController(config, controller) nsResult := nsResults.getNamespaceResult(controller.GetNamespace()) nsResult.Summary.appendResults(*controllerResult.PodResult.Summary) - switch controller.GetType() { - case conf.StatefulSets: - nsResult.StatefulSetResults = append(nsResult.StatefulSetResults, controllerResult) - case conf.DaemonSets: - nsResult.DaemonSetResults = append(nsResult.DaemonSetResults, controllerResult) - case conf.Deployments: - nsResult.DeploymentResults = append(nsResult.DeploymentResults, controllerResult) - case conf.Jobs: - nsResult.JobResults = append(nsResult.JobResults, controllerResult) - case conf.CronJobs: - nsResult.CronJobResults = append(nsResult.CronJobResults, controllerResult) + if err := nsResult.AddResult(controller.GetType(), controllerResult); err != nil { + logrus.Errorf("Internal Error: Failed to add a grouped result: %s", err) } } } diff --git a/pkg/validator/controllers/cronjob.go b/pkg/validator/controllers/cronjob.go index ec695a74a..35dfc3469 100644 --- a/pkg/validator/controllers/cronjob.go +++ b/pkg/validator/controllers/cronjob.go @@ -13,17 +13,17 @@ type CronJobController struct { } // GetPodTemplate returns the original template spec -func (d CronJobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { - return &d.K8SResource.Spec.JobTemplate.Spec.Template +func (c CronJobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { + return &c.K8SResource.Spec.JobTemplate.Spec.Template } // GetPodSpec returns the original kubernetes template pod spec -func (d CronJobController) GetPodSpec() *kubeAPICoreV1.PodSpec { - return &d.K8SResource.Spec.JobTemplate.Spec.Template.Spec +func (c CronJobController) GetPodSpec() *kubeAPICoreV1.PodSpec { + return &c.K8SResource.Spec.JobTemplate.Spec.Template.Spec } // GetType returns the supportedcontroller enum type -func (d CronJobController) GetType() config.SupportedController { +func (c CronJobController) GetType() config.SupportedController { return config.CronJobs } diff --git a/pkg/validator/controllers/daemonset.go b/pkg/validator/controllers/daemonset.go index 16b7bb5ab..7e2339b3b 100644 --- a/pkg/validator/controllers/daemonset.go +++ b/pkg/validator/controllers/daemonset.go @@ -28,10 +28,10 @@ func (d DaemonSetController) GetType() config.SupportedController { } // NewDaemonSetController builds a new controller interface for Deployments -func NewDaemonSetController(originalDeploymentResource kubeAPIAppsV1.DaemonSet) Interface { +func NewDaemonSetController(originalResource kubeAPIAppsV1.DaemonSet) Interface { controller := DaemonSetController{} - controller.Name = originalDeploymentResource.Name - controller.Namespace = originalDeploymentResource.Namespace - controller.K8SResource = originalDeploymentResource + controller.Name = originalResource.Name + controller.Namespace = originalResource.Namespace + controller.K8SResource = originalResource return controller } diff --git a/pkg/validator/controllers/interface.go b/pkg/validator/controllers/interface.go index 44d1bcf56..20dfa2ac7 100644 --- a/pkg/validator/controllers/interface.go +++ b/pkg/validator/controllers/interface.go @@ -57,6 +57,10 @@ func LoadControllersByType(controllerType config.SupportedController, kubeResour 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 diff --git a/pkg/validator/controllers/job.go b/pkg/validator/controllers/job.go index 7d206b402..4401923db 100644 --- a/pkg/validator/controllers/job.go +++ b/pkg/validator/controllers/job.go @@ -13,25 +13,25 @@ type JobController struct { } // GetPodTemplate returns the original template spec -func (d JobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { - return &d.K8SResource.Spec.Template +func (j JobController) GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec { + return &j.K8SResource.Spec.Template } // GetPodSpec returns the original kubernetes template pod spec -func (d JobController) GetPodSpec() *kubeAPICoreV1.PodSpec { - return &d.K8SResource.Spec.Template.Spec +func (j JobController) GetPodSpec() *kubeAPICoreV1.PodSpec { + return &j.K8SResource.Spec.Template.Spec } // GetType returns the supportedcontroller enum type -func (d JobController) GetType() config.SupportedController { +func (j JobController) GetType() config.SupportedController { return config.Jobs } // NewJobController builds a new controller interface for Deployments -func NewJobController(originalDeploymentResource kubeAPIBatchV1.Job) Interface { +func NewJobController(originalResource kubeAPIBatchV1.Job) Interface { controller := JobController{} - controller.Name = originalDeploymentResource.Name - controller.Namespace = originalDeploymentResource.Namespace - controller.K8SResource = originalDeploymentResource + controller.Name = originalResource.Name + controller.Namespace = originalResource.Namespace + controller.K8SResource = originalResource return controller } diff --git a/pkg/validator/controllers/replicationcontroller.go b/pkg/validator/controllers/replicationcontroller.go new file mode 100644 index 000000000..9e80ebd67 --- /dev/null +++ b/pkg/validator/controllers/replicationcontroller.go @@ -0,0 +1,39 @@ +package controllers + +import ( + "github.com/reactiveops/polaris/pkg/config" + kubeAPICoreV1 "k8s.io/api/core/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 +} + +// GetType returns the supportedcontroller enum type +func (r ReplicationControllerController) GetType() config.SupportedController { + return config.ReplicationControllers +} + +// NewReplicationControllerController builds a new controller interface for Deployments +func NewReplicationControllerController(originalResource kubeAPICoreV1.ReplicationController) Interface { + controller := ReplicationControllerController{} + controller.Name = originalResource.Name + controller.Namespace = originalResource.Namespace + controller.K8SResource = originalResource + return controller +} diff --git a/pkg/validator/controllers/statefulsets.go b/pkg/validator/controllers/statefulsets.go index a5a1869fe..007f29998 100644 --- a/pkg/validator/controllers/statefulsets.go +++ b/pkg/validator/controllers/statefulsets.go @@ -28,10 +28,10 @@ func (s StatefulSetController) GetType() config.SupportedController { } // NewStatefulSetController builds a statefulset controller -func NewStatefulSetController(originalStatefulSetResource kubeAPIAppsV1.StatefulSet) Interface { +func NewStatefulSetController(originalResource kubeAPIAppsV1.StatefulSet) Interface { controller := StatefulSetController{} - controller.Name = originalStatefulSetResource.Name - controller.Namespace = originalStatefulSetResource.Namespace - controller.K8SResource = originalStatefulSetResource + controller.Name = originalResource.Name + controller.Namespace = originalResource.Namespace + controller.K8SResource = originalResource return controller } diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go index 4b0246002..65e815159 100644 --- a/pkg/validator/fullaudit.go +++ b/pkg/validator/fullaudit.go @@ -14,17 +14,18 @@ const ( // ClusterSummary contains Polaris results as well as some high-level stats type ClusterSummary struct { - Results ResultSummary - Version string - Nodes int - Pods int - Namespaces int - Deployments int - StatefulSets int - DaemonSets int - Jobs int - CronJobs int - Score uint + Results ResultSummary + Version string + Nodes int + Pods int + Namespaces int + Deployments int + StatefulSets int + DaemonSets int + Jobs int + CronJobs int + ReplicationControllers int + Score uint } // AuditData contains all the data from a full Polaris audit @@ -46,13 +47,8 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) ( clusterResults := ResultSummary{} // Aggregate all summary counts to get a clusterwide count. - for _, nsRes := range nsResults { - for _, dr := range nsRes.DeploymentResults { - clusterResults.appendResults(*dr.PodResult.Summary) - } - for _, dr := range nsRes.StatefulSetResults { - clusterResults.appendResults(*dr.PodResult.Summary) - } + for _, result := range nsResults.GetAllControllerResults() { + clusterResults.appendResults(*result.PodResult.Summary) } displayName := config.DisplayName @@ -67,17 +63,18 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) ( SourceName: kubeResources.SourceName, DisplayName: displayName, ClusterSummary: ClusterSummary{ - 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), - Results: clusterResults, - Score: clusterResults.Totals.GetScore(), + 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), + Results: clusterResults, + Score: clusterResults.Totals.GetScore(), }, NamespacedResults: nsResults, } diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go index 48b7daa2e..9132c649f 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -23,24 +23,29 @@ func TestGetTemplateData(t *testing.T) { ControllersToScan: []conf.SupportedController{ conf.Deployments, conf.StatefulSets, + conf.DaemonSets, + conf.Jobs, + conf.CronJobs, + conf.ReplicationControllers, }, } + // TODO: split out the logic for calculating summaries into another set of tests sum := ResultSummary{ Totals: CountSummary{ - Successes: uint(8), - Warnings: uint(2), - Errors: uint(2), + Successes: uint(24), + Warnings: uint(6), + Errors: uint(6), }, ByCategory: CategorySummary{}, } sum.ByCategory["Health Checks"] = &CountSummary{ Successes: uint(0), - Warnings: uint(2), - Errors: uint(2), + Warnings: uint(6), + Errors: uint(6), } sum.ByCategory["Resources"] = &CountSummary{ - Successes: uint(8), + Successes: uint(24), Warnings: uint(0), Errors: uint(0), } diff --git a/pkg/validator/types.go b/pkg/validator/types.go index 200654464..8f12c84b0 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -14,7 +14,13 @@ package validator -import corev1 "k8s.io/api/core/v1" +import ( + "fmt" + + "github.com/reactiveops/polaris/pkg/config" + conf "github.com/reactiveops/polaris/pkg/config" + corev1 "k8s.io/api/core/v1" +) // MessageType represents the type of Message type MessageType string @@ -32,29 +38,87 @@ const ( // NamespaceResult groups container results by parent resource. type NamespaceResult struct { - Name string - Summary *ResultSummary - DeploymentResults []ControllerResult - StatefulSetResults []ControllerResult - DaemonSetResults []ControllerResult - JobResults []ControllerResult - CronJobResults []ControllerResult + Name string + Summary *ResultSummary + + // TODO: This struct could use some love to reorganize it as just having "results" + // and then having methods to return filtered results by type + // (deploy, daemonset, etc) + // The way this is structured right now makes it difficult to add + // additional result types and potentially miss things in the metrics + // summary. + DeploymentResults []ControllerResult + StatefulSetResults []ControllerResult + DaemonSetResults []ControllerResult + JobResults []ControllerResult + CronJobResults []ControllerResult + ReplicationControllerResults []ControllerResult +} + +// AddResult adds a result to the result sets by leveraging the types supported by NamespaceResult +func (n *NamespaceResult) AddResult(resourceType config.SupportedController, result ControllerResult) error { + // Iterate all the resource types supported in this struct + var results *[]ControllerResult + switch resourceType { + case conf.Deployments: + results = &n.DeploymentResults + case conf.StatefulSets: + results = &n.StatefulSetResults + case conf.DaemonSets: + results = &n.DaemonSetResults + case conf.Jobs: + results = &n.JobResults + case conf.CronJobs: + results = &n.CronJobResults + case conf.ReplicationControllers: + results = &n.ReplicationControllerResults + default: + return fmt.Errorf("Unknown Resource Type: (%s) Missing Implementation in NamespacedResult", resourceType) + } + + // Append the new result to the results pointer loaded from the supported values + *results = append(*results, result) + + return nil +} + +// GetAllControllerResults grabs all the different types of controller results from the namespaced result as a single list for easier iteration +func (n NamespaceResult) GetAllControllerResults() []ControllerResult { + all := []ControllerResult{} + all = append(all, n.DeploymentResults...) + all = append(all, n.StatefulSetResults...) + all = append(all, n.DaemonSetResults...) + all = append(all, n.JobResults...) + all = append(all, n.CronJobResults...) + all = append(all, n.ReplicationControllerResults...) + + return all } // NamespacedResults is a mapping of namespace name to the validation results. type NamespacedResults map[string]*NamespaceResult +// GetAllControllerResults aggregates all the namespaced results in the set together +func (nsResults NamespacedResults) GetAllControllerResults() []ControllerResult { + all := []ControllerResult{} + for _, nsResult := range nsResults { + all = append(all, nsResult.GetAllControllerResults()...) + } + return all +} + func (nsResults NamespacedResults) getNamespaceResult(nsName string) *NamespaceResult { nsResult := &NamespaceResult{} switch nsResults[nsName] { case nil: nsResult = &NamespaceResult{ - Summary: &ResultSummary{}, - DeploymentResults: []ControllerResult{}, - StatefulSetResults: []ControllerResult{}, - DaemonSetResults: []ControllerResult{}, - JobResults: []ControllerResult{}, - CronJobResults: []ControllerResult{}, + Summary: &ResultSummary{}, + DeploymentResults: []ControllerResult{}, + StatefulSetResults: []ControllerResult{}, + DaemonSetResults: []ControllerResult{}, + JobResults: []ControllerResult{}, + CronJobResults: []ControllerResult{}, + ReplicationControllerResults: []ControllerResult{}, } nsResults[nsName] = nsResult default: diff --git a/test/fixtures.go b/test/fixtures.go index a64fe100d..8f41c8d4a 100644 --- a/test/fixtures.go +++ b/test/fixtures.go @@ -4,6 +4,8 @@ import ( "fmt" appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" @@ -49,6 +51,43 @@ func mockStatefulSet() appsv1.StatefulSet { return s } +func mockDaemonSet() appsv1.DaemonSet { + return appsv1.DaemonSet{ + Spec: appsv1.DaemonSetSpec{ + Template: MockPod(), + }, + } +} + +func mockJob() batchv1.Job { + return batchv1.Job{ + Spec: batchv1.JobSpec{ + Template: MockPod(), + }, + } +} + +func mockCronJob() batchv1beta1.CronJob { + return batchv1beta1.CronJob{ + Spec: batchv1beta1.CronJobSpec{ + JobTemplate: batchv1beta1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: MockPod(), + }, + }, + }, + } +} + +func mockReplicationController() corev1.ReplicationController { + p := MockPod() + return corev1.ReplicationController{ + Spec: corev1.ReplicationControllerSpec{ + Template: &p, + }, + } +} + // SetupTestAPI creates a test kube API struct. func SetupTestAPI() kubernetes.Interface { return fake.NewSimpleClientset() @@ -57,14 +96,34 @@ func SetupTestAPI() kubernetes.Interface { // SetupAddControllers creates mock controllers and adds them to the test clientset. func SetupAddControllers(k kubernetes.Interface, namespace string) kubernetes.Interface { d1 := mockDeploy() - _, err := k.AppsV1().Deployments(namespace).Create(&d1) - if err != nil { + if _, err := k.AppsV1().Deployments(namespace).Create(&d1); err != nil { fmt.Println(err) } + s1 := mockStatefulSet() - _, err = k.AppsV1().StatefulSets(namespace).Create(&s1) - if err != nil { + if _, err := k.AppsV1().StatefulSets(namespace).Create(&s1); err != nil { + fmt.Println(err) + } + + ds1 := mockDaemonSet() + if _, err := k.AppsV1().DaemonSets(namespace).Create(&ds1); err != nil { + fmt.Println(err) + } + + j1 := mockJob() + if _, err := k.BatchV1().Jobs(namespace).Create(&j1); err != nil { + fmt.Println(err) + } + + cj1 := mockCronJob() + if _, err := k.BatchV1beta1().CronJobs(namespace).Create(&cj1); err != nil { fmt.Println(err) } + + rc1 := mockReplicationController() + if _, err := k.CoreV1().ReplicationControllers(namespace).Create(&rc1); err != nil { + fmt.Println(err) + } + return k } From 355b8daf8eb5c783cccb2d30d0657bfa480768b1 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 17:13:59 -0600 Subject: [PATCH 07/28] Adjusted docs movement which apparently affects the dashboard :? --- pkg/dashboard/dashboard.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index a5d606ff3..1d03abc70 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -72,7 +72,7 @@ func GetTemplateBox() *packr.Box { // GetMarkdownBox returns a binary-friendly set of markdown files with error details func GetMarkdownBox() *packr.Box { if markdownBox == (*packr.Box)(nil) { - markdownBox = packr.New("Markdown", "../../docs") + markdownBox = packr.New("Markdown", "../../docs/check-documentation") } return markdownBox } From 2f7230943a92d5ce52a2e3a6488eb1a37c1af2c0 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 17:28:20 -0600 Subject: [PATCH 08/28] Added functionality to webhook --- pkg/webhook/validator.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index 415fdcb0e..150a48e92 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -26,6 +26,8 @@ import ( "github.com/sirupsen/logrus" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -96,15 +98,19 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons var controller controllers.Interface 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) - // FIXME: Should we be returning an OK response as skipped? - return admission.ErrorResponse(http.StatusBadRequest, err) + return admission.ValidationResponse(true, fmt.Sprintf("Skipping: (%s) isn't something we're configured to scan.", req.AdmissionRequest.Kind.Kind)) } + + // We should never hit this case unless something is misconfiured in CheckIfKindIsConfiguredForValidation controllerType, err := conf.GetSupportedControllerFromString(req.AdmissionRequest.Kind.Kind) if err != nil { msg := fmt.Errorf("Unexpected error occurred. Expected Kind to be a supported type (%s)", req.AdmissionRequest.Kind.Kind) logrus.Error(msg) return admission.ErrorResponse(http.StatusInternalServerError, err) } + + // For each type, perform the scan + // TODO: This isn't really that elegant due to the decoder and NewXXXController setup :( could use love switch controllerType { case conf.Deployments: deploy := appsv1.Deployment{} @@ -114,6 +120,22 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons statefulSet := appsv1.StatefulSet{} err = v.decoder.Decode(req, &statefulSet) controller = controllers.NewStatefulSetController(statefulSet) + case conf.DaemonSets: + daemonSet := appsv1.DaemonSet{} + err = v.decoder.Decode(req, &daemonSet) + controller = controllers.NewDaemonSetController(daemonSet) + case conf.Jobs: + job := batchv1.Job{} + err = v.decoder.Decode(req, &job) + controller = controllers.NewJobController(job) + case conf.CronJobs: + cronJob := batchv1beta1.CronJob{} + err = v.decoder.Decode(req, &cronJob) + controller = controllers.NewCronJobController(cronJob) + case conf.ReplicationControllers: + replicationController := corev1.ReplicationController{} + err = v.decoder.Decode(req, &replicationController) + controller = controllers.NewReplicationControllerController(replicationController) } controllerResult := validator.ValidateController(v.Config, controller) podResult = controllerResult.PodResult From 4dc3b0c6356c1972bb8a4edee337907d38d28108 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Tue, 23 Jul 2019 17:54:53 -0600 Subject: [PATCH 09/28] Added testing for getting supported controllers from string --- pkg/config/supportedcontrollers_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/config/supportedcontrollers_test.go b/pkg/config/supportedcontrollers_test.go index f94b75b0b..f84c3b60e 100644 --- a/pkg/config/supportedcontrollers_test.go +++ b/pkg/config/supportedcontrollers_test.go @@ -18,6 +18,8 @@ import ( "encoding/json" "fmt" "testing" + + "github.com/stretchr/testify/assert" ) type checkMarshal struct { @@ -103,3 +105,21 @@ func TestCheckIfControllerKindIsConfiguredForValidation(t *testing.T) { } } } + +func TestGetSupportedControllerFromString(t *testing.T) { + fixture := map[string]SupportedController{ + "": Unsupported, + "asdfasdf": Unsupported, + "\000": Unsupported, + "deployMENTS": Deployments, + "JOB": Jobs, + } + + for inputString, expectedType := range fixture { + resolvedType, err := GetSupportedControllerFromString(inputString) + if expectedType == Unsupported && err == nil { + t.Errorf("Expected (%s) to resolve to an unsupported type and throw an error.", inputString) + } + assert.Equal(t, expectedType, resolvedType, fmt.Sprintf("Expected (%s) to return (%s) controller type.", inputString, expectedType)) + } +} From f0b7ce9f19e2ff7a20402d37ec93dac275ca0fb8 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 08:51:00 -0600 Subject: [PATCH 10/28] Adjustments - Added requested var name changes - Fixed Fairwinds typo - Adjusted the logic for finding key in map --- README.md | 2 +- pkg/config/supportedcontrollers.go | 5 +++-- pkg/kube/resources.go | 36 +++++++++++++++--------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 679eaa397..5577270f6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ problems in the future. Polaris can be run in a few different modes: - An experimental validating webhook that can prevent any future deployments that do not live up to a configured standard. - A command-line audit that can be incorporated into your CI/CD pipeline -**Want to learn more?** FairWinds holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@fairwinds.com` +**Want to learn more?** Fairwinds holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@fairwinds.com` # Quickstart Dashboard diff --git a/pkg/config/supportedcontrollers.go b/pkg/config/supportedcontrollers.go index a50c6d8a7..0d918868f 100644 --- a/pkg/config/supportedcontrollers.go +++ b/pkg/config/supportedcontrollers.go @@ -88,10 +88,11 @@ func (s *SupportedController) UnmarshalJSON(b []byte) error { // GetSupportedControllerFromString fuzzy matches a string with a SupportedController Enum func GetSupportedControllerFromString(str string) (SupportedController, error) { lowerStr := strings.ToLower(str) - if stringLookupForSupportedControllers[lowerStr] == Unsupported { + controller, keyFound := stringLookupForSupportedControllers[lowerStr] + if !keyFound || controller == Unsupported { return 0, fmt.Errorf("Value ('%v') in configuration was not found in Supported Controllers: (%v)", str, strings.Join(ControllerStrings, ",")) } - return stringLookupForSupportedControllers[lowerStr], nil + return controller, nil } // CheckIfKindIsConfiguredForValidation takes a kind (in string format) and checks if Polaris is configured to scan this type of controller diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go index 7e2d1bfea..6b867517c 100644 --- a/pkg/kube/resources.go +++ b/pkg/kube/resources.go @@ -199,29 +199,29 @@ func addResourceFromString(contents string, resources *ResourceProvider) error { } decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000) if resource.Kind == "Deployment" { - dep := appsv1.Deployment{} - err = decoder.Decode(&dep) - resources.Deployments = append(resources.Deployments, dep) + controller := appsv1.Deployment{} + err = decoder.Decode(&controller) + resources.Deployments = append(resources.Deployments, controller) } else if resource.Kind == "StatefulSet" { - dep := appsv1.StatefulSet{} - err = decoder.Decode(&dep) - resources.StatefulSets = append(resources.StatefulSets, dep) + controller := appsv1.StatefulSet{} + err = decoder.Decode(&controller) + resources.StatefulSets = append(resources.StatefulSets, controller) } else if resource.Kind == "DaemonSet" { - dep := appsv1.DaemonSet{} - err = decoder.Decode(&dep) - resources.DaemonSets = append(resources.DaemonSets, dep) + controller := appsv1.DaemonSet{} + err = decoder.Decode(&controller) + resources.DaemonSets = append(resources.DaemonSets, controller) } else if resource.Kind == "Job" { - dep := batchv1.Job{} - err = decoder.Decode(&dep) - resources.Jobs = append(resources.Jobs, dep) + controller := batchv1.Job{} + err = decoder.Decode(&controller) + resources.Jobs = append(resources.Jobs, controller) } else if resource.Kind == "CronJob" { - dep := batchv1beta1.CronJob{} - err = decoder.Decode(&dep) - resources.CronJobs = append(resources.CronJobs, dep) + controller := batchv1beta1.CronJob{} + err = decoder.Decode(&controller) + resources.CronJobs = append(resources.CronJobs, controller) } else if resource.Kind == "ReplicationController" { - dep := corev1.ReplicationController{} - err = decoder.Decode(&dep) - resources.ReplicationControllers = append(resources.ReplicationControllers, dep) + controller := corev1.ReplicationController{} + err = decoder.Decode(&controller) + resources.ReplicationControllers = append(resources.ReplicationControllers, controller) } else if resource.Kind == "Namespace" { ns := corev1.Namespace{} err = decoder.Decode(&ns) From 4ac9257ac8d2be26d94f483982b998359d962e54 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 13:45:09 -0600 Subject: [PATCH 11/28] Added RBAC changes --- deploy/dashboard.yaml | 13 ++++++++++++- deploy/webhook.yaml | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/deploy/dashboard.yaml b/deploy/dashboard.yaml index 80e1e3398..db2a3f797 100644 --- a/deploy/dashboard.yaml +++ b/deploy/dashboard.yaml @@ -81,6 +81,15 @@ rules: resources: - 'deployments' - 'statefulsets' + - 'daemonsets' + verbs: + - 'get' + - 'list' + - apiGroups: + - 'batch' + resources: + - 'jobs' + - 'cronjobs' verbs: - 'get' - 'list' @@ -90,6 +99,7 @@ rules: - 'nodes' - 'namespaces' - 'pods' + - 'replicationcontrollers' verbs: - 'get' - 'list' @@ -161,7 +171,7 @@ spec: - --dashboard - --config - /opt/app/config.yaml - image: 'quay.io/reactiveops/polaris:0.3' + image: 'quay.io/reactiveops/polaris:0.4' imagePullPolicy: 'Always' name: dashboard ports: @@ -219,3 +229,4 @@ spec: --- # Source: polaris/templates/webhook.service.yaml + diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index 8fa182b59..fd0bab5b8 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -87,12 +87,32 @@ metadata: labels: app: polaris rules: + # auditor rules rbac - apiGroups: - 'apps' - 'extensions' resources: - 'deployments' - 'statefulsets' + - 'daemonsets' + verbs: + - 'get' + - 'list' + - apiGroups: + - 'batch' + resources: + - 'jobs' + - 'cronjobs' + verbs: + - 'get' + - 'list' + - apiGroups: + - '' + resources: + - 'nodes' + - 'namespaces' + - 'pods' + - 'replicationcontrollers' verbs: - 'get' - 'list' @@ -211,7 +231,7 @@ spec: - --webhook - --config - /opt/app/config.yaml - image: 'quay.io/reactiveops/polaris:0.3' + image: 'quay.io/reactiveops/polaris:0.2' imagePullPolicy: 'Always' ports: - containerPort: 9876 From 6f73ffbfb62f0de20700fa62cada0e5c7e1b6be7 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 13:57:44 -0600 Subject: [PATCH 12/28] Adjusted version --- deploy/webhook.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index fd0bab5b8..36c4376e8 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -231,7 +231,7 @@ spec: - --webhook - --config - /opt/app/config.yaml - image: 'quay.io/reactiveops/polaris:0.2' + image: 'quay.io/reactiveops/polaris:0.4' imagePullPolicy: 'Always' ports: - containerPort: 9876 From 77293c4ec0a0504a11fd2bc580eee3f010061cb7 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 13:58:34 -0600 Subject: [PATCH 13/28] Adjusted version in main.go --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 9279f7cbd..4b90331fc 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ import ( const ( // Version represents the current release version of Polaris - Version = "0.3.1" + Version = "0.4.0" ) func main() { From 8f7def899175db74679d0c4fd69a8517734ccf44 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 14:23:07 -0600 Subject: [PATCH 14/28] added testing for binary image --- .circleci/config.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 90f8e97cd..83873fbd3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,6 +70,7 @@ references: run: name: Test Dashboard command: | + sed -ri "s|'(quay.io/reactiveops/polaris:).+|'\1${CI_SHA1}'|" ./deploy/dashboard.yaml kubectl apply -f ./deploy/dashboard.yaml sleep 10 kubectl get pods --namespace polaris @@ -172,19 +173,13 @@ workflows: build: jobs: - test - - test_k8s: - # Ignore update-version branch, which changes deploy/ image references before the images are built - filters: - branches: - ignore: /.*\/update-version/ - build: requires: - test context: org-global - # Allow using testing tags for testing circle test + build steps - filters: - tags: - only: /^testing-.*/ + - test_k8s: + requires: + - build release: jobs: - release_binary: From 5204b1460bfc7239d1b875b1e1524bab335d01f3 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 14:34:26 -0600 Subject: [PATCH 15/28] Adjustements getting tag filter back --- .circleci/config.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 83873fbd3..04c4cf2a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,7 +70,7 @@ references: run: name: Test Dashboard command: | - sed -ri "s|'(quay.io/reactiveops/polaris:).+|'\1${CI_SHA1}'|" ./deploy/dashboard.yaml + sed -ri "s|'(quay.io/reactiveops/polaris:).+'|'\1${CI_SHA1}'|" ./deploy/dashboard.yaml kubectl apply -f ./deploy/dashboard.yaml sleep 10 kubectl get pods --namespace polaris @@ -177,6 +177,10 @@ workflows: requires: - test context: org-global + # Allow using testing tags for testing circle test + build steps + filters: + tags: + only: /^testing-.*/ - test_k8s: requires: - build From 832142ccc30da9a3252736659e2a9f53da05ed42 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 24 Jul 2019 14:51:25 -0600 Subject: [PATCH 16/28] Adjusted sha --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04c4cf2a0..ee8b82797 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,7 +70,7 @@ references: run: name: Test Dashboard command: | - sed -ri "s|'(quay.io/reactiveops/polaris:).+'|'\1${CI_SHA1}'|" ./deploy/dashboard.yaml + sed -ri "s|'(quay.io/reactiveops/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/dashboard.yaml kubectl apply -f ./deploy/dashboard.yaml sleep 10 kubectl get pods --namespace polaris From 641ed8c309d23f25e267025a9c84fcf8bd5671f1 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 09:42:07 -0600 Subject: [PATCH 17/28] Adjusted naming to match master --- docs/usage.md | 8 ++++---- pkg/kube/resources_test.go | 2 +- pkg/validator/controller.go | 8 ++++---- pkg/validator/controllers/cronjob.go | 2 +- pkg/validator/controllers/daemonset.go | 2 +- pkg/validator/controllers/deployment.go | 2 +- pkg/validator/controllers/interface.go | 4 ++-- pkg/validator/controllers/job.go | 2 +- pkg/validator/controllers/replicationcontroller.go | 2 +- pkg/validator/controllers/statefulsets.go | 2 +- pkg/validator/types.go | 4 ++-- pkg/webhook/validator.go | 6 +++--- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 2f8c19aef..46d31dd15 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -5,7 +5,7 @@ or run against local YAML files. ## Configuration -Polaris supports a wide range of validations covering a number of Kubernetes best practices. Here's a sample configuration file that includes all currently supported checks. The [default configuration](https://github.com/reactiveops/polaris/blob/master/examples/config.yaml) contains a number of those checks. This repository also includes a sample [full configuration file](https://github.com/reactiveops/polaris/blob/master/examples/config-full.yaml) that enables all available checks. +Polaris supports a wide range of validations covering a number of Kubernetes best practices. Here's a sample configuration file that includes all currently supported checks. The [default configuration](https://github.com/fairwindsops/polaris/blob/master/examples/config.yaml) contains a number of those checks. This repository also includes a sample [full configuration file](https://github.com/fairwindsops/polaris/blob/master/examples/config-full.yaml) that enables all available checks. Each check can be assigned a `severity`. Only checks with a severity of `error` or `warning` will be validated. The results of these validations are visible on the dashboard. In the case of the validating webhook, only failures with a severity of `error` will result in a change being rejected. @@ -73,13 +73,13 @@ There are several ways to install and use Polaris. Below outline ways to install ## kubectl ### Dashboard ``` -kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/dashboard.yaml +kubectl apply -f https://github.com/fairwindsops/polaris/releases/latest/download/dashboard.yaml kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80 ``` ### Webhook ``` -kubectl apply -f https://github.com/reactiveops/polaris/releases/latest/download/webhook.yaml +kubectl apply -f https://github.com/fairwindsops/polaris/releases/latest/download/webhook.yaml ``` ## Helm @@ -102,7 +102,7 @@ helm upgrade --install polaris reactiveops-stable/polaris --namespace polaris \ ## Local Binary ### Installation -Binary releases are available on the [releases page](https://github.com/reactiveops/polaris/releases) or can be installed with [Homebrew](https://brew.sh/): +Binary releases are available on the [releases page](https://github.com/fairwindsops/polaris/releases) or can be installed with [Homebrew](https://brew.sh/): ``` brew tap reactiveops/tap brew install reactiveops/tap/polaris diff --git a/pkg/kube/resources_test.go b/pkg/kube/resources_test.go index e5fa09b6f..b3326ea4e 100644 --- a/pkg/kube/resources_test.go +++ b/pkg/kube/resources_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/reactiveops/polaris/test" + "github.com/fairwindsops/polaris/test" "github.com/stretchr/testify/assert" ) diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index c4c694e3a..2dec6d722 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -15,10 +15,10 @@ package validator import ( - conf "github.com/reactiveops/polaris/pkg/config" - "github.com/reactiveops/polaris/pkg/kube" - "github.com/reactiveops/polaris/pkg/validator/controllers" - controller "github.com/reactiveops/polaris/pkg/validator/controllers" + 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" "github.com/sirupsen/logrus" ) diff --git a/pkg/validator/controllers/cronjob.go b/pkg/validator/controllers/cronjob.go index 35dfc3469..114c402a0 100644 --- a/pkg/validator/controllers/cronjob.go +++ b/pkg/validator/controllers/cronjob.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPIBatchV1beta1 "k8s.io/api/batch/v1beta1" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/daemonset.go b/pkg/validator/controllers/daemonset.go index 7e2339b3b..4d5032097 100644 --- a/pkg/validator/controllers/daemonset.go +++ b/pkg/validator/controllers/daemonset.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPIAppsV1 "k8s.io/api/apps/v1" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/deployment.go b/pkg/validator/controllers/deployment.go index 5de2ef4cf..d82d5fb79 100644 --- a/pkg/validator/controllers/deployment.go +++ b/pkg/validator/controllers/deployment.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPIAppsV1 "k8s.io/api/apps/v1" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/interface.go b/pkg/validator/controllers/interface.go index 20dfa2ac7..c0bf6ab37 100644 --- a/pkg/validator/controllers/interface.go +++ b/pkg/validator/controllers/interface.go @@ -3,8 +3,8 @@ package controllers import ( "fmt" - "github.com/reactiveops/polaris/pkg/config" - "github.com/reactiveops/polaris/pkg/kube" + "github.com/fairwindsops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/kube" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/job.go b/pkg/validator/controllers/job.go index 4401923db..5f92ae412 100644 --- a/pkg/validator/controllers/job.go +++ b/pkg/validator/controllers/job.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPIBatchV1 "k8s.io/api/batch/v1" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/replicationcontroller.go b/pkg/validator/controllers/replicationcontroller.go index 9e80ebd67..0586e9ed1 100644 --- a/pkg/validator/controllers/replicationcontroller.go +++ b/pkg/validator/controllers/replicationcontroller.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/controllers/statefulsets.go b/pkg/validator/controllers/statefulsets.go index 007f29998..a06e7a366 100644 --- a/pkg/validator/controllers/statefulsets.go +++ b/pkg/validator/controllers/statefulsets.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" kubeAPIAppsV1 "k8s.io/api/apps/v1" kubeAPICoreV1 "k8s.io/api/core/v1" ) diff --git a/pkg/validator/types.go b/pkg/validator/types.go index 8f12c84b0..620b7bbe1 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -17,8 +17,8 @@ package validator import ( "fmt" - "github.com/reactiveops/polaris/pkg/config" - conf "github.com/reactiveops/polaris/pkg/config" + "github.com/fairwindsops/polaris/pkg/config" + conf "github.com/fairwindsops/polaris/pkg/config" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index 150a48e92..99e7a5a8a 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -20,9 +20,9 @@ import ( "net/http" "os" - conf "github.com/reactiveops/polaris/pkg/config" - validator "github.com/reactiveops/polaris/pkg/validator" - "github.com/reactiveops/polaris/pkg/validator/controllers" + conf "github.com/fairwindsops/polaris/pkg/config" + validator "github.com/fairwindsops/polaris/pkg/validator" + "github.com/fairwindsops/polaris/pkg/validator/controllers" "github.com/sirupsen/logrus" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" From 05ae1e8b7ab6a019af9b9f6c3b76e8b79a1b1574 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 09:43:17 -0600 Subject: [PATCH 18/28] matching license naming --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 5466f7785..6c9c09704 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Fairwinds + Copyright 2019 FairwindsOps Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 770017feed4dcf935223e42c471a930239cadcd6 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 09:48:14 -0600 Subject: [PATCH 19/28] adjusted copyright info --- main.go | 2 +- pkg/config/config.go | 2 +- pkg/config/config_test.go | 2 +- pkg/config/severity.go | 2 +- pkg/config/supportedcontrollers_test.go | 2 +- pkg/dashboard/dashboard.go | 2 +- pkg/dashboard/helpers.go | 2 +- pkg/validator/container.go | 2 +- pkg/validator/container_test.go | 2 +- pkg/validator/controller.go | 2 +- pkg/validator/pod.go | 2 +- pkg/validator/pod_test.go | 2 +- pkg/validator/resource.go | 2 +- pkg/validator/types.go | 2 +- pkg/webhook/validator.go | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index 841519d9e..67096c0b1 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/config.go b/pkg/config/config.go index 23777a7c2..3ade093e4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 600ed02ac..977ae69ff 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/severity.go b/pkg/config/severity.go index cc419e783..f1ddc9d7f 100644 --- a/pkg/config/severity.go +++ b/pkg/config/severity.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/supportedcontrollers_test.go b/pkg/config/supportedcontrollers_test.go index f84c3b60e..0aeeec2fc 100644 --- a/pkg/config/supportedcontrollers_test.go +++ b/pkg/config/supportedcontrollers_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index e79cb78bd..711d5bc04 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/dashboard/helpers.go b/pkg/dashboard/helpers.go index ca5746cbd..0f748366b 100644 --- a/pkg/dashboard/helpers.go +++ b/pkg/dashboard/helpers.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/container.go b/pkg/validator/container.go index a8db9de12..b43fe93b2 100644 --- a/pkg/validator/container.go +++ b/pkg/validator/container.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index d837642a2..5c9fcccdc 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 2dec6d722..7cee2b2c8 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index 121f9630c..1b0021c81 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index 0075ca341..7e53901eb 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/resource.go b/pkg/validator/resource.go index 42d3ef70f..aa8afca60 100644 --- a/pkg/validator/resource.go +++ b/pkg/validator/resource.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/validator/types.go b/pkg/validator/types.go index 620b7bbe1..91d5f45ef 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/webhook/validator.go b/pkg/webhook/validator.go index 99e7a5a8a..48d6e9d22 100644 --- a/pkg/webhook/validator.go +++ b/pkg/webhook/validator.go @@ -1,4 +1,4 @@ -// Copyright 2019 Fairwinds +// Copyright 2019 FairwindsOps Inc // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From b815b549dac4a3c8681c33548545ea4877365957 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 09:52:01 -0600 Subject: [PATCH 20/28] Added new supported type to tests --- pkg/config/config_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 977ae69ff..b7899966f 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -64,6 +64,7 @@ controllers_to_scan: - Jobs - CronJobs - DaemonSets + - ReplicationControllers ` var resourceConfJSON1 = `{ @@ -109,7 +110,7 @@ var resourceConfJSON1 = `{ } } }, - "controllers_to_scan": ["Deployments", "StatefulSets", "Jobs", "CronJobs", "DaemonSets"] + "controllers_to_scan": ["Deployments", "StatefulSets", "Jobs", "CronJobs", "DaemonSets", "ReplicationControllers"] }` func TestParseError(t *testing.T) { @@ -188,5 +189,5 @@ func testParsedConfig(t *testing.T, config *Configuration) { assert.Equal(t, int64(4000), memLimits.Warning.Above.ScaledValue(resource.Mega)) controllersToScan := config.ControllersToScan - assert.ElementsMatch(t, []SupportedController{Deployments, StatefulSets, Jobs, CronJobs, DaemonSets}, controllersToScan) + assert.ElementsMatch(t, []SupportedController{Deployments, StatefulSets, Jobs, CronJobs, DaemonSets, ReplicationControllers}, controllersToScan) } From fb8e5edf989653ee1d53cefe5f9c312244d882e7 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 10:36:01 -0600 Subject: [PATCH 21/28] Added supported controllers to webhook --- deploy/webhook.yaml | 10 ++++++++-- main.go | 26 +++++++++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index 36c4376e8..e5ec5c76e 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -69,7 +69,13 @@ data: - SYS_CHROOT - KILL - AUDIT_WRITE - + controllers_to_scan: + - Deployments + - StatefulSets + - DaemonSets + - Jobs + - CronJobs + - ReplicationControllers --- # Source: polaris/templates/webhook.rbac.yaml apiVersion: v1 @@ -231,7 +237,7 @@ spec: - --webhook - --config - /opt/app/config.yaml - image: 'quay.io/reactiveops/polaris:0.4' + image: 'quay.io/reactiveops/polaris:b815b549dac4a3c8681c33548545ea4877365957' imagePullPolicy: 'Always' ports: - containerPort: 9876 diff --git a/main.go b/main.go index 67096c0b1..3371a8276 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,8 @@ import ( "net/http" "os" + "k8s.io/apimachinery/pkg/runtime" + conf "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/dashboard" "github.com/fairwindsops/polaris/pkg/kube" @@ -30,6 +32,9 @@ import ( fwebhook "github.com/fairwindsops/polaris/pkg/webhook" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" + corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" apitypes "k8s.io/apimachinery/pkg/types" _ "k8s.io/client-go/plugin/pkg/client/auth" // Required for other auth providers like GKE. @@ -185,12 +190,23 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool logrus.Infof("Polaris webhook server listening on port %d", port) - d1 := fwebhook.NewWebhook("deployments", mgr, fwebhook.Validator{Config: c}, &appsv1.Deployment{}) - d2 := fwebhook.NewWebhook("deployments-ext", mgr, fwebhook.Validator{Config: c}, &extensionsv1beta1.Deployment{}) + // TODO: This logic really should probably live with the SupportedController struct in config - to house the supported versions and specs + supportedControllersToScan := map[runtime.Object]string{ + &appsv1.Deployment{}: "deployments", + &extensionsv1beta1.Deployment{}: "deployments-ext", + &appsv1.StatefulSet{}: "statefulsets", + &appsv1.DaemonSet{}: "daemonsets", + &batchv1.Job{}: "jobs", + &batchv1beta1.CronJob{}: "cronjobs", + &corev1.ReplicationController{}: "replicationcontrollers", + } logrus.Debug("Registering webhooks to the webhook server") - if err = as.Register(d1, d2); err != nil { - logrus.Debugf("Unable to register webhooks in the admission server: %v", err) - os.Exit(1) + for supportedAPIType, name := range supportedControllersToScan { + webhook := fwebhook.NewWebhook(name, mgr, fwebhook.Validator{Config: c}, supportedAPIType) + if err = as.Register(webhook); err != nil { + logrus.Debugf("Unable to register webhooks in the admission server: %v", err) + os.Exit(1) + } } logrus.Debug("Starting webhook manager") From 15b8ef277bda80f751451783fd7489c0230174df Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 10:39:41 -0600 Subject: [PATCH 22/28] Adjusted version in webhook accidentally --- deploy/webhook.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index e5ec5c76e..46fae5cf6 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -237,7 +237,7 @@ spec: - --webhook - --config - /opt/app/config.yaml - image: 'quay.io/reactiveops/polaris:b815b549dac4a3c8681c33548545ea4877365957' + image: 'quay.io/reactiveops/polaris:0.4' imagePullPolicy: 'Always' ports: - containerPort: 9876 From bbdd9242ed9cbc594c8e5d30a5f1ac0b16654a50 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 10:57:41 -0600 Subject: [PATCH 23/28] Added refactor of webhook registration --- main.go | 32 ++++++++---------------- pkg/config/supportedcontrollers.go | 40 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/main.go b/main.go index 3371a8276..983a16fd5 100644 --- a/main.go +++ b/main.go @@ -23,19 +23,12 @@ import ( "net/http" "os" - "k8s.io/apimachinery/pkg/runtime" - conf "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/dashboard" "github.com/fairwindsops/polaris/pkg/kube" "github.com/fairwindsops/polaris/pkg/validator" fwebhook "github.com/fairwindsops/polaris/pkg/webhook" "github.com/sirupsen/logrus" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - batchv1beta1 "k8s.io/api/batch/v1beta1" - corev1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" apitypes "k8s.io/apimachinery/pkg/types" _ "k8s.io/client-go/plugin/pkg/client/auth" // Required for other auth providers like GKE. "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -190,22 +183,17 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool logrus.Infof("Polaris webhook server listening on port %d", port) - // TODO: This logic really should probably live with the SupportedController struct in config - to house the supported versions and specs - supportedControllersToScan := map[runtime.Object]string{ - &appsv1.Deployment{}: "deployments", - &extensionsv1beta1.Deployment{}: "deployments-ext", - &appsv1.StatefulSet{}: "statefulsets", - &appsv1.DaemonSet{}: "daemonsets", - &batchv1.Job{}: "jobs", - &batchv1beta1.CronJob{}: "cronjobs", - &corev1.ReplicationController{}: "replicationcontrollers", - } + // Iterate all the configurations supported controllers to scan and register them for webhooks + // Should only register controllers that are configured to be scanned logrus.Debug("Registering webhooks to the webhook server") - for supportedAPIType, name := range supportedControllersToScan { - webhook := fwebhook.NewWebhook(name, mgr, fwebhook.Validator{Config: c}, supportedAPIType) - if err = as.Register(webhook); err != nil { - logrus.Debugf("Unable to register webhooks in the admission server: %v", err) - os.Exit(1) + for index, controllerToScan := range c.ControllersToScan { + for innerIndex, supportedAPIType := range controllerToScan.ListSupportedAPIVersions() { + webhookName := fmt.Sprintf("%s-%d-%d", controllerToScan, index, innerIndex) + webhook := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: c}, supportedAPIType) + if err = as.Register(webhook); err != nil { + logrus.Debugf("Unable to register webhooks in the admission server: %v", err) + os.Exit(1) + } } } diff --git a/pkg/config/supportedcontrollers.go b/pkg/config/supportedcontrollers.go index 0d918868f..9329515fe 100644 --- a/pkg/config/supportedcontrollers.go +++ b/pkg/config/supportedcontrollers.go @@ -5,6 +5,13 @@ import ( "encoding/json" "fmt" "strings" + + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" + corev1 "k8s.io/api/core/v1" + extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime" ) const ( @@ -85,6 +92,39 @@ func (s *SupportedController) UnmarshalJSON(b []byte) error { return nil } +// ListSupportedAPIVersions for SupportedController returns all the apimachinery object type supported +func (s SupportedController) ListSupportedAPIVersions() []runtime.Object { + var supportedVersions []runtime.Object + switch s { + case Deployments: + supportedVersions = []runtime.Object{ + &appsv1.Deployment{}, + &extensionsv1beta1.Deployment{}, + } + case StatefulSets: + supportedVersions = []runtime.Object{ + &appsv1.StatefulSet{}, + } + case DaemonSets: + supportedVersions = []runtime.Object{ + &appsv1.DaemonSet{}, + } + case Jobs: + supportedVersions = []runtime.Object{ + &batchv1.Job{}, + } + case CronJobs: + supportedVersions = []runtime.Object{ + &batchv1beta1.CronJob{}, + } + case ReplicationControllers: + supportedVersions = []runtime.Object{ + &corev1.ReplicationController{}, + } + } + return supportedVersions +} + // GetSupportedControllerFromString fuzzy matches a string with a SupportedController Enum func GetSupportedControllerFromString(str string) (SupportedController, error) { lowerStr := strings.ToLower(str) From f16bc3d3356b50f9ce59ee5e61952d56ebee276f Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 11:13:09 -0600 Subject: [PATCH 24/28] Adjusted naming of webhooks to conform to DNS-1123 --- main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 983a16fd5..9bc73d37c 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "net/http" "os" + "strings" conf "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/dashboard" @@ -188,7 +189,7 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool logrus.Debug("Registering webhooks to the webhook server") for index, controllerToScan := range c.ControllersToScan { for innerIndex, supportedAPIType := range controllerToScan.ListSupportedAPIVersions() { - webhookName := fmt.Sprintf("%s-%d-%d", controllerToScan, index, innerIndex) + webhookName := strings.ToLower(fmt.Sprintf("%s-%d-%d", controllerToScan, index, innerIndex)) webhook := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: c}, supportedAPIType) if err = as.Register(webhook); err != nil { logrus.Debugf("Unable to register webhooks in the admission server: %v", err) From 59531c84130cbfff841d1722157914ca7247c2b5 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 14:35:35 -0600 Subject: [PATCH 25/28] Adjust method of registration --- main.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 9bc73d37c..14c1371d2 100644 --- a/main.go +++ b/main.go @@ -187,17 +187,20 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool // Iterate all the configurations supported controllers to scan and register them for webhooks // Should only register controllers that are configured to be scanned logrus.Debug("Registering webhooks to the webhook server") + var webhooks []webhook.Webhook for index, controllerToScan := range c.ControllersToScan { for innerIndex, supportedAPIType := range controllerToScan.ListSupportedAPIVersions() { webhookName := strings.ToLower(fmt.Sprintf("%s-%d-%d", controllerToScan, index, innerIndex)) - webhook := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: c}, supportedAPIType) - if err = as.Register(webhook); err != nil { - logrus.Debugf("Unable to register webhooks in the admission server: %v", err) - os.Exit(1) - } + hook := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: c}, supportedAPIType) + webhooks = append(webhooks, hook) } } + if err = as.Register(webhooks...); err != nil { + logrus.Debugf("Unable to register webhooks in the admission server: %v", err) + os.Exit(1) + } + logrus.Debug("Starting webhook manager") if err := mgr.Start(signals.SetupSignalHandler()); err != nil { logrus.Errorf("Error starting manager: %v", err) From 9e398a2e3c2f8e2b9bea25b4c4cd33178e68dd45 Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 15:20:59 -0600 Subject: [PATCH 26/28] Adjusted webhook and dashboard plus fixed view of new controllers --- deploy/dashboard.yaml | 11 ++++++++++- deploy/webhook.yaml | 5 ++++- pkg/dashboard/helpers.go | 6 +++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/deploy/dashboard.yaml b/deploy/dashboard.yaml index db2a3f797..021e8656b 100644 --- a/deploy/dashboard.yaml +++ b/deploy/dashboard.yaml @@ -57,6 +57,13 @@ data: - SYS_CHROOT - KILL - AUDIT_WRITE + controllers_to_scan: + - Deployments + - StatefulSets + - DaemonSets + - Jobs + - CronJobs + - ReplicationControllers --- # Source: polaris/templates/dashboard.rbac.yaml @@ -143,7 +150,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - checksum/config: '6ec4a4dc87403cae67c01438398a5f1d4ef836ffeaf26a33b685c066b940495a' + checksum/config: '8aa5a565fba7a2db98d46752087de8c1dcc83b70cd762c5829d5ba01270d54a2' name: polaris-dashboard namespace: polaris labels: @@ -209,6 +216,8 @@ spec: subPath: config.yaml readOnly: true serviceAccountName: polaris-dashboard + nodeSelector: {} + tolerations: [] --- # Source: polaris/templates/audit.job.yaml diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index 46fae5cf6..4b2c1d495 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -76,6 +76,7 @@ data: - Jobs - CronJobs - ReplicationControllers + --- # Source: polaris/templates/webhook.rbac.yaml apiVersion: v1 @@ -212,7 +213,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - checksum/config: '6ec4a4dc87403cae67c01438398a5f1d4ef836ffeaf26a33b685c066b940495a' + checksum/config: '8aa5a565fba7a2db98d46752087de8c1dcc83b70cd762c5829d5ba01270d54a2' name: polaris-webhook namespace: polaris labels: @@ -287,6 +288,8 @@ spec: mountPath: /tmp/ readOnly: false serviceAccountName: polaris-webhook + nodeSelector: {} + tolerations: [] volumes: - name: config configMap: diff --git a/pkg/dashboard/helpers.go b/pkg/dashboard/helpers.go index 0f748366b..562276884 100644 --- a/pkg/dashboard/helpers.go +++ b/pkg/dashboard/helpers.go @@ -16,14 +16,14 @@ package dashboard import ( "fmt" - "github.com/fairwindsops/polaris/pkg/validator" "strings" + + "github.com/fairwindsops/polaris/pkg/validator" ) func getAllControllerResults(nr validator.NamespaceResult) []validator.ControllerResult { results := []validator.ControllerResult{} - results = append(results, nr.DeploymentResults...) - results = append(results, nr.StatefulSetResults...) + results = nr.GetAllControllerResults() return results } From ea06a3677a8f190d938dda8b8acdf21bcdad072c Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 15:37:59 -0600 Subject: [PATCH 27/28] Adjusted request --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e422ec202..7d516e409 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ problems in the future. Polaris can be run in a few different modes: **Want to learn more?** Fairwinds holds [office hours on Zoom](https://zoom.us/j/242508205) the first Friday of every month, at 12pm Eastern. You can also reach out via email at `opensource@fairwinds.com` -# Quickstart Dashboard +# Dashboard Quickstart ``` kubectl apply -f https://github.com/FairwindsOps/polaris/releases/latest/download/dashboard.yaml From c168c0144ed50d186522ea4af8f08955fdb58e2b Mon Sep 17 00:00:00 2001 From: Nick Huanca Date: Wed, 31 Jul 2019 15:46:16 -0600 Subject: [PATCH 28/28] Added requested change --- pkg/dashboard/helpers.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/dashboard/helpers.go b/pkg/dashboard/helpers.go index 562276884..961bf2c16 100644 --- a/pkg/dashboard/helpers.go +++ b/pkg/dashboard/helpers.go @@ -22,9 +22,7 @@ import ( ) func getAllControllerResults(nr validator.NamespaceResult) []validator.ControllerResult { - results := []validator.ControllerResult{} - results = nr.GetAllControllerResults() - return results + return nr.GetAllControllerResults() } func getWarningWidth(counts validator.CountSummary, fullWidth int) uint {