The car updates itself again, from this fork's own releases, while the repository stays private: - publish-car (flake) creates the pre-release car-<version> on the forge and attaches the APK and its .sha256. It needs a forge token that may write releases, from ~/.config/nilsmg4/forge-token or $FORGE_TOKEN; it is sent in a header and never printed. - The server relays releases and attachments (/v1/app/releases, /v1/app/assets/...), read with a read-only FORGE_TOKEN from its Secret, GitHub-shaped with download URLs back at itself. Redirects away from the forge are not followed; off (404) without a token. - The app asks its server with its own paired token, allows exactly the server's host, downloads the APK itself instead of through the DownloadManager (so the token stays in the app), verifies SHA-256 and package name, and tries a PackageInstaller session (it may install silently as the system user) before falling back to Downloads. OTA.md is rewritten for this flow; the k8s and server READMEs explain the new token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
81 lines
4.1 KiB
Markdown
81 lines
4.1 KiB
Markdown
# Kubernetes manifests — car.k8s.stinnesbeck.com
|
|
|
|
Plain manifests for the car server, InfluxDB 2 and Grafana. They are only files: nothing here
|
|
is applied automatically. Apply them yourself.
|
|
|
|
| File | What |
|
|
|---|---|
|
|
| `namespace.yaml` | Namespace `mg4` |
|
|
| `influxdb.yaml` | InfluxDB 2.7.12 StatefulSet (10Gi PVC, default storage class), cluster-internal Service, NetworkPolicy admitting only the server and Grafana |
|
|
| `server.yaml` | Go server Deployment (1 replica, non-root, read-only root filesystem) and Service |
|
|
| `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/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/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), `grafana-admin` and
|
|
`grafana-influx` (see below). Keep 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`.
|
|
|
|
## Apply
|
|
|
|
```sh
|
|
kubectl apply -f k8s/namespace.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.
|
|
|
|
## Grafana's InfluxDB token
|
|
|
|
Grafana reads InfluxDB with a token that can only read the `mg4` bucket, from the
|
|
`grafana-influx` Secret. InfluxDB has to create that token, so it is a step of its own:
|
|
|
|
1. With InfluxDB running, create the token. The `influx` CLI in the pod is already signed in
|
|
as admin:
|
|
|
|
```sh
|
|
kubectl -n mg4 exec influxdb-0 -- sh -c \
|
|
'influx auth create --org mg4 --description grafana-read-only --json \
|
|
--read-bucket "$(influx bucket find --name mg4 --hide-headers | cut -f1)"'
|
|
```
|
|
|
|
2. Copy the `"token"` value from the output into `grafana-influx` → `token` in
|
|
`k8s/secrets/secrets.yaml`, apply that file, and restart Grafana:
|
|
`kubectl -n mg4 rollout restart deployment/grafana`.
|
|
|
|
Until the Secret exists, Grafana does not start (the container waits for the missing Secret).
|
|
On a fresh install, apply everything once without Grafana working, create the token, then
|
|
apply the Secret.
|
|
|
|
## App updates
|
|
|
|
The server relays the app's releases from the private repository to the car's updater
|
|
(`OTA.md`). It reads them with `FORGE_TOKEN` from the `mg4-server` Secret: create a token in
|
|
the forge that can only **read** the `nils/mg4` repository, put it there, apply the Secret and
|
|
restart the server. Without it, updates are off (the car's updater gets a 404). `FORGE_URL`
|
|
and `FORGE_REPO` are set in `server.yaml`.
|
|
|
|
## Notes
|
|
|
|
- InfluxDB's setup mode runs only on an empty volume. Changing `admin-token` in the Secret later
|
|
does not change the token inside InfluxDB.
|
|
- The server uses the InfluxDB admin token; a token scoped to read/write on the `mg4` bucket can
|
|
replace it later. Grafana has its own read-only token (below).
|
|
- 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 `server.yaml`. To upgrade, push a new tag and put its
|
|
digest there (`skopeo inspect docker://git.stinnesbeck.com/nils/mg4-server:<tag>`).
|