Files
cozystack/packages/system/postgres-operator/templates/webhook-ready-hook.yaml
Aleksei Sviridkin fc057c0393 fix(postgres-operator): set resources on webhook-ready wait container
Address review feedback from gemini-code-assist on
packages/system/postgres-operator/templates/webhook-ready-hook.yaml:115.

The wait container had no resource requests or limits, so schedulers
treated it as BestEffort and downstream quota enforcement had no signal.
Set small requests (10m CPU, 32Mi memory) and conservative limits
(100m CPU, 64Mi memory) matching the actual footprint of the kubectl
polling loop.

Add a matching unittest assertion so the values stay in sync if anyone
touches the template.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-24 13:32:33 +03:00

149 lines
6.1 KiB
YAML

{{- /*
Post-install gate: block the HelmRelease from reporting Ready until the
cnpg admission webhook actually serves through the cluster Service. Helm
--wait on the controller pod passes once its readinessProbe passes, but
EndpointSlice propagation and kube-proxy/cilium data-plane programming
can lag by a second or two — long enough for any HelmRelease that
depends on postgres-operator (e.g. cozy-keycloak, tenant Postgres apps)
to fire its own install and have kube-apiserver hit the mcluster.cnpg.io
mutating webhook with "dial tcp <svc-ip>:443: connect: connection refused".
The Job uses the apiserver service proxy, which exercises the same
endpoint-resolution and apiserver-initiated pod dial that the admission
webhook path uses. Once /readyz answers through the proxy the data-plane
race is resolved. It does not verify the webhook's TLS CA bundle, so
this gate is scoped to reachability regressions, not cert rotation.
The service name and port name are hardcoded literals. Upstream cnpg
pins the service name in charts/cloudnative-pg/values.yaml with a
comment "DO NOT CHANGE THE SERVICE NAME as it is currently used to
generate the certificate and can not be configured". The port name is
fixed in charts/cloudnative-pg/templates/service.yaml (ports[0].name:
webhook-server). If a future `make update` ever changes either literal
upstream, the sync-check helm-unittest test
(tests/webhook-ready-hook_test.yaml) renders the subchart Service and
fails if the literal drifts — forcing this template to be updated in
the same change.
*/}}
{{- $_ := required "webhookReady.image.repository must be set to the container image providing kubectl for the post-install readiness Job" .Values.webhookReady.image.repository -}}
{{- $_ := required "webhookReady.image.tag must be set for the post-install readiness Job" .Values.webhookReady.image.tag -}}
{{- /* $svcName and $portName are hardcoded literals; see header comment. */ -}}
{{- $svcName := "cnpg-webhook-service" -}}
{{- /* $portName is the service port NAME, not number — matches ports[0].name in the vendored subchart's Service. */ -}}
{{- $portName := "webhook-server" -}}
{{- $resourceName := printf "https:%s:%s" $svcName $portName -}}
{{- $maxAttempts := .Values.webhookReady.maxAttempts | default 60 -}}
{{- $sleepSeconds := .Values.webhookReady.sleepSeconds | default 2 -}}
{{- /* Derive activeDeadlineSeconds from retries + 60s slack so a values override that raises maxAttempts doesn't get silently cut. */ -}}
{{- $deadline := add (mul (int $maxAttempts) (int $sleepSeconds)) 60 -}}
{{- $image := printf "%s:%s" .Values.webhookReady.image.repository .Values.webhookReady.image.tag -}}
{{- if .Values.webhookReady.image.digest }}
{{- $image = printf "%s@%s" $image .Values.webhookReady.image.digest -}}
{{- end }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Release.Name }}-webhook-ready
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-webhook-ready
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
rules:
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: [{{ $resourceName | quote }}]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ .Release.Name }}-webhook-ready
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ .Release.Name }}-webhook-ready
subjects:
- kind: ServiceAccount
name: {{ .Release.Name }}-webhook-ready
namespace: {{ .Release.Namespace }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-webhook-ready
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "10"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
backoffLimit: {{ .Values.webhookReady.backoffLimit | default 2 }}
activeDeadlineSeconds: {{ $deadline }}
template:
spec:
restartPolicy: Never
serviceAccountName: {{ .Release.Name }}-webhook-ready
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: wait
image: {{ $image }}
imagePullPolicy: {{ if .Values.webhookReady.image.digest }}IfNotPresent{{ else }}Always{{ end }}
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
command:
- sh
- -c
- |
set -e
ns={{ .Release.Namespace }}
proxy="/api/v1/namespaces/${ns}/services/{{ $resourceName }}/proxy/readyz"
max_attempts={{ $maxAttempts }}
sleep_seconds={{ $sleepSeconds }}
i=0
last_err=""
until last_err=$(kubectl get --raw "$proxy" 2>&1 >/dev/null); do
i=$((i + 1))
if [ $i -gt $max_attempts ]; then
echo "timeout: cnpg webhook did not respond through the apiserver proxy after ${max_attempts} attempts (${sleep_seconds}s each)"
echo "last error: ${last_err}"
exit 1
fi
if [ $((i % 10)) -eq 1 ]; then
echo "attempt $i/${max_attempts}: ${last_err}"
fi
sleep "$sleep_seconds"
done
echo "cnpg webhook is ready"