mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-29 22:17:16 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f504de33cf | ||
|
|
2d33bf2565 | ||
|
|
9824be2c26 |
+54
-3
@@ -6,13 +6,64 @@ meta:
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
* Change `metadataAndNameMismatched` to `metadataAndInstanceMismatched`
|
||||
## 9.1.0
|
||||
* Add HPA `minAvailable` and HPA `maxAvailable` checks
|
||||
* Fix typo for PDB `minAvailable`
|
||||
|
||||
## 8.1.1
|
||||
## 9.0.1
|
||||
* Fix comments handling in `addOrReplaceValue` function
|
||||
|
||||
## 9.0.0
|
||||
* Expose issue fixer and mutations in the library
|
||||
* Remove `packr` in favor of `go:embed`
|
||||
|
||||
## 8.5.6
|
||||
* Fix trying to list cluster-level resources
|
||||
|
||||
## 8.5.5
|
||||
* Fix missing PDB check
|
||||
|
||||
## 8.5.4
|
||||
* Fix conditional expressions should be at very top of `additionalSchemaStrings`
|
||||
* Update alpine to 3.19
|
||||
|
||||
## 8.5.3
|
||||
* Add quiet flag to polaris audit CLI command to suppress 'upload to Insights' prompt
|
||||
|
||||
## 8.5.2
|
||||
* Switch to `controller-utils` package to get workloads
|
||||
|
||||
## 8.5.1
|
||||
* Update `topologySpreadConstraint` check
|
||||
|
||||
## 8.5.0
|
||||
* Add helm-skip-tests flag
|
||||
* Update CLI docs
|
||||
* Handle multiple helm-values files
|
||||
|
||||
## 8.4.0
|
||||
* Change kubernetes.io/ label from name to instance
|
||||
|
||||
## 8.3.0
|
||||
* Add option to filter audit results by severity level
|
||||
* Add insights prompt
|
||||
|
||||
## 8.2.4
|
||||
* Fix nil pointer issue with webhook
|
||||
|
||||
## 8.2.3
|
||||
* Add category for `metadataAndNameMismatched`.
|
||||
* Fix category for `priorityClassNotSet`.
|
||||
|
||||
## 8.2.2
|
||||
* Fix webhook server cert dir argument
|
||||
|
||||
## 8.2.1
|
||||
* Fix on Insights integration
|
||||
|
||||
## 8.2.0
|
||||
* Minor fixes for NSA checks
|
||||
|
||||
## 8.1.0
|
||||
* Add `insights-host` global flag to configure Fairwinds Insights host (defaults to `https://insights.fairwinds.com`).
|
||||
* Add new `auth` sub-commands be able to authenticate on Polaris using Fairwinds Insights credentials
|
||||
|
||||
@@ -19,6 +19,8 @@ key | default | description
|
||||
`missingPodDisruptionBudget` | `warning` | Fails when PDB is missing.
|
||||
`metadataAndInstanceMismatched` | `warning` | Fails when label `app.kubernetes.io/instance` and `metadata.name` mismatch
|
||||
`topologySpreadConstraint` | `warning` | Fails when there is no topology spread constraint on the pod
|
||||
`hpaMaxAvailability` | `warning` | Fails when `maxAvailable` lesser or equal than `minAvailable` (if defined) for a HorizontalPodAutoscaler
|
||||
`hpaMinAvailability` | `warning` | Fails when `minAvailable` (if defined) lesser or equal to one for a HorizontalPodAutoscaler
|
||||
|
||||
## Background
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ var (
|
||||
"clusterrolebindingClusterAdmin",
|
||||
"rolebindingClusterAdminClusterRole",
|
||||
"rolebindingClusterAdminRole",
|
||||
"hpaMaxAvailability",
|
||||
"hpaMinAvailability",
|
||||
}
|
||||
|
||||
// BuiltInChecks contains the checks that come pre-installed w/ Polaris
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
successMessage: HPA has a valid max and min replica configuration
|
||||
failureMessage: HPA maxReplicas and minReplicas should be different
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
@@ -0,0 +1,14 @@
|
||||
successMessage: HPA has a valid min replica configuration
|
||||
failureMessage: HPA maxReplicas should be greater than minReplicas
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
schema:
|
||||
"$schema": http://json-schema.org/draft-07/schema#
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: integer
|
||||
minimum: 2
|
||||
@@ -10,6 +10,8 @@ checks:
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
topologySpreadConstraint: warning
|
||||
hpaMaxAvailability: warning
|
||||
hpaMinAvailability: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
|
||||
@@ -10,6 +10,8 @@ checks:
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
hpaMaxAvailability: warning
|
||||
hpaMinAvailability: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
|
||||
@@ -94,12 +94,16 @@ func (rkm resourceKindMap) GetNumberOfControllers() int {
|
||||
return total
|
||||
}
|
||||
|
||||
var kindRewrites = map[string]string{
|
||||
"Ingress": "networking.k8s.io/Ingress",
|
||||
"PodDisruptionBudget": "policy/PodDisruptionBudget",
|
||||
"HorizontalPodAutoscaler": "autoscaling/HorizontalPodAutoscaler",
|
||||
}
|
||||
|
||||
// This is here for backward compatibility reasons
|
||||
func maybeTransformKindIntoGroupKind(k string) string {
|
||||
if k == "Ingress" {
|
||||
return "networking.k8s.io/Ingress"
|
||||
} else if k == "PodDisruptionBudget" {
|
||||
return "policy/PodDisruptionBudget"
|
||||
if val, ok := kindRewrites[k]; ok {
|
||||
return val
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
@@ -367,8 +367,12 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
|
||||
prefix += "/containers/" + strconv.Itoa(containerIndex)
|
||||
}
|
||||
passes, issues, err = check.CheckContainer(test.Container)
|
||||
} else {
|
||||
} else if check.Validator.SchemaURI != "" {
|
||||
passes, issues, err = check.CheckObject(test.Resource.Resource.Object)
|
||||
} else if customValidators[checkID] != nil {
|
||||
passes, issues, err = customValidators[checkID](test.Resource.Resource.Object)
|
||||
} else {
|
||||
passes, issues, err = true, []jsonschema.ValError{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/qri-io/jsonschema"
|
||||
)
|
||||
|
||||
type customValidator func(data interface{}) (bool, []jsonschema.ValError, error)
|
||||
|
||||
// customValidators is a map of validation functions that can be used in schema checks
|
||||
// sometimes we need to validate things that aren't covered by the JSON validation schema
|
||||
var customValidators = map[string]customValidator{
|
||||
"hpaMaxAvailability": validateHPAMaxAvailability,
|
||||
}
|
||||
|
||||
type HorizontalPodAutoscalerView struct {
|
||||
Spec struct {
|
||||
MinReplicas *int `json:"minReplicas"`
|
||||
MaxReplicas int `json:"maxReplicas"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
|
||||
func validateHPAMaxAvailability(data any) (bool, []jsonschema.ValError, error) {
|
||||
jsonString, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
hpa := HorizontalPodAutoscalerView{}
|
||||
err = json.Unmarshal(jsonString, &hpa)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if hpa.Spec.MinReplicas == nil {
|
||||
return true, []jsonschema.ValError{}, nil
|
||||
}
|
||||
|
||||
if hpa.Spec.MaxReplicas != *hpa.Spec.MinReplicas {
|
||||
return true, []jsonschema.ValError{}, nil
|
||||
}
|
||||
|
||||
return false, []jsonschema.ValError{{PropertyPath: "spec.maxReplicas", Message: fmt.Sprintf("maxReplicas (%d) and minReplicas (%d) should be different", hpa.Spec.MaxReplicas, *hpa.Spec.MinReplicas)}}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 5
|
||||
maxReplicas: 5
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 5
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 50
|
||||
status:
|
||||
observedGeneration: 1
|
||||
lastScaleTime: "2023-06-26T15:04:05Z"
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 1
|
||||
currentMetrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
current:
|
||||
averageUtilization: 0
|
||||
averageValue: 0
|
||||
@@ -5,5 +5,5 @@ metadata:
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
minAvaiable: 5
|
||||
minAvailable: 5
|
||||
maxUnavailable: 10%
|
||||
|
||||
@@ -5,4 +5,4 @@ metadata:
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
minAvaiable: 5
|
||||
minAvailable: 5
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
autoscalingV2 "k8s.io/api/autoscaling/v2"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
@@ -227,6 +228,12 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
|
||||
{Name: "poddisruptionbudgets", Namespaced: true, Kind: "PodDisruptionBudget", Version: "v1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: autoscalingV2.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler", Version: "v2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "core/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
|
||||
+5
-2
@@ -44,7 +44,7 @@ var mutatedYamlContentMap = map[string]string{}
|
||||
var mutationTestCasesMap = map[string][]testCase{}
|
||||
|
||||
func init() {
|
||||
checkToTest := os.Getenv("POLARIS_CHECK_TEST")
|
||||
checkToTest := os.Getenv("POLARIS_CHECK_TEST") // if set, only run tests for this check
|
||||
_, baseDir, _, _ := runtime.Caller(0)
|
||||
baseDir = filepath.Dir(baseDir) + "/checks"
|
||||
dirs, err := os.ReadDir(baseDir)
|
||||
@@ -56,6 +56,9 @@ func init() {
|
||||
if checkToTest != "" && checkToTest != check {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(check, "_") {
|
||||
continue // skip directories starting with _
|
||||
}
|
||||
checkDir := baseDir + "/" + check
|
||||
cases, err := os.ReadDir(checkDir)
|
||||
if err != nil {
|
||||
@@ -119,7 +122,7 @@ func TestChecks(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
results, err := validator.ApplyAllSchemaChecksToResourceProvider(&tc.config, tc.resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
t.Fatalf("Error running checks: %v", err)
|
||||
}
|
||||
auditData := validator.AuditData{Results: results}
|
||||
summary := auditData.GetSummary()
|
||||
|
||||
Reference in New Issue
Block a user