Compare commits

...
4 Commits
Author SHA1 Message Date
Vitor Rodrigo VezaniandAndy Suderman 952b6aed65 INSIGHTS-157 - PDB <> HPA check (#1057)
* fix typo

* fix failure message

* fix changelog

* fix missingPodDisruptionBudget validation

* add tests for pdbMinAvailableLessThenHPAMaxReplicas

* add simple success test

* fix typo

* lowercasing warnings

* WIP implement pdbMinAvailableLessThanHPAMaxReplicas

* change check name

* rename testes

* fix check message

* change check name

* minor fixes

* improving tests

* improve tests

* fix check name

* Update docs/checks/reliability.md

Co-authored-by: Andy Suderman <andy@fairwinds.com>

* fix/add tests

* fixes from PR

* fix error message

---------

Co-authored-by: Andy Suderman <andy@fairwinds.com>
2024-07-08 14:47:54 -03:00
Terraform User 875a8ffd40 Managed by Terraform 2024-07-01 17:24:36 +00:00
Vitor Rodrigo Vezani 61e0d34e8b INSIGHTS-156 - Fix typo and improve failure message (#1055)
* fix typo

* fix failure message

* fix changelog

* fix missingPodDisruptionBudget validation

* Update failure.empty-labels.yaml

* Update failure.no-metadata.yaml

* INSIGHTS-159 - use go templating instead of custom function validation (#1056)

* use go templating instead of custom function validation

* fix changelog
2024-06-27 17:10:21 -03:00
Vitor Rodrigo Vezani 8b236c2fa2 INSIGHTS-159 - use go templating instead of custom function validation (#1056)
* use go templating instead of custom function validation

* fix changelog
2024-06-27 17:01:59 -03:00
34 changed files with 813 additions and 69 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
## DO NOT EDIT - Managed by Terraform
* @rbren @sudermanjr
* @sudermanjr
+5
View File
@@ -6,6 +6,11 @@ meta:
---
## 9.1.1
* Fix `hpaMinAvailability` failure message
* Fix `missingPodDisruptionBudget` typo
* Rewrite `hpaMaxAvailability` check to use go-template
## 9.1.0
* Add HPA `minAvailable` and HPA `maxAvailable` checks
* Fix typo for PDB `minAvailable`
+1
View File
@@ -21,6 +21,7 @@ key | default | description
`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
`pdbMinAvailableGreaterThanHPAMinReplicas` | `warning` | Fails when PDB `minAvailable` is greater than HPA `minReplicas`
## Background
+1
View File
@@ -69,6 +69,7 @@ var (
"rolebindingClusterAdminRole",
"hpaMaxAvailability",
"hpaMinAvailability",
"pdbMinAvailableGreaterThanHPAMinReplicas",
}
// BuiltInChecks contains the checks that come pre-installed w/ Polaris
+32 -1
View File
@@ -1,4 +1,35 @@
successMessage: HPA has a valid max and min replica configuration
failureMessage: HPA maxReplicas and minReplicas should be different
category: Reliability
target: autoscaling/HorizontalPodAutoscaler
target: autoscaling/HorizontalPodAutoscaler
schemaString: |
"$schema": http://json-schema.org/draft-07/schema#
type: object
properties:
spec:
type: object
properties:
minReplicas:
type: integer
minimum: 1
maxReplicas:
type: integer
minimum: 1
required:
- maxReplicas
{{- if .spec.minReplicas }}
if:
properties:
minReplicas:
type: integer
maxReplicas:
type: integer
then:
properties:
maxReplicas:
exclusiveMinimum: {{ .spec.minReplicas }}
else:
properties:
maxReplicas:
minimum: 1
{{- end }}
+1 -1
View File
@@ -1,5 +1,5 @@
successMessage: HPA has a valid min replica configuration
failureMessage: HPA maxReplicas should be greater than minReplicas
failureMessage: HPA minReplicas should be 2 or more
category: Reliability
target: autoscaling/HorizontalPodAutoscaler
schema:
@@ -4,19 +4,23 @@ category: Reliability
target: Controller
controllers:
include:
- Deployment
- Deployment
schema:
'$schema': http://json-schema.org/draft-07/schema
"$schema": http://json-schema.org/draft-07/schema#
type: object
required: [spec]
properties:
spec:
type: object
required: [template]
properties:
template:
type: object
properites:
required: [metadata]
properties:
metadata:
type: object
required: [labels]
properties:
labels:
type: object
@@ -0,0 +1,7 @@
successMessage: PDB and HPA are correctly configured
failureMessage: PDB minAvailable is greater than HPA minReplicas
category: Reliability
target: Controller
controllers:
include:
- Deployment
+1
View File
@@ -12,6 +12,7 @@ checks:
topologySpreadConstraint: warning
hpaMaxAvailability: warning
hpaMinAvailability: warning
pdbMinAvailableGreaterThanHPAMinReplicas: warning
# efficiency
cpuRequestsMissing: warning
+1
View File
@@ -12,6 +12,7 @@ checks:
metadataAndInstanceMismatched: warning
hpaMaxAvailability: warning
hpaMinAvailability: warning
pdbMinAvailableGreaterThanHPAMinReplicas: warning
# efficiency
cpuRequestsMissing: warning
+5 -5
View File
@@ -181,7 +181,7 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
err = cacheAllObjectsOfKind(ctx, firstOwner.APIVersion, firstOwner.Kind, dynamicClient, restMapper, objectCache)
}
if err != nil {
logrus.Warnf("Error caching objects of Kind %s %v", firstOwner.Kind, err)
logrus.Warnf("error caching objects of Kind %s %v", firstOwner.Kind, err)
break
}
abstractObject, ok = objectCache[key]
@@ -193,7 +193,7 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
objMeta, err := meta.Accessor(&abstractObject)
if err != nil {
logrus.Warnf("Error retrieving parent metadata %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
logrus.Warnf("error retrieving parent metadata %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
return GenericResource{}, err
}
podSpec := GetPodSpec(abstractObject.Object)
@@ -221,7 +221,7 @@ func cacheSingleObject(ctx context.Context, apiVersion, kind, namespace, name st
logrus.Debugf("Caching a single %s", kind)
object, err := getObject(ctx, namespace, kind, apiVersion, name, dynamicClient, restMapper)
if err != nil {
logrus.Warnf("Error retrieving object %s/%s/%s/%s because of error: %v", kind, apiVersion, namespace, name, err)
logrus.Warnf("error retrieving object %s/%s/%s/%s because of error: %v", kind, apiVersion, namespace, name, err)
return err
}
key := fmt.Sprintf("%s/%s/%s", object.GetKind(), object.GetNamespace(), object.GetName())
@@ -235,13 +235,13 @@ func cacheAllObjectsOfKind(ctx context.Context, apiVersion, kind string, dynamic
fqKind := schema.FromAPIVersionAndKind(apiVersion, kind)
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
if err != nil {
logrus.Warnf("Error retrieving mapping of API %s and Kind %s because of error: %v", apiVersion, kind, err)
logrus.Warnf("error retrieving mapping of API %s and Kind %s because of error: %v", apiVersion, kind, err)
return err
}
objects, err := dynamicClient.Resource(mapping.Resource).Namespace("").List(ctx, kubeAPIMetaV1.ListOptions{})
if err != nil {
logrus.Warnf("Error retrieving parent object API %s and Kind %s because of error: %v", mapping.Resource.Version, mapping.Resource.Resource, err)
logrus.Warnf("error retrieving parent object API %s and Kind %s because of error: %v", mapping.Resource.Version, mapping.Resource.Resource, err)
return err
}
for idx, object := range objects.Items {
+3 -3
View File
@@ -206,7 +206,7 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
}
err = resources.addResourcesFromYaml(string(contents))
if err != nil {
logrus.Warnf("Skipping %s: cannot add resource from YAML: %v", path, err)
logrus.Warnf("skipping %s: cannot add resource from YAML: %v", path, err)
}
return nil
}
@@ -340,7 +340,7 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
groupKind := parseGroupKind(maybeTransformKindIntoGroupKind(string(kind)))
mapping, err := restMapper.RESTMapping(groupKind)
if err != nil {
logrus.Warnf("Error retrieving mapping of Kind %s because of error: %v", kind, err)
logrus.Warnf("error retrieving mapping of Kind %s because of error: %v", kind, err)
return nil, err
}
if c.Namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameNamespace {
@@ -351,7 +351,7 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
logrus.Info("Loading " + kind)
objects, err := dynamic.Resource(mapping.Resource).Namespace(c.Namespace).List(ctx, metav1.ListOptions{})
if err != nil {
logrus.Warnf("Error retrieving parent object API %s and Kind %s because of error: %v", mapping.Resource.Version, mapping.Resource.Resource, err)
logrus.Warnf("error retrieving parent object API %s and Kind %s because of error: %v", mapping.Resource.Version, mapping.Resource.Resource, err)
return nil, err
}
for _, obj := range objects.Items {
+19
View File
@@ -0,0 +1,19 @@
package validator
import (
"sync"
"github.com/qri-io/jsonschema"
)
type validatorFunction func(test schemaTestCase) (bool, []jsonschema.ValError, error)
var validatorMapper = map[string]validatorFunction{}
var lock = &sync.Mutex{}
func registerCustomChecks(name string, check validatorFunction) {
lock.Lock()
defer lock.Unlock()
validatorMapper[name] = check
}
+150
View File
@@ -0,0 +1,150 @@
package validator
import (
"fmt"
"strconv"
"strings"
"github.com/fairwindsops/polaris/pkg/kube"
"github.com/qri-io/jsonschema"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
policyv1 "k8s.io/api/policy/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
)
func init() {
registerCustomChecks("pdbMinAvailableGreaterThanHPAMinReplicas", pdbMinAvailableGreaterThanHPAMinReplicas)
}
func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []jsonschema.ValError, error) {
if test.ResourceProvider == nil {
logrus.Debug("ResourceProvider is nil")
return true, nil, nil
}
deployment := &appsv1.Deployment{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(test.Resource.Resource.Object, deployment)
if err != nil {
logrus.Warnf("error converting unstructured to Deployment: %v", err)
return true, nil, nil
}
attachedPDB, err := hasPDBAttached(*deployment, test.ResourceProvider.Resources["policy/PodDisruptionBudget"])
if err != nil {
logrus.Warnf("error getting PodDisruptionBudget: %v", err)
return true, nil, nil
}
attachedHPA, err := hasHPAAttached(*deployment, test.ResourceProvider.Resources["autoscaling/HorizontalPodAutoscaler"])
if err != nil {
logrus.Warnf("error getting HorizontalPodAutoscaler: %v", err)
return true, nil, nil
}
if attachedPDB != nil && attachedHPA != nil {
logrus.Debugf("both PDB and HPA are attached to deployment %s", deployment.Name)
pdbMinAvailable, isPercent, err := getIntOrPercentValueSafely(attachedPDB.Spec.MinAvailable)
if err != nil {
logrus.Warnf("error getting getIntOrPercentValueSafely: %v", err)
return true, nil, nil
}
if isPercent {
// if the value is a percentage, we need to calculate the actual value
if attachedHPA.Spec.MinReplicas == nil {
logrus.Debug("attachedHPA.Spec.MinReplicas is nil")
return true, nil, nil
}
pdbMinAvailable, err = intstr.GetScaledValueFromIntOrPercent(attachedPDB.Spec.MinAvailable, int(*attachedHPA.Spec.MinReplicas), true)
if err != nil {
logrus.Warnf("error getting minAvailable value from PodDisruptionBudget: %v", err)
return true, nil, nil
}
}
if attachedHPA.Spec.MinReplicas != nil && pdbMinAvailable >= int(*attachedHPA.Spec.MinReplicas) {
return false, []jsonschema.ValError{
{
PropertyPath: "spec.minAvailable",
InvalidValue: pdbMinAvailable,
Message: fmt.Sprintf("The minAvailable value in the PodDisruptionBudget(%s) is %d, which is greater or equal than the minReplicas value in the HorizontalPodAutoscaler(%s) (%d)", attachedPDB.Name, pdbMinAvailable, attachedHPA.Name, *attachedHPA.Spec.MinReplicas),
},
}, nil
}
}
return true, nil, nil
}
func hasPDBAttached(deployment appsv1.Deployment, pdbs []kube.GenericResource) (*policyv1.PodDisruptionBudget, error) {
for _, generic := range pdbs {
pdb := &policyv1.PodDisruptionBudget{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(generic.Resource.Object, pdb)
if err != nil {
return nil, fmt.Errorf("error converting unstructured to PodDisruptionBudget: %v", err)
}
if pdb.Spec.Selector == nil {
logrus.Debug("pdb.Spec.Selector is nil")
continue
}
if matchesPDBForDeployment(deployment.Spec.Template.Labels, pdb.Spec.Selector.MatchLabels) {
return pdb, nil
}
}
return nil, nil
}
// matchesPDBForDeployment checks if the labels of the deployment match the labels of the PDB
func matchesPDBForDeployment(deploymentLabels, pdbLabels map[string]string) bool {
for key, value := range pdbLabels {
if deploymentLabels[key] == value {
return true
}
}
return false
}
func hasHPAAttached(deployment appsv1.Deployment, hpas []kube.GenericResource) (*autoscalingv1.HorizontalPodAutoscaler, error) {
for _, generic := range hpas {
hpa := &autoscalingv1.HorizontalPodAutoscaler{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(generic.Resource.Object, hpa)
if err != nil {
return nil, fmt.Errorf("error converting unstructured to HorizontalPodAutoscaler: %v", err)
}
if hpa.Spec.ScaleTargetRef.Kind == "Deployment" && hpa.Spec.ScaleTargetRef.Name == deployment.Name {
return hpa, nil
}
}
return nil, nil
}
// getIntOrPercentValueSafely is a safer version of getIntOrPercentValue based on private function intstr.getIntOrPercentValueSafely
func getIntOrPercentValueSafely(intOrStr *intstr.IntOrString) (int, bool, error) {
switch intOrStr.Type {
case intstr.Int:
return intOrStr.IntValue(), false, nil
case intstr.String:
isPercent := false
s := intOrStr.StrVal
if strings.HasSuffix(s, "%") {
isPercent = true
s = strings.TrimSuffix(intOrStr.StrVal, "%")
} else {
return 0, false, fmt.Errorf("invalid type: string is not a percentage")
}
v, err := strconv.Atoi(s)
if err != nil {
return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
}
return int(v), isPercent, nil
}
return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
}
+3 -3
View File
@@ -369,8 +369,8 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
passes, issues, err = check.CheckContainer(test.Container)
} 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 if validatorMapper[checkID] != nil {
passes, issues, err = validatorMapper[checkID](test)
} else {
passes, issues, err = true, []jsonschema.ValError{}, nil
}
@@ -382,7 +382,7 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
break
}
if test.ResourceProvider == nil {
logrus.Warnf("No ResourceProvider available, check %s will not work in this context (e.g. admission control)", checkID)
logrus.Warnf("no ResourceProvider available, check %s will not work in this context (e.g. admission control)", checkID)
break
}
resources := test.ResourceProvider.Resources[groupkind]
-46
View File
@@ -1,46 +0,0 @@
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,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels: {} # empty labels
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata: {} # missing labels
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
@@ -0,0 +1,20 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template: # missing metadata
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
@@ -0,0 +1,44 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
replicas: 10
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 150% # 1.5 * 10 = 15
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 10
maxReplicas: 15
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 10
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 5
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,44 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
replicas: 10
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 50% # 0.5 * 10 = 5
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 10
maxReplicas: 15
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 5
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: no-match
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 5
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 5
selector:
matchLabels:
app.kubernetes.io/name: no-match
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: no-match
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 5
selector:
matchLabels:
app.kubernetes.io/name: no-match
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zookeeper-pdb
spec:
minAvailable: 50%
selector:
matchLabels:
app.kubernetes.io/name: zookeeper
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zookeeper-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zookeeper
minReplicas: 5
maxReplicas: 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
@@ -0,0 +1,14 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zookeeper
spec:
template:
metadata:
labels:
app.kubernetes.io/name: zookeeper
foo: bar
spec:
containers:
- name: zookeeper
image: zookeeper
+2
View File
@@ -34,6 +34,8 @@ func TestMutations(t *testing.T) {
c, err := config.Parse([]byte(configYaml))
assert.NoError(t, err)
assert.Len(t, c.Mutations, 0)
_, mutatedYamlContentMap, mutationTestCasesMap := initTestCases()
for mutationStr := range mutationTestCasesMap {
if len(mutationTestCasesMap[mutationStr]) == 0 {
panic("No test cases found for " + mutationStr)
+9 -6
View File
@@ -29,8 +29,6 @@ import (
"github.com/fairwindsops/polaris/pkg/validator"
)
var testCases = []testCase{}
type testCase struct {
check string
filename string
@@ -40,10 +38,7 @@ type testCase struct {
manifest string
}
var mutatedYamlContentMap = map[string]string{}
var mutationTestCasesMap = map[string][]testCase{}
func init() {
func initTestCases() ([]testCase, map[string]string, map[string][]testCase) {
checkToTest := os.Getenv("POLARIS_CHECK_TEST") // if set, only run tests for this check
_, baseDir, _, _ := runtime.Caller(0)
baseDir = filepath.Dir(baseDir) + "/checks"
@@ -51,6 +46,12 @@ func init() {
if err != nil {
panic(err)
}
if checkToTest != "" {
fmt.Printf("POLARIS_CHECK_TEST is set... Running tests for '%s' only\n", checkToTest)
}
var testCases = []testCase{}
var mutatedYamlContentMap = map[string]string{}
var mutationTestCasesMap = map[string][]testCase{}
for _, dir := range dirs {
check := dir.Name()
if checkToTest != "" && checkToTest != check {
@@ -116,9 +117,11 @@ func init() {
}
}
}
return testCases, mutatedYamlContentMap, mutationTestCasesMap
}
func TestChecks(t *testing.T) {
testCases, _, _ := initTestCases()
for _, tc := range testCases {
results, err := validator.ApplyAllSchemaChecksToResourceProvider(&tc.config, tc.resources)
if err != nil {