mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-05-15 05:37:21 +00:00
including successes in dashboard output
This commit is contained in:
11
config.yml
11
config.yml
@@ -1,7 +1,7 @@
|
||||
resources:
|
||||
requests:
|
||||
cpu:
|
||||
min: 100m
|
||||
min: 50m
|
||||
max: 1
|
||||
memory:
|
||||
min: 100M
|
||||
@@ -13,3 +13,12 @@ resources:
|
||||
memory:
|
||||
min: 150M
|
||||
max: 4G
|
||||
healthChecks:
|
||||
readiness:
|
||||
require: true
|
||||
liveness:
|
||||
require: true
|
||||
images:
|
||||
tagRequired: true
|
||||
whitelistRepos:
|
||||
- gcr.io
|
||||
|
||||
@@ -11,26 +11,43 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// ResourceMinMax sets a range for a min and max setting for a resource.
|
||||
type ResourceMinMax struct {
|
||||
Min *resource.Quantity
|
||||
Max *resource.Quantity
|
||||
// Configuration contains all of the config for the validation checks.
|
||||
type Configuration struct {
|
||||
Resources RequestsAndLimits `json:"resources"`
|
||||
HealthChecks Probes `json:"healthChecks"`
|
||||
Images Images `json:"images"`
|
||||
}
|
||||
|
||||
// RequestsAndLimits contains config for resource requests and limits.
|
||||
type RequestsAndLimits struct {
|
||||
Requests ResourceList `json:"requests"`
|
||||
Limits ResourceList `json:"limits"`
|
||||
}
|
||||
|
||||
// ResourceList maps the resource name to a range on min and max values.
|
||||
type ResourceList map[corev1.ResourceName]ResourceMinMax
|
||||
|
||||
// RequestsAndLimits contains config for resource requests and limits.
|
||||
type RequestsAndLimits struct {
|
||||
Requests ResourceList
|
||||
Limits ResourceList
|
||||
// ResourceMinMax sets a range for a min and max setting for a resource.
|
||||
type ResourceMinMax struct {
|
||||
Min *resource.Quantity `json:"min"`
|
||||
Max *resource.Quantity `json:"max"`
|
||||
}
|
||||
|
||||
// Configuration contains all of the config for the validation checks.
|
||||
type Configuration struct {
|
||||
Resources RequestsAndLimits
|
||||
HealthChecks Probes
|
||||
Images Images
|
||||
// Probes contains config for the readiness and liveness probes.
|
||||
type Probes struct {
|
||||
Readiness ResourceRequire `json:"readiness"`
|
||||
Liveness ResourceRequire `json:"liveness"`
|
||||
}
|
||||
|
||||
// ResourceRequire indicates if this resource should be validated.
|
||||
type ResourceRequire struct {
|
||||
Require bool `json:"require"`
|
||||
}
|
||||
|
||||
// Images contains the config for images.
|
||||
type Images struct {
|
||||
TagRequired bool `json:"tagRequired"`
|
||||
WhitelistRepos []string `json:"whitelistRepos"`
|
||||
}
|
||||
|
||||
// ParseFile parses config from a file.
|
||||
@@ -52,24 +69,7 @@ func Parse(rawBytes []byte) (Configuration, error) {
|
||||
if err == io.EOF {
|
||||
return conf, nil
|
||||
}
|
||||
return Configuration{}, fmt.Errorf("Decoding config failed: %v", err)
|
||||
return conf, fmt.Errorf("Decoding config failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Probes contains config for the readiness and liveness probes.
|
||||
type Probes struct {
|
||||
Readiness ResourceRequire
|
||||
Liveness ResourceRequire
|
||||
}
|
||||
|
||||
// ResourceRequire indicates if this resource should be validated.
|
||||
type ResourceRequire struct {
|
||||
Require bool
|
||||
}
|
||||
|
||||
// Images contains the config for images.
|
||||
type Images struct {
|
||||
TagRequired bool
|
||||
WhitelistRepos []string
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ func getDashboardData(c conf.Configuration) (DashboardData, error) {
|
||||
|
||||
dashboardData := DashboardData{
|
||||
ClusterSummary: &validator.ResultSummary{
|
||||
Successes: 46,
|
||||
Warnings: 8,
|
||||
Failures: 5,
|
||||
Successes: 0,
|
||||
Warnings: 4,
|
||||
Failures: 0,
|
||||
},
|
||||
NamespacedResults: map[string]*validator.NamespacedResult{},
|
||||
}
|
||||
@@ -67,9 +67,9 @@ func getDashboardData(c conf.Configuration) (DashboardData, error) {
|
||||
Name: deploy.Name,
|
||||
Type: "Deployment",
|
||||
Summary: &validator.ResultSummary{
|
||||
Successes: 16,
|
||||
Warnings: 4,
|
||||
Failures: 2,
|
||||
Successes: 0,
|
||||
Warnings: 2,
|
||||
Failures: 0,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -77,26 +77,21 @@ func getDashboardData(c conf.Configuration) (DashboardData, error) {
|
||||
dashboardData.NamespacedResults[deploy.Namespace] = &validator.NamespacedResult{
|
||||
Results: []validator.ResourceResult{},
|
||||
Summary: &validator.ResultSummary{
|
||||
Successes: 16,
|
||||
Warnings: 4,
|
||||
Failures: 2,
|
||||
Successes: 0,
|
||||
Warnings: 3,
|
||||
Failures: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for _, containerValidation := range validationFailures.InitContainerValidations {
|
||||
for _, failure := range containerValidation.Failures {
|
||||
dashboardData.ClusterSummary.Failures++
|
||||
dashboardData.NamespacedResults[deploy.Namespace].Summary.Failures++
|
||||
resResult.Summary.Failures++
|
||||
resResult.Messages = append(resResult.Messages, validator.ResultMessage{
|
||||
Message: failure.Reason(),
|
||||
Type: "failure",
|
||||
})
|
||||
containerValidations := append(validationFailures.InitContainerValidations, validationFailures.ContainerValidations...)
|
||||
for _, containerValidation := range containerValidations {
|
||||
for _, success := range containerValidation.Successes {
|
||||
dashboardData.ClusterSummary.Successes++
|
||||
dashboardData.NamespacedResults[deploy.Namespace].Summary.Successes++
|
||||
resResult.Summary.Successes++
|
||||
resResult.Messages = append(resResult.Messages, success)
|
||||
}
|
||||
}
|
||||
|
||||
for _, containerValidation := range validationFailures.ContainerValidations {
|
||||
for _, failure := range containerValidation.Failures {
|
||||
dashboardData.ClusterSummary.Failures++
|
||||
dashboardData.NamespacedResults[deploy.Namespace].Summary.Failures++
|
||||
|
||||
@@ -13,7 +13,7 @@ type Failure struct {
|
||||
|
||||
// Reason returns a string that describes the reason for a Failure.
|
||||
func (f *Failure) Reason() string {
|
||||
return fmt.Sprintf("- %s: Expected: %s, Actual: %s.\n",
|
||||
return fmt.Sprintf("%s: Expected: %s, Actual: %s.",
|
||||
f.Name,
|
||||
f.Expected,
|
||||
f.Actual,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
conf "github.com/reactiveops/fairwinds/pkg/config"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
type ContainerValidation struct {
|
||||
Container corev1.Container
|
||||
Failures []types.Failure
|
||||
Successes []ResultMessage
|
||||
}
|
||||
|
||||
func validateContainer(conf conf.Configuration, container corev1.Container) ContainerValidation {
|
||||
@@ -49,6 +51,13 @@ func (cv *ContainerValidation) addFailure(name, expected, actual string) {
|
||||
})
|
||||
}
|
||||
|
||||
func (cv *ContainerValidation) addSuccess(message string) {
|
||||
cv.Successes = append(cv.Successes, ResultMessage{
|
||||
Message: message,
|
||||
Type: "success",
|
||||
})
|
||||
}
|
||||
|
||||
func (cv *ContainerValidation) validateResources(conf conf.RequestsAndLimits) {
|
||||
actualRes := cv.Container.Resources
|
||||
cv.withinRange("requests.cpu", conf.Requests["cpu"], actualRes.Requests.Cpu())
|
||||
@@ -64,15 +73,26 @@ func (cv *ContainerValidation) withinRange(resourceName string, expectedRange co
|
||||
cv.addFailure(resourceName, expectedMin.String(), actual.String())
|
||||
} else if expectedMax != nil && expectedMax.MilliValue() < actual.MilliValue() {
|
||||
cv.addFailure(resourceName, expectedMax.String(), actual.String())
|
||||
} else {
|
||||
cv.addSuccess(fmt.Sprintf("Resource %s within expected range", resourceName))
|
||||
}
|
||||
}
|
||||
|
||||
func (cv *ContainerValidation) validateHealthChecks(conf conf.Probes) {
|
||||
if conf.Readiness.Require && cv.Container.ReadinessProbe == nil {
|
||||
cv.addFailure("readiness", "probe needs to be configured", "nil")
|
||||
if conf.Readiness.Require {
|
||||
if cv.Container.ReadinessProbe == nil {
|
||||
cv.addFailure("readiness", "probe needs to be configured", "nil")
|
||||
} else {
|
||||
cv.addSuccess("Readiness probe configured")
|
||||
}
|
||||
}
|
||||
if conf.Liveness.Require && cv.Container.LivenessProbe == nil {
|
||||
cv.addFailure("liveness", "probe needs to be configured", "nil")
|
||||
|
||||
if conf.Liveness.Require {
|
||||
if cv.Container.LivenessProbe == nil {
|
||||
cv.addFailure("liveness", "probe needs to be configured", "nil")
|
||||
} else {
|
||||
cv.addSuccess("Liveness probe configured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +101,8 @@ func (cv *ContainerValidation) validateImage(conf conf.Images) {
|
||||
img := strings.Split(cv.Container.Image, ":")
|
||||
if len(img) == 1 || img[1] == "latest" {
|
||||
cv.addFailure("Image Tag", "not latest", "latest")
|
||||
} else {
|
||||
cv.addSuccess("Image tag specified")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func validContainer(sb *strings.Builder, cv *ContainerValidation) bool {
|
||||
s := fmt.Sprintf("\nContainer: %s\n Failure/s:\n", cv.Container.Name)
|
||||
sb.WriteString(s)
|
||||
for _, failure := range cv.Failures {
|
||||
sb.WriteString(failure.Reason())
|
||||
sb.WriteString(fmt.Sprintf("- %s\n", failure.Reason()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
package validator
|
||||
|
||||
// namespaceResults: [{
|
||||
// name: "kube-system",
|
||||
// resourceResults: [{
|
||||
// name: "example-deployment",
|
||||
// type: "DaemonSet",
|
||||
// summary: {
|
||||
// successes: 28,
|
||||
// warnings: 12,
|
||||
// failures: 18,
|
||||
// },
|
||||
// messages: [{
|
||||
// message: "Resource requests are not set",
|
||||
// type: "failure",
|
||||
// }]
|
||||
// }]
|
||||
// }]
|
||||
|
||||
type NamespacedResult struct {
|
||||
Summary *ResultSummary
|
||||
Results []ResourceResult
|
||||
|
||||
Reference in New Issue
Block a user