mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-05-07 17:57:09 +00:00
* added mutate webhook * fix mutation operation type * if no mutation just use valid response
221 lines
5.9 KiB
Go
221 lines
5.9 KiB
Go
// 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.
|
|
// 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 (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/thoas/go-funk"
|
|
"gomodules.xyz/jsonpatch/v2"
|
|
|
|
"github.com/fairwindsops/polaris/pkg/config"
|
|
)
|
|
|
|
const (
|
|
// PolarisOutputVersion is the version of the current output structure
|
|
PolarisOutputVersion = "1.0"
|
|
)
|
|
|
|
var (
|
|
successMessage = "🎉 Success"
|
|
dangerMessage = "❌ Danger"
|
|
warningMessage = "😬 Warning"
|
|
)
|
|
|
|
var (
|
|
titleColor = color.New(color.FgBlue).Add(color.Bold)
|
|
checkColor = color.New(color.FgCyan)
|
|
)
|
|
|
|
// AuditData contains all the data from a full Polaris audit
|
|
type AuditData struct {
|
|
PolarisOutputVersion string
|
|
AuditTime string
|
|
SourceType string
|
|
SourceName string
|
|
DisplayName string
|
|
ClusterInfo ClusterInfo
|
|
Results []Result
|
|
Score uint
|
|
}
|
|
|
|
// RemoveSuccessfulResults remove all test that have passed.
|
|
func (res AuditData) RemoveSuccessfulResults() AuditData {
|
|
resCopy := res
|
|
resCopy.Results = funk.Map(res.Results, func(auditDataResult Result) Result {
|
|
return auditDataResult.removeSuccessfulResults()
|
|
}).([]Result)
|
|
return resCopy
|
|
}
|
|
|
|
// ClusterInfo contains Polaris results as well as some high-level stats
|
|
type ClusterInfo struct {
|
|
Version string
|
|
Nodes int
|
|
Pods int
|
|
Namespaces int
|
|
Controllers int
|
|
}
|
|
|
|
// ResultMessage is the result of a given check
|
|
type ResultMessage struct {
|
|
ID string
|
|
Message string
|
|
Details []string
|
|
Success bool
|
|
Severity config.Severity
|
|
Category string
|
|
Mutations []jsonpatch.Operation
|
|
Comments []config.MutationComment
|
|
}
|
|
|
|
// ResultSet contiains the results for a set of checks
|
|
type ResultSet map[string]ResultMessage
|
|
|
|
func (res ResultSet) removeSuccessfulResults() ResultSet {
|
|
newResults := ResultSet{}
|
|
for k, resultMessage := range res {
|
|
if !resultMessage.Success {
|
|
newResults[k] = resultMessage
|
|
}
|
|
}
|
|
return newResults
|
|
}
|
|
|
|
// Result provides results for a Kubernetes object
|
|
type Result struct {
|
|
Name string
|
|
Namespace string
|
|
Kind string
|
|
Results ResultSet
|
|
PodResult *PodResult
|
|
CreatedTime time.Time
|
|
}
|
|
|
|
func (res Result) removeSuccessfulResults() Result {
|
|
resCopy := res
|
|
resCopy.Results = res.Results.removeSuccessfulResults()
|
|
if res.PodResult != nil {
|
|
podCopy := res.PodResult.removeSuccessfulResults()
|
|
resCopy.PodResult = &podCopy
|
|
}
|
|
return resCopy
|
|
}
|
|
|
|
// PodResult provides a list of validation messages for each pod.
|
|
type PodResult struct {
|
|
Name string
|
|
Results ResultSet
|
|
ContainerResults []ContainerResult
|
|
}
|
|
|
|
func (res PodResult) removeSuccessfulResults() PodResult {
|
|
resCopy := PodResult{}
|
|
resCopy.Results = res.Results.removeSuccessfulResults()
|
|
resCopy.ContainerResults = funk.Map(res.ContainerResults, func(containerResult ContainerResult) ContainerResult {
|
|
return containerResult.removeSuccessfulResults()
|
|
}).([]ContainerResult)
|
|
return resCopy
|
|
}
|
|
|
|
// ContainerResult provides a list of validation messages for each container.
|
|
type ContainerResult struct {
|
|
Name string
|
|
Results ResultSet
|
|
}
|
|
|
|
func (res ContainerResult) removeSuccessfulResults() ContainerResult {
|
|
resCopy := res
|
|
resCopy.Results = res.Results.removeSuccessfulResults()
|
|
return resCopy
|
|
}
|
|
|
|
func fillString(id string, l int) string {
|
|
for len(id) < l {
|
|
id += " "
|
|
}
|
|
return id
|
|
}
|
|
|
|
// GetPrettyOutput returns a human-readable string
|
|
func (res AuditData) GetPrettyOutput(useColor bool) string {
|
|
color.NoColor = !useColor
|
|
str := titleColor.Sprint(fmt.Sprintf("\n\nPolaris audited %s %s at %s\n", res.SourceType, res.SourceName, res.AuditTime))
|
|
str += color.CyanString(fmt.Sprintf(" Nodes: %d | Namespaces: %d | Controllers: %d\n", res.ClusterInfo.Nodes, res.ClusterInfo.Namespaces, res.ClusterInfo.Controllers))
|
|
str += color.GreenString(fmt.Sprintf(" Final score: %d\n", res.Score))
|
|
str += "\n"
|
|
for _, result := range res.Results {
|
|
str += result.GetPrettyOutput() + "\n"
|
|
}
|
|
color.NoColor = false
|
|
return str
|
|
}
|
|
|
|
// GetPrettyOutput returns a human-readable string
|
|
func (res Result) GetPrettyOutput() string {
|
|
str := titleColor.Sprint(fmt.Sprintf("%s %s", res.Kind, res.Name))
|
|
if res.Namespace != "" {
|
|
str += titleColor.Sprint(fmt.Sprintf(" in namespace %s", res.Namespace))
|
|
}
|
|
str += "\n"
|
|
str += res.Results.GetPrettyOutput()
|
|
if res.PodResult != nil {
|
|
str += res.PodResult.GetPrettyOutput()
|
|
}
|
|
return str
|
|
}
|
|
|
|
// GetPrettyOutput returns a human-readable string
|
|
func (res PodResult) GetPrettyOutput() string {
|
|
str := res.Results.GetPrettyOutput()
|
|
for _, cont := range res.ContainerResults {
|
|
str += cont.GetPrettyOutput() + "\n"
|
|
}
|
|
return str
|
|
}
|
|
|
|
// GetPrettyOutput returns a human-readable string
|
|
func (res ContainerResult) GetPrettyOutput() string {
|
|
str := titleColor.Sprint(fmt.Sprintf(" Container %s\n", res.Name))
|
|
str += res.Results.GetPrettyOutput()
|
|
return str
|
|
}
|
|
|
|
const minIDLength = 40
|
|
|
|
// GetPrettyOutput returns a human-readable string
|
|
func (res ResultSet) GetPrettyOutput() string {
|
|
indent := " "
|
|
str := ""
|
|
for _, msg := range res {
|
|
status := color.GreenString(successMessage)
|
|
if !msg.Success {
|
|
if msg.Severity == config.SeverityWarning {
|
|
status = color.YellowString(warningMessage)
|
|
} else {
|
|
status = color.RedString(dangerMessage)
|
|
}
|
|
}
|
|
if color.NoColor {
|
|
status = status[2:] // remove emoji
|
|
}
|
|
str += fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status)
|
|
str += fmt.Sprintf("%s %s - %s\n", indent, msg.Category, msg.Message)
|
|
}
|
|
return str
|
|
}
|