Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ The following table lists the configurable parameters and their default values.

| Parameter | Description | Default |
|-----------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `replicaCount` | Number of replicas (omitted from the Deployment when `autoscaling.enabled`) | `1` |
| `autoscaling.enabled` | Render a HorizontalPodAutoscaler for the control-layer deployment | `false` |
| `autoscaling.minReplicas` | HPA minimum replicas | `1` |
| `autoscaling.maxReplicas` | HPA maximum replicas | `3` |
| `autoscaling.metrics` | Raw `autoscaling/v2` metrics list, rendered verbatim | `[]` |
| `autoscaling.behavior` | Raw `autoscaling/v2` behavior block, rendered verbatim | `{}` |
| `image.repository` | Container image repository | `ghcr.io/doublewordai/control-layer` |
| `image.tag` | Container image tag | Chart appVersion |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
Expand Down Expand Up @@ -191,16 +196,19 @@ The fusillade daemon handles background batch processing tasks. By default, it r
| Parameter | Description | Default |
|-----------|-------------|---------|
| `fusillade.enabled` | Deploy fusillade as a separate deployment | `false` |
| `fusillade.replicaCount` | Number of fusillade replicas | `1` |
| `fusillade.replicaCount` | Number of fusillade replicas (omitted from a Deployment whose effective autoscaling is enabled) | `1` |
| `fusillade.autoscaling` | HPA defaults for the fusillade daemon and both split roles (`enabled`, `minReplicas`, `maxReplicas`, `metrics`, `behavior`) | `enabled: false` |
| `fusillade.mode` | Optional daemon mode for the standard fusillade deployment (`both`, `request_only`, `batch_only`); empty uses the application default | `""` |
| `fusillade.split.enabled` | Render separate request-only and batch-only daemon deployments | `false` |
| `fusillade.split.request.enabled` | Render the request-only daemon deployment | `true` |
| `fusillade.split.request.replicaCount` | Number of request daemon replicas | inherits `fusillade.replicaCount` |
| `fusillade.split.request.autoscaling` | HPA override for the request daemon | merged over `fusillade.autoscaling` |
| `fusillade.split.request.resources` | CPU/Memory requests/limits for request daemon pods | inherits `fusillade.resources` |
| `fusillade.split.request.env` | Additional environment variables for request daemon pods | merged after `env` and `fusillade.env` |
| `fusillade.split.request.database` | Database pool overrides for request daemon pods | merged over `fusillade.database` |
| `fusillade.split.batch.enabled` | Render the batch-only daemon deployment | `true` |
| `fusillade.split.batch.replicaCount` | Number of batch daemon replicas | inherits `fusillade.replicaCount` |
| `fusillade.split.batch.autoscaling` | HPA override for the batch daemon | merged over `fusillade.autoscaling` |
| `fusillade.split.batch.resources` | CPU/Memory requests/limits for batch daemon pods | inherits `fusillade.resources` |
| `fusillade.split.batch.env` | Additional environment variables for batch daemon pods | merged after `env` and `fusillade.env` |
| `fusillade.split.batch.database` | Database pool overrides for batch daemon pods | merged over `fusillade.database` |
Expand Down
4 changes: 4 additions & 0 deletions templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ metadata:
labels:
{{- include "control-layer.labels" . | nindent 4 }}
spec:
{{- /* Argo CD reconciles continuously; a rendered replicas field would revert
every HPA scale-up on the next sync, so omit it when the HPA owns the count. */}}
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
{{- if .Values.rollout.enabled }}
strategy:
{{- toYaml .Values.rollout.strategy | nindent 4 }}
Expand Down
5 changes: 5 additions & 0 deletions templates/fusillade/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
{{- $database := mergeOverwrite (deepCopy ($fusillade.database | default dict)) (get $config "database" | default dict) -}}
{{- $podAnnotations := mergeOverwrite (deepCopy ($fusillade.podAnnotations | default dict)) (get $config "podAnnotations" | default dict) -}}
{{- $podLabels := mergeOverwrite (deepCopy ($fusillade.podLabels | default dict)) (get $config "podLabels" | default dict) -}}
{{- $autoscaling := mergeOverwrite (deepCopy ($fusillade.autoscaling | default dict)) (get $config "autoscaling" | default dict) -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "control-layer.fullname" $root }}{{ $suffix }}
labels:
{{- include "control-layer.fusillade.labelsFor" (dict "root" $root "component" $component) | nindent 4 }}
spec:
{{- /* Argo CD reconciles continuously; a rendered replicas field would revert
every HPA scale-up on the next sync, so omit it when the HPA owns the count. */}}
{{- if not $autoscaling.enabled }}
replicas: {{- if hasKey $config "replicaCount" }} {{ get $config "replicaCount" }}{{- else }} {{ $fusillade.replicaCount }}{{- end }}
{{- end }}
selector:
matchLabels:
{{- include "control-layer.fusillade.selectorLabelsFor" (dict "root" $root "component" $component) | nindent 6 }}
Expand Down
58 changes: 58 additions & 0 deletions templates/fusillade/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{{- /*
One HorizontalPodAutoscaler per fusillade workload whose effective autoscaling
is enabled. Iterates the same workloads as fusillade/deployment.yaml (single
mode: -fusillade; split mode: -fusillade-request / -fusillade-batch). Effective
config = fusillade.autoscaling overlaid with the per-role autoscaling block.
*/}}
{{- define "control-layer.fusillade.hpa" -}}
{{- $root := .root -}}
{{- $fusillade := $root.Values.fusillade -}}
{{- $config := .config | default dict -}}
{{- $component := .component -}}
{{- $suffix := .suffix -}}
{{- $autoscaling := mergeOverwrite (deepCopy ($fusillade.autoscaling | default dict)) (get $config "autoscaling" | default dict) -}}
{{- if $autoscaling.enabled }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "control-layer.fullname" $root }}{{ $suffix }}
labels:
{{- include "control-layer.fusillade.labelsFor" (dict "root" $root "component" $component) | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "control-layer.fullname" $root }}{{ $suffix }}
minReplicas: {{ $autoscaling.minReplicas }}
maxReplicas: {{ $autoscaling.maxReplicas }}
{{- with $autoscaling.metrics }}
metrics:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $autoscaling.behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- end -}}

{{- if .Values.fusillade.enabled }}
{{- $split := .Values.fusillade.split | default dict -}}
{{- if $split.enabled }}
{{- $request := $split.request | default dict -}}
{{- $batch := $split.batch | default dict -}}
{{- $requestEnabled := true -}}
{{- $batchEnabled := true -}}
{{- if hasKey $request "enabled" }}{{- $requestEnabled = $request.enabled -}}{{- end -}}
{{- if hasKey $batch "enabled" }}{{- $batchEnabled = $batch.enabled -}}{{- end -}}
{{- if $requestEnabled }}
{{- include "control-layer.fusillade.hpa" (dict "root" . "suffix" "-fusillade-request" "component" "fusillade-request" "config" $request) }}
{{- end }}
{{- if $batchEnabled }}
{{- include "control-layer.fusillade.hpa" (dict "root" . "suffix" "-fusillade-batch" "component" "fusillade-batch" "config" $batch) }}
{{- end }}
{{- else }}
{{- include "control-layer.fusillade.hpa" (dict "root" . "suffix" "-fusillade" "component" "fusillade" "config" dict) }}
{{- end }}
{{- end }}
23 changes: 23 additions & 0 deletions templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "control-layer.fullname" . }}
labels:
{{- include "control-layer.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "control-layer.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
{{- with .Values.autoscaling.metrics }}
metrics:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.autoscaling.behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
16 changes: 16 additions & 0 deletions tests/control_layer_deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,19 @@ tests:
asserts:
- failedTemplate:
errorMessage: rollout.connectionDrainTimeoutSeconds must be 3600 to match the control-layer request timeout

- it: should render replicas by default (no autoscaling)
template: deployment.yaml
asserts:
- equal:
path: spec.replicas
value: 1

- it: should omit replicas when autoscaling is enabled
template: deployment.yaml
set:
autoscaling.enabled: true
asserts:
- isNull:
path: spec.replicas

17 changes: 17 additions & 0 deletions tests/fusillade_deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,20 @@ tests:
content:
name: DWCTL_BACKGROUND_SERVICES__BATCH_DAEMON__MODE
value: "request_only"

- it: should keep replicas in split mode by default
template: fusillade/deployment.yaml
set:
fusillade:
enabled: true
split:
enabled: true
asserts:
- equal:
path: spec.replicas
value: 1
documentIndex: 0
- equal:
path: spec.replicas
value: 1
documentIndex: 1
Loading
Loading