From 02f64ba5ffbb1544fa951849df3eae9012f796ce Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Mon, 24 Aug 2026 12:01:11 -0700 Subject: [PATCH 1/2] feat: allow external redis --- Chart.yaml | 2 +- README.md | 47 ++++++++++++++++++++++++++------- templates/_helpers.tpl | 10 +++++++ templates/configmap.yaml | 2 -- templates/deployment.yaml | 2 ++ templates/networkpolicy.yaml | 8 ++++++ templates/redis-deployment.yaml | 9 ++++++- templates/redis-pdb.yaml | 2 ++ templates/redis-service.yaml | 2 ++ test/test.sh | 16 +++++++++++ test/values-redis-external.yaml | 9 +++++++ test/values-redis-image.yaml | 10 +++++++ values.yaml | 13 ++++++++- 13 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 test/values-redis-external.yaml create mode 100644 test/values-redis-image.yaml diff --git a/Chart.yaml b/Chart.yaml index d3a3884..e2c00e0 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: pgdog-control description: PgDog Control type: application -version: 0.2.14 +version: 0.2.15 appVersion: "29a6513b" diff --git a/README.md b/README.md index 3de293d..acd202f 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The three somewhat complex steps are: ## Chart summary -This chart installs two deployments: PgDog control plane and Redis. +This chart installs the PgDog control plane and, by default, a Redis instance. The PgDog deployment contains the following components: @@ -50,14 +50,33 @@ The PgDog deployment contains the following components: | Service account, Cluster role, Cluster role bindings | Service account with RBAC to access select Kube APIs. See [RBAC](#rbac) for more details. | | NetworkPolicy | Optional; restricts ingress/egress traffic. See [NetworkPolicy](#networkpolicy) for more details. | -In addition to installing the PgDog control plane, this chart will deploy a Redis deployment (with one replica). The control plane uses Redis for storing -metrics. The Redis deployment has the following components: +By default, the chart also deploys a single-replica Redis instance. The control plane uses Redis for storing metrics. Set `redis.enabled: false` and provide `redis.url` to use an external Redis instead. The chart-managed Redis has the following components: | Components | Description | |-|-| | Deployment | Redis deployment with one replica. | | Service | Redis service pointing to the deployment, with selector labels configured automatically. | +```yaml +redis: + enabled: true + url: "" # defaults to redis://-redis..svc.cluster.local:6379 + image: + repository: redis + tag: "7-alpine" + pullPolicy: IfNotPresent + pullSecrets: [] +``` + +| Option | Description | +|-|-| +| `redis.enabled` | Deploy the chart-managed Redis resources (bool, default `true`). | +| `redis.url` | Redis connection string injected into the control container as `REDIS_URL`. When empty, defaults to the chart-managed Redis Service (string, default `""`). | +| `redis.image.repository` | Redis image repository (string, default `redis`). | +| `redis.image.tag` | Redis image tag (string, default `7-alpine`). | +| `redis.image.pullPolicy` | Redis image pull policy (string, default `IfNotPresent`). | +| `redis.image.pullSecrets` | Image pull secrets attached to the Redis pod (list, default `[]`). | + ### Ingress The PgDog control plane has a web dashboard. It can be accessed through the Ingress or HTTPRoute the chart creates. The chart supports 4 presets (called modes): @@ -296,11 +315,11 @@ If your cluster manages RBAC out-of-band (a platform team's controller, GitOps, ## NetworkPolicy -When `networkPolicy.enabled` is `true`, the chart renders a `NetworkPolicy` for the control pod and one for Redis, restricting traffic to what the control plane actually needs: +When `networkPolicy.enabled` is `true`, the chart renders a `NetworkPolicy` for the control pod and, when `redis.enabled` is true, one for Redis, restricting traffic to what the control plane actually needs: - Ingress on `control.port` from the `ingress-nginx` namespace only. -- Egress to Redis, to `kube-system` for DNS, and to the public internet on 5432 (Postgres) and 443 (HTTPS, e.g. the AWS/CloudWatch/RDS APIs), excluding RFC1918 private ranges. -- Redis accepts ingress only from the control pod and allows no egress. +- Egress to Redis on port 6379, to `kube-system` for DNS, and to the public internet on 5432 (Postgres) and 443 (HTTPS, e.g. the AWS/CloudWatch/RDS APIs), excluding RFC1918 private ranges. When chart-managed Redis is enabled, the Redis rule is restricted to its pods; otherwise it permits egress to any destination on port 6379 so the external Redis can be reached. +- Chart-managed Redis accepts ingress only from the control pod and allows no egress. In clusters that deny pod-to-pod traffic by default, the built-in ingress-nginx rule alone often isn't enough — for example, PgDog pods calling the control plane's API need their own rule. Use `networkPolicy.extraIngress` to add any number of additional ingress rules to the control `NetworkPolicy`: @@ -322,7 +341,7 @@ networkPolicy: | Option | Description | |-|-| -| `networkPolicy.enabled` | Render the control and Redis `NetworkPolicy` resources (bool, default `false`). | +| `networkPolicy.enabled` | Render the control and, when enabled, Redis `NetworkPolicy` resources (bool, default `false`). | | `networkPolicy.extraIngress` | Additional ingress rules appended to the control `NetworkPolicy`, on top of the built-in ingress-nginx rule. Each entry follows the standard `NetworkPolicyIngressRule` schema (`from`/`ports`) and is passed through verbatim (list, default `[]`). | ## AWS access (EKS / IRSA) @@ -712,21 +731,29 @@ control: ### Redis persistence -`control.config.redis` controls how the in-memory store is snapshotted to Redis between process restarts. The chart already provisions an in-cluster Redis (`-redis`) and the control plane points at it by default, so most installs leave this section alone. +`control.config.redis` controls how often the in-memory store is snapshotted to Redis between process restarts. The chart provisions an in-cluster Redis (`-redis`) by default and injects its connection string as `REDIS_URL`. ```yaml control: config: redis: - url: redis://my-redis.cache:6379 save_interval_secs: 60 ``` | Option | Description | |-|-| -| `url` | Redis connection string. Leave empty to use the in-cluster Redis the chart installs; set it only to point at an external Redis (string, optional). | | `save_interval_secs` | How often the background task snapshots the store to Redis (int, default `60`). | +To use an external Redis, disable all chart-managed Redis resources and set its URL: + +```yaml +redis: + enabled: false + url: redis://my-redis.cache:6379 +``` + +For backwards compatibility, `control.config.redis.url` is still accepted, but `redis.url` is preferred. + ## Examples ```sh diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 4f59a08..ac02274 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -51,6 +51,16 @@ the same cluster don't collide. {{- printf "%s-redis" .Release.Name | trunc 63 | trimSuffix "-" }} {{- end }} +{{/* +Redis URL used by the control plane. redis.url is the public chart setting; +control.config.redis.url remains supported for backwards compatibility. +*/}} +{{- define "pgdog-control.redis.url" -}} +{{- $config := .Values.control.config | default dict -}} +{{- $redisConfig := $config.redis | default dict -}} +{{- .Values.redis.url | default $redisConfig.url | default (printf "redis://%s.%s.svc.cluster.local:6379" (include "pgdog-control.redis.fullname" .) .Release.Namespace) -}} +{{- end }} + {{/* ServiceAccount name for the control component. Falls back to the control fullname when not explicitly set in values. diff --git a/templates/configmap.yaml b/templates/configmap.yaml index f1edb25..00584de 100644 --- a/templates/configmap.yaml +++ b/templates/configmap.yaml @@ -229,10 +229,8 @@ data: {{- end }} {{- $redis := $config.redis | default dict }} - {{- $redisUrl := $redis.url | default (printf "redis://%s.%s.svc.cluster.local:6379" (include "pgdog-control.redis.fullname" .) .Release.Namespace) }} [redis] - url = {{ $redisUrl | quote }} {{- with $redis.save_interval_secs }} save_interval_secs = {{ . }} {{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 7719694..fb91acd 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -80,6 +80,8 @@ spec: env: - name: CONTROL_CONFIG value: /etc/pgdog-control/control.toml + - name: REDIS_URL + value: {{ include "pgdog-control.redis.url" . | quote }} - name: HOME value: /var/lib/pgdog-control - name: XDG_CACHE_HOME diff --git a/templates/networkpolicy.yaml b/templates/networkpolicy.yaml index 9487ea1..bdd7cbb 100644 --- a/templates/networkpolicy.yaml +++ b/templates/networkpolicy.yaml @@ -24,6 +24,7 @@ spec: {{- toYaml . | nindent 2 }} {{- end }} egress: + {{- if .Values.redis.enabled }} - to: - podSelector: matchLabels: @@ -31,6 +32,11 @@ spec: ports: - protocol: TCP port: 6379 + {{- else }} + - ports: + - protocol: TCP + port: 6379 + {{- end }} - to: - ipBlock: cidr: 0.0.0.0/0 @@ -60,6 +66,7 @@ spec: port: 53 - protocol: TCP port: 53 +{{- if .Values.redis.enabled }} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy @@ -84,3 +91,4 @@ spec: port: 6379 egress: [] {{- end }} +{{- end }} diff --git a/templates/redis-deployment.yaml b/templates/redis-deployment.yaml index 03ce85f..c6a0a75 100644 --- a/templates/redis-deployment.yaml +++ b/templates/redis-deployment.yaml @@ -1,3 +1,4 @@ +{{- if .Values.redis.enabled }} apiVersion: apps/v1 kind: Deployment metadata: @@ -17,13 +18,18 @@ spec: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" spec: automountServiceAccountToken: false + {{- with .Values.redis.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.redis.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} containers: - name: redis - image: redis:7-alpine + image: "{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}" + imagePullPolicy: {{ .Values.redis.image.pullPolicy }} {{- with .Values.redis.containerSecurityContext }} securityContext: {{- toYaml . | nindent 10 }} @@ -38,3 +44,4 @@ spec: limits: memory: {{ .Values.redis.resources.limits.memory | quote }} cpu: {{ .Values.redis.resources.limits.cpu | quote }} +{{- end }} diff --git a/templates/redis-pdb.yaml b/templates/redis-pdb.yaml index 8d15b69..e886136 100644 --- a/templates/redis-pdb.yaml +++ b/templates/redis-pdb.yaml @@ -1,3 +1,4 @@ +{{- if .Values.redis.enabled }} apiVersion: policy/v1 kind: PodDisruptionBudget metadata: @@ -9,3 +10,4 @@ spec: selector: matchLabels: {{- include "pgdog-control.redis.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/templates/redis-service.yaml b/templates/redis-service.yaml index 00390c2..36bec57 100644 --- a/templates/redis-service.yaml +++ b/templates/redis-service.yaml @@ -1,3 +1,4 @@ +{{- if .Values.redis.enabled }} apiVersion: v1 kind: Service metadata: @@ -13,3 +14,4 @@ spec: name: redis selector: {{- include "pgdog-control.redis.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/test/test.sh b/test/test.sh index 9bef6a6..291d5e1 100755 --- a/test/test.sh +++ b/test/test.sh @@ -14,5 +14,21 @@ for values_file in "$TEST_DIR"/values-*.yaml; do helm template test-release "$CHART_DIR" -f "$values_file" > /dev/null done +echo "" +echo "==> Verifying external Redis rendering..." +external_render=$(helm template test-release "$CHART_DIR" -f "$TEST_DIR/values-redis-external.yaml") +if grep -q 'app.kubernetes.io/component: redis' <<< "$external_render"; then + echo "chart-managed Redis resources rendered while redis.enabled=false" >&2 + exit 1 +fi +grep -A1 -- '- name: REDIS_URL' <<< "$external_render" | grep -q 'redis://external-redis.example.com:6379' + +echo "" +echo "==> Verifying configurable Redis image..." +image_render=$(helm template test-release "$CHART_DIR" -f "$TEST_DIR/values-redis-image.yaml") +grep -q 'image: "registry.example.com/platform/redis:8-alpine"' <<< "$image_render" +grep -q 'imagePullPolicy: Always' <<< "$image_render" +grep -q 'name: registry-credentials' <<< "$image_render" + echo "" echo "==> All chart tests passed!" diff --git a/test/values-redis-external.yaml b/test/values-redis-external.yaml new file mode 100644 index 0000000..e38c39f --- /dev/null +++ b/test/values-redis-external.yaml @@ -0,0 +1,9 @@ +ingress: + enabled: false + +networkPolicy: + enabled: true + +redis: + enabled: false + url: redis://external-redis.example.com:6379 diff --git a/test/values-redis-image.yaml b/test/values-redis-image.yaml new file mode 100644 index 0000000..89d8cbe --- /dev/null +++ b/test/values-redis-image.yaml @@ -0,0 +1,10 @@ +ingress: + enabled: false + +redis: + image: + repository: registry.example.com/platform/redis + tag: "8-alpine" + pullPolicy: Always + pullSecrets: + - name: registry-credentials diff --git a/values.yaml b/values.yaml index 55ffffe..a6a1c48 100644 --- a/values.yaml +++ b/values.yaml @@ -161,10 +161,21 @@ control: # bot_token: "" # optional; defaults to SLACK_BOT_TOKEN env when absent # channel: "" # optional; defaults to SLACK_CHANNEL env when absent redis: {} - # url: "" # optional; defaults to in-cluster redis + # url: "" # deprecated; use redis.url instead # save_interval_secs: 60 redis: + # Deploy the chart-managed Redis instance. Disable this when using an + # external Redis and set redis.url to its connection string. + enabled: true + # Connection string injected into the control deployment as REDIS_URL. + # When empty, it points to the chart-managed Redis service. + url: "" + image: + repository: redis + tag: "7-alpine" + pullPolicy: IfNotPresent + pullSecrets: [] resources: requests: memory: "128Mi" From a7663cb4b55a84af344138bfdb297fa80a743060 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Mon, 24 Aug 2026 12:04:10 -0700 Subject: [PATCH 2/2] env --- README.md | 4 ++-- templates/configmap.yaml | 1 + templates/deployment.yaml | 2 -- test/test.sh | 6 +++++- values.yaml | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index acd202f..687a58f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ redis: | Option | Description | |-|-| | `redis.enabled` | Deploy the chart-managed Redis resources (bool, default `true`). | -| `redis.url` | Redis connection string injected into the control container as `REDIS_URL`. When empty, defaults to the chart-managed Redis Service (string, default `""`). | +| `redis.url` | Redis connection string written to `[redis].url` in `control.toml`. When empty, defaults to the chart-managed Redis Service (string, default `""`). | | `redis.image.repository` | Redis image repository (string, default `redis`). | | `redis.image.tag` | Redis image tag (string, default `7-alpine`). | | `redis.image.pullPolicy` | Redis image pull policy (string, default `IfNotPresent`). | @@ -731,7 +731,7 @@ control: ### Redis persistence -`control.config.redis` controls how often the in-memory store is snapshotted to Redis between process restarts. The chart provisions an in-cluster Redis (`-redis`) by default and injects its connection string as `REDIS_URL`. +`control.config.redis` controls how often the in-memory store is snapshotted to Redis between process restarts. The chart provisions an in-cluster Redis (`-redis`) by default and writes its connection string to `[redis].url` in `control.toml`. ```yaml control: diff --git a/templates/configmap.yaml b/templates/configmap.yaml index 00584de..f2ed014 100644 --- a/templates/configmap.yaml +++ b/templates/configmap.yaml @@ -231,6 +231,7 @@ data: {{- $redis := $config.redis | default dict }} [redis] + url = {{ include "pgdog-control.redis.url" . | quote }} {{- with $redis.save_interval_secs }} save_interval_secs = {{ . }} {{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml index fb91acd..7719694 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -80,8 +80,6 @@ spec: env: - name: CONTROL_CONFIG value: /etc/pgdog-control/control.toml - - name: REDIS_URL - value: {{ include "pgdog-control.redis.url" . | quote }} - name: HOME value: /var/lib/pgdog-control - name: XDG_CACHE_HOME diff --git a/test/test.sh b/test/test.sh index 291d5e1..f4a4d21 100755 --- a/test/test.sh +++ b/test/test.sh @@ -21,7 +21,11 @@ if grep -q 'app.kubernetes.io/component: redis' <<< "$external_render"; then echo "chart-managed Redis resources rendered while redis.enabled=false" >&2 exit 1 fi -grep -A1 -- '- name: REDIS_URL' <<< "$external_render" | grep -q 'redis://external-redis.example.com:6379' +if grep -q -- '- name: REDIS_URL' <<< "$external_render"; then + echo "REDIS_URL environment variable rendered, but the app only reads control.toml" >&2 + exit 1 +fi +grep -A1 '^ \[redis\]$' <<< "$external_render" | grep -q 'url = "redis://external-redis.example.com:6379"' echo "" echo "==> Verifying configurable Redis image..." diff --git a/values.yaml b/values.yaml index a6a1c48..6cf615f 100644 --- a/values.yaml +++ b/values.yaml @@ -168,8 +168,8 @@ redis: # Deploy the chart-managed Redis instance. Disable this when using an # external Redis and set redis.url to its connection string. enabled: true - # Connection string injected into the control deployment as REDIS_URL. - # When empty, it points to the chart-managed Redis service. + # Connection string written to [redis].url in control.toml. When empty, + # it points to the chart-managed Redis service. url: "" image: repository: redis