diff --git a/charts/library/common-test/tests/hpa/names_test.yaml b/charts/library/common-test/tests/hpa/names_test.yaml new file mode 100644 index 00000000000..8590c53b989 --- /dev/null +++ b/charts/library/common-test/tests/hpa/names_test.yaml @@ -0,0 +1,63 @@ +suite: hpa name test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should generate correct hpa name + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name-2: + enabled: true + type: Deployment + podSpec: {} + hpa: + vpa-name: + enabled: true + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: &deploymentDoc 1 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + isAPIVersion: + of: apps/v1 + - documentIndex: *deploymentDoc + equal: + path: metadata.name + value: test-release-name-common-test-workload-name-2 + - documentIndex: &hpaDoc 2 + isKind: + of: HorizontalPodAutoscaler + - documentIndex: *hpaDoc + isAPIVersion: + of: autoscaling/v2 + - documentIndex: *hpaDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: &otherHPADoc 3 + isKind: + of: HorizontalPodAutoscaler + - documentIndex: *otherHPADoc + isAPIVersion: + of: autoscaling/v2 + - documentIndex: *otherHPADoc + equal: + path: metadata.name + value: test-release-name-common-test-workload-name-2 diff --git a/charts/library/common-test/tests/hpa/spec_test.yaml b/charts/library/common-test/tests/hpa/spec_test.yaml new file mode 100644 index 00000000000..713b38c7d5e --- /dev/null +++ b/charts/library/common-test/tests/hpa/spec_test.yaml @@ -0,0 +1,265 @@ +suite: hpa spec test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should create hpa + set: + workload: &workload + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name: + enabled: true + primary: true + probes: + readiness: + enabled: false + liveness: + enabled: false + startup: + enabled: false + hpa: + hpa-name: + enabled: true + asserts: + - documentIndex: &hpaDoc 1 + isKind: + of: HorizontalPodAutoscaler + - documentIndex: *hpaDoc + isAPIVersion: + of: autoscaling/v2 + - documentIndex: *hpaDoc + isSubset: + path: spec + content: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: test-release-name-common-test + minReplicas: 1 + maxReplicas: 3 + + - it: should create hpa with targetSelector + set: + workload: + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: {} + workload-name-2: + enabled: true + type: Deployment + podSpec: {} + hpa: + hpa-name: + enabled: true + targetSelector: + - workload-name-2 + asserts: + - hasDocuments: + count: 3 # 2 Deployments, 1 HPA + - documentIndex: &hpaDoc 2 + isKind: + of: HorizontalPodAutoscaler + - documentIndex: *hpaDoc + isAPIVersion: + of: autoscaling/v2 + - documentIndex: *hpaDoc + isSubset: + path: spec + content: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: test-release-name-common-test-workload-name-2 + minReplicas: 1 + maxReplicas: 3 + + - it: should create hpa with options + set: + workload: *workload + hpa: + hpa-name: + enabled: true + minReplicas: 50 + maxReplicas: 100 + behavior: + scaleUp: + stabilizationWindowSeconds: 50 + policies: + - type: Pods + value: 10 + periodSeconds: 60 + scaleDown: + selectPolicy: Min + stabilizationWindowSeconds: 150 + policies: + - type: Percent + value: 20 + periodSeconds: 60 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: cpu + target: + type: AverageValue + averageValue: 1000m + value: 1000m + - type: ContainerResource + containerResource: + name: memory + container: container-name + target: + type: Utilization + averageUtilization: 80 + - type: Pods + pods: + metric: + name: custom-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m + - type: Object + object: + metric: + name: object-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m + describedObject: + apiVersion: apps/v1 + kind: Deployment + name: some-name + - type: External + external: + metric: + name: external-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m + asserts: + - documentIndex: &hpaDoc 1 + isKind: + of: HorizontalPodAutoscaler + - documentIndex: *hpaDoc + isAPIVersion: + of: autoscaling/v2 + - documentIndex: *hpaDoc + equal: + path: spec.minReplicas + value: 50 + - documentIndex: *hpaDoc + equal: + path: spec.maxReplicas + value: 100 + - documentIndex: *hpaDoc + equal: + path: spec.behavior + value: + scaleUp: + selectPolicy: Max + stabilizationWindowSeconds: 50 + policies: + - type: Pods + value: 10 + periodSeconds: 60 + scaleDown: + selectPolicy: Min + stabilizationWindowSeconds: 150 + policies: + - type: Percent + value: 20 + periodSeconds: 60 + - documentIndex: *hpaDoc + isSubset: + path: spec + content: + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + - type: Resource + resource: + name: cpu + target: + type: AverageValue + averageValue: 1000m + value: 1000m + - type: ContainerResource + containerResource: + name: memory + container: container-name + target: + type: Utilization + averageUtilization: 80 + - type: Pods + pods: + metric: + name: custom-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m + - type: Object + object: + metric: + name: object-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m + describedObject: + apiVersion: apps/v1 + kind: Deployment + name: some-name + - type: External + external: + metric: + name: external-metric + selector: + matchLabels: + custom-label: custom-value + target: + type: AverageValue + averageValue: 1000m diff --git a/charts/library/common-test/tests/hpa/validation_test.yaml b/charts/library/common-test/tests/hpa/validation_test.yaml new file mode 100644 index 00000000000..0dd1a9a846a --- /dev/null +++ b/charts/library/common-test/tests/hpa/validation_test.yaml @@ -0,0 +1,1179 @@ +suite: hpa validation test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should fail with name longer than 63 characters + set: + workload: &workload + workload-name: + enabled: true + primary: true + type: Deployment + podSpec: + containers: + container-name: + enabled: true + primary: true + probes: + readiness: + enabled: false + liveness: + enabled: false + startup: + enabled: false + hpa: + other-horizontal-pod-autoscale-name-super-long-name-that-is-longer-than-63-characters: + enabled: true + asserts: + - failedTemplate: + errorMessage: Name [other-horizontal-pod-autoscale-name-super-long-name-that-is-longer-than-63-characters] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with name starting with underscore + set: + workload: *workload + hpa: + _other-hpa-name: + enabled: true + asserts: + - failedTemplate: + errorMessage: Name [_other-hpa-name] is not valid. Must start and end with an alphanumeric lowercase character. It can contain '-'. And must be at most 63 characters. + + - it: should fail with labels not a dict + set: + workload: *workload + hpa: + hpa-name: + enabled: true + labels: "not a dict" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [labels] to be a dictionary, but got [string] + + - it: should fail with annotations not a dict + set: + workload: *workload + hpa: + hpa-name: + enabled: true + annotations: "not a dict" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [annotations] to be a dictionary, but got [string] + - it: should fail with minReplicas not an integer + set: + workload: *workload + hpa: + hpa-name: + enabled: true + minReplicas: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.minReplicas] to be an integer, but got [string] + - it: should fail with maxReplicas not an integer + set: + workload: *workload + hpa: + hpa-name: + enabled: true + maxReplicas: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.maxReplicas] to be an integer, but got [string] + - it: should fail with minReplicas greater than maxReplicas + set: + workload: *workload + hpa: + hpa-name: + enabled: true + minReplicas: 5 + maxReplicas: 3 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.minReplicas] to be less than [hpa.hpa-name.maxReplicas], but got [5] and [3] + + - it: should fail with invalid selectPolicy (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + selectPolicy: "not a valid policy" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.selectPolicy] to be one of [Max, Min, Disabled], but got [not a valid policy] + + - it: should fail with invalid stabilityWindowSeconds (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + stabilizationWindowSeconds: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.stabilizationWindowSeconds] to be an integer, but got [string] + + - it: should fail with invalid policies (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: "not a valid policies" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies] to be a list, but got [string] + + - it: should fail with invalid policy type (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.type] to be one of [Pods, Percent], but got [not a valid type] + + - it: should fail with invalid policy value (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: Percent + value: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.value] to be an integer, but got [string] + + - it: should fail with invalid policy periodSeconds (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: Percent + value: 50 + periodSeconds: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.periodSeconds] to be an integer, but got [string] + + - it: should fail with number less than 1 in periodSeconds (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: Percent + periodSeconds: 0 + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.periodSeconds] to be between 1 and 1800, but got [0] + + - it: should fail with number greater than 1800 in periodSeconds (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: Percent + periodSeconds: 1801 + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.periodSeconds] to be between 1 and 1800, but got [1801] + + - it: should fail with invalid number in value (scaleUp) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleUp: + policies: + - type: Percent + periodSeconds: 60 + value: -1 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleUp.policies.0.value] to be greater than 0, but got [-1] + + - it: should fail with invalid selectPolicy (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + selectPolicy: "not a valid policy" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.selectPolicy] to be one of [Max, Min, Disabled], but got [not a valid policy] + + - it: should fail with invalid stabilityWindowSeconds (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + stabilizationWindowSeconds: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.stabilizationWindowSeconds] to be an integer, but got [string] + + - it: should fail with invalid policies (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: "not a valid policies" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies] to be a list, but got [string] + + - it: should fail with invalid policy type (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.type] to be one of [Pods, Percent], but got [not a valid type] + + - it: should fail with invalid policy value (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: Percent + value: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.value] to be an integer, but got [string] + + - it: should fail with invalid policy periodSeconds (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: Percent + value: 50 + periodSeconds: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.periodSeconds] to be an integer, but got [string] + + - it: should fail with number less than 1 in periodSeconds (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: Percent + periodSeconds: 0 + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.periodSeconds] to be between 1 and 1800, but got [0] + + - it: should fail with number greater than 1800 in periodSeconds (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: Percent + periodSeconds: 1801 + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.periodSeconds] to be between 1 and 1800, but got [1801] + + - it: should fail with invalid number in value (scaleDown) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + behavior: + scaleDown: + policies: + - type: Percent + periodSeconds: 60 + value: -1 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.behavior.scaleDown.policies.0.value] to be greater than 0, but got [-1] + + - it: should fail with invalid metrics + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: "not a valid metrics" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics] to be a list, but got [string] + + - it: should fail with invalid metric type + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.type] to be one of [Resource, Pods, Object, External, ContainerResource], but got [not a valid type] + + # Resource metric + - it: should fail with invalid resource (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: "not a valid resource" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource] to be a map, but got [string] + + - it: should fail with invalid resource name (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: invalid-name-123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.name] to be one of [cpu, memory], but got [invalid-name-123] + + - it: should fail with invalid resource target (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: cpu + target: "not a valid target" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.target] to be a map, but got [string] + + - it: should fail with invalid resource target type (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: cpu + target: + type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.target.type] to be one of [AverageValue, Utilization], but got [not a valid type] + + - it: should fail with invalid resource target averageValue (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: cpu + target: + type: AverageValue + averageValue: {} + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.target.averageValue] to be an integer or string, but got [map] + + - it: should fail with invalid resource target utilization (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.target.averageUtilization] to be an integer, but got [string] + + - it: should fail with invalid resource target value (resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 100 + value: ["not an integer"] + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.resource.target.value] to be an integer or string, but got [slice] + + # ContainerResource metric + - it: should fail with invalid containerResource (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: "not a valid containerResource" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource] to be a map, but got [string] + + - it: should fail with invalid containerResource name (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: invalid-name-123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.name] to be one of [cpu, memory], but got [invalid-name-123] + + - it: should fail with invalid containerResource target (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: container-name + target: "not a valid target" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.target] to be a map, but got [string] + + - it: should fail with invalid containerResource target type (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: container-name + target: + type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.target.type] to be one of [AverageValue, Utilization], but got [not a valid type] + + - it: should fail with invalid containerResource target averageValue (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: container-name + target: + type: AverageValue + averageValue: {} + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.target.averageValue] to be an integer or string, but got [map] + + - it: should fail with invalid containerResource target utilization (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: container-name + target: + type: Utilization + averageUtilization: "not an integer" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.target.averageUtilization] to be an integer, but got [string] + + - it: should fail with invalid containerResource target value (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: container-name + target: + type: Utilization + averageUtilization: 100 + value: ["not an integer"] + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.target.value] to be an integer or string, but got [slice] + + - it: should fail with invalid containerResource container (container resource metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: ContainerResource + containerResource: + name: cpu + container: "not a valid container" + target: + type: Utilization + averageUtilization: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.containerResource.container] to be one of [container-name], but got [not a valid container] + + - it: should fail with invalid pods metric + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: "not a valid pods" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods] to be a map, but got [string] + + - it: should fail with invalid pods metric metric (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: "not a valid metric" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.metric] to be a map, but got [string] + + - it: should fail with invalid pods metric name (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: 123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.metric.name] to be a string, but got [float64] + + - it: should fail with invalid pods target (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: pod-metric + target: "not a valid target" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.target] to be a map, but got [string] + + - it: should fail with invalid pods target averageValue (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: pod-metric + target: + type: AverageValue + averageValue: {} + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.target.averageValue] to be an integer or string, but got [map] + + - it: should fail with invalid pods metric selector (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: pod-metric + selector: "not a valid selector" + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.metric.selector] to be a map, but got [string] + + - it: should fail with invalid pods metric selector matchLabels (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: pod-metric + selector: + matchLabels: "not a valid matchLabels" + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.metric.selector.matchLabels] to be a map, but got [string] + + - it: should fail with invalid pods metric selector matchLabels value (pods metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Pods + pods: + metric: + name: pod-metric + selector: + matchLabels: + key: {} + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.pods.metric.selector.matchLabels.key] to be a string, but got [map] + + - it: should fail with invalid object metric + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: "not a valid object" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object] to be a map, but got [string] + + - it: should fail with invalid object metric (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: "not a valid metric" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.metric] to be a map, but got [string] + - it: should fail with invalid object metric name (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: 123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.metric.name] to be a string, but got [float64] + - it: should fail with invalid object target (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: "not a valid target" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.target] to be a map, but got [string] + - it: should fail with invalid object target type (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.target.type] to be one of [AverageValue, Value], but got [not a valid type] + - it: should fail with invalid object target averageValue (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: AverageValue + averageValue: {} + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.target.averageValue] to be an integer or string, but got [map] + - it: should fail with invalid object target value (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: Value + value: ["not an integer"] + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.target.value] to be an integer or string, but got [slice] + - it: should fail with invalid object metric selector (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + selector: "not a valid selector" + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.metric.selector] to be a map, but got [string] + - it: should fail with invalid object metric selector matchLabels (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + selector: + matchLabels: "not a valid matchLabels" + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.metric.selector.matchLabels] to be a map, but got [string] + - it: should fail with invalid object metric selector matchLabels value (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + selector: + matchLabels: + key: {} + target: + type: AverageValue + averageValue: 100m + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.metric.selector.matchLabels.key] to be a string, but got [map] + - it: should fail with invalid object describedObject (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: AverageValue + averageValue: 100m + describedObject: "not a valid describedObject" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.describedObject] to be a map, but got [string] + + - it: should fail with invalid object describedObject name (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: AverageValue + averageValue: 100m + describedObject: + apiVersion: apps/v1 + kind: Deployment + name: 123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.describedObject.name] to be a string, but got [float64] + + - it: should fail with invalid object describedObject kind (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: AverageValue + averageValue: 100m + describedObject: + name: some-name + kind: 1 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.describedObject.kind] to be a string, but got [float64] + + - it: should fail with invalid object describedObject apiVersion (object metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: Object + object: + metric: + name: object-metric + target: + type: AverageValue + averageValue: 100m + describedObject: + name: some-name + kind: Deployment + apiVersion: 1 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.object.describedObject.apiVersion] to be a string, but got [float64] + + - it: should fail with invalid external metric + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: "not a valid external" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external] to be a map, but got [string] + + - it: should fail with invalid external metric metric (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: "not a valid metric" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.metric] to be a map, but got [string] + + - it: should fail with invalid external metric name (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: 123 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.metric.name] to be a string, but got [float64] + + - it: should fail with invalid external target (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + target: "not a valid target" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.target] to be a map, but got [string] + + - it: should fail with invalid external target type (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + target: + type: "not a valid type" + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.target.type] to be one of [AverageValue, Value], but got [not a valid type] + + - it: should fail with invalid external target averageValue (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + target: + type: AverageValue + averageValue: {} + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.target.averageValue] to be an integer or string, but got [map] + - it: should fail with invalid external target value (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + target: + type: Value + value: ["not an integer"] + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.target.value] to be an integer or string, but got [slice] + + - it: should fail with invalid external metric selector (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + selector: "not a valid selector" + target: + type: Value + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.metric.selector] to be a map, but got [string] + + - it: should fail with invalid external metric selector matchLabels (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + selector: + matchLabels: "not a valid matchLabels" + target: + type: Value + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.metric.selector.matchLabels] to be a map, but got [string] + + - it: should fail with invalid external metric selector matchLabels value (external metric) + set: + workload: *workload + hpa: + hpa-name: + enabled: true + metrics: + - type: External + external: + metric: + name: external-metric + selector: + matchLabels: + key: {} + target: + type: Value + value: 100 + asserts: + - failedTemplate: + errorMessage: Horizontal Pod Autoscaler - Expected [hpa.hpa-name.metrics.0.external.metric.selector.matchLabels.key] to be a string, but got [map] diff --git a/charts/library/common/Chart.yaml b/charts/library/common/Chart.yaml index f37d6a825fb..14493523227 100644 --- a/charts/library/common/Chart.yaml +++ b/charts/library/common/Chart.yaml @@ -48,5 +48,4 @@ sources: - https://hub.docker.com/_/ - https://hub.docker.com/r/mikefarah/yq type: library -version: 28.8.1 - +version: 28.9.0 diff --git a/charts/library/common/templates/class/_horizontalPodAutoscaler.tpl b/charts/library/common/templates/class/_horizontalPodAutoscaler.tpl index 92abf3ff4e1..4f6b6354234 100644 --- a/charts/library/common/templates/class/_horizontalPodAutoscaler.tpl +++ b/charts/library/common/templates/class/_horizontalPodAutoscaler.tpl @@ -3,56 +3,190 @@ This template serves as a blueprint for horizontal pod autoscaler objects that a using the common library. */}} {{- define "tc.v1.common.class.hpa" -}} - {{- $targetName := include "tc.v1.common.lib.chart.names.fullname" . -}} - {{- $fullName := include "tc.v1.common.lib.chart.names.fullname" . -}} - {{- $hpaName := $fullName -}} - {{- $values := .Values.hpa -}} - - {{- if hasKey . "ObjectValues" -}} - {{- with .ObjectValues.hpa -}} - {{- $values = . -}} - {{- end -}} - {{- end -}} - {{- $hpaLabels := $values.labels -}} - {{- $hpaAnnotations := $values.annotations -}} - - {{- if and (hasKey $values "nameOverride") $values.nameOverride -}} - {{- $hpaName = printf "%v-%v" $hpaName $values.nameOverride -}} - {{- end }} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} --- -apiVersion: {{ include "tc.v1.common.capabilities.hpa.apiVersion" $ }} +apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ $hpaName }} - namespace: {{ $.Values.namespace | default $.Values.global.namespace | default $.Release.Namespace }} - {{- $labels := (mustMerge ($hpaLabels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $ | fromYaml)) -}} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $ "labels" $labels) | trim) }} + name: {{ $objectData.name }} + namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "VPA") }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} labels: {{- . | nindent 4 }} {{- end -}} - {{- $annotations := (mustMerge ($hpaAnnotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $ | fromYaml)) -}} - {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $ "annotations" $annotations) | trim) }} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} annotations: {{- . | nindent 4 }} - {{- end -}} + {{- end }} spec: scaleTargetRef: apiVersion: apps/v1 - kind: {{ $values.targetKind | default ( include "tc.v1.common.names.controllerType" . ) }} - name: {{ $values.target | default $targetName }} - minReplicas: {{ $values.minReplicas | default 1 }} - maxReplicas: {{ $values.maxReplicas | default 3 }} + kind: {{ $objectData.workload.type }} + name: {{ $objectData.name }} + minReplicas: {{ $objectData.minReplicas }} + maxReplicas: {{ $objectData.maxReplicas }} + {{- if $objectData.metrics }} metrics: - {{- if $values.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ $values.targetCPUUtilizationPercentage }} + {{- include "tc.v1.common.class.hpa.metrics" (dict "objectData" $objectData "rootCtx" $rootCtx) | nindent 4 }} + {{- end -}} + {{- if $objectData.behavior }} + behavior: + {{- if $objectData.behavior.scaleUp }} + scaleUp: + {{- include "tc.v1.common.class.hpa.behavior" (dict "objectData" $objectData "rootCtx" $rootCtx "mode" "up") | nindent 4 }} {{- end -}} - {{- if $values.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ $values.targetMemoryUtilizationPercentage }} + {{- if $objectData.behavior.scaleDown }} + scaleDown: + {{- include "tc.v1.common.class.hpa.behavior" (dict "objectData" $objectData "rootCtx" $rootCtx "mode" "down") | nindent 4 }} {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.behavior" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $mode := .mode -}} + + {{- $key := ternary "scaleUp" "scaleDown" (eq $mode "up") -}} + {{- $behavior := get $objectData.behavior $key -}} + + {{- $defaultStabilizationWindowSeconds := ternary 0 300 (eq $mode "up") }} + selectPolicy: {{ $behavior.selectPolicy | default "Max" }} + stabilizationWindowSeconds: {{ $behavior.stabilizationWindowSeconds | default $defaultStabilizationWindowSeconds }} + {{- if $behavior.policies }} + policies: + {{- range $idx, $policy := $behavior.policies }} + - type: {{ $policy.type }} + value: {{ $policy.value }} + periodSeconds: {{ $policy.periodSeconds }} + {{- end }} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- range $idx, $metric := $objectData.metrics }} + {{- if eq $metric.type "Resource" }} + {{- include "tc.v1.common.class.hpa.metrics.resource" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric) | nindent 6 }} + {{- else if eq $metric.type "ContainerResource" }} + {{- include "tc.v1.common.class.hpa.metrics.containerResource" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric) | nindent 6 }} + {{- else if eq $metric.type "Pods" }} + {{- include "tc.v1.common.class.hpa.metrics.pods" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric) | nindent 6 }} + {{- else if eq $metric.type "Object" }} + {{- include "tc.v1.common.class.hpa.metrics.object" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric) | nindent 6 }} + {{- else if eq $metric.type "External" }} + {{- include "tc.v1.common.class.hpa.metrics.external" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric) | nindent 6 }} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics.resource" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx }} + - type: Resource + resource: + name: {{ .metric.resource.name }} + target: + type: {{ .metric.resource.target.type }} + {{- if eq .metric.resource.target.type "AverageValue" }} + averageValue: {{ .metric.resource.target.averageValue | quote }} + {{- else if eq .metric.resource.target.type "Utilization" }} + averageUtilization: {{ .metric.resource.target.averageUtilization }} + {{- end -}} + {{- with .metric.resource.target.value }} + value: {{ . | quote }} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics.containerResource" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx }} + - type: ContainerResource + containerResource: + name: {{ .metric.containerResource.name }} + container: {{ .metric.containerResource.container}} + target: + type: {{ .metric.containerResource.target.type }} + {{- if eq .metric.containerResource.target.type "AverageValue" }} + averageValue: {{ .metric.containerResource.target.averageValue | quote }} + {{- else if eq .metric.containerResource.target.type "Utilization" }} + averageUtilization: {{ .metric.containerResource.target.averageUtilization }} + {{- end -}} + {{- with .metric.containerResource.target.value }} + value: {{ . | quote }} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics.pods" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx }} + - type: Pods + pods: + target: + type: AverageValue + averageValue: {{ .metric.pods.target.averageValue | quote }} + metric: + name: {{ .metric.pods.metric.name }} + {{- if .metric.pods.metric.selector }} + selector: + matchLabels: + {{- range $key, $value := .metric.pods.metric.selector.matchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics.object" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx }} + - type: Object + object: + target: + type: {{ .metric.object.target.type }} + {{- if eq .metric.object.target.type "Value" }} + value: {{ .metric.object.target.value | quote }} + {{- else if eq .metric.object.target.type "AverageValue" }} + averageValue: {{ .metric.object.target.averageValue | quote }} + {{- end }} + describedObject: + apiVersion: {{ .metric.object.describedObject.apiVersion }} + kind: {{ .metric.object.describedObject.kind }} + name: {{ .metric.object.describedObject.name }} + metric: + name: {{ .metric.object.metric.name }} + {{- if .metric.object.metric.selector }} + selector: + matchLabels: + {{- range $key, $value := .metric.object.metric.selector.matchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.class.hpa.metrics.external" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx }} + - type: External + external: + metric: + name: {{ .metric.external.metric.name }} + {{- if .metric.external.metric.selector }} + selector: + matchLabels: + {{- range $key, $value := .metric.external.metric.selector.matchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end -}} + {{- end }} + target: + type: {{ .metric.external.target.type }} + {{- if eq .metric.external.target.type "Value" }} + value: {{ .metric.external.target.value | quote }} + {{- else if eq .metric.external.target.type "AverageValue" }} + averageValue: {{ .metric.external.target.averageValue | quote }} + {{- end -}} {{- end -}} diff --git a/charts/library/common/templates/lib/_tc_capabilities.tpl b/charts/library/common/templates/lib/_tc_capabilities.tpl index c3e6170670f..df9c5d54856 100644 --- a/charts/library/common/templates/lib/_tc_capabilities.tpl +++ b/charts/library/common/templates/lib/_tc_capabilities.tpl @@ -17,8 +17,3 @@ {{- define "tc.v1.common.capabilities.networkpolicy.apiVersion" -}} {{- print "networking.k8s.io/v1" -}} {{- end -}} - -{{/* Return the appropriate apiVersion for HorizontalPodAutoscaler aka HPA*/}} -{{- define "tc.v1.common.capabilities.hpa.apiVersion" -}} - {{- print "autoscaling/v2" -}} -{{- end -}} diff --git a/charts/library/common/templates/lib/hpa/_validation.tpl b/charts/library/common/templates/lib/hpa/_validation.tpl new file mode 100644 index 00000000000..a8195df3a6e --- /dev/null +++ b/charts/library/common/templates/lib/hpa/_validation.tpl @@ -0,0 +1,360 @@ +{{- define "tc.v1.common.lib.hpa.validation" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- $minReplicas := 1 -}} + {{- with $objectData.minReplicas -}} + {{- if not (mustHas (kindOf $objectData.minReplicas) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.minReplicas] to be an integer, but got [%s]" $objectData.hpaName (kindOf $objectData.minReplicas)) -}} + {{- end -}} + {{- $minReplicas = $objectData.minReplicas -}} + {{- end -}} + + {{- $maxReplicas := 3 -}} + {{- with $objectData.maxReplicas -}} + {{- if not (mustHas (kindOf $objectData.maxReplicas) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.maxReplicas] to be an integer, but got [%s]" $objectData.hpaName (kindOf $objectData.maxReplicas)) -}} + {{- end -}} + {{- $maxReplicas = $objectData.maxReplicas -}} + {{- end -}} + + {{- $_ := set $objectData "minReplicas" $minReplicas -}} + {{- $_ := set $objectData "maxReplicas" $maxReplicas -}} + + {{- if lt $maxReplicas $minReplicas -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.minReplicas] to be less than [hpa.%s.maxReplicas], but got [%d] and [%d]" $objectData.hpaName $objectData.hpaName ($minReplicas | int) ($maxReplicas | int)) -}} + {{- end -}} + + {{- if $objectData.behavior -}} + {{- if $objectData.behavior.scaleUp -}} + {{- include "tc.v1.common.lib.hpa.validation.behavior" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $objectData.behavior.scaleUp "key" "scaleUp") -}} + {{- end -}} + {{- if $objectData.behavior.scaleDown -}} + {{- include "tc.v1.common.lib.hpa.validation.behavior" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $objectData.behavior.scaleDown "key" "scaleDown") -}} + {{- end -}} + {{- end -}} + + {{- if $objectData.metrics -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $objectData.metrics) -}} + {{- end -}} + +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.behavior" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $data := .data -}} + {{- $key := .key -}} + + {{- if $data.selectPolicy -}} + {{- $validSelectPolicies := list "Max" "Min" "Disabled" -}} + {{- if not (mustHas $data.selectPolicy $validSelectPolicies) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.selectPolicy] to be one of [%s], but got [%s]" $objectData.hpaName $key (join ", " $validSelectPolicies) $data.selectPolicy) -}} + {{- end -}} + {{- end -}} + + {{- if $data.stabilizationWindowSeconds -}} + {{- if not (mustHas (kindOf $data.stabilizationWindowSeconds) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.stabilizationWindowSeconds] to be an integer, but got [%s]" $objectData.hpaName $key (kindOf $data.stabilizationWindowSeconds)) -}} + {{- end -}} + {{- end -}} + + {{- if $data.policies -}} + {{- if not (kindIs "slice" $data.policies) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies] to be a list, but got [%s]" $objectData.hpaName $key (kindOf $data.policies)) -}} + {{- end -}} + + {{- $validPolicies := list "Pods" "Percent" -}} + {{- range $idx, $policy := $data.policies -}} + {{- if not (kindIs "map" $policy) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d] to be a map, but got [%s]" $objectData.hpaName $key $idx (kindOf $policy)) -}} + {{- end -}} + + {{- if not (mustHas $policy.type $validPolicies) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d.type] to be one of [%s], but got [%s]" $objectData.hpaName $key $idx (join ", " $validPolicies) $policy.type) -}} + {{- end -}} + + {{- if not (mustHas (kindOf $policy.value) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d.value] to be an integer, but got [%s]" $objectData.hpaName $key $idx (kindOf $policy.value)) -}} + {{- end -}} + + {{- if not (mustHas (kindOf $policy.periodSeconds) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d.periodSeconds] to be an integer, but got [%s]" $objectData.hpaName $key $idx (kindOf $policy.periodSeconds)) -}} + {{- end -}} + + {{- if le ($policy.value | int) 0 -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d.value] to be greater than 0, but got [%v]" $objectData.hpaName $key $idx $policy.value) -}} + {{- end -}} + + {{- if or (lt ($policy.periodSeconds | int) 1) (gt ($policy.periodSeconds | int) 1800) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.behavior.%s.policies.%d.periodSeconds] to be between 1 and 1800, but got [%v]" $objectData.hpaName $key $idx $policy.periodSeconds) -}} + {{- end -}} + + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + + {{- if not (kindIs "slice" $objectData.metrics) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics] to be a list, but got [%s]" $objectData.hpaName (kindOf $objectData.metrics)) -}} + {{- end -}} + + {{- range $idx, $metric := $objectData.metrics -}} + {{- if not (kindIs "map" $metric) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric)) -}} + {{- end -}} + + {{- if not (mustHas $metric.type (list "Resource" "Pods" "Object" "External" "ContainerResource")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.type] to be one of [Resource, Pods, Object, External, ContainerResource], but got [%s]" $objectData.hpaName $idx $metric.type) -}} + {{- end -}} + + {{- if eq $metric.type "Resource" -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics.resource" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric "idx" $idx) -}} + {{- else if eq $metric.type "Pods" -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics.pods" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric "idx" $idx) -}} + {{- else if eq $metric.type "Object" -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics.object" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric "idx" $idx) -}} + {{- else if eq $metric.type "External" -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics.external" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric "idx" $idx) -}} + {{- else if eq $metric.type "ContainerResource" -}} + {{- include "tc.v1.common.lib.hpa.validation.metrics.containerResource" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric "idx" $idx) -}} + {{- end -}} + + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.resource" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $metric := .metric -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $metric.resource) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.resource] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.resource)) -}} + {{- end -}} + + {{- $validNames := list "cpu" "memory" -}} + {{- if not (mustHas $metric.resource.name $validNames) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.resource.name] to be one of [%s], but got [%s]" $objectData.hpaName $idx (join ", " $validNames) $metric.resource.name) -}} + {{- end -}} + + {{- include "tc.v1.common.lib.hpa.validation.metrics.metric.target" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric.resource "key" "resource" "idx" $idx) -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.containerResource" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $metric := .metric -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $metric.containerResource) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.containerResource] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.containerResource)) -}} + {{- end -}} + + {{- $validNames := list "cpu" "memory" -}} + {{- if not (mustHas $metric.containerResource.name $validNames) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.containerResource.name] to be one of [%s], but got [%s]" $objectData.hpaName $idx (join ", " $validNames) $metric.containerResource.name) -}} + {{- end -}} + + {{- if not (mustHas $metric.containerResource.container $objectData.containerNames) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.containerResource.container] to be one of [%s], but got [%s]" $objectData.hpaName $idx (join ", " $objectData.containerNames) $metric.containerResource.container) -}} + {{- end -}} + + {{- include "tc.v1.common.lib.hpa.validation.metrics.metric.target" (dict "objectData" $objectData "rootCtx" $rootCtx "metric" $metric.containerResource "key" "containerResource" "idx" $idx) -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.pods" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $metric := .metric -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $metric.pods) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.pods] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.pods)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.pods.metric) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.pods.metric] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.pods.metric)) -}} + {{- end -}} + + {{- if or (not $metric.pods.metric.name) (not (kindIs "string" $metric.pods.metric.name)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.pods.metric.name] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.pods.metric.name)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.pods.target) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.pods.target] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.pods.target)) -}} + {{- end -}} + + {{- if not (mustHas (kindOf $metric.pods.target.averageValue) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.pods.target.averageValue] to be an integer or string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.pods.target.averageValue)) -}} + {{- end -}} + + {{- if $metric.pods.metric.selector -}} + {{- include "tc.v1.common.lib.hpa.validation.metric.selector" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $metric.pods "key" "pods" "idx" $idx) -}} + {{- end -}} + +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metric.selector" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $data := .data -}} + {{- $key := .key -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $data.metric.selector) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.metric.selector] to be a map, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.metric.selector)) -}} + {{- end -}} + + {{- if not (kindIs "map" $data.metric.selector.matchLabels) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.metric.selector.matchLabels] to be a map, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.metric.selector.matchLabels)) -}} + {{- end -}} + + {{- range $k, $v := $data.metric.selector.matchLabels -}} + {{- if not (kindIs "string" $k) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.metric.selector.matchLabels] to have string keys, but got [%s]" $objectData.hpaName $idx $key (kindOf $k)) -}} + {{- end -}} + + {{- if not (kindIs "string" $v) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.metric.selector.matchLabels.%s] to be a string, but got [%s]" $objectData.hpaName $idx $key $k (kindOf $v)) -}} + {{- end -}} + {{- end -}} + +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.object" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $metric := .metric -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $metric.object) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.object.metric) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.metric] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.metric)) -}} + {{- end -}} + + {{- if or (not $metric.object.metric.name) (not (kindIs "string" $metric.object.metric.name)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.metric.name] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.metric.name)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.object.target) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.target] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.target)) -}} + {{- end -}} + + {{- $validTypes := list "AverageValue" "Value" -}} + {{- if not (mustHas $metric.object.target.type $validTypes) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.target.type] to be one of [%s], but got [%s]" $objectData.hpaName $idx (join ", " $validTypes) $metric.object.target.type) -}} + {{- end -}} + + {{- if eq $metric.object.target.type "AverageValue" -}} + {{- if not (mustHas (kindOf $metric.object.target.averageValue) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.target.averageValue] to be an integer or string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.target.averageValue)) -}} + {{- end -}} + {{- else if eq $metric.object.target.type "Value" -}} + {{- if not (mustHas (kindOf $metric.object.target.value) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.target.value] to be an integer or string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.target.value)) -}} + {{- end -}} + {{- end -}} + + {{- if $metric.object.metric.selector -}} + {{- include "tc.v1.common.lib.hpa.validation.metric.selector" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $metric.object "key" "object" "idx" $idx) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.object.describedObject) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.describedObject] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.describedObject)) -}} + {{- end -}} + + {{- if or (not $metric.object.describedObject.name) (not (kindIs "string" $metric.object.describedObject.name)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.describedObject.name] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.describedObject.name)) -}} + {{- end -}} + + {{- if or (not $metric.object.describedObject.kind) (not (kindIs "string" $metric.object.describedObject.kind)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.describedObject.kind] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.describedObject.kind)) -}} + {{- end -}} + + {{- if or (not $metric.object.describedObject.apiVersion) (not (kindIs "string" $metric.object.describedObject.apiVersion)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.object.describedObject.apiVersion] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.object.describedObject.apiVersion)) -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.external" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $metric := .metric -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $metric.external) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.external.metric) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.metric] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external.metric)) -}} + {{- end -}} + + {{- if or (not $metric.external.metric.name) (not (kindIs "string" $metric.external.metric.name)) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.metric.name] to be a string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external.metric.name)) -}} + {{- end -}} + + {{- if not (kindIs "map" $metric.external.target) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.target] to be a map, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external.target)) -}} + {{- end -}} + + {{- $validTypes := list "AverageValue" "Value" -}} + {{- if not (mustHas $metric.external.target.type $validTypes) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.target.type] to be one of [%s], but got [%s]" $objectData.hpaName $idx (join ", " $validTypes) $metric.external.target.type) -}} + {{- end -}} + + {{- if eq $metric.external.target.type "AverageValue" -}} + {{- if not (mustHas (kindOf $metric.external.target.averageValue) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.target.averageValue] to be an integer or string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external.target.averageValue)) -}} + {{- end -}} + {{- else if eq $metric.external.target.type "Value" -}} + {{- if not (mustHas (kindOf $metric.external.target.value) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.external.target.value] to be an integer or string, but got [%s]" $objectData.hpaName $idx (kindOf $metric.external.target.value)) -}} + {{- end -}} + {{- end -}} + + {{- if $metric.external.metric.selector -}} + {{- include "tc.v1.common.lib.hpa.validation.metric.selector" (dict "objectData" $objectData "rootCtx" $rootCtx "data" $metric.external "key" "external" "idx" $idx) -}} + {{- end -}} +{{- end -}} + +{{- define "tc.v1.common.lib.hpa.validation.metrics.metric.target" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} + {{- $data := .metric -}} + {{- $key := .key -}} + {{- $idx := .idx -}} + + {{- if not (kindIs "map" $data.target) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.target] to be a map, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.target)) -}} + {{- end -}} + + {{- $validTargetTypes := list "AverageValue" "Utilization" -}} + {{- if not (mustHas $data.target.type $validTargetTypes) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.target.type] to be one of [%s], but got [%s]" $objectData.hpaName $idx $key (join ", " $validTargetTypes) $data.target.type) -}} + {{- end -}} + + {{- if eq $data.target.type "AverageValue" -}} + {{- if not (mustHas (kindOf $data.target.averageValue) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.target.averageValue] to be an integer or string, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.target.averageValue)) -}} + {{- end -}} + {{- else if eq $data.target.type "Utilization" -}} + {{- if not (mustHas (kindOf $data.target.averageUtilization) (list "int" "int64" "float64")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.target.averageUtilization] to be an integer, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.target.averageUtilization)) -}} + {{- end -}} + {{- end -}} + + {{- if $data.target.value -}} + {{- if not (mustHas (kindOf $data.target.value) (list "int" "int64" "float64" "string")) -}} + {{- fail (printf "Horizontal Pod Autoscaler - Expected [hpa.%s.metrics.%d.%s.target.value] to be an integer or string, but got [%s]" $objectData.hpaName $idx $key (kindOf $data.target.value)) -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/charts/library/common/templates/spawner/_horizontalPodAutoscaler.tpl b/charts/library/common/templates/spawner/_horizontalPodAutoscaler.tpl index 1a14dbc0a47..a88ebaa279a 100644 --- a/charts/library/common/templates/spawner/_horizontalPodAutoscaler.tpl +++ b/charts/library/common/templates/spawner/_horizontalPodAutoscaler.tpl @@ -1,19 +1,69 @@ -{{/* -Renders the configMap objects required by the chart. +{{/* horizontal Pod Autoscaler Spawner */}} +{{/* Call this template: +{{ include "tc.v1.common.spawner.hpa" $ -}} */}} -{{- define "tc.v1.common.spawner.hpa" -}} - {{/* Generate named configMaps as required */}} - {{- range $name, $hpa := .Values.horizontalPodAutoscaler -}} - {{- if $hpa.enabled -}} - {{- $hpaValues := $hpa -}} - {{/* set the default nameOverride to the hpa name */}} - {{- if not $hpaValues.nameOverride -}} - {{- $_ := set $hpaValues "nameOverride" $name -}} +{{- define "tc.v1.common.spawner.hpa" -}} + {{- $fullname := include "tc.v1.common.lib.chart.names.fullname" $ -}} + {{- range $name, $hpa := .Values.hpa -}} + {{- $enabledHPA := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $hpa + "name" $name "caller" "Horizontal Pod Autoscaler" + "key" "hpa")) -}} + + {{- if ne $enabledHPA "true" -}}{{- continue -}}{{- end -}} + + {{- $objectData := (mustDeepCopy $hpa) -}} + {{- $_ := set $objectData "hpaName" $name -}} + {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $name) -}} + + {{- range $workloadName, $workload := $.Values.workload -}} + + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $workload + "name" $name "caller" "hpa" + "key" "workload")) -}} + + {{- if ne $enabled "true" -}}{{- continue -}}{{- end -}} + {{- $containerNames := list -}} + {{- range $cName, $c := $workload.podSpec.containers -}} + {{- $enabledContainer := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $c + "name" $cName "caller" "Vertical Pod Autoscaler" + "key" "workload.podSpec.containers")) -}} + {{- if ne $enabledContainer "true" -}}{{- continue -}}{{- end -}} + {{- $containerNames = mustAppend $containerNames $cName -}} + {{- end -}} + {{- $_ := set $objectData "containerNames" $containerNames -}} + {{- include "tc.v1.common.lib.hpa.validation" (dict "objectData" $objectData "rootCtx" $) -}} + + {{/* Create a copy of the workload */}} + {{- $_ := set $objectData "workload" (mustDeepCopy $workload) -}} + + {{/* Generate the name of the hpa */}} + {{- $objectName := $fullname -}} + {{- if not $objectData.workload.primary -}} + {{- $objectName = printf "%s-%s" $fullname $workloadName -}} + {{- end -}} + + {{/* Perform validations */}} + {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "tc.v1.common.lib.metadata.validation" (dict "objectData" $objectData "caller" "Horizontal Pod Autoscaler") -}} + + {{/* Set the name of the workload */}} + {{- $_ := set $objectData "name" $objectName -}} + + {{/* Short name is the one that defined on the chart, used on selectors */}} + {{- $_ := set $objectData "shortName" $workloadName -}} + + {{- if or (not $objectData.targetSelector) (mustHas $workloadName $objectData.targetSelector) -}} + {{/* Call class to create the object */}} + {{- $types := (list "Deployment" "StatefulSet" "DaemonSet") -}} + {{- if (mustHas $objectData.workload.type $types) -}} + {{- include "tc.v1.common.class.hpa" (dict "rootCtx" $ "objectData" $objectData) -}} + {{- end -}} {{- end -}} - {{- $_ := set $ "ObjectValues" (dict "hpa" $hpaValues) -}} - {{- include "tc.v1.common.class.hpa" $ -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/charts/library/common/values.yaml b/charts/library/common/values.yaml index 435dd80e264..bfd6c3a615f 100644 --- a/charts/library/common/values.yaml +++ b/charts/library/common/values.yaml @@ -213,6 +213,44 @@ vpa: memory: 20Gi controlledResources: ["cpu", "memory"] +# -- Horizontal pod autoscaler +hpa: + main: + enabled: false + targetSelector: [] + # minReplicas: 1 + # maxReplicas: 3 + + # metrics: # Optional, list of metric specs + # - type: Resource # Can be Resource, Pods, Object, External, or ContainerResource + # resource: + # name: cpu + # target: + # type: Utilization # Or Value / AverageValue + # averageUtilization: 50 + + # - type: Resource + # resource: + # name: memory + # target: + # type: AverageValue + # averageValue: 500Mi + + # behavior: # Optional: controls scaling behavior + # scaleUp: + # stabilizationWindowSeconds: 0 + # policies: + # - type: Percent + # value: 100 + # periodSeconds: 15 + # scaleDown: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 4 + # periodSeconds: 60 + + # -- (docs/service/README.md) service: main: