mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Add support for stateful sets (#145)
This commit is contained in:
@@ -265,6 +265,11 @@ ul.message-list li i.message-icon {
|
||||
color: #a11f4c;
|
||||
}
|
||||
|
||||
.controller-type {
|
||||
display: inline-block;
|
||||
min-width: 115px;
|
||||
}
|
||||
|
||||
a.more-info {
|
||||
color: #bbb;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -87,14 +87,15 @@ type templateData struct {
|
||||
// GetBaseTemplate puts together the dashboard template. Individual pieces can be overridden before rendering.
|
||||
func GetBaseTemplate(name string) (*template.Template, error) {
|
||||
tmpl := template.New(name).Funcs(template.FuncMap{
|
||||
"getWarningWidth": getWarningWidth,
|
||||
"getSuccessWidth": getSuccessWidth,
|
||||
"getWeatherIcon": getWeatherIcon,
|
||||
"getWeatherText": getWeatherText,
|
||||
"getGrade": getGrade,
|
||||
"getIcon": getIcon,
|
||||
"getCategoryLink": getCategoryLink,
|
||||
"getCategoryInfo": getCategoryInfo,
|
||||
"getWarningWidth": getWarningWidth,
|
||||
"getSuccessWidth": getSuccessWidth,
|
||||
"getWeatherIcon": getWeatherIcon,
|
||||
"getWeatherText": getWeatherText,
|
||||
"getGrade": getGrade,
|
||||
"getIcon": getIcon,
|
||||
"getCategoryLink": getCategoryLink,
|
||||
"getCategoryInfo": getCategoryInfo,
|
||||
"getAllControllerResults": getAllControllerResults,
|
||||
})
|
||||
|
||||
templateFileNames := []string{
|
||||
|
||||
@@ -20,6 +20,13 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getAllControllerResults(nr validator.NamespaceResult) []validator.ControllerResult {
|
||||
results := []validator.ControllerResult{}
|
||||
results = append(results, nr.DeploymentResults...)
|
||||
results = append(results, nr.StatefulSetResults...)
|
||||
return results
|
||||
}
|
||||
|
||||
func getWarningWidth(counts validator.CountSummary, fullWidth int) uint {
|
||||
return uint(float64(counts.Successes+counts.Warnings) / float64(counts.Successes+counts.Warnings+counts.Errors) * float64(fullWidth))
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<div class="card namespace">
|
||||
<h3>Namespace: <strong>{{ $namespace }}</strong></h3>
|
||||
<div class="expandable-table">
|
||||
{{ range .DeploymentResults }}
|
||||
{{ range getAllControllerResults $nsResult }}
|
||||
<div class="resource-info">
|
||||
<div class="status-bar">
|
||||
<div class="status">
|
||||
@@ -90,7 +90,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="name"><span class="caret-expander"></span>Deployment: <strong>{{ .Name }}</strong></div>
|
||||
<div class="name"><span class="caret-expander"></span>
|
||||
<span class="controller-type">{{ .Type }}:</span>
|
||||
<strong>{{ .Name }}</strong></div>
|
||||
<div class="result-messages expandable-content">
|
||||
<h4>Pod Spec:</h4>
|
||||
<ul class="message-list">
|
||||
|
||||
+49
-41
@@ -27,6 +27,7 @@ type ResourceProvider struct {
|
||||
SourceType string
|
||||
Nodes []corev1.Node
|
||||
Deployments []appsv1.Deployment
|
||||
StatefulSets []appsv1.StatefulSet
|
||||
Namespaces []corev1.Namespace
|
||||
Pods []corev1.Pod
|
||||
}
|
||||
@@ -51,47 +52,13 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
|
||||
SourceName: directory,
|
||||
Nodes: []corev1.Node{},
|
||||
Deployments: []appsv1.Deployment{},
|
||||
StatefulSets: []appsv1.StatefulSet{},
|
||||
Namespaces: []corev1.Namespace{},
|
||||
Pods: []corev1.Pod{},
|
||||
}
|
||||
|
||||
addYaml := func(contents string) error {
|
||||
contentBytes := []byte(contents)
|
||||
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
|
||||
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
|
||||
}
|
||||
decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
|
||||
if resource.Kind == "Deployment" {
|
||||
dep := appsv1.Deployment{}
|
||||
err = decoder.Decode(&dep)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing deployment %v", err)
|
||||
return err
|
||||
}
|
||||
resources.Deployments = append(resources.Deployments, dep)
|
||||
} else if resource.Kind == "Namespace" {
|
||||
ns := corev1.Namespace{}
|
||||
err = decoder.Decode(&ns)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing namespace %v", err)
|
||||
return err
|
||||
}
|
||||
resources.Namespaces = append(resources.Namespaces, ns)
|
||||
} else if resource.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
err = decoder.Decode(&pod)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing pod %v", err)
|
||||
return err
|
||||
}
|
||||
resources.Pods = append(resources.Pods, pod)
|
||||
}
|
||||
return nil
|
||||
return addResourceFromString(contents, &resources)
|
||||
}
|
||||
|
||||
visitFile := func(path string, f os.FileInfo, err error) error {
|
||||
@@ -144,27 +111,32 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
|
||||
listOpts := metav1.ListOptions{}
|
||||
serverVersion, err := kube.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes API version %v", err)
|
||||
logrus.Errorf("Error fetching Cluster API version %v", err)
|
||||
return nil, err
|
||||
}
|
||||
deploys, err := kube.AppsV1().Deployments("").List(listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes Deployments %v", err)
|
||||
logrus.Errorf("Error fetching Deployments %v", err)
|
||||
return nil, err
|
||||
}
|
||||
statefulSets, err := kube.AppsV1().StatefulSets("").List(listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching StatefulSets%v", err)
|
||||
return nil, err
|
||||
}
|
||||
nodes, err := kube.CoreV1().Nodes().List(listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes Nodes %v", err)
|
||||
logrus.Errorf("Error fetching Nodes %v", err)
|
||||
return nil, err
|
||||
}
|
||||
namespaces, err := kube.CoreV1().Namespaces().List(listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes Namespaces %v", err)
|
||||
logrus.Errorf("Error fetching Namespaces %v", err)
|
||||
return nil, err
|
||||
}
|
||||
pods, err := kube.CoreV1().Pods("").List(listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes Pods %v", err)
|
||||
logrus.Errorf("Error fetching Pods %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -174,9 +146,45 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
|
||||
SourceName: clusterName,
|
||||
CreationTime: time.Now(),
|
||||
Deployments: deploys.Items,
|
||||
StatefulSets: statefulSets.Items,
|
||||
Nodes: nodes.Items,
|
||||
Namespaces: namespaces.Items,
|
||||
Pods: pods.Items,
|
||||
}
|
||||
return &api, nil
|
||||
}
|
||||
|
||||
func addResourceFromString(contents string, resources *ResourceProvider) error {
|
||||
contentBytes := []byte(contents)
|
||||
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
|
||||
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
|
||||
}
|
||||
decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
|
||||
if resource.Kind == "Deployment" {
|
||||
dep := appsv1.Deployment{}
|
||||
err = decoder.Decode(&dep)
|
||||
resources.Deployments = append(resources.Deployments, dep)
|
||||
} else if resource.Kind == "StatefulSet" {
|
||||
dep := appsv1.StatefulSet{}
|
||||
err = decoder.Decode(&dep)
|
||||
resources.StatefulSets = append(resources.StatefulSets, dep)
|
||||
} else if resource.Kind == "Namespace" {
|
||||
ns := corev1.Namespace{}
|
||||
err = decoder.Decode(&ns)
|
||||
resources.Namespaces = append(resources.Namespaces, ns)
|
||||
} else if resource.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
err = decoder.Decode(&pod)
|
||||
resources.Pods = append(resources.Pods, pod)
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing %s: %v", resource.Kind, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ func TestGetResourcesFromPath(t *testing.T) {
|
||||
assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
|
||||
assert.Equal(t, "ubuntu", resources.Deployments[0].Spec.Template.Spec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 1, len(resources.StatefulSets), "Should have a stateful set")
|
||||
assert.Equal(t, "nginx", resources.StatefulSets[0].Spec.Template.Spec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 1, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "two", resources.Namespaces[0].ObjectMeta.Name)
|
||||
|
||||
@@ -52,7 +55,7 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {
|
||||
|
||||
func TestGetResourceFromAPI(t *testing.T) {
|
||||
k8s := test.SetupTestAPI()
|
||||
k8s = test.SetupAddDeploys(k8s, "test")
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
resources, err := CreateResourceProviderFromAPI(k8s, "test")
|
||||
assert.Equal(t, nil, err, "Error should be nil")
|
||||
|
||||
@@ -62,6 +65,7 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
|
||||
assert.Equal(t, 1, len(resources.StatefulSets), "Should have a stateful set")
|
||||
assert.Equal(t, 0, len(resources.Pods), "Should have a pod")
|
||||
|
||||
assert.Equal(t, "", resources.Deployments[0].ObjectMeta.Name)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
apiVersion: apps/v1 # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: web
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
serviceName: "nginx"
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
replicas: 14
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: k8s.gcr.io/nginx-slim:0.8
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: web
|
||||
volumeMounts:
|
||||
- name: www
|
||||
mountPath: /usr/share/nginx/html
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: www
|
||||
spec:
|
||||
accessModes: [ "ReadWriteOnce" ]
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
storageClassName: thin-disk
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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 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"
|
||||
)
|
||||
|
||||
// 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)
|
||||
return ControllerResult{
|
||||
Type: controller.Type,
|
||||
Name: controller.Name,
|
||||
PodResult: podResult,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateControllers validates that each deployment conforms to the Polaris config,
|
||||
// returns 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))
|
||||
}
|
||||
for _, controller := range controllers {
|
||||
controllerResult := ValidateController(config, controller)
|
||||
nsResult := nsResults.getNamespaceResult(controller.Namespace)
|
||||
nsResult.Summary.appendResults(*controllerResult.PodResult.Summary)
|
||||
if controller.Type == "Deployment" {
|
||||
nsResult.DeploymentResults = append(nsResult.DeploymentResults, controllerResult)
|
||||
} else if controller.Type == "StatefulSet" {
|
||||
nsResult.StatefulSetResults = append(nsResult.StatefulSetResults, 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,
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// 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 validator
|
||||
|
||||
import (
|
||||
conf "github.com/reactiveops/polaris/pkg/config"
|
||||
"github.com/reactiveops/polaris/pkg/kube"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
// ValidateDeployment validates a single deployment, returns a PodResult.
|
||||
func ValidateDeployment(conf conf.Configuration, deploy *appsv1.Deployment) ControllerResult {
|
||||
pod := deploy.Spec.Template.Spec
|
||||
podResult := ValidatePod(conf, &pod)
|
||||
return ControllerResult{
|
||||
Name: deploy.Name,
|
||||
Type: "Deployment",
|
||||
PodResult: podResult,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateDeployments validates that each deployment conforms to the Polaris config,
|
||||
// returns a list of ResourceResults organized by namespace.
|
||||
func ValidateDeployments(config conf.Configuration, kubeResources *kube.ResourceProvider) (NamespacedResults, error) {
|
||||
nsResults := NamespacedResults{}
|
||||
|
||||
for _, deploy := range kubeResources.Deployments {
|
||||
deploymentResult := ValidateDeployment(config, &deploy)
|
||||
nsResults = addResult(deploymentResult, nsResults, deploy.Namespace)
|
||||
}
|
||||
|
||||
return nsResults, nil
|
||||
}
|
||||
|
||||
func addResult(deploymentResult ControllerResult, nsResults NamespacedResults, nsName string) NamespacedResults {
|
||||
nsResult := &NamespaceResult{}
|
||||
|
||||
// If there is already data stored for this namespace name,
|
||||
// then append to the ResourceResults to the existing data.
|
||||
switch nsResults[nsName] {
|
||||
case nil:
|
||||
nsResult = &NamespaceResult{
|
||||
Summary: &ResultSummary{},
|
||||
DeploymentResults: []ControllerResult{},
|
||||
}
|
||||
nsResults[nsName] = nsResult
|
||||
default:
|
||||
nsResult = nsResults[nsName]
|
||||
}
|
||||
|
||||
nsResult.DeploymentResults = append(nsResult.DeploymentResults, deploymentResult)
|
||||
nsResult.Summary.appendResults(*deploymentResult.PodResult.Summary)
|
||||
|
||||
return nsResults
|
||||
}
|
||||
+21
-24
@@ -14,13 +14,14 @@ 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
|
||||
Score uint
|
||||
Results ResultSummary
|
||||
Version string
|
||||
Nodes int
|
||||
Pods int
|
||||
Namespaces int
|
||||
Deployments int
|
||||
StatefulSets int
|
||||
Score uint
|
||||
}
|
||||
|
||||
// AuditData contains all the data from a full Polaris audit
|
||||
@@ -36,16 +37,8 @@ type AuditData struct {
|
||||
|
||||
// RunAudit runs a full Polaris audit and returns an AuditData object
|
||||
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) {
|
||||
// TODO: Validate StatefulSets, DaemonSets, Cron jobs
|
||||
// in addition to deployments
|
||||
|
||||
// TODO: Once we are validating more than deployments,
|
||||
// we will need to merge the namespaceResults that get returned
|
||||
// from each validation.
|
||||
nsResults, err := ValidateDeployments(config, kubeResources)
|
||||
if err != nil {
|
||||
return AuditData{}, err
|
||||
}
|
||||
nsResults := NamespacedResults{}
|
||||
ValidateControllers(config, kubeResources, &nsResults)
|
||||
|
||||
clusterResults := ResultSummary{}
|
||||
|
||||
@@ -54,6 +47,9 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (
|
||||
for _, dr := range nsRes.DeploymentResults {
|
||||
clusterResults.appendResults(*dr.PodResult.Summary)
|
||||
}
|
||||
for _, dr := range nsRes.StatefulSetResults {
|
||||
clusterResults.appendResults(*dr.PodResult.Summary)
|
||||
}
|
||||
}
|
||||
|
||||
displayName := config.DisplayName
|
||||
@@ -68,13 +64,14 @@ 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),
|
||||
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),
|
||||
Results: clusterResults,
|
||||
Score: clusterResults.Totals.GetScore(),
|
||||
},
|
||||
NamespacedResults: nsResults,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func TestGetTemplateData(t *testing.T) {
|
||||
k8s := test.SetupTestAPI()
|
||||
k8s = test.SetupAddDeploys(k8s, "test")
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
resources, err := kube.CreateResourceProviderFromAPI(k8s, "test")
|
||||
assert.Equal(t, err, nil, "error should be nil")
|
||||
|
||||
@@ -24,19 +24,19 @@ func TestGetTemplateData(t *testing.T) {
|
||||
|
||||
sum := ResultSummary{
|
||||
Totals: CountSummary{
|
||||
Successes: uint(4),
|
||||
Warnings: uint(1),
|
||||
Errors: uint(1),
|
||||
Successes: uint(8),
|
||||
Warnings: uint(2),
|
||||
Errors: uint(2),
|
||||
},
|
||||
ByCategory: CategorySummary{},
|
||||
}
|
||||
sum.ByCategory["Health Checks"] = &CountSummary{
|
||||
Successes: uint(0),
|
||||
Warnings: uint(1),
|
||||
Errors: uint(1),
|
||||
Warnings: uint(2),
|
||||
Errors: uint(2),
|
||||
}
|
||||
sum.ByCategory["Resources"] = &CountSummary{
|
||||
Successes: uint(4),
|
||||
Successes: uint(8),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
}
|
||||
@@ -47,8 +47,14 @@ func TestGetTemplateData(t *testing.T) {
|
||||
assert.EqualValues(t, sum, actualAudit.ClusterSummary.Results)
|
||||
assert.Equal(t, actualAudit.SourceType, "Cluster", "should be from a cluster")
|
||||
assert.Equal(t, actualAudit.SourceName, "test", "should be from a cluster")
|
||||
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].DeploymentResults), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].DeploymentResults), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].DeploymentResults[0].PodResult.ContainerResults), "should be equal")
|
||||
assert.Equal(t, 6, len(actualAudit.NamespacedResults["test"].DeploymentResults[0].PodResult.ContainerResults[0].Messages), "should be equal")
|
||||
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].StatefulSetResults), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].StatefulSetResults), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].StatefulSetResults[0].PodResult.ContainerResults), "should be equal")
|
||||
assert.Equal(t, 6, len(actualAudit.NamespacedResults["test"].StatefulSetResults[0].PodResult.ContainerResults[0].Messages), "should be equal")
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestValidatePod(t *testing.T) {
|
||||
}
|
||||
|
||||
k8s := test.SetupTestAPI()
|
||||
k8s = test.SetupAddDeploys(k8s, "test")
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
pod := test.MockPod()
|
||||
|
||||
expectedSum := ResultSummary{
|
||||
|
||||
+22
-5
@@ -28,14 +28,31 @@ const (
|
||||
MessageTypeError MessageType = "error"
|
||||
)
|
||||
|
||||
// NamespaceResult groups container results by parent resource.
|
||||
type NamespaceResult struct {
|
||||
Name string
|
||||
Summary *ResultSummary
|
||||
DeploymentResults []ControllerResult
|
||||
StatefulSetResults []ControllerResult
|
||||
}
|
||||
|
||||
// NamespacedResults is a mapping of namespace name to the validation results.
|
||||
type NamespacedResults map[string]*NamespaceResult
|
||||
|
||||
// NamespaceResult groups container results by parent resource.
|
||||
type NamespaceResult struct {
|
||||
Name string
|
||||
Summary *ResultSummary
|
||||
DeploymentResults []ControllerResult
|
||||
func (nsResults NamespacedResults) getNamespaceResult(nsName string) *NamespaceResult {
|
||||
nsResult := &NamespaceResult{}
|
||||
switch nsResults[nsName] {
|
||||
case nil:
|
||||
nsResult = &NamespaceResult{
|
||||
Summary: &ResultSummary{},
|
||||
DeploymentResults: []ControllerResult{},
|
||||
StatefulSetResults: []ControllerResult{},
|
||||
}
|
||||
nsResults[nsName] = nsResult
|
||||
default:
|
||||
nsResult = nsResults[nsName]
|
||||
}
|
||||
return nsResult
|
||||
}
|
||||
|
||||
// CountSummary provides a high level overview of success, warnings, and errors.
|
||||
|
||||
+17
-10
@@ -87,19 +87,24 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons
|
||||
var err error
|
||||
var podResult validator.PodResult
|
||||
|
||||
allowed := true
|
||||
reason := ""
|
||||
|
||||
switch req.AdmissionRequest.Kind.Kind {
|
||||
case "Deployment":
|
||||
deploy := appsv1.Deployment{}
|
||||
err = v.decoder.Decode(req, &deploy)
|
||||
deployResult := validator.ValidateDeployment(v.Config, &deploy)
|
||||
podResult = deployResult.PodResult
|
||||
case "Pod":
|
||||
if req.AdmissionRequest.Kind.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
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":
|
||||
deploy := appsv1.Deployment{}
|
||||
err = v.decoder.Decode(req, &deploy)
|
||||
controller = validator.ControllerFromDeployment(deploy)
|
||||
case "StatefulSet":
|
||||
statefulSet := appsv1.StatefulSet{}
|
||||
err = v.decoder.Decode(req, &statefulSet)
|
||||
controller = validator.ControllerFromStatefulSet(statefulSet)
|
||||
}
|
||||
controllerResult := validator.ValidateController(v.Config, controller)
|
||||
podResult = controllerResult.PodResult
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -107,6 +112,8 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
allowed := true
|
||||
reason := ""
|
||||
if podResult.Summary.Totals.Errors > 0 {
|
||||
allowed = false
|
||||
reason = getFailureReason(podResult)
|
||||
|
||||
+17
-2
@@ -39,17 +39,32 @@ func mockDeploy() appsv1.Deployment {
|
||||
return d
|
||||
}
|
||||
|
||||
func mockStatefulSet() appsv1.StatefulSet {
|
||||
p := MockPod()
|
||||
s := appsv1.StatefulSet{
|
||||
Spec: appsv1.StatefulSetSpec{
|
||||
Template: p,
|
||||
},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SetupTestAPI creates a test kube API struct.
|
||||
func SetupTestAPI() kubernetes.Interface {
|
||||
return fake.NewSimpleClientset()
|
||||
}
|
||||
|
||||
// SetupAddDeploys creates a mock deployment and adds it to the test clientset.
|
||||
func SetupAddDeploys(k kubernetes.Interface, namespace string) 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 {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s1 := mockStatefulSet()
|
||||
_, err = k.AppsV1().StatefulSets(namespace).Create(&s1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user