Skip to content

Commit 58c8819

Browse files
authored
Updated cpln-advisor template (#535)
1 parent 5d64a58 commit 58c8819

28 files changed

Lines changed: 253 additions & 2272 deletions

‎CHANGELOG.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ High-level, user-facing catalog changes by month: new templates and notable vers
88
## 2026-08
99

1010
- **Every template now deploys into a GVC you already have.** Eleven templates used to create their own; none do. This closes a data-loss path: a chart that creates a GVC and later stops declaring it makes `helm upgrade` prune it — and deleting a GVC takes **every workload, volumeset and identity inside**, in about six seconds, while printing `upgraded successfully`. Each converted template now refuses that upgrade at render time. **If you run any 1.x/2.x release listed below, do not `helm upgrade` onto the new major** — install it as a new release against an existing GVC, move your data across, then remove the old release. Each README carries the migration steps
11-
- **cpln-advisor 2.0.0** — no longer creates a GVC. This one mattered most: 1.0.0 named the GVC it created after the one you installed into, so it **adopted your existing GVC** — and `helm uninstall` would then have deleted it along with every unrelated workload in it. Also fixes placement: on a GVC with more than one location, 1.0.0 would have run a second scheduler firing every scan twice and a second, independent database. Note the bundled Postgres still cannot be pinned to one location, so a multi-location GVC gives it one empty database per extra location — harmless today because the service DNS is location-local, but changing `location` later repoints the app at the empty one
1211
- **mongodb-cluster 2.0.0** — no longer creates a GVC, and **the shipped default could never fully work**. It defaulted to nine members, but MongoDB allows at most seven voters: the eighth and ninth joined nothing, ran a healthy-looking `mongod`, reported ready, and were never in the replica set. The default is now one location × three members and a roster above seven is refused. `backup.mode: physical` is **removed** — its restore could never run, because Percona Backup for MongoDB must execute `mongod` and the agent image does not contain it, so it wrote real-looking snapshots that could never be restored and failed silently doing it. The logical restore is verified end to end, and the documented steps used to point at an address that cannot resolve from your machine
1312
- **grafana-multi-location 2.0.0** — no longer creates a GVC, and picks up the converted `postgres-multi-location` and `redis-multi-location`. Losing the location named in `alerting.location` still stops alert evaluation while dashboards look perfectly healthy — set `alerting.highAvailability.enabled: true` if that matters, and note a fresh install now fails loudly rather than half-working
1413
- **airflow 2.0.0** — no longer creates a GVC. **KEDA autoscaling is now opt-in and off by default**, because it is a GVC-level setting a chart cannot turn on for you: enabling it against a GVC without KEDA is accepted silently and leaves the workers dead at zero replicas with no error anywhere. Turn KEDA on for your GVC first, then set `keda.enabled: true`. Also fixes a placement bug where a multi-location GVC would have run one scheduler and one broker **per location**

‎briefings/cpln-advisor.md‎

Lines changed: 35 additions & 45 deletions
Large diffs are not rendered by default.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

‎cpln-advisor/versions/1.0.0/Chart.yaml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ appVersion: "1.0.0"
88

99
annotations:
1010
created: "2026-08-17"
11-
lastModified: "2026-08-17"
11+
lastModified: "2026-09-01"
1212
category: "observability"
13-
createsGvc: true
13+
createsGvc: false
1414

1515
dependencies:
1616
- name: cpln-common
1717
version: 1.0.0
1818
repository: "oci://ghcr.io/controlplane-com/templates"
19+
1920
# The advisor's database. Bundled so a single install brings up everything:
2021
# the chart arrives with a readiness probe, a private firewall, its own
2122
# identity and policy, and scheduled backups behind a switch.
2223
#
23-
# 3.4.1, NOT 3.3.0: from 3.4.0 the template reads its username/password/database
24+
# Version 3.4.1 of the postgres template reads its username/password/database
2425
# from a prerequisite secret instead of taking them inline, which is what lets
2526
# this chart bundle a database and still keep every credential out of values.
2627
- name: postgres

‎cpln-advisor/versions/1.0.0/README.md‎

Lines changed: 39 additions & 75 deletions
Large diffs are not rendered by default.

‎cpln-advisor/versions/1.0.0/templates/_helpers.tpl‎

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,8 @@ Advisor Policy Name
5050
{{- end }}
5151

5252
{{/*
53-
Bundled Postgres workload name. The `postgres` subchart names it
54-
`{{ .Release.Name }}-postgres`, and as a subchart that Release.Name is OURS — so
55-
this must track the subchart's own helper. A rename there breaks this silently.
56-
*/}}
57-
{{- define "cpln-advisor.postgres.name" -}}
58-
{{- printf "%s-postgres" .Release.Name }}
59-
{{- end }}
60-
61-
{{/*
62-
Internal address of Redis. Plain redis:// is correct — the sidecar adds mTLS.
53+
Internal address of Redis, in the GVC this release is installed into. Plain
54+
redis:// is correct — the sidecar adds mTLS.
6355
*/}}
6456
{{- define "cpln-advisor.redis.url" -}}
6557
{{- printf "redis://%s.%s.cpln.local:6379" (include "cpln-advisor.redis.name" .) .Values.global.cpln.gvc }}
@@ -72,15 +64,6 @@ Internal address of the API, on the CONTAINER port (8000), not 443.
7264
{{- printf "http://%s.%s.cpln.local:8000" (include "cpln-advisor.api.name" .) .Values.global.cpln.gvc }}
7365
{{- end }}
7466

75-
{{/*
76-
Every credential the advisor reads, by key, out of the ONE prerequisite dictionary
77-
secret. Nothing sensitive passes through values, so nothing sensitive lands in the
78-
Helm release. Key names match the app's own environment variable names.
79-
*/}}
80-
{{- define "cpln-advisor.secretRef" -}}
81-
{{- printf "cpln://secret/%s.%s" .name .key }}
82-
{{- end }}
83-
8467
{{/* Resource ratio guard */}}
8568

8669
{{/*
@@ -170,18 +153,10 @@ Call with (dict "who" "api" "r" .Values.api.resources).
170153

171154
{{- define "cpln-advisor.validate" -}}
172155
{{- if not .Values.global.cpln.gvc -}}
173-
{{- fail "cpln-advisor: global.cpln.gvc is required — the name of the GVC this chart creates, e.g. 'advisor'. It lives under `global` so cpln-common tags every resource with it, and so a subchart would inherit it." -}}
174-
{{- end -}}
175-
{{- if not .Values.gvc.locations -}}
176-
{{- fail "cpln-advisor: gvc.locations must contain exactly one location, e.g. `locations:` / ` - name: aws-us-east-1`. Run `cpln location get` to list the ones available to your org." -}}
177-
{{- end -}}
178-
{{- if ne (len .Values.gvc.locations) 1 -}}
179-
{{- fail (printf "cpln-advisor: gvc.locations must contain EXACTLY ONE location, got %d. A workload runs in every location of its GVC and minScale/maxScale are per-location, so a second location silently doubles the API, worker and scheduler — a second scheduler would fire every cron twice. The bundled Postgres is a single stateful workload on a read-write-once volume, so a second location would also give it a second, independent database rather than a replica." (len .Values.gvc.locations)) -}}
180-
{{- end -}}
181-
{{- range .Values.gvc.locations -}}
182-
{{- if not .name -}}
183-
{{- fail "cpln-advisor: every entry in gvc.locations needs a `name`, e.g. `- name: aws-us-east-1`" -}}
156+
{{- fail "cpln-advisor: global.cpln.gvc is empty. This chart installs into an EXISTING GVC and does not create one — the install tooling supplies this from the GVC you select (`cpln helm install ... --gvc YOUR_GVC`), so an empty value usually means no GVC was selected. It is read here to build internal service addresses and to tag every resource, and the bundled postgres subchart inherits it." -}}
184157
{{- end -}}
158+
{{- if .Values.gvc -}}
159+
{{- fail "cpln-advisor: the `gvc` values key is no longer used and has been ignored. This chart installs into an EXISTING GVC selected at install time (`--gvc YOUR_GVC`) rather than creating one, so `gvc.locations` no longer controls anything — it is a prerequisite instead: the GVC you install into must already exist and must have exactly ONE location. Remove the `gvc` block from your values file. See README > GVC." -}}
185160
{{- end -}}
186161
{{- if not .Values.auth.secretName -}}
187162
{{- fail "cpln-advisor: auth.secretName is required — the name of a `dictionary` secret that MUST EXIST BEFORE INSTALL, holding the keys ADVISOR_API_TOKEN, ADVISOR_SECRET_KEY, ADVISOR_SESSION_SECRET, ADVISOR_USERNAME, ADVISOR_PASSWORD and DATABASE_URL. This chart creates no secret and accepts no credential as a value. See README Prerequisites." -}}
@@ -200,21 +175,10 @@ Call with (dict "who" "api" "r" .Values.api.resources).
200175

201176
{{/* Labeling */}}
202177

203-
{{/*
204-
Create chart name and version as used by the chart label.
205-
*/}}
206-
{{- define "cpln-advisor.chart" -}}
207-
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
208-
{{- end }}
209-
210178
{{/*
211179
Common labels
212180
*/}}
213181
{{- define "cpln-advisor.tags" -}}
214182
{{- include "cpln-common.tags" . }}
215183
{{- end }}
216184

217-
{{- define "cpln-advisor.selectorLabels" -}}
218-
app.cpln.io/name: {{ .Release.Name }}
219-
app.cpln.io/instance: {{ .Release.Name }}
220-
{{- end }}

‎cpln-advisor/versions/1.0.0/templates/gvc.yaml‎

Lines changed: 0 additions & 22 deletions
This file was deleted.

‎cpln-advisor/versions/1.0.0/templates/identity.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
---
1+
{{- include "cpln-advisor.validate" . -}}
22
kind: identity
33
gvc: {{ .Values.global.cpln.gvc }}
44
name: {{ include "cpln-advisor.identity.name" . }}

‎cpln-advisor/versions/1.0.0/templates/policy.yaml‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
---
1+
{{- include "cpln-advisor.validate" . -}}
22
kind: policy
33
name: {{ include "cpln-advisor.policy.name" . }}
44
description: CPLN Advisor policy
55
tags: {{- include "cpln-advisor.tags" . | nindent 4 }}
66
bindings:
7-
# `reveal`, NOT `view` — view exposes metadata only, and the cpln://secret/…
8-
# reference then resolves to an empty string. The containers start happily and
9-
# behave as though nothing were configured, so this failure is silent.
107
- permissions:
118
- reveal
129
principalLinks:

‎cpln-advisor/versions/1.0.0/templates/workload-api.yaml‎

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
---
1+
{{- include "cpln-advisor.validate" . -}}
22
kind: workload
33
name: {{ include "cpln-advisor.api.name" . }}
44
description: CPLN Advisor API
@@ -25,29 +25,12 @@ spec:
2525
- -c
2626
- alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000
2727
env:
28-
# Nothing here may be named CPLN_* — that prefix is RESERVED and a
29-
# workload setting one is rejected. The platform injects CPLN_ORG,
30-
# CPLN_GVC, CPLN_TOKEN and friends itself, which is where the app reads
31-
# its org from. The Control Plane API token cannot be injected for the
32-
# same reason: set it once on the Configuration page, where it is stored
33-
# encrypted in the database.
3428
- name: LOG_LEVEL
3529
value: {{ .Values.logLevel | quote }}
3630
- name: REDIS_URL
3731
value: {{ include "cpln-advisor.redis.url" . | quote }}
38-
# Postgres, from the bundled `postgres` subchart. The URL carries the
39-
# password, so it is a key in YOUR credentials secret rather than a value
40-
# here. Its username/password/database must match the database's own
41-
# prerequisite secret — nothing cross-checks the two.
4232
- name: DATABASE_URL
4333
value: cpln://secret/{{ .Values.auth.secretName }}.DATABASE_URL
44-
# The app derives the dashboard's public URL — the Slack "View in Advisor"
45-
# links and the CORS origin — from Control Plane's built-in env vars, as
46-
# `https://{ADVISOR_WEB_WORKLOAD}-{gvc alias}.cpln.app`. It defaults to
47-
# the literal "web", so it MUST be told this chart's actual dashboard
48-
# workload name or every derived link points at a workload that does not
49-
# exist. It is NOT built from CPLN_GLOBAL_ENDPOINT, which is the host of
50-
# whichever workload reads it — internal here, and not where a browser goes.
5134
- name: ADVISOR_WEB_WORKLOAD
5235
value: {{ include "cpln-advisor.web.name" . | quote }}
5336
{{- if .Values.appUrl }}
@@ -81,30 +64,36 @@ spec:
8164
httpGet:
8265
path: /health # the only unauthenticated route, and it leaks nothing
8366
port: 8000
67+
scheme: HTTP
8468
initialDelaySeconds: 5
8569
periodSeconds: 10
8670
failureThreshold: 3
71+
successThreshold: 1
72+
timeoutSeconds: 2
8773
livenessProbe:
8874
httpGet:
8975
path: /health
9076
port: 8000
77+
scheme: HTTP
9178
initialDelaySeconds: 20
9279
periodSeconds: 30
9380
failureThreshold: 3
81+
successThreshold: 1
82+
timeoutSeconds: 2
9483
defaultOptions:
9584
capacityAI: true
9685
timeoutSeconds: 120 # a scan's Control Plane and LLM calls can take a while
9786
autoscaling:
87+
maxConcurrency: 0
9888
metric: disabled
9989
minScale: 1
10090
# ONE replica, because two would race on the same startup migration. That
10191
# is now the only reason — Postgres handles concurrent writers fine — so
10292
# raising this is a question of moving `alembic upgrade head` out of the
10393
# container start command, not of the database.
10494
maxScale: 1
105-
# Non-root. `filesystemGroupId` is gone with the shared volume it existed for.
106-
# (Never 1337: that is the mesh proxy's UID, and a container running as it
107-
# bypasses the Envoy redirect.)
95+
scaleToZeroDelay: 300
96+
target: 95
10897
securityOptions:
10998
runAsUser: 10001
11099
firewallConfig:

0 commit comments

Comments
 (0)