mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-05-14 13:16:54 +00:00
Merge pull request #65 from reactiveops/rb/output-structure
Add categories to dashboard
This commit is contained in:
@@ -18,12 +18,12 @@ import (
|
||||
"github.com/reactiveops/fairwinds/pkg/validator"
|
||||
)
|
||||
|
||||
func getWarningWidth(rs validator.ResultSummary, fullWidth int) uint {
|
||||
return uint(float64(rs.Successes+rs.Warnings) / float64(rs.Successes+rs.Warnings+rs.Errors) * float64(fullWidth))
|
||||
func getWarningWidth(counts validator.CountSummary, fullWidth int) uint {
|
||||
return uint(float64(counts.Successes+counts.Warnings) / float64(counts.Successes+counts.Warnings+counts.Errors) * float64(fullWidth))
|
||||
}
|
||||
|
||||
func getSuccessWidth(rs validator.ResultSummary, fullWidth int) uint {
|
||||
return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Errors) * float64(fullWidth))
|
||||
func getSuccessWidth(counts validator.CountSummary, fullWidth int) uint {
|
||||
return uint(float64(counts.Successes) / float64(counts.Successes+counts.Warnings+counts.Errors) * float64(fullWidth))
|
||||
}
|
||||
|
||||
func getGrade(rs validator.ResultSummary) string {
|
||||
@@ -58,8 +58,8 @@ func getGrade(rs validator.ResultSummary) string {
|
||||
}
|
||||
|
||||
func getScore(rs validator.ResultSummary) uint {
|
||||
total := (rs.Successes * 2) + rs.Warnings + (rs.Errors * 2)
|
||||
return uint((float64(rs.Successes*2) / float64(total)) * 100)
|
||||
total := (rs.Totals.Successes * 2) + rs.Totals.Warnings + (rs.Totals.Errors * 2)
|
||||
return uint((float64(rs.Totals.Successes*2) / float64(total)) * 100)
|
||||
}
|
||||
|
||||
func getWeatherIcon(rs validator.ResultSummary) string {
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
</div>
|
||||
<div class="result-messages">
|
||||
<ul class="message-list">
|
||||
<li class="success"><i class="fas fa-check"></i> {{ .AuditData.ClusterSummary.Results.Successes }} checks passed</li>
|
||||
<li class="warning"><i class="fas fa-exclamation"></i> {{ .AuditData.ClusterSummary.Results.Warnings }} checks had warnings</li>
|
||||
<li class="error"><i class="fas fa-times"></i> {{ .AuditData.ClusterSummary.Results.Errors }} checks had errors</li>
|
||||
<li class="success"><i class="fas fa-check"></i> {{ .AuditData.ClusterSummary.Results.Totals.Successes }} checks passed</li>
|
||||
<li class="warning"><i class="fas fa-exclamation"></i> {{ .AuditData.ClusterSummary.Results.Totals.Warnings }} checks had warnings</li>
|
||||
<li class="error"><i class="fas fa-times"></i> {{ .AuditData.ClusterSummary.Results.Totals.Errors }} checks had errors</li>
|
||||
</ul>
|
||||
</div>
|
||||
<canvas id="clusterScoreChart"></canvas>
|
||||
@@ -80,7 +80,32 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="resource-info">
|
||||
<div class="name"><span class="caret-expander"></span>Health summary</div>
|
||||
<div class="expandable-content">
|
||||
<ul class="message-list">
|
||||
{{ range $category, $summary := .AuditData.ClusterSummary.Results.ByCategory }}
|
||||
<li>
|
||||
<span class="detail-label">{{ $category }}</span>
|
||||
<span class="detail-value">{{ $summary.Errors }} errors, {{ $summary.Warnings }} warnings</span>
|
||||
<div class="status-bar">
|
||||
<div class="status">
|
||||
<div class="failing">
|
||||
<div class="warning" style="width: {{ getWarningWidth $summary 280 }}px;">
|
||||
<div class="passing" style="width: {{ getSuccessWidth $summary 280 }}px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
{{ range $namespace, $results := .AuditData.NamespacedResults }}
|
||||
@@ -116,8 +141,8 @@
|
||||
<td class="status-bar">
|
||||
<div class="status">
|
||||
<div class="failing">
|
||||
<div class="warning" style="width: {{ getWarningWidth .Summary 200 }}px;">
|
||||
<div class="passing" style="width: {{ getSuccessWidth .Summary 200 }}px;"></div>
|
||||
<div class="warning" style="width: {{ getWarningWidth .Summary.Totals 200 }}px;">
|
||||
<div class="passing" style="width: {{ getSuccessWidth .Summary.Totals 200 }}px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,12 +31,10 @@ type ContainerValidation struct {
|
||||
}
|
||||
|
||||
// ValidateContainer validates that each pod conforms to the Fairwinds config, returns a ResourceResult.
|
||||
func ValidateContainer(cnConf *conf.Configuration, container *corev1.Container) ResourceResult {
|
||||
func ValidateContainer(cnConf *conf.Configuration, container *corev1.Container) ContainerResult {
|
||||
cv := ContainerValidation{
|
||||
Container: container,
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: container,
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
cv.validateResources(&cnConf.Resources)
|
||||
@@ -48,16 +46,10 @@ func ValidateContainer(cnConf *conf.Configuration, container *corev1.Container)
|
||||
cRes := ContainerResult{
|
||||
Name: container.Name,
|
||||
Messages: cv.messages(),
|
||||
Summary: cv.summary(),
|
||||
}
|
||||
|
||||
rr := ResourceResult{
|
||||
Name: container.Name,
|
||||
Type: "Container",
|
||||
Summary: cv.Summary,
|
||||
ContainerResults: []ContainerResult{cRes},
|
||||
}
|
||||
|
||||
return rr
|
||||
return cRes
|
||||
}
|
||||
|
||||
func (cv *ContainerValidation) validateResources(resConf *conf.Resources) {
|
||||
|
||||
@@ -69,10 +69,8 @@ func TestValidateResourcesEmptyConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
cv := ContainerValidation{
|
||||
Container: &container,
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &container,
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
expected := conf.Resources{}
|
||||
@@ -195,10 +193,8 @@ func TestValidateResourcesFullyValid(t *testing.T) {
|
||||
|
||||
func testValidateResources(t *testing.T, container *corev1.Container, resourceConf *string, expectedErrors *[]*ResultMessage, expectedWarnings *[]*ResultMessage) {
|
||||
cv := ContainerValidation{
|
||||
Container: container,
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: container,
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
parsedConf, err := conf.Parse([]byte(*resourceConf))
|
||||
@@ -227,10 +223,8 @@ func TestValidateHealthChecks(t *testing.T) {
|
||||
|
||||
probe := corev1.Probe{}
|
||||
cv1 := ContainerValidation{
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
cv2 := ContainerValidation{
|
||||
Container: &corev1.Container{
|
||||
@@ -238,9 +232,7 @@ func TestValidateHealthChecks(t *testing.T) {
|
||||
LivenessProbe: &probe,
|
||||
ReadinessProbe: &probe,
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
l := &ResultMessage{Type: "warning", Message: "Liveness probe should be configured", Category: "Health Checks"}
|
||||
@@ -286,31 +278,23 @@ func TestValidateImage(t *testing.T) {
|
||||
i3 := conf.Images{TagNotSpecified: conf.SeverityError}
|
||||
|
||||
cv1 := ContainerValidation{
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
cv2 := ContainerValidation{
|
||||
Container: &corev1.Container{Name: "", Image: "test:tag"},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: "", Image: "test:tag"},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
cv3 := ContainerValidation{
|
||||
Container: &corev1.Container{Name: "", Image: "test:latest"},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: "", Image: "test:latest"},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
cv4 := ContainerValidation{
|
||||
Container: &corev1.Container{Name: "", Image: "test"},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: "", Image: "test"},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
f := &ResultMessage{Message: "Image tag should be specified", Type: "error", Category: "Images"}
|
||||
@@ -351,10 +335,8 @@ func TestValidateNetworking(t *testing.T) {
|
||||
}
|
||||
|
||||
emptyCV := ContainerValidation{
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
badCV := ContainerValidation{
|
||||
@@ -364,9 +346,7 @@ func TestValidateNetworking(t *testing.T) {
|
||||
HostPort: 443,
|
||||
}},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
goodCV := ContainerValidation{
|
||||
@@ -375,9 +355,7 @@ func TestValidateNetworking(t *testing.T) {
|
||||
ContainerPort: 3000,
|
||||
}},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
@@ -497,10 +475,8 @@ func TestValidateSecurity(t *testing.T) {
|
||||
}
|
||||
|
||||
emptyCV := ContainerValidation{
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Container: &corev1.Container{Name: ""},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
badCV := ContainerValidation{
|
||||
@@ -513,9 +489,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Add: []corev1.Capability{"AUDIT_CONTROL", "SYS_ADMIN", "NET_ADMIN"},
|
||||
},
|
||||
}},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
goodCV := ContainerValidation{
|
||||
@@ -528,9 +502,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Drop: []corev1.Capability{"NET_BIND_SERVICE", "FOWNER"},
|
||||
},
|
||||
}},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
strongCV := ContainerValidation{
|
||||
@@ -543,9 +515,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Drop: []corev1.Capability{"ALL"},
|
||||
},
|
||||
}},
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
|
||||
@@ -63,10 +63,7 @@ func addResult(resResult ResourceResult, nsResults NamespacedResults, nsName str
|
||||
}
|
||||
|
||||
nsResult.Results = append(nsResult.Results, resResult)
|
||||
nsResult.Summary.appendResults(*resResult.Summary)
|
||||
|
||||
// Aggregate all resource results summary counts to get a namespace wide count.
|
||||
nsResult.Summary.Successes += resResult.Summary.Successes
|
||||
nsResult.Summary.Warnings += resResult.Summary.Warnings
|
||||
nsResult.Summary.Errors += resResult.Summary.Errors
|
||||
return nsResults
|
||||
}
|
||||
|
||||
@@ -40,14 +40,12 @@ func RunAudit(config conf.Configuration, kubeAPI *kube.API) (AuditData, error) {
|
||||
return AuditData{}, err
|
||||
}
|
||||
|
||||
var clusterSuccesses, clusterErrors, clusterWarnings uint
|
||||
clusterResults := ResultSummary{}
|
||||
|
||||
// Aggregate all summary counts to get a clusterwide count.
|
||||
for _, nsRes := range nsResults {
|
||||
for _, rr := range nsRes.Results {
|
||||
clusterErrors += rr.Summary.Errors
|
||||
clusterWarnings += rr.Summary.Warnings
|
||||
clusterSuccesses += rr.Summary.Successes
|
||||
clusterResults.appendResults(*rr.Summary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +79,7 @@ func RunAudit(config conf.Configuration, kubeAPI *kube.API) (AuditData, error) {
|
||||
Nodes: len(nodes.Items),
|
||||
Pods: numPods,
|
||||
Namespaces: len(namespaces.Items),
|
||||
Results: ResultSummary{
|
||||
Errors: clusterErrors,
|
||||
Warnings: clusterWarnings,
|
||||
Successes: clusterSuccesses,
|
||||
},
|
||||
Results: clusterResults,
|
||||
},
|
||||
NamespacedResults: nsResults,
|
||||
}
|
||||
|
||||
@@ -20,17 +20,30 @@ func TestGetTemplateData(t *testing.T) {
|
||||
}
|
||||
|
||||
sum := ResultSummary{
|
||||
Successes: uint(4),
|
||||
Totals: CountSummary{
|
||||
Successes: uint(4),
|
||||
Warnings: uint(1),
|
||||
Errors: uint(1),
|
||||
},
|
||||
ByCategory: CategorySummary{},
|
||||
}
|
||||
sum.ByCategory["Health Checks"] = &CountSummary{
|
||||
Successes: uint(0),
|
||||
Warnings: uint(1),
|
||||
Errors: uint(1),
|
||||
}
|
||||
sum.ByCategory["Resources"] = &CountSummary{
|
||||
Successes: uint(4),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
}
|
||||
|
||||
actualAudit, err := RunAudit(c, k8s)
|
||||
assert.Equal(t, err, nil, "error should be nil")
|
||||
|
||||
assert.EqualValues(t, actualAudit.ClusterSummary.Results, sum)
|
||||
assert.Equal(t, len(actualAudit.NamespacedResults["test"].Results), 1, "should be equal")
|
||||
assert.Equal(t, len(actualAudit.NamespacedResults["test"].Results[0].PodResults), 1, "should be equal")
|
||||
assert.Equal(t, len(actualAudit.NamespacedResults["test"].Results[0].PodResults[0].ContainerResults), 1, "should be equal")
|
||||
assert.Equal(t, len(actualAudit.NamespacedResults["test"].Results[0].PodResults[0].ContainerResults[0].Messages), 6, "should be equal")
|
||||
assert.EqualValues(t, sum, actualAudit.ClusterSummary.Results)
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].Results), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].Results[0].PodResults), "should be equal")
|
||||
assert.Equal(t, 1, len(actualAudit.NamespacedResults["test"].Results[0].PodResults[0].ContainerResults), "should be equal")
|
||||
assert.Equal(t, 6, len(actualAudit.NamespacedResults["test"].Results[0].PodResults[0].ContainerResults[0].Messages), "should be equal")
|
||||
}
|
||||
|
||||
@@ -32,10 +32,8 @@ type PodValidation struct {
|
||||
// ValidatePod validates that each pod conforms to the Fairwinds config, returns a ResourceResult.
|
||||
func ValidatePod(podConf conf.Configuration, pod *corev1.PodSpec) ResourceResult {
|
||||
pv := PodValidation{
|
||||
Pod: pod,
|
||||
ResourceValidation: &ResourceValidation{
|
||||
Summary: &ResultSummary{},
|
||||
},
|
||||
Pod: pod,
|
||||
ResourceValidation: &ResourceValidation{},
|
||||
}
|
||||
|
||||
pv.validateSecurity(&podConf.Security)
|
||||
@@ -51,23 +49,20 @@ func ValidatePod(podConf conf.Configuration, pod *corev1.PodSpec) ResourceResult
|
||||
|
||||
rr := ResourceResult{
|
||||
Type: "Pod",
|
||||
Summary: pv.Summary,
|
||||
Summary: pv.summary(),
|
||||
PodResults: []PodResult{pRes},
|
||||
}
|
||||
for _, cRes := range pRes.ContainerResults {
|
||||
rr.Summary.appendResults(*cRes.Summary)
|
||||
}
|
||||
|
||||
return rr
|
||||
}
|
||||
|
||||
func (pv *PodValidation) validateContainers(containers []corev1.Container, pRes *PodResult, podConf *conf.Configuration) {
|
||||
for _, container := range containers {
|
||||
ctrRR := ValidateContainer(podConf, &container)
|
||||
pv.Summary.Successes += ctrRR.Summary.Successes
|
||||
pv.Summary.Warnings += ctrRR.Summary.Warnings
|
||||
pv.Summary.Errors += ctrRR.Summary.Errors
|
||||
pRes.ContainerResults = append(
|
||||
pRes.ContainerResults,
|
||||
ctrRR.ContainerResults[0],
|
||||
)
|
||||
cRes := ValidateContainer(podConf, &container)
|
||||
pRes.ContainerResults = append(pRes.ContainerResults, cRes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,25 @@ func TestValidatePod(t *testing.T) {
|
||||
pod := test.MockPod()
|
||||
|
||||
expectedSum := ResultSummary{
|
||||
Successes: uint(8),
|
||||
Totals: CountSummary{
|
||||
Successes: uint(8),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
},
|
||||
ByCategory: make(map[string]*CountSummary),
|
||||
}
|
||||
expectedSum.ByCategory["Networking"] = &CountSummary{
|
||||
Successes: uint(2),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
}
|
||||
expectedSum.ByCategory["Resources"] = &CountSummary{
|
||||
Successes: uint(4),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
}
|
||||
expectedSum.ByCategory["Security"] = &CountSummary{
|
||||
Successes: uint(2),
|
||||
Warnings: uint(0),
|
||||
Errors: uint(0),
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
|
||||
// ResourceValidation contains methods shared by PodValidation and ContainerValidation
|
||||
type ResourceValidation struct {
|
||||
Summary *ResultSummary
|
||||
Errors []*ResultMessage
|
||||
Warnings []*ResultMessage
|
||||
Successes []*ResultMessage
|
||||
@@ -36,6 +35,43 @@ func (rv *ResourceValidation) messages() []*ResultMessage {
|
||||
return messages
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) summary() *ResultSummary {
|
||||
counts := CountSummary{
|
||||
Errors: uint(len(rv.Errors)),
|
||||
Warnings: uint(len(rv.Warnings)),
|
||||
Successes: uint(len(rv.Successes)),
|
||||
}
|
||||
byCategory := CategorySummary{}
|
||||
for _, msg := range rv.messages() {
|
||||
if _, ok := byCategory[msg.Category]; !ok {
|
||||
byCategory[msg.Category] = &CountSummary{}
|
||||
}
|
||||
if msg.Type == MessageTypeError {
|
||||
byCategory[msg.Category].Errors++
|
||||
} else if msg.Type == MessageTypeWarning {
|
||||
byCategory[msg.Category].Warnings++
|
||||
} else if msg.Type == MessageTypeSuccess {
|
||||
byCategory[msg.Category].Successes++
|
||||
}
|
||||
}
|
||||
return &ResultSummary{
|
||||
Totals: counts,
|
||||
ByCategory: byCategory,
|
||||
}
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) addMessage(message ResultMessage) {
|
||||
if message.Type == MessageTypeError {
|
||||
rv.Errors = append(rv.Errors, &message)
|
||||
} else if message.Type == MessageTypeWarning {
|
||||
rv.Warnings = append(rv.Warnings, &message)
|
||||
} else if message.Type == MessageTypeSuccess {
|
||||
rv.Successes = append(rv.Successes, &message)
|
||||
} else {
|
||||
panic("Bad message type")
|
||||
}
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) addFailure(message string, severity conf.Severity, category string) {
|
||||
if severity == conf.SeverityError {
|
||||
rv.addError(message, category)
|
||||
@@ -48,7 +84,6 @@ func (rv *ResourceValidation) addFailure(message string, severity conf.Severity,
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) addError(message string, category string) {
|
||||
rv.Summary.Errors++
|
||||
rv.Errors = append(rv.Errors, &ResultMessage{
|
||||
Message: message,
|
||||
Type: MessageTypeError,
|
||||
@@ -57,7 +92,6 @@ func (rv *ResourceValidation) addError(message string, category string) {
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) addWarning(message string, category string) {
|
||||
rv.Summary.Warnings++
|
||||
rv.Warnings = append(rv.Warnings, &ResultMessage{
|
||||
Message: message,
|
||||
Type: MessageTypeWarning,
|
||||
@@ -66,7 +100,6 @@ func (rv *ResourceValidation) addWarning(message string, category string) {
|
||||
}
|
||||
|
||||
func (rv *ResourceValidation) addSuccess(message string, category string) {
|
||||
rv.Summary.Successes++
|
||||
rv.Successes = append(rv.Successes, &ResultMessage{
|
||||
Message: message,
|
||||
Type: MessageTypeSuccess,
|
||||
|
||||
@@ -46,22 +46,52 @@ type ResourceResult struct {
|
||||
PodResults []PodResult
|
||||
}
|
||||
|
||||
// ResultSummary provides a high level overview of success, warnings, and errors.
|
||||
type ResultSummary struct {
|
||||
// CountSummary provides a high level overview of success, warnings, and errors.
|
||||
type CountSummary struct {
|
||||
Successes uint
|
||||
Warnings uint
|
||||
Errors uint
|
||||
}
|
||||
|
||||
func (cs *CountSummary) appendCounts(toAppend CountSummary) {
|
||||
cs.Errors += toAppend.Errors
|
||||
cs.Warnings += toAppend.Warnings
|
||||
cs.Successes += toAppend.Successes
|
||||
}
|
||||
|
||||
// CategorySummary provides a map from category name to a CountSummary
|
||||
type CategorySummary map[string]*CountSummary
|
||||
|
||||
// ResultSummary provides a high level overview of success, warnings, and errors.
|
||||
type ResultSummary struct {
|
||||
Totals CountSummary
|
||||
ByCategory CategorySummary
|
||||
}
|
||||
|
||||
func (rs *ResultSummary) appendResults(toAppend ResultSummary) {
|
||||
rs.Totals.appendCounts(toAppend.Totals)
|
||||
for category, summary := range toAppend.ByCategory {
|
||||
if rs.ByCategory == nil {
|
||||
rs.ByCategory = CategorySummary{}
|
||||
}
|
||||
if _, exists := rs.ByCategory[category]; !exists {
|
||||
rs.ByCategory[category] = &CountSummary{}
|
||||
}
|
||||
rs.ByCategory[category].appendCounts(*summary)
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerResult provides a list of validation messages for each container.
|
||||
type ContainerResult struct {
|
||||
Name string
|
||||
Messages []*ResultMessage
|
||||
Summary *ResultSummary
|
||||
}
|
||||
|
||||
// PodResult provides a list of validation messages for each pod.
|
||||
type PodResult struct {
|
||||
Name string
|
||||
Summary *ResultSummary
|
||||
Messages []*ResultMessage
|
||||
ContainerResults []ContainerResult
|
||||
}
|
||||
@@ -72,8 +102,3 @@ type ResultMessage struct {
|
||||
Type MessageType
|
||||
Category string
|
||||
}
|
||||
|
||||
// Score represents a percentage of validations that were successful.
|
||||
func (rs *ResultSummary) Score() uint {
|
||||
return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Errors) * 100)
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func (v *Validator) Handle(ctx context.Context, req types.Request) types.Respons
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
if results.Summary.Errors > 0 {
|
||||
if results.Summary.Totals.Errors > 0 {
|
||||
// TODO: Decide what message we want to return here.
|
||||
allowed, reason = false, "failed validation checks, view details on dashbaord."
|
||||
}
|
||||
|
||||
@@ -99,6 +99,10 @@ body {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.cluster .expandable-table ul.message-list {
|
||||
margin: 10px 26px;
|
||||
}
|
||||
|
||||
#clusterScoreChart {
|
||||
width: 550px;
|
||||
position: relative;
|
||||
@@ -223,35 +227,40 @@ ul.message-list li i {
|
||||
color: #a11f4c;
|
||||
}
|
||||
|
||||
.namespace td.status-bar {
|
||||
.namespace .status-bar {
|
||||
vertical-align: top;
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
.namespace .status {
|
||||
.namespace .status-bar .status {
|
||||
float: right;
|
||||
animation: fadeIn 2s;
|
||||
}
|
||||
|
||||
.cluster .status {
|
||||
width: 280px;
|
||||
}
|
||||
.namespace .status {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.namespace .status div {
|
||||
.status div {
|
||||
height: 15px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.namespace .status .passing {
|
||||
.status .passing {
|
||||
background-color: #8BD2DC;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.namespace .status .warning {
|
||||
.status .warning {
|
||||
background-color: #f26c21;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.namespace .status .failing {
|
||||
.status .failing {
|
||||
background-color: #a11f4c;
|
||||
width: 200px;
|
||||
animation: fadeIn 2s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
|
||||
@@ -5,9 +5,9 @@ $(function () {
|
||||
labels: ["Passing", "Warning", "Error"],
|
||||
datasets: [{
|
||||
data: [
|
||||
fairwindsAuditData.ClusterSummary.Results.Successes,
|
||||
fairwindsAuditData.ClusterSummary.Results.Warnings,
|
||||
fairwindsAuditData.ClusterSummary.Results.Errors,
|
||||
fairwindsAuditData.ClusterSummary.Results.Totals.Successes,
|
||||
fairwindsAuditData.ClusterSummary.Results.Totals.Warnings,
|
||||
fairwindsAuditData.ClusterSummary.Results.Totals.Errors,
|
||||
],
|
||||
backgroundColor: ['#8BD2DC', '#f26c21', '#a11f4c'],
|
||||
}]
|
||||
|
||||
Reference in New Issue
Block a user