diff --git a/.gitignore b/.gitignore index c0a29a9..90dee08 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,6 @@ mise.local.toml /EVHardware/.gradle /EVHardware/**/build -# Filled-in Kubernetes Secrets (copy of k8s/secrets.example.yaml). Never commit. +# Filled-in Kubernetes Secrets (copy of k8s/secrets/secrets.example.yaml). Never commit. /k8s/secrets.yaml +/k8s/secrets/secrets.yaml diff --git a/SPECS.md b/SPECS.md index 7272a3c..a0e0914 100644 --- a/SPECS.md +++ b/SPECS.md @@ -40,7 +40,7 @@ car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP - [x] Manifests validated offline (including Traefik CRDs). **Not applied.** - [x] Car app 2.4.0 (pairing by QR): unit tests pass; **not yet tried on the car**. - [x] Image `mg4-server:2.1.0` (with pairing) pushed to git.stinnesbeck.com (public; pinned by - digest `sha256:e5f6e2d2…` in `k8s/kustomization.yaml`). + digest `sha256:e5f6e2d2…` in `k8s/server.yaml`). - [ ] Check Grafana's host in `k8s/ingress.yaml` (placeholder `car-grafana.k8s.stinnesbeck.com`). - [ ] First pairing and drive with 2.4.0: telemetry arrives in InfluxDB, Grafana and ABRP. @@ -89,7 +89,8 @@ car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP app 2.4.0 shows the QR code and collects its token. Tested end to end against InfluxDB. - 2026-10-17 — Incident: a commit swept the filled-in `secrets.example.yaml` (InfluxDB admin token, INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in - the now gitignored `k8s/secrets.yaml`. Filled-in secrets live only there from now on. + the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside + `kubectl apply -f k8s/`). Filled-in secrets live only there from now on. - 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb; removed again (1002962) once values showed on the car. Revert 1002962 to bring them back. diff --git a/k8s/README.md b/k8s/README.md index 27b4bd2..f2d2c0b 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -11,19 +11,19 @@ is applied automatically. Apply them yourself. | `grafana.yaml` | Grafana 13.2.3 Deployment (1Gi PVC), Service; datasource and dashboards provisioned | | `grafana/` | The provisioned InfluxDB datasource and the MG4 dashboards (Live, Consumption, Charging) | | `ingress.yaml` | Traefik `IngressRoute`s for car.k8s.stinnesbeck.com and Grafana, TLS from Traefik's default certificate | -| `secrets.example.yaml` | **Template** for the Secrets; placeholders only, not in `kustomization.yaml` | -| `secrets.yaml` | Your filled-in copy — **gitignored**, never committed | -| `kustomization.yaml` | Everything except the Secrets; pins the server image, generates the Grafana ConfigMaps | +| `secrets/secrets.example.yaml` | **Template** for the Secrets; placeholders only, not in `kustomization.yaml` | +| `secrets/secrets.yaml` | Your filled-in copy — **gitignored**, never committed | +| `kustomization.yaml` | Everything except the Secrets; generates the Grafana ConfigMaps | ## Before the first apply 1. **Check `ingress.yaml`:** Grafana's host (placeholder `car-grafana.k8s.stinnesbeck.com`). Both routes use the `traefik-internal` class, and both hosts must be covered by Traefik's default certificate. -2. **Create the Secrets.** `cp k8s/secrets.example.yaml k8s/secrets.yaml` (gitignored), replace +2. **Create the Secrets.** `cp k8s/secrets/secrets.example.yaml k8s/secrets/secrets.yaml` (gitignored), replace every `CHANGE-ME` there (`openssl rand -hex 32`): `influxdb-auth`, `mg4-server` (ingest token, approval password for pairing, your ABRP API key and user token) and `grafana-admin`. Keep - `secrets.example.yaml` itself on placeholders only. + the template itself on placeholders only. 3. **Registry access.** The `mg4-server` package on git.stinnesbeck.com is public, so no pull secret is needed. If you make it private, add an `imagePullSecrets` entry to `server.yaml`. @@ -31,10 +31,13 @@ is applied automatically. Apply them yourself. ```sh kubectl apply -f k8s/namespace.yaml -kubectl apply -f k8s/secrets.yaml +kubectl apply -f k8s/secrets/secrets.yaml kubectl apply -k k8s/ ``` +Use **`-k`**, not `-f k8s/`: plain `-f` applies the files without kustomize, so the Grafana +ConfigMaps (datasource and dashboards) are never created and Grafana does not start. + ## Notes - InfluxDB's setup mode runs only on an empty volume. Changing `admin-token` in the Secret later @@ -43,5 +46,5 @@ kubectl apply -k k8s/ for Grafana, read/write for the server) can replace it later. - The dashboards are read-only in Grafana's UI. Change the JSON in `grafana/dashboards/`; the ConfigMap name changes with its content, which rolls Grafana. -- The server image is pinned by digest in `kustomization.yaml`. To upgrade, push a new tag and - put its digest there (`skopeo inspect docker://git.stinnesbeck.com/nils/mg4-server:`). +- The server image is pinned by digest in `server.yaml`. To upgrade, push a new tag and put its + digest there (`skopeo inspect docker://git.stinnesbeck.com/nils/mg4-server:`). diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml index 335dc03..0719533 100644 --- a/k8s/ingress.yaml +++ b/k8s/ingress.yaml @@ -1,5 +1,6 @@ -# Routes on the traefik-internal class, like the rest of *.k8s.stinnesbeck.com. TLS uses -# Traefik's default certificate, so there is no per-host Certificate here. +# Routes on the traefik-internal class, like the rest of *.k8s.stinnesbeck.com. No tls section +# and no per-host Certificate: TLS is configured on Traefik's websecure entrypoint, which serves +# the default certificate. apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: @@ -18,7 +19,6 @@ spec: name: mg4-server namespace: mg4 port: 80 - tls: {} --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute @@ -39,4 +39,3 @@ spec: name: grafana namespace: mg4 port: 80 - tls: {} diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml index bd41dd3..a82bbc6 100644 --- a/k8s/kustomization.yaml +++ b/k8s/kustomization.yaml @@ -1,5 +1,5 @@ -# Render with `kustomize build k8s/` (or `kubectl kustomize k8s/`) and apply it yourself. -# The Secrets are not included: create them from secrets.example.yaml first. +# Apply with `kubectl apply -k k8s/` — not `-f k8s/`, which skips the Grafana ConfigMaps below. +# The Secrets are not included: create them from secrets/secrets.example.yaml first. apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: @@ -8,10 +8,6 @@ resources: - server.yaml - grafana.yaml - ingress.yaml -images: - - name: git.stinnesbeck.com/nils/mg4-server - # 2.1.0, pinned by digest so a re-pushed tag cannot change what runs. - digest: sha256:e5f6e2d2ef9dc2650055a0823a3f86d8903e46918a76e990540482edf819766d generatorOptions: labels: app.kubernetes.io/part-of: mg4 diff --git a/k8s/secrets/secrets.example.yaml b/k8s/secrets/secrets.example.yaml index 6bea4d4..f97d66b 100644 --- a/k8s/secrets/secrets.example.yaml +++ b/k8s/secrets/secrets.example.yaml @@ -1,5 +1,6 @@ -# TEMPLATE ONLY — not part of kustomization.yaml. Copy it to k8s/secrets.yaml (gitignored), -# fill in real values there, and apply that file yourself. Never put real values in here. +# TEMPLATE ONLY — not part of kustomization.yaml. Copy it to k8s/secrets/secrets.yaml +# (gitignored), fill in real values there, and apply that file yourself. Never put real values +# in here. This folder is outside `kubectl apply -f k8s/`, which does not recurse. # # openssl rand -hex 32 for every token and password below apiVersion: v1 diff --git a/k8s/server.yaml b/k8s/server.yaml index 7fc4743..d698e1e 100644 --- a/k8s/server.yaml +++ b/k8s/server.yaml @@ -29,7 +29,8 @@ spec: type: RuntimeDefault containers: - name: server - image: git.stinnesbeck.com/nils/mg4-server:latest # pinned by kustomization.yaml + # 2.1.0, pinned by digest so a re-pushed tag cannot change what runs. + image: git.stinnesbeck.com/nils/mg4-server@sha256:e5f6e2d2ef9dc2650055a0823a3f86d8903e46918a76e990540482edf819766d ports: - name: http containerPort: 8080