Commit Graph
4354 Commits
Author SHA1 Message Date
k.rennerandClaude Opus 4.7 fa11376a22 fix(influxdb-seed): pre-create errors-file (influx CLI won't create it)
influx CLI 2.7.4 opens --errors-file with O_WRONLY and fails with
"no such file or directory" if the file doesn't exist. touch it
before calling influx write.

Bump v7 → v8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:05:33 +02:00
k.rennerandClaude Opus 4.7 f35ab128f7 fix(home-assistant): move namespace to infrastructure/namespaces, drop circular dep
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
Seed Kustomization tried to create the PVC in `home-assistant` ns but
the ns lived in the `home-assistant` app Kustomization, which depends
on seed — deadlock. Matches the music-assistant pattern now: namespace
(with privileged PSA labels for hostNetwork+USB) lives in
infrastructure/namespaces, apps just consume it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:59:54 +02:00
k.rennerandClaude Opus 4.7 61801dfa09 fix(home-assistant): drop USB hostPaths for canary; add back at cutover
hostPath volumes with type=CharDevice refuse to mount if the path is
absent, so the placeholder TBD paths would have left both pods stuck
in ContainerCreating during canary — blocking the whole "does the
seeded /config boot HA on K8s?" test.

Strip stromzaehler + ZWA-2 volumes for now; zwavejs scaled to 0
(nothing to do until its stick is present). Adding the usb=[...] block
to vm_153_k8s_edge.tf + the hostPath volumes back + replicas:1 all land
in the same cutover commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:53:54 +02:00
k.rennerandClaude Opus 4.7 cc17db1944 feat(home-assistant): migrate to K8s, pinned to edge node
Land HA Core on k8s-edge alongside Music Assistant. /config comes from
HAOS's own backup framework writing directly to Garage (S3 Compatible
HACS integration on VM 103, bucket k8s-migration/home-assistant/),
which the seed Job picks up — newest .tar under the prefix, extract
outer HAOS tar, extract inner homeassistant.tar.gz onto /config, yq-
rewrite http.trusted_proxies for the ingress-nginx pod CIDR.

Three Flux Kustomizations in clusters/homelab/home-assistant.yaml:
  - home-assistant-seed  (Job-gated, 4h timeout, wait:true)
  - home-assistant-zwavejs  (wait:false — ZWA-2 hostPath is TBD until
    the USB physically moves to saturn at cutover)
  - home-assistant  (depends on seed, wait:false — stromzaehler USB
    same caveat)

Ingress is home.renner.uno only; Caddy keeps fronting production until
cutover. HA auths itself via the openid HACS integration against
auth.renner.uno — no proxy-layer forward-auth needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:51:10 +02:00
k.rennerandClaude Opus 4.7 f75fdc95ab fix(influxdb-seed): tolerate malformed rows via --errors-file
v6's first pod ran 6h39m before dying on a single bad line — a Home
Assistant `release_summary_str` value with unbalanced quotes (literal
newline embedded in a quoted LP string). Without error tolerance, one
bad row nukes the whole import and forces a full restart.

--errors-file captures rejected rows to /scratch/rejected.lp and lets
influx write keep going. Summary is logged at the end for visibility
into what got dropped.

Bump v6 → v7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:46:59 +02:00
k.rennerandClaude Opus 4.7 b493dbb691 fix(home-assistant): drop SSH-based backup; HA uploads directly via S3 Compatible HACS
The SSH-to-nas-1 approach was wrong — the CI container doesn't need
shell access to produce a backup, it just needs the backup to exist
somewhere it can read. Route HA's backup framework at Garage directly
via the PhantomPhoton/S3-Compatible HACS integration; the K8s seed Job
picks the newest .tar under s3://k8s-migration/home-assistant/.

Removes:
- ansible/plays/backup-home-assistant.yml (nas-1 tar → Garage)
- .woodpecker/migrate-home-assistant.yml (pipeline that drove it)

The seed Job update (kubernetes/apps/home-assistant-seed/seed-job.yaml)
rides along in the upcoming K8s apps commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:20:36 +02:00
k.rennerandClaude Opus 4.7 ed54a79927 fix(home-assistant): install sshpass in migrate-home-assistant pipeline
nas-1 requires password SSH (DSM doesn't support key auth for ladmin),
and Ansible refuses to pass a password without sshpass on PATH.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:20:36 +02:00
k.renner 72e7e432c8 Merge pull request 'feat(coder-pg): wire barman-cloud plugin + daily backup' (#3) from coder-migration into master 2026-04-24 20:18:09 +02:00
k.rennerandClaude Opus 4.7 9ca0b6cb0d feat(coder-pg): wire barman-cloud plugin + daily backup
The CNPG instance manager's reconcile loop blocks on "Unknown plugin:
barman-cloud.cloudnative-pg.io" when a Cluster omits a plugin declaration,
which in turn prevents TLS cert loading on the status endpoint and leaves
the pod stuck 0/1 Ready.

Match the house pattern (authentik-db, paperless-db, immich-db):

- Declare the plugin in Cluster spec with barmanObjectName: coder-pg-store
- ObjectStore coder-pg-store → s3://k8s-cnpg/coder-pg on Garage (nas-1)
- Daily base backup at 02:30 UTC with immediate first backup
- barman-cloud-creds reused from authentik-db (same Garage creds,
  namespace rewritten to coder)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:16:26 +02:00
k.rennerandClaude Opus 4.7 69e31cd76f fix(home-assistant): forward existing HAOS backup from nas-1, don't SSH into HAOS
ci/woodpecker/manual/migrate-home-assistant Pipeline failed
Prior version tried to SSH root@homeassistant.iot.renner.uno:22 and tar
/config, which fails because HAOS doesn't run SSH on 22 and that's the
wrong way to back up HAOS anyway — it bypasses supervisor metadata.

Instead, pick the newest .tar under /volume1/Homeassistant Backup on
nas-1 (produced by the existing HAOS → Synology backup pipeline) and
stream it to Garage untouched. Zero HA-side impact — no stop, no restart.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:56:45 +02:00
k.rennerandClaude Opus 4.7 a68ee15b15 feat(home-assistant): migrate-home-assistant pipeline exports /config to Garage
ci/woodpecker/manual/migrate-home-assistant Pipeline failed
Stops HA core on VM 103, streams tar of /config (excluding logs/tts/deps)
into s3://k8s-migration/home-assistant/home-assistant-config.tar.gz, restarts
HA. Manual trigger, rerunnable. K8s-side seed Job + manifests land in a
follow-up once the tarball is in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:40:05 +02:00
k.renner fd4c1da5ed Merge pull request 'fix(coder): trust LE staging roots for OIDC to Authentik' (#2) from coder-migration into master 2026-04-24 18:05:19 +02:00
k.rennerandClaude Opus 4.7 74aefdc28f fix(coder): trust LE staging roots for OIDC to Authentik
Coder's OIDC init was crashlooping with "x509: certificate signed by
unknown authority" when fetching auth.renner.uno's OpenID discovery
document, because every ingress in this cluster uses the
letsencrypt-staging ClusterIssuer today.

Mount the staging roots (Pretend Pear X1 + Bogus Broccoli X2) via
coder.certs.secrets; the chart drops them at /etc/ssl/certs/ where
Go's crypto/x509 picks them up on Linux.

Remove once the cluster migrates to letsencrypt-prod.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:04:17 +02:00
k.renner fc4573249b Merge pull request 'feat(coder): add Coder control plane with Authentik OIDC' (#1) from coder-migration into master 2026-04-24 17:39:53 +02:00
k.rennerandClaude Opus 4.7 0e44b32d4a feat(coder): add Coder control plane with Authentik OIDC
Replaces EOL Kasm VM 124 with a K8s-native platform for dynamically
provisioned desktop environments. Kasm was ruled out — its Helm chart
is Technical Preview and does not include the agent, Proxmox is being
decommissioned, and Talos refuses DinD.

HelmRelease `coder` (chart >=2.32.0 from helm.coder.com/v2), CNPG
Cluster `coder-pg` (2 instances, ceph-block 10Gi) as external DB,
SOPS-encrypted Authentik OIDC credentials, ingress at coder.renner.uno
behind letsencrypt-staging. Workspace templates follow separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 17:31:34 +02:00
k.rennerandClaude Opus 4.7 33db402b52 fix(influxdb-seed): swap emptyDir for ceph-block ephemeral PVC
ci/woodpecker/cron/security-scan Pipeline was successful
emptyDir backs onto node ephemeral-storage. With only ~4.5 GiB free per
node, the 10.5 GiB download triggered kubelet eviction (Reason: Evicted,
Exit 137) long before the file finished.

Generic ephemeral volume claims a 20 GiB RWO PVC on ceph-block for the
pod's lifetime — Ceph-backed, auto-deleted with the pod, and won't
compete with node-level ephemeral pressure.

Bump Job v5 → v6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:53:06 +02:00
k.rennerandClaude Opus 4.7 fe5cca970d feat(influxdb-seed): stage export to emptyDir before influx write
Streaming aws s3 cp | influx write kept the S3 GET TCP connection open
for hours at ~17 MB/min (influx write's ingest pace), and every transient
network hiccup aborted the transfer with an 'unexpected EOF' — aws-cli has
no resume semantics for streaming-to-stdout.

Staging to a 15 GiB pod-scoped emptyDir decouples the two phases: the
S3 GET now completes in minutes, and influx write runs against a stable
local file for as long as ingest takes.

Bump Job v4 → v5 for fresh template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:31:48 +02:00
k.rennerandClaude Opus 4.7 2459bc017b fix(influxdb-seed): drop bogus --batch-size flag (not in influx 2.7.4)
`influx write` 2.7.4 doesn't expose --batch-size; the flag triggers
an immediate CLI parse error and the Job crashlooped. Remove it and
rely on the server-side INFLUXD_STORAGE_WRITE_TIMEOUT=60s knob to
absorb slow TSM writes.

Bump Job v3 → v4 for fresh template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 07:59:35 +02:00
k.rennerandClaude Opus 4.7 4ff239c9ee fix(influxdb): raise memory limit + probe delays for large startups
Partial seeding produced enough shards that opening them on startup
pushes memory past the 2Gi limit → OOMKilled. Meanwhile the chart's
default zero-delay liveness probe kills the pod before shard open
finishes, causing a CrashLoopBackOff.

- resources.limits.memory: 2Gi → 6Gi
- livenessProbe: initialDelaySeconds 600, failureThreshold 6
- readinessProbe: initialDelaySeconds 30

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 06:58:04 +02:00
k.rennerandClaude Opus 4.7 de94c0279d fix(influxdb): address 500 write timeouts on seed stream
Root cause: `influx write` batches of 5000 points routinely blew past
InfluxDB's default 10s storage-write timeout against the cold-cache
RWO Ceph-block PVC under concurrent TSM compaction. Server responded
500 → client aborted → `aws s3 cp` got broken pipe.

Two-sided fix:
- Seed Job: --batch-size 1000 so each HTTP write finishes well
  inside any plausible write deadline.
- InfluxDB server: INFLUXD_STORAGE_WRITE_TIMEOUT=60s to tolerate
  transient ingest+compaction contention.

Bump Job name v2 → v3 to force a clean re-roll; pod template is
immutable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 06:53:37 +02:00
k.rennerandClaude Opus 4.7 840e7b703e fix(influxdb-seed): bump Job name v1 → v2 to pick up hostname fix
Kubernetes Job pod templates are immutable, so the hostname fix in
716dd7f never reached the live v1 Job. Bumping the name forces Flux
to create a fresh Job with the correct influxdb-influxdb2 service
name — matches the re-seed procedure documented in the file header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:34:06 +02:00
k.rennerandClaude Opus 4.7 716dd7f387 fix(influxdb-seed): use correct chart service hostname influxdb-influxdb2
kube-influxdb2 chart produces service name <release>-<chart> (so
`influxdb-influxdb2`, not `influxdb`). Verified with `influx ping`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:19:14 +02:00
k.rennerandClaude Opus 4.7 950fb6c4b0 fix(influxdb-seed): stream S3 → influx write, no /tmp staging
Same class of bug as the backup side — seed was staging the 10 GiB
gzipped line protocol to pod /tmp before importing. Pipe `aws s3 cp -`
straight into `influx write` via stdin. set -o pipefail so any stage
failing kills the Job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:11:20 +02:00
k.rennerandClaude Opus 4.7 f2ebe301d8 fix(influxdb-seed): pass --endpoint-url explicitly on every aws call
aws-cli 2.9.19 in the influxdb:2.7.4 base image predates
AWS_ENDPOINT_URL env support (added in 2.15+). The Job's wait loop
was silently hitting real AWS instead of Garage, so head-object
always returned not-found. Factor the CLI into `$AWS` with the
explicit endpoint baked in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:06:45 +02:00
k.rennerandClaude Opus 4.7 fe14598a0e fix(monitoring): grafana deploymentStrategy Recreate for RWO volume
kube-prometheus-stack default RollingUpdate deadlocks on Multi-Attach
when Grafana is a single replica on RWO ceph-block — new pod can't
mount the volume while the old pod is still Running. Use Recreate so
the old pod terminates first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:06:41 +02:00
k.rennerandClaude Opus 4.7 e6a81a68e7 feat(monitoring): add legacy prom + VM 108 influx datasources, enable dashboard sidecar
- Pin Loki datasource UID to `loki` so dashboards referencing that UID resolve
- Add `prometheus-legacy` datasource pointing at LXC 227 so host-level
  dashboards (Alloy, pve host node_exporter, docker) keep working until
  the legacy Prometheus retires at global cut-over
- Add `influxdb` datasource pointing at VM 108 (Stage A); the
  suedstr28/renner bucket is the heatpump-roi dashboard's source.
  Token from grafana-influxdb-token SOPS secret (same read-only token
  the legacy Grafana role uses). Stage B flip to the in-cluster
  InfluxDB is deferred to the HA migration PR.
- Enable kube-prometheus-stack's Grafana sidecar on the
  `grafana_dashboard: "1"` label so per-dashboard ConfigMaps from
  kubernetes/apps/monitoring/dashboards/ get auto-loaded

DASHBOARD-MIGRATION.md tracks the 27-dashboard port queue (bucketed
k8s-native / host-legacy / mixed). Empty dashboards/ kustomization is
ready to accept per-dashboard PRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:52:03 +02:00
k.rennerandClaude Opus 4.7 906c505889 feat(influxdb-seed): wire seed Job into the Flux Kustomization
Add garage-migration-creds secret (reused key from other seed apps) and
the kustomization resource list so Flux actually applies the seed-job
landed in the export-lp commit. Seed waits for
s3://k8s-migration/influxdb/suedstr28.lp.gz then runs `influx write`
against the in-cluster InfluxDB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:51:22 +02:00
k.rennerandClaude Opus 4.7 5b59cb738f feat(influxdb): deploy in-cluster InfluxDB 2.7.4
influxdata/influxdb2 HelmRelease in its own namespace. 50Gi ceph-block
PVC, single replica (OSS). Admin password + token come from a SOPS-
managed cluster-internal secret so credentials are deterministic across
cluster rebuilds; the seed Job reuses the same token for `influx write`.

Clusters/homelab wires two Flux Kustomizations: influxdb (the chart) and
influxdb-seed (depends on influxdb being healthy so the seed only runs
once the API is reachable).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:50:47 +02:00
k.rennerandClaude Opus 4.7 3c7ce6a1cb fix(influxdb): stream export-lp directly to Garage, no /tmp staging
ci/woodpecker/manual/migrate-influxdb Pipeline was successful
Pipeline 672 filled /tmp with the partial export before failing,
leaving influxd stopped and HA writes failing. Pipe `influxd inspect
export-lp --output-path - --compress` straight to `mc pipe` — no local
staging, so the export survives any bucket size regardless of /tmp.

Matches the streaming pattern in backup-music-assistant.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:41:25 +02:00
k.rennerandClaude Opus 4.7 3251ccf812 fix(influxdb): engine path is /var/lib/influxdb/engine on VM 108
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Ubuntu influxdb2 2.7.4 package uses the legacy /var/lib/influxdb path
(no '2' suffix) — confirmed by probe in pipeline 671. Drop the probe
block, set engine_path accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:15:13 +02:00
k.rennerandClaude Opus 4.7 06ee0caae7 chore(influxdb): probe engine path — /var/lib/influxdb2/engine missing
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Pipeline 670 failed the stat assert — the assumed engine path doesn't
exist. Dump the data dir candidates, systemd unit, /etc/default/influxdb2,
and /etc/influxdb/config.toml to find where VM 108 actually stores its
TSM files before running export-lp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:12:08 +02:00
k.rennerandClaude Opus 4.7 d30ac63562 feat(influxdb): export via influxd inspect export-lp, import via influx write
ci/woodpecker/manual/migrate-influxdb Pipeline failed
`influx backup` is observably broken in 2.7.x — multiple upstream threads
(incl. 2.7.11, June 2025) report the same 'unexpected EOF' during metadata
SQLite download with a root-scope token, disk space fine, no resolution
published. Even the All-Access token permission model is documented as
under-scoped for backup.

Pivot to `influxd inspect export-lp`: reads engine TSM files on disk,
emits gzipped line-protocol for the target bucket only. Seed Job imports
with `influx write --compression gzip --file`. Server stopped for a few
seconds on VM 108 to read a consistent TSM snapshot; HA write retries
cover the gap.

Trade-off: loses dashboards/tasks/auth state from VM 108, which we don't
want to carry across (K8s Grafana has its own dashboards, HA is the sole
writer, K8s InfluxDB uses its own bootstrap admin from SOPS).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:09:30 +02:00
k.rennerandClaude Opus 4.7 7dbb01cb64 chore(influxdb): curl the backup metadata endpoint directly
Replace the auth-list probe with a direct curl to
/api/v2/backup/metadata so the HTTP status + first 2KB of body are
visible. Unexpected-EOF on the CLI suggests the server is returning
an error payload the CLI can't parse; curl will show what it actually is.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:01:47 +02:00
k.rennerandClaude Opus 4.7 675c4293ce fix(influxdb): pin v2 CLI to 2.7.4 to match server
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Pipeline 668 got past auth (local admin token worked) but `influx backup`
failed with `unexpected EOF` while saving the metadata sqlite. CLI 2.7.5
vs server 2.7.4 protocol skew is the most likely cause.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:59:09 +02:00
k.rennerandClaude Opus 4.7 4ef2c3e7cd chore(influxdb): probe token before influx backup
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Add a prefix+length dump and `influx auth list` probe so we can
distinguish three cases behind the 401: (a) Woodpecker secret didn't
propagate, (b) token missing read:authorizations despite All Access
label, (c) wrong InfluxDB instance on localhost:8086.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:52:30 +02:00
k.rennerandClaude Opus 4.7 a1b844a3ab feat(influxdb): switch backup to influx backup with operator token
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Stop probing, commit to the `influx backup` approach: pull the v2 CLI
static binary to /tmp on VM 108, run a scoped backup of the
suedstr28/renner bucket, upload tarball to Garage, clean up. Token
read from INFLUX_BACKUP_TOKEN (Woodpecker secret) — the scoped-bucket
backup still needs operator read on metadata, so the token is an
operator-scope token on VM 108, not the Grafana read token.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:47:31 +02:00
k.rennerandClaude Opus 4.7 1553b89689 feat(influxdb): fetch v2 CLI static binary for backup on VM 108
ci/woodpecker/manual/migrate-influxdb Pipeline failed
VM 108 runs InfluxDB 2.7.4 server but only has the legacy `influxdb-client`
(v1) package installed. Probe output (pipeline 663):

  influxdb-client 1.6.7~rc0-1ubuntu0.22.04.3
  influxdb2 2.7.4-1

Pull the matching v2 CLI tarball to /tmp, run scoped `influx backup`,
clean up. No persistent package install, no apt config changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:35:46 +02:00
k.rennerandClaude Opus 4.7 f837faebb1 fix(seed-jobs): decouple restore sentinel name from Job version
audiobookshelf-seed wrote `.restore-v3-done` while the app's
wait-for-restore init polled `.restore-done` — the pod has been stuck
in Init:0/2 since the last rebuild despite a successful seed.

Strip the `-vN-` infix from all four seed sentinels (audiobookshelf,
immich, music-assistant, paperless) so the app/seed contract no longer
drifts each Job bump. Short-circuit path accepts either new or legacy
sentinel and normalizes forward, so PVCs seeded under the old name
unblock on next Job run without a re-extract.

Job object names (`*-data-seed-vN`) are unchanged — those are the
manifest-bump trick for forcing re-runs, orthogonal to the sentinel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:33:53 +02:00
k.rennerandClaude Opus 4.7 49d47cadb1 chore(influxdb): probe VM 108 — the /usr/bin/influx on the host is 1.x
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Last run showed `influx version` is the 1.x client (single-dash flags,
no `backup` subcommand). Before committing to a migration tool, dump
package list, binary locations, influxd version, and the :8086 health
endpoint to pick the right path (1.x → 2.x upgrade vs scoped backup
vs line-protocol export).

Play fails at the end so the debug output is visible in CI logs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:33:23 +02:00
k.rennerandClaude Opus 4.7 720d052fa2 fix(influxdb): surface influx backup error; pass token via env
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Remove no_log on the backup task (it was hiding the underlying failure)
and move the token to INFLUX_TOKEN env so the command line stays clean.
Add a which + version probe so the CLI path and install are logged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:30:05 +02:00
k.rennerandClaude Opus 4.7 6c3733cc4d feat(influxdb): migrate-influxdb pipeline exports suedstr28 to Garage
ci/woodpecker/manual/migrate-influxdb Pipeline failed
Online `influx backup` scoped to the Home Assistant bucket, tarballed
and uploaded to s3://k8s-migration/influxdb/. Re-runnable — each run
overwrites the tarball with current VM 108 state, so a rebuilt cluster
picks up everything HA wrote since the last run.

Uses the existing read-only influxdb_token from the grafana-observability
role defaults (single source of truth; the same token already backs the
legacy Grafana → InfluxDB datasource).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:17:15 +02:00
k.rennerandClaude Opus 4.7 74f7d72994 feat(music-assistant-seed): migrate LXC 229 data into K8s PVC
ci/woodpecker/manual/migrate-music-assistant Pipeline was successful
ci/woodpecker/manual/deploy-infrastructure Pipeline was successful
ci/woodpecker/cron/security-scan Pipeline was successful
The K8s Deployment landed with an empty PVC, so MA came up
unconfigured — no providers, no players, no library index. Adds the
seed infrastructure that matches audiobookshelf / paperless:

* Woodpecker .woodpecker/migrate-music-assistant.yml — manual trigger
  that runs an Ansible playbook against LXC 229, stops the
  docker-compose stack, tars /opt/music-assistant/data, streams the
  tarball to s3://k8s-migration/music-assistant/, and restarts the
  container.
* kubernetes/apps/music-assistant-seed/ — seed Job polls Garage for
  the tarball (up to 3 h), wipes first-run remnants under /data,
  extracts, writes /data/.restore-v1-done. PVC definition moves here
  with prune-disabled and is bumped 10Gi → 20Gi.
* MA Deployment gains a wait-for-restore init container blocking on
  the sentinel; the inline PVC definition is removed.
* Flux: new music-assistant-seed Kustomization; music-assistant
  Kustomization now dependsOn it.

Run order: push → Flux reconciles seed (Job waits) → trigger
migrate-music-assistant pipeline → tarball lands → Job extracts →
Deployment unblocks with restored state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 13:11:55 +02:00
k.rennerandClaude Opus 4.7 80f6cb982f fix(tofu): drop all=false — schema rejects [all,vmid] together
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/manual/deploy-infrastructure Pipeline failed
ci/woodpecker/push/security-scan Pipeline was successful
Provider validator treats all and vmid as mutually exclusive at config
level, so the transition must be driven by specifying vmid alone.
Provider will clear PVE's all=1 internally when the API call sets the
vmid mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 13:06:02 +02:00
k.rennerandClaude Opus 4.7 c45f90ad96 fix(tofu): explicitly disable all-mode on vzdump job
ci/woodpecker/push/deploy-infrastructure Pipeline failed
ci/woodpecker/push/security-scan Pipeline failed
First plan showed `all = true` unchanged and `vmid` added — PVE with both
set is ambiguous. Driving `all -> false` makes the selection mode
unambiguous.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 13:03:44 +02:00
k.rennerandClaude Opus 4.7 15d072ac45 feat(tofu): manage PVE vzdump job as code, exclude K8s VMs
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/push/security-scan Pipeline failed
Imports the existing cluster-wide backup job (backup-51b113b6-676a) into
OpenTofu. The job previously lived only in PVE's UI, which let a stray
manual edit cascade into an I/O freeze that took down Rook-Ceph and the
K8s control plane.

Switches from all+exclude to an explicit vmid include list — bpg/proxmox
doesn't expose vzdump's exclude attribute. Same 37 guests are covered;
K8s VMs 150-153 remain outside the list by design (Flux + Velero/CNPG
are the K8s-side protection path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 13:00:04 +02:00
k.rennerandClaude Opus 4.7 7a9a2f924a feat(music-assistant): migrate to K8s, pinned to edge node
Host-networked Deployment on the edge node (workload-class=pinned,
topology.kubernetes.io/zone=edge) so mDNS / uPnP / SlimProto reach
Chromecasts, DLNA renderers, and Squeezeboxes on v30iot with
native-kernel network path (lowest latency/jitter). State on a
ceph-block PVC, retained on Kustomization retirement.

Prereqs bundled in:
 * rook-ceph operator: csi.pluginTolerations so the CSI node plugin
   lands on the pinned edge node and MA can mount RBD.
 * ingress-isolation: allow ingress-nginx egress to remote-node, since
   hostNetwork pods take node identity and namespace selectors don't
   match them.
 * music-assistant namespace labeled pod-security enforce=privileged
   (required for hostNetwork + SYS_ADMIN/DAC_READ_SEARCH + AppArmor
   unconfined, matching the LXC docker-compose).

LXC 229 stays running for now; decommission once K8s side is verified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 12:54:01 +02:00
k.rennerandClaude Opus 4.7 bc28be54e0 feat(k8s-edge): attach v30iot secondary NIC for host-networked pods
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/push/security-scan Pipeline was successful
ci/woodpecker/manual/deploy-infrastructure Pipeline was successful
Music Assistant needs L2 reach to the IoT VLAN for mDNS / uPnP /
Chromecast / SlimProto discovery. Adds an optional
extra_network_bridges list to the proxmox_vm module and uses it to
give VM 153 a second NIC on v30iot, keeping v40srv as the primary
cluster interface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 12:39:23 +02:00
rebuild-k8s pipeline f2946c01b9 ci: Export cluster access files from rebuild 2026-04-23 09:33:59 +00:00
k.rennerandClaude Opus 4.7 f539db955e fix(network-policies): allow ingress-nginx egress to immich namespace
ci/woodpecker/manual/rebuild-k8s Pipeline failed
ingress-nginx-egress CNP whitelists target namespaces; immich was
missing from the list, so ingress → immich-server pod times out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:17:38 +02:00
k.rennerandClaude Opus 4.7 57a3a62b96 fix(immich): bind server to 0.0.0.0 so ingress can reach it
Immich v2.7.5 defaults to listening on IPv6 localhost (`[::1]:3001`),
which makes the pod unreachable from the in-cluster Service — ingress
returns 504 gateway timeout because nginx can't establish a connection
to the upstream. Set IMMICH_HOST=0.0.0.0 to bind on all interfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:11:11 +02:00