From 9b44a8ec514eb84fa97e42bba13b50861ecc55b9 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 24 Feb 2026 14:25:19 -0800 Subject: [PATCH] Add kustomize images: and configMapGenerator: across services (#264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Move hardcoded image tags to kustomization.yaml `images:` transformer across **22 services** — image names in manifests become version-agnostic templates, with tags centralized in one place per service - Replace hand-written ConfigMap manifests with `configMapGenerator:` in **12 services** — config data extracted to standalone files, generated ConfigMaps include content hashes that trigger automatic pod rollouts on changes - Create new `kustomization.yaml` for **forgejo-runner** and **nvidia-device-plugin** (switches ArgoCD from directory mode to kustomize mode, rendered output identical) ### Services modified **Images only (8):** cv, devpi, docs, kube-state-metrics, miniflux, navidrome, teslamate, torrent **Images + configMapGenerator (10):** alloy-k8s, forgejo-runner, frigate, grafana, homepage, kiwix, loki, mosquitto, ntfy, prometheus **Images only, no configMapGenerator (4):** authentik (skip blueprints — special YAML tags), tailscale-operator-base (Deployment only, CRD image fields left as-is) **Skipped entirely (6):** argocd (remote upstream), databases (no image fields), external-secrets, grafana-config (cross-kustomization dashboards), immich (Helm-managed), 1password-connect/cloudnative-pg (no kustomization.yaml) ### What changes at deploy time - **images:** — no functional diff, `kustomize build` produces identical output with tags - **configMapGenerator:** — ConfigMap names gain hash suffixes (e.g., `prometheus-config` → `prometheus-config-6f42fhctcb`) and all Deployment/StatefulSet/DaemonSet references are updated automatically. Pods will restart once per service on first sync due to the name change ## Test plan - [x] `kubectl kustomize` builds all 30 service directories successfully - [x] Image tags verified in rendered output for all modified services - [x] ConfigMap hash suffixes verified in rendered output - [x] ConfigMap references in Deployments/StatefulSets confirmed to use hashed names - [x] All pre-commit hooks pass (yamllint, shellcheck, prettier, etc.) - [ ] `argocd app diff` each service to confirm only expected ConfigMap name changes - [ ] Deploy from branch starting with a low-risk service (e.g., mosquitto) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/264 --- argocd/manifests/alloy-k8s/config.alloy | 169 +++++++++++++++++ argocd/manifests/alloy-k8s/configmap.yaml | 176 ------------------ argocd/manifests/alloy-k8s/daemonset.yaml | 2 +- argocd/manifests/alloy-k8s/kustomization.yaml | 13 +- .../manifests/authentik/deployment-redis.yaml | 2 +- .../authentik/deployment-server.yaml | 2 +- .../authentik/deployment-worker.yaml | 2 +- argocd/manifests/authentik/kustomization.yaml | 5 + argocd/manifests/cv/deployment.yaml | 2 +- argocd/manifests/cv/kustomization.yaml | 3 + argocd/manifests/devpi/kustomization.yaml | 4 + argocd/manifests/devpi/statefulset.yaml | 2 +- argocd/manifests/docs/deployment.yaml | 2 +- argocd/manifests/docs/kustomization.yaml | 3 + argocd/manifests/forgejo-runner/config.yaml | 19 ++ .../manifests/forgejo-runner/configmap.yaml | 30 --- argocd/manifests/forgejo-runner/daemon.json | 3 + .../manifests/forgejo-runner/deployment.yaml | 4 +- .../forgejo-runner/kustomization.yaml | 21 +++ .../manifests/frigate/configmap-notify.yaml | 42 ----- argocd/manifests/frigate/configmap.yaml | 90 --------- .../manifests/frigate/deployment-notify.yaml | 2 +- argocd/manifests/frigate/deployment.yaml | 4 +- argocd/manifests/frigate/frigate-config.yml | 83 +++++++++ .../frigate/frigate-notify-config.yml | 35 ++++ argocd/manifests/frigate/kustomization.yaml | 18 +- argocd/manifests/grafana/configmap.yaml | 100 ---------- argocd/manifests/grafana/datasources.yaml | 34 ++++ argocd/manifests/grafana/deployment.yaml | 6 +- argocd/manifests/grafana/grafana.ini | 32 ++++ argocd/manifests/grafana/kustomization.yaml | 18 +- argocd/manifests/grafana/provider.yaml | 11 ++ argocd/manifests/homepage/bookmarks.yaml | 16 ++ argocd/manifests/homepage/configmap.yaml | 155 --------------- argocd/manifests/homepage/deployment.yaml | 2 +- argocd/manifests/homepage/docker.yaml | 0 argocd/manifests/homepage/kubernetes.yaml | 1 + argocd/manifests/homepage/kustomization.yaml | 15 +- argocd/manifests/homepage/services.yaml | 76 ++++++++ argocd/manifests/homepage/settings.yaml | 17 ++ argocd/manifests/homepage/widgets.yaml | 26 +++ .../kiwix/configmap-sync-script.yaml | 68 ------- .../kiwix/configmap-zim-torrents.yaml | 69 ------- .../manifests/kiwix/cronjob-zim-watcher.yaml | 2 +- argocd/manifests/kiwix/deployment.yaml | 4 +- argocd/manifests/kiwix/kustomization.yaml | 18 +- argocd/manifests/kiwix/sync-zim-torrents.sh | 60 ++++++ argocd/manifests/kiwix/torrents.txt | 61 ++++++ .../kube-state-metrics/deployment.yaml | 2 +- .../kube-state-metrics/kustomization.yaml | 3 + argocd/manifests/loki/configmap.yaml | 58 ------ argocd/manifests/loki/kustomization.yaml | 10 +- argocd/manifests/loki/loki-config.yaml | 51 +++++ argocd/manifests/loki/statefulset.yaml | 2 +- argocd/manifests/miniflux/deployment.yaml | 2 +- argocd/manifests/miniflux/kustomization.yaml | 4 + argocd/manifests/mosquitto/configmap.yaml | 10 - argocd/manifests/mosquitto/deployment.yaml | 2 +- argocd/manifests/mosquitto/kustomization.yaml | 8 +- argocd/manifests/mosquitto/mosquitto.conf | 3 + argocd/manifests/navidrome/deployment.yaml | 2 +- argocd/manifests/navidrome/kustomization.yaml | 3 + argocd/manifests/ntfy/configmap.yaml | 13 -- argocd/manifests/ntfy/deployment.yaml | 2 +- argocd/manifests/ntfy/kustomization.yaml | 8 +- argocd/manifests/ntfy/server.yml | 6 + .../nvidia-device-plugin/daemonset.yaml | 2 +- .../nvidia-device-plugin/kustomization.yaml | 12 ++ argocd/manifests/prometheus/configmap.yaml | 53 ------ .../manifests/prometheus/kustomization.yaml | 10 +- argocd/manifests/prometheus/prometheus.yml | 46 +++++ argocd/manifests/prometheus/statefulset.yaml | 2 +- .../kustomization.yaml | 4 + .../tailscale-operator-base/operator.yaml | 2 +- argocd/manifests/teslamate/deployment.yaml | 2 +- argocd/manifests/teslamate/kustomization.yaml | 4 + argocd/manifests/torrent/deployment.yaml | 2 +- argocd/manifests/torrent/kustomization.yaml | 3 + ...ure-kustomize-images-configmapgen.infra.md | 1 + 79 files changed, 956 insertions(+), 905 deletions(-) create mode 100644 argocd/manifests/alloy-k8s/config.alloy delete mode 100644 argocd/manifests/alloy-k8s/configmap.yaml create mode 100644 argocd/manifests/forgejo-runner/config.yaml delete mode 100644 argocd/manifests/forgejo-runner/configmap.yaml create mode 100644 argocd/manifests/forgejo-runner/daemon.json create mode 100644 argocd/manifests/forgejo-runner/kustomization.yaml delete mode 100644 argocd/manifests/frigate/configmap-notify.yaml delete mode 100644 argocd/manifests/frigate/configmap.yaml create mode 100644 argocd/manifests/frigate/frigate-config.yml create mode 100644 argocd/manifests/frigate/frigate-notify-config.yml delete mode 100644 argocd/manifests/grafana/configmap.yaml create mode 100644 argocd/manifests/grafana/datasources.yaml create mode 100644 argocd/manifests/grafana/grafana.ini create mode 100644 argocd/manifests/grafana/provider.yaml create mode 100644 argocd/manifests/homepage/bookmarks.yaml delete mode 100644 argocd/manifests/homepage/configmap.yaml create mode 100644 argocd/manifests/homepage/docker.yaml create mode 100644 argocd/manifests/homepage/kubernetes.yaml create mode 100644 argocd/manifests/homepage/services.yaml create mode 100644 argocd/manifests/homepage/settings.yaml create mode 100644 argocd/manifests/homepage/widgets.yaml delete mode 100644 argocd/manifests/kiwix/configmap-sync-script.yaml delete mode 100644 argocd/manifests/kiwix/configmap-zim-torrents.yaml create mode 100644 argocd/manifests/kiwix/sync-zim-torrents.sh create mode 100644 argocd/manifests/kiwix/torrents.txt delete mode 100644 argocd/manifests/loki/configmap.yaml create mode 100644 argocd/manifests/loki/loki-config.yaml delete mode 100644 argocd/manifests/mosquitto/configmap.yaml create mode 100644 argocd/manifests/mosquitto/mosquitto.conf delete mode 100644 argocd/manifests/ntfy/configmap.yaml create mode 100644 argocd/manifests/ntfy/server.yml create mode 100644 argocd/manifests/nvidia-device-plugin/kustomization.yaml delete mode 100644 argocd/manifests/prometheus/configmap.yaml create mode 100644 argocd/manifests/prometheus/prometheus.yml create mode 100644 docs/changelog.d/feature-kustomize-images-configmapgen.infra.md diff --git a/argocd/manifests/alloy-k8s/config.alloy b/argocd/manifests/alloy-k8s/config.alloy new file mode 100644 index 0000000..582a692 --- /dev/null +++ b/argocd/manifests/alloy-k8s/config.alloy @@ -0,0 +1,169 @@ +// Alloy k8s configuration - collects pod logs from all namespaces + +// ============== K8S POD LOG DISCOVERY ============== + +// Discover all pods in the cluster +discovery.kubernetes "pods" { + role = "pod" +} + +// Relabel to extract useful metadata +discovery.relabel "pods" { + targets = discovery.kubernetes.pods.targets + + // Keep only running pods + rule { + source_labels = ["__meta_kubernetes_pod_phase"] + regex = "Pending|Succeeded|Failed|Unknown" + action = "drop" + } + + // Set namespace label + rule { + source_labels = ["__meta_kubernetes_namespace"] + target_label = "namespace" + } + + // Set pod name label + rule { + source_labels = ["__meta_kubernetes_pod_name"] + target_label = "pod" + } + + // Set container name label + rule { + source_labels = ["__meta_kubernetes_pod_container_name"] + target_label = "container" + } + + // Set app label from pod labels + rule { + source_labels = ["__meta_kubernetes_pod_label_app"] + target_label = "app" + } + + // Fallback: use app.kubernetes.io/name if no app label + rule { + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"] + target_label = "app" + regex = "(.+)" + action = "replace" + } + + // Set node name + rule { + source_labels = ["__meta_kubernetes_pod_node_name"] + target_label = "node" + } + + // Build the log path for the pod container + rule { + source_labels = ["__meta_kubernetes_pod_uid", "__meta_kubernetes_pod_container_name"] + target_label = "__path__" + separator = "/" + replacement = "/var/log/pods/*$1/$2/*.log" + } +} + +// Tail pod logs +loki.source.kubernetes "pods" { + targets = discovery.relabel.pods.output + forward_to = [loki.process.pods.receiver] +} + +// Process logs - parse JSON if present, add labels +loki.process "pods" { + forward_to = [loki.write.loki.receiver] + + // Drop noisy deprecation warning from minikube storage-provisioner + // See: https://github.com/kubernetes/minikube/issues/21009 + stage.drop { + source = "" + expression = "v1 Endpoints is deprecated" + } + + // Try to parse JSON logs (e.g., structured app logs) + // Handle both "msg" (common) and "message" (zot) field names + stage.json { + expressions = { + level = "level", + msg = "msg", + message = "message", + time = "time", + caller = "caller", + repository = "repository", + } + } + + // Drop JSON parsing error labels (non-JSON logs are fine, just won't have extracted fields) + stage.label_drop { + values = ["__error__", "__error_details__"] + } + + // Extract labels from parsed JSON data + stage.labels { + values = { + level = "", + caller = "", + repository = "", + } + } +} + +// Write logs to Loki +loki.write "loki" { + endpoint { + url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push" + } +} + +// ============== SERVICE HEALTH PROBES ============== + +// Blackbox-style HTTP probes for k8s services +prometheus.exporter.blackbox "services" { + config = "{ modules: { http_2xx: { prober: http, timeout: 5s } } }" + + target { + name = "miniflux" + address = "http://miniflux.miniflux.svc.cluster.local:8080/healthcheck" + module = "http_2xx" + } + + target { + name = "kiwix" + address = "http://kiwix.kiwix.svc.cluster.local:80/" + module = "http_2xx" + } + + target { + name = "transmission" + address = "http://transmission.torrent.svc.cluster.local:9091/transmission/web/" + module = "http_2xx" + } + + target { + name = "devpi" + address = "http://devpi.devpi.svc.cluster.local:3141/+api" + module = "http_2xx" + } + + target { + name = "argocd" + address = "http://argocd-server.argocd.svc.cluster.local:80/healthz" + module = "http_2xx" + } +} + +// Scrape blackbox probe results +prometheus.scrape "blackbox" { + targets = prometheus.exporter.blackbox.services.targets + scrape_interval = "30s" + forward_to = [prometheus.remote_write.prometheus.receiver] +} + +// Push metrics to Prometheus +prometheus.remote_write "prometheus" { + endpoint { + url = "http://prometheus.monitoring.svc.cluster.local:9090/api/v1/write" + } +} diff --git a/argocd/manifests/alloy-k8s/configmap.yaml b/argocd/manifests/alloy-k8s/configmap.yaml deleted file mode 100644 index b0e6643..0000000 --- a/argocd/manifests/alloy-k8s/configmap.yaml +++ /dev/null @@ -1,176 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: alloy-config - namespace: alloy -data: - config.alloy: | - // Alloy k8s configuration - collects pod logs from all namespaces - - // ============== K8S POD LOG DISCOVERY ============== - - // Discover all pods in the cluster - discovery.kubernetes "pods" { - role = "pod" - } - - // Relabel to extract useful metadata - discovery.relabel "pods" { - targets = discovery.kubernetes.pods.targets - - // Keep only running pods - rule { - source_labels = ["__meta_kubernetes_pod_phase"] - regex = "Pending|Succeeded|Failed|Unknown" - action = "drop" - } - - // Set namespace label - rule { - source_labels = ["__meta_kubernetes_namespace"] - target_label = "namespace" - } - - // Set pod name label - rule { - source_labels = ["__meta_kubernetes_pod_name"] - target_label = "pod" - } - - // Set container name label - rule { - source_labels = ["__meta_kubernetes_pod_container_name"] - target_label = "container" - } - - // Set app label from pod labels - rule { - source_labels = ["__meta_kubernetes_pod_label_app"] - target_label = "app" - } - - // Fallback: use app.kubernetes.io/name if no app label - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"] - target_label = "app" - regex = "(.+)" - action = "replace" - } - - // Set node name - rule { - source_labels = ["__meta_kubernetes_pod_node_name"] - target_label = "node" - } - - // Build the log path for the pod container - rule { - source_labels = ["__meta_kubernetes_pod_uid", "__meta_kubernetes_pod_container_name"] - target_label = "__path__" - separator = "/" - replacement = "/var/log/pods/*$1/$2/*.log" - } - } - - // Tail pod logs - loki.source.kubernetes "pods" { - targets = discovery.relabel.pods.output - forward_to = [loki.process.pods.receiver] - } - - // Process logs - parse JSON if present, add labels - loki.process "pods" { - forward_to = [loki.write.loki.receiver] - - // Drop noisy deprecation warning from minikube storage-provisioner - // See: https://github.com/kubernetes/minikube/issues/21009 - stage.drop { - source = "" - expression = "v1 Endpoints is deprecated" - } - - // Try to parse JSON logs (e.g., structured app logs) - // Handle both "msg" (common) and "message" (zot) field names - stage.json { - expressions = { - level = "level", - msg = "msg", - message = "message", - time = "time", - caller = "caller", - repository = "repository", - } - } - - // Drop JSON parsing error labels (non-JSON logs are fine, just won't have extracted fields) - stage.label_drop { - values = ["__error__", "__error_details__"] - } - - // Extract labels from parsed JSON data - stage.labels { - values = { - level = "", - caller = "", - repository = "", - } - } - } - - // Write logs to Loki - loki.write "loki" { - endpoint { - url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push" - } - } - - // ============== SERVICE HEALTH PROBES ============== - - // Blackbox-style HTTP probes for k8s services - prometheus.exporter.blackbox "services" { - config = "{ modules: { http_2xx: { prober: http, timeout: 5s } } }" - - target { - name = "miniflux" - address = "http://miniflux.miniflux.svc.cluster.local:8080/healthcheck" - module = "http_2xx" - } - - target { - name = "kiwix" - address = "http://kiwix.kiwix.svc.cluster.local:80/" - module = "http_2xx" - } - - target { - name = "transmission" - address = "http://transmission.torrent.svc.cluster.local:9091/transmission/web/" - module = "http_2xx" - } - - target { - name = "devpi" - address = "http://devpi.devpi.svc.cluster.local:3141/+api" - module = "http_2xx" - } - - target { - name = "argocd" - address = "http://argocd-server.argocd.svc.cluster.local:80/healthz" - module = "http_2xx" - } - } - - // Scrape blackbox probe results - prometheus.scrape "blackbox" { - targets = prometheus.exporter.blackbox.services.targets - scrape_interval = "30s" - forward_to = [prometheus.remote_write.prometheus.receiver] - } - - // Push metrics to Prometheus - prometheus.remote_write "prometheus" { - endpoint { - url = "http://prometheus.monitoring.svc.cluster.local:9090/api/v1/write" - } - } diff --git a/argocd/manifests/alloy-k8s/daemonset.yaml b/argocd/manifests/alloy-k8s/daemonset.yaml index b78633a..89cb930 100644 --- a/argocd/manifests/alloy-k8s/daemonset.yaml +++ b/argocd/manifests/alloy-k8s/daemonset.yaml @@ -19,7 +19,7 @@ spec: fsGroup: 473 # alloy user group containers: - name: alloy - image: grafana/alloy:v1.13.1 + image: grafana/alloy args: - run - --server.http.listen-addr=0.0.0.0:12345 diff --git a/argocd/manifests/alloy-k8s/kustomization.yaml b/argocd/manifests/alloy-k8s/kustomization.yaml index 17cd3c4..1d43d8f 100644 --- a/argocd/manifests/alloy-k8s/kustomization.yaml +++ b/argocd/manifests/alloy-k8s/kustomization.yaml @@ -1,7 +1,18 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization + +namespace: alloy + resources: - namespace.yaml - rbac.yaml - - configmap.yaml - daemonset.yaml + +images: + - name: grafana/alloy + newTag: v1.13.1 + +configMapGenerator: + - name: alloy-config + files: + - config.alloy diff --git a/argocd/manifests/authentik/deployment-redis.yaml b/argocd/manifests/authentik/deployment-redis.yaml index 03c5873..e70ac98 100644 --- a/argocd/manifests/authentik/deployment-redis.yaml +++ b/argocd/manifests/authentik/deployment-redis.yaml @@ -18,7 +18,7 @@ spec: spec: containers: - name: redis - image: docker.io/library/redis:7-alpine + image: docker.io/library/redis ports: - name: redis containerPort: 6379 diff --git a/argocd/manifests/authentik/deployment-server.yaml b/argocd/manifests/authentik/deployment-server.yaml index 085e16b..c3fdb52 100644 --- a/argocd/manifests/authentik/deployment-server.yaml +++ b/argocd/manifests/authentik/deployment-server.yaml @@ -18,7 +18,7 @@ spec: spec: containers: - name: server - image: registry.ops.eblu.me/blumeops/authentik:v2025.10.1-a72a0d8-nix + image: registry.ops.eblu.me/blumeops/authentik args: ["server"] ports: - name: http diff --git a/argocd/manifests/authentik/deployment-worker.yaml b/argocd/manifests/authentik/deployment-worker.yaml index 9bcc9fd..14becb6 100644 --- a/argocd/manifests/authentik/deployment-worker.yaml +++ b/argocd/manifests/authentik/deployment-worker.yaml @@ -18,7 +18,7 @@ spec: spec: containers: - name: worker - image: registry.ops.eblu.me/blumeops/authentik:v2025.10.1-a72a0d8-nix + image: registry.ops.eblu.me/blumeops/authentik args: ["worker"] env: - name: AUTHENTIK_SECRET_KEY diff --git a/argocd/manifests/authentik/kustomization.yaml b/argocd/manifests/authentik/kustomization.yaml index f639eba..9075142 100644 --- a/argocd/manifests/authentik/kustomization.yaml +++ b/argocd/manifests/authentik/kustomization.yaml @@ -11,3 +11,8 @@ resources: - service.yaml - service-redis.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/authentik + newTag: v2025.10.1-a72a0d8-nix + - name: docker.io/library/redis + newTag: 7-alpine diff --git a/argocd/manifests/cv/deployment.yaml b/argocd/manifests/cv/deployment.yaml index 656e932..57c850b 100644 --- a/argocd/manifests/cv/deployment.yaml +++ b/argocd/manifests/cv/deployment.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: cv - image: registry.ops.eblu.me/blumeops/cv:v1.0.3-ffa8727 + image: registry.ops.eblu.me/blumeops/cv ports: - containerPort: 80 name: http diff --git a/argocd/manifests/cv/kustomization.yaml b/argocd/manifests/cv/kustomization.yaml index 43c806c..9ba628f 100644 --- a/argocd/manifests/cv/kustomization.yaml +++ b/argocd/manifests/cv/kustomization.yaml @@ -6,3 +6,6 @@ resources: - deployment.yaml - service.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/cv + newTag: v1.0.3-ffa8727 diff --git a/argocd/manifests/devpi/kustomization.yaml b/argocd/manifests/devpi/kustomization.yaml index ef6566c..1943401 100644 --- a/argocd/manifests/devpi/kustomization.yaml +++ b/argocd/manifests/devpi/kustomization.yaml @@ -8,3 +8,7 @@ resources: - service.yaml - ingress-tailscale.yaml - external-secret.yaml + +images: + - name: registry.ops.eblu.me/blumeops/devpi + newTag: v6.19.1-ffa8727 diff --git a/argocd/manifests/devpi/statefulset.yaml b/argocd/manifests/devpi/statefulset.yaml index 23ff8a5..7bb5db0 100644 --- a/argocd/manifests/devpi/statefulset.yaml +++ b/argocd/manifests/devpi/statefulset.yaml @@ -18,7 +18,7 @@ spec: fsGroup: 1000 containers: - name: devpi - image: registry.ops.eblu.me/blumeops/devpi:v6.19.1-ffa8727 + image: registry.ops.eblu.me/blumeops/devpi env: - name: DEVPI_ROOT_PASSWORD valueFrom: diff --git a/argocd/manifests/docs/deployment.yaml b/argocd/manifests/docs/deployment.yaml index cb3b87b..1e1b825 100644 --- a/argocd/manifests/docs/deployment.yaml +++ b/argocd/manifests/docs/deployment.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: docs - image: registry.ops.eblu.me/blumeops/quartz:v1.28.2-ffa8727 + image: registry.ops.eblu.me/blumeops/quartz ports: - containerPort: 80 name: http diff --git a/argocd/manifests/docs/kustomization.yaml b/argocd/manifests/docs/kustomization.yaml index ef01217..492c8a3 100644 --- a/argocd/manifests/docs/kustomization.yaml +++ b/argocd/manifests/docs/kustomization.yaml @@ -6,3 +6,6 @@ resources: - deployment.yaml - service.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/quartz + newTag: v1.28.2-ffa8727 diff --git a/argocd/manifests/forgejo-runner/config.yaml b/argocd/manifests/forgejo-runner/config.yaml new file mode 100644 index 0000000..c92d616 --- /dev/null +++ b/argocd/manifests/forgejo-runner/config.yaml @@ -0,0 +1,19 @@ +# Reviewed against v12.7.0 defaults (2026-02-22) +log: + level: info + +runner: + file: /data/.runner + capacity: 2 + timeout: 3h + shutdown_timeout: 3h + # Env vars injected into all job containers + envs: + DOCKER_HOST: tcp://127.0.0.1:2375 + TZ: America/Los_Angeles + +container: + # Job execution image is set via RUNNER_LABELS in deployment.yaml + network: "host" + # Connect to DinD sidecar via TCP (not socket) + docker_host: tcp://127.0.0.1:2375 diff --git a/argocd/manifests/forgejo-runner/configmap.yaml b/argocd/manifests/forgejo-runner/configmap.yaml deleted file mode 100644 index 3b0df5b..0000000 --- a/argocd/manifests/forgejo-runner/configmap.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: forgejo-runner-config - namespace: forgejo-runner -data: - config.yaml: | - # Reviewed against v12.7.0 defaults (2026-02-22) - log: - level: info - - runner: - file: /data/.runner - capacity: 2 - timeout: 3h - shutdown_timeout: 3h - # Env vars injected into all job containers - envs: - DOCKER_HOST: tcp://127.0.0.1:2375 - TZ: America/Los_Angeles - - container: - # Job execution image is set via RUNNER_LABELS in deployment.yaml - network: "host" - # Connect to DinD sidecar via TCP (not socket) - docker_host: tcp://127.0.0.1:2375 - daemon.json: | - { - "registry-mirrors": ["http://host.minikube.internal:5050"] - } diff --git a/argocd/manifests/forgejo-runner/daemon.json b/argocd/manifests/forgejo-runner/daemon.json new file mode 100644 index 0000000..637acdc --- /dev/null +++ b/argocd/manifests/forgejo-runner/daemon.json @@ -0,0 +1,3 @@ +{ + "registry-mirrors": ["http://host.minikube.internal:5050"] +} diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index b4d5b88..4f67672 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -18,7 +18,7 @@ spec: containers: # Forgejo runner daemon - name: runner - image: code.forgejo.org/forgejo/runner:12.7.0 + image: code.forgejo.org/forgejo/runner env: - name: TZ value: America/Los_Angeles @@ -68,7 +68,7 @@ spec: # Docker-in-Docker sidecar - name: dind - image: docker:27-dind + image: docker securityContext: privileged: true env: diff --git a/argocd/manifests/forgejo-runner/kustomization.yaml b/argocd/manifests/forgejo-runner/kustomization.yaml new file mode 100644 index 0000000..67527de --- /dev/null +++ b/argocd/manifests/forgejo-runner/kustomization.yaml @@ -0,0 +1,21 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: forgejo-runner + +resources: + - namespace.yaml + - external-secret.yaml + - deployment.yaml + +images: + - name: code.forgejo.org/forgejo/runner + newTag: "12.7.0" + - name: docker + newTag: 27-dind + +configMapGenerator: + - name: forgejo-runner-config + files: + - config.yaml + - daemon.json diff --git a/argocd/manifests/frigate/configmap-notify.yaml b/argocd/manifests/frigate/configmap-notify.yaml deleted file mode 100644 index b638849..0000000 --- a/argocd/manifests/frigate/configmap-notify.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: frigate-notify-config - namespace: frigate -data: - config.yml: | - frigate: - server: http://frigate:5000 - public_url: https://nvr.ops.eblu.me - - webapi: - enabled: true - interval: 15 - - mqtt: - enabled: false - - alerts: - general: - title: "Frigate Alert" - nosnap: drop - snap_hires: true - notify_once: true - - zones: - unzoned: drop - allow: - - driveway_entrance - - driveway - - labels: - allow: - - person - - car - - ntfy: - enabled: true - server: http://ntfy.ntfy.svc.cluster.local:80 - topic: frigate-alerts - headers: - - X-Actions: "view, Open Event, {{.Extra.PublicURL}}/review?id={{.ID}}, clear=true; view, Open Camera, {{.Extra.PublicURL}}/#/cameras/{{.Camera}}" diff --git a/argocd/manifests/frigate/configmap.yaml b/argocd/manifests/frigate/configmap.yaml deleted file mode 100644 index 16bd4d8..0000000 --- a/argocd/manifests/frigate/configmap.yaml +++ /dev/null @@ -1,90 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: frigate-config - namespace: frigate -data: - config.yml: | - mqtt: - host: mosquitto.mqtt.svc.cluster.local - port: 1883 - - go2rtc: - streams: - # GableCam IP is reserved in UX7 DHCP config - gablecam: - - "rtsp://{FRIGATE_CAMERA_USER}:{FRIGATE_CAMERA_PASSWORD}@192.168.1.159:554/h264Preview_01_main" - gablecam_sub: - - "rtsp://{FRIGATE_CAMERA_USER}:{FRIGATE_CAMERA_PASSWORD}@192.168.1.159:554/h264Preview_01_sub" - - cameras: - gablecam: - enabled: true - ffmpeg: - inputs: - - path: rtsp://127.0.0.1:8554/gablecam - input_args: preset-rtsp-restream - roles: [record] - - path: rtsp://127.0.0.1:8554/gablecam_sub - input_args: preset-rtsp-restream - roles: [detect] - detect: - enabled: true - stationary: - max_frames: - default: 1500 - motion: - mask: - - 0.401,0.026,0.4,0.078,0.587,0.072,0.585,0.02 - - 0.881,0.422,0.789,0.245,0.595,0.054,0.531,0,0.634,0,0.824,0.192,0.892,0.307 - zones: - driveway_entrance: - coordinates: 0.841,0.37,0.735,0.344,0.681,0.2,0.78,0.259 - objects: [car, dog, person] - inertia: 3 - loitering_time: 0 - driveway: - coordinates: 0.767,0.25,0.58,0.2,0.218,0.25,0.128,0.296,0.003,0.565,0.001,0.992,0.826,0.992,0.897,0.665,0.869,0.608,0.788,0.354 - review: - alerts: - labels: [person, car] - required_zones: - - driveway_entrance - - driveway - detections: - required_zones: - - driveway - - driveway_entrance - objects: - track: [person, car, dog, cat, bird] - - detectors: - onnx: - type: onnx - - model: - model_type: yolo-generic - width: 640 - height: 640 - input_tensor: nchw - input_dtype: float - path: /media/frigate/models/yolov9-c-640.onnx - labelmap_path: /labelmap/coco-80.txt - - record: - enabled: true - continuous: - days: 3 - alerts: - retain: - days: 30 - mode: active_objects - detections: - retain: - days: 14 - mode: motion - - snapshots: - enabled: true - retain: - default: 14 diff --git a/argocd/manifests/frigate/deployment-notify.yaml b/argocd/manifests/frigate/deployment-notify.yaml index 751cdf5..a6e1361 100644 --- a/argocd/manifests/frigate/deployment-notify.yaml +++ b/argocd/manifests/frigate/deployment-notify.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: frigate-notify - image: ghcr.io/0x2142/frigate-notify:v0.5.4 + image: ghcr.io/0x2142/frigate-notify env: - name: TZ value: America/Los_Angeles diff --git a/argocd/manifests/frigate/deployment.yaml b/argocd/manifests/frigate/deployment.yaml index 1460bb3..77ac007 100644 --- a/argocd/manifests/frigate/deployment.yaml +++ b/argocd/manifests/frigate/deployment.yaml @@ -19,7 +19,7 @@ spec: runtimeClassName: nvidia initContainers: - name: copy-config - image: busybox:1.37 + image: busybox command: ["cp", "/config-ro/config.yml", "/config/config.yml"] volumeMounts: - name: config-ro @@ -28,7 +28,7 @@ spec: mountPath: /config containers: - name: frigate - image: ghcr.io/blakeblackshear/frigate:0.17.0-rc2-tensorrt + image: ghcr.io/blakeblackshear/frigate ports: - containerPort: 5000 name: http diff --git a/argocd/manifests/frigate/frigate-config.yml b/argocd/manifests/frigate/frigate-config.yml new file mode 100644 index 0000000..8f6bff4 --- /dev/null +++ b/argocd/manifests/frigate/frigate-config.yml @@ -0,0 +1,83 @@ +mqtt: + host: mosquitto.mqtt.svc.cluster.local + port: 1883 + +go2rtc: + streams: + # GableCam IP is reserved in UX7 DHCP config + gablecam: + - "rtsp://{FRIGATE_CAMERA_USER}:{FRIGATE_CAMERA_PASSWORD}@192.168.1.159:554/h264Preview_01_main" + gablecam_sub: + - "rtsp://{FRIGATE_CAMERA_USER}:{FRIGATE_CAMERA_PASSWORD}@192.168.1.159:554/h264Preview_01_sub" + +cameras: + gablecam: + enabled: true + ffmpeg: + inputs: + - path: rtsp://127.0.0.1:8554/gablecam + input_args: preset-rtsp-restream + roles: [record] + - path: rtsp://127.0.0.1:8554/gablecam_sub + input_args: preset-rtsp-restream + roles: [detect] + detect: + enabled: true + stationary: + max_frames: + default: 1500 + motion: + mask: + - 0.401,0.026,0.4,0.078,0.587,0.072,0.585,0.02 + - 0.881,0.422,0.789,0.245,0.595,0.054,0.531,0,0.634,0,0.824,0.192,0.892,0.307 + zones: + driveway_entrance: + coordinates: 0.841,0.37,0.735,0.344,0.681,0.2,0.78,0.259 + objects: [car, dog, person] + inertia: 3 + loitering_time: 0 + driveway: + coordinates: 0.767,0.25,0.58,0.2,0.218,0.25,0.128,0.296,0.003,0.565,0.001,0.992,0.826,0.992,0.897,0.665,0.869,0.608,0.788,0.354 + review: + alerts: + labels: [person, car] + required_zones: + - driveway_entrance + - driveway + detections: + required_zones: + - driveway + - driveway_entrance + objects: + track: [person, car, dog, cat, bird] + +detectors: + onnx: + type: onnx + +model: + model_type: yolo-generic + width: 640 + height: 640 + input_tensor: nchw + input_dtype: float + path: /media/frigate/models/yolov9-c-640.onnx + labelmap_path: /labelmap/coco-80.txt + +record: + enabled: true + continuous: + days: 3 + alerts: + retain: + days: 30 + mode: active_objects + detections: + retain: + days: 14 + mode: motion + +snapshots: + enabled: true + retain: + default: 14 diff --git a/argocd/manifests/frigate/frigate-notify-config.yml b/argocd/manifests/frigate/frigate-notify-config.yml new file mode 100644 index 0000000..69072df --- /dev/null +++ b/argocd/manifests/frigate/frigate-notify-config.yml @@ -0,0 +1,35 @@ +frigate: + server: http://frigate:5000 + public_url: https://nvr.ops.eblu.me + + webapi: + enabled: true + interval: 15 + + mqtt: + enabled: false + +alerts: + general: + title: "Frigate Alert" + nosnap: drop + snap_hires: true + notify_once: true + + zones: + unzoned: drop + allow: + - driveway_entrance + - driveway + + labels: + allow: + - person + - car + + ntfy: + enabled: true + server: http://ntfy.ntfy.svc.cluster.local:80 + topic: frigate-alerts + headers: + - X-Actions: "view, Open Event, {{.Extra.PublicURL}}/review?id={{.ID}}, clear=true; view, Open Camera, {{.Extra.PublicURL}}/#/cameras/{{.Camera}}" diff --git a/argocd/manifests/frigate/kustomization.yaml b/argocd/manifests/frigate/kustomization.yaml index 0610eca..ac6079c 100644 --- a/argocd/manifests/frigate/kustomization.yaml +++ b/argocd/manifests/frigate/kustomization.yaml @@ -4,8 +4,6 @@ kind: Kustomization namespace: frigate resources: - external-secret.yaml - - configmap.yaml - - configmap-notify.yaml - pv-nfs.yaml - pvc-recordings.yaml - pvc-database.yaml @@ -13,3 +11,19 @@ resources: - deployment-notify.yaml - service.yaml - ingress-tailscale.yaml + +images: + - name: busybox + newTag: "1.37" + - name: ghcr.io/blakeblackshear/frigate + newTag: 0.17.0-rc2-tensorrt + - name: ghcr.io/0x2142/frigate-notify + newTag: v0.5.4 + +configMapGenerator: + - name: frigate-config + files: + - config.yml=frigate-config.yml + - name: frigate-notify-config + files: + - config.yml=frigate-notify-config.yml diff --git a/argocd/manifests/grafana/configmap.yaml b/argocd/manifests/grafana/configmap.yaml deleted file mode 100644 index 48baebb..0000000 --- a/argocd/manifests/grafana/configmap.yaml +++ /dev/null @@ -1,100 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: grafana - namespace: monitoring - labels: - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana -data: - grafana.ini: | - [analytics] - check_for_updates = false - reporting_enabled = false - - [auth.generic_oauth] - allow_sign_up = true - api_url = https://authentik.ops.eblu.me/application/o/userinfo/ - auth_url = https://authentik.ops.eblu.me/application/o/authorize/ - auto_login = false - client_id = grafana - client_secret = $__env{GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET} - enabled = true - name = Authentik - role_attribute_path = contains(groups[*], 'admins') && 'Admin' || 'Viewer' - skip_org_role_sync = false - scopes = openid profile email - token_url = https://authentik.ops.eblu.me/application/o/token/ - - [log] - mode = console - - [paths] - data = /var/lib/grafana/ - logs = /var/log/grafana - plugins = /var/lib/grafana/plugins - provisioning = /etc/grafana/provisioning - - [security] - allow_embedding = false - - [server] - root_url = https://grafana.ops.eblu.me - - datasources.yaml: | - apiVersion: 1 - datasources: - - access: proxy - editable: false - isDefault: true - name: Prometheus - orgId: 1 - type: prometheus - uid: prometheus - url: http://prometheus.monitoring.svc.cluster.local:9090 - - access: proxy - editable: false - name: Loki - orgId: 1 - type: loki - uid: loki - url: http://loki.monitoring.svc.cluster.local:3100 - - access: proxy - database: teslamate - editable: false - jsonData: - database: teslamate - connMaxLifetime: 14400 - maxIdleConns: 2 - maxOpenConns: 5 - sslmode: disable - name: TeslaMate - orgId: 1 - secureJsonData: - password: $TESLAMATE_DB_PASSWORD - type: postgres - uid: TeslaMate - url: blumeops-pg-rw.databases.svc.cluster.local:5432 - user: teslamate ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: grafana-config-dashboards - namespace: monitoring - labels: - app.kubernetes.io/name: grafana - app.kubernetes.io/instance: grafana -data: - provider.yaml: | - apiVersion: 1 - providers: - - name: 'sidecarProvider' - orgId: 1 - type: file - disableDeletion: false - allowUiUpdates: false - updateIntervalSeconds: 30 - options: - foldersFromFilesStructure: true - path: /tmp/dashboards diff --git a/argocd/manifests/grafana/datasources.yaml b/argocd/manifests/grafana/datasources.yaml new file mode 100644 index 0000000..864fcb1 --- /dev/null +++ b/argocd/manifests/grafana/datasources.yaml @@ -0,0 +1,34 @@ +apiVersion: 1 +datasources: +- access: proxy + editable: false + isDefault: true + name: Prometheus + orgId: 1 + type: prometheus + uid: prometheus + url: http://prometheus.monitoring.svc.cluster.local:9090 +- access: proxy + editable: false + name: Loki + orgId: 1 + type: loki + uid: loki + url: http://loki.monitoring.svc.cluster.local:3100 +- access: proxy + database: teslamate + editable: false + jsonData: + database: teslamate + connMaxLifetime: 14400 + maxIdleConns: 2 + maxOpenConns: 5 + sslmode: disable + name: TeslaMate + orgId: 1 + secureJsonData: + password: $TESLAMATE_DB_PASSWORD + type: postgres + uid: TeslaMate + url: blumeops-pg-rw.databases.svc.cluster.local:5432 + user: teslamate diff --git a/argocd/manifests/grafana/deployment.yaml b/argocd/manifests/grafana/deployment.yaml index a027d01..69b7eb9 100644 --- a/argocd/manifests/grafana/deployment.yaml +++ b/argocd/manifests/grafana/deployment.yaml @@ -32,7 +32,7 @@ spec: runAsUser: 472 initContainers: - name: init-chown-data - image: docker.io/library/busybox:1.31.1 + image: docker.io/library/busybox imagePullPolicy: IfNotPresent command: ["chown", "-R", "472:472", "/var/lib/grafana"] securityContext: @@ -48,7 +48,7 @@ spec: containers: # Dashboard sidecar - watches ConfigMaps with grafana_dashboard=1 - name: grafana-sc-dashboard - image: quay.io/kiwigrid/k8s-sidecar:1.28.0 + image: quay.io/kiwigrid/k8s-sidecar imagePullPolicy: IfNotPresent env: - name: METHOD @@ -88,7 +88,7 @@ spec: mountPath: /tmp/dashboards # Grafana - name: grafana - image: registry.ops.eblu.me/blumeops/grafana:v12.3.3-d05d2fb + image: registry.ops.eblu.me/blumeops/grafana imagePullPolicy: IfNotPresent env: - name: POD_IP diff --git a/argocd/manifests/grafana/grafana.ini b/argocd/manifests/grafana/grafana.ini new file mode 100644 index 0000000..61cdd7e --- /dev/null +++ b/argocd/manifests/grafana/grafana.ini @@ -0,0 +1,32 @@ +[analytics] +check_for_updates = false +reporting_enabled = false + +[auth.generic_oauth] +allow_sign_up = true +api_url = https://authentik.ops.eblu.me/application/o/userinfo/ +auth_url = https://authentik.ops.eblu.me/application/o/authorize/ +auto_login = false +client_id = grafana +client_secret = $__env{GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET} +enabled = true +name = Authentik +role_attribute_path = contains(groups[*], 'admins') && 'Admin' || 'Viewer' +skip_org_role_sync = false +scopes = openid profile email +token_url = https://authentik.ops.eblu.me/application/o/token/ + +[log] +mode = console + +[paths] +data = /var/lib/grafana/ +logs = /var/log/grafana +plugins = /var/lib/grafana/plugins +provisioning = /etc/grafana/provisioning + +[security] +allow_embedding = false + +[server] +root_url = https://grafana.ops.eblu.me diff --git a/argocd/manifests/grafana/kustomization.yaml b/argocd/manifests/grafana/kustomization.yaml index 2707d42..f8c823e 100644 --- a/argocd/manifests/grafana/kustomization.yaml +++ b/argocd/manifests/grafana/kustomization.yaml @@ -5,8 +5,24 @@ namespace: monitoring resources: - serviceaccount.yaml - - configmap.yaml - pvc.yaml - deployment.yaml - service.yaml - rbac.yaml + +images: + - name: docker.io/library/busybox + newTag: 1.31.1 + - name: quay.io/kiwigrid/k8s-sidecar + newTag: 1.28.0 + - name: registry.ops.eblu.me/blumeops/grafana + newTag: v12.3.3-d05d2fb + +configMapGenerator: + - name: grafana + files: + - grafana.ini + - datasources.yaml + - name: grafana-config-dashboards + files: + - provider.yaml diff --git a/argocd/manifests/grafana/provider.yaml b/argocd/manifests/grafana/provider.yaml new file mode 100644 index 0000000..a35d172 --- /dev/null +++ b/argocd/manifests/grafana/provider.yaml @@ -0,0 +1,11 @@ +apiVersion: 1 +providers: + - name: 'sidecarProvider' + orgId: 1 + type: file + disableDeletion: false + allowUiUpdates: false + updateIntervalSeconds: 30 + options: + foldersFromFilesStructure: true + path: /tmp/dashboards diff --git a/argocd/manifests/homepage/bookmarks.yaml b/argocd/manifests/homepage/bookmarks.yaml new file mode 100644 index 0000000..854ff7b --- /dev/null +++ b/argocd/manifests/homepage/bookmarks.yaml @@ -0,0 +1,16 @@ +- Admin: + - Tailscale Admin: + - href: https://login.tailscale.com/admin + icon: tailscale + - 1Password: + - href: https://my.1password.com + icon: 1password + - Pulumi: + - href: https://app.pulumi.com/eblume/blumeops-tailnet + icon: si-pulumi + - ArgoCD: + - href: https://argocd.ops.eblu.me + icon: argo-cd + - UniFi: + - href: https://unifi.ui.com + icon: ubiquiti diff --git a/argocd/manifests/homepage/configmap.yaml b/argocd/manifests/homepage/configmap.yaml deleted file mode 100644 index 858b479..0000000 --- a/argocd/manifests/homepage/configmap.yaml +++ /dev/null @@ -1,155 +0,0 @@ -# Homepage configuration files -# Extracted from former Helm values.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: homepage-config - namespace: homepage -data: - bookmarks.yaml: | - - Admin: - - Tailscale Admin: - - href: https://login.tailscale.com/admin - icon: tailscale - - 1Password: - - href: https://my.1password.com - icon: 1password - - Pulumi: - - href: https://app.pulumi.com/eblume/blumeops-tailnet - icon: si-pulumi - - ArgoCD: - - href: https://argocd.ops.eblu.me - icon: argo-cd - - UniFi: - - href: https://unifi.ui.com - icon: ubiquiti - - services.yaml: | - - Host Services: - - Forgejo: - href: https://forge.ops.eblu.me - icon: forgejo - description: Git forge - widget: - type: gitea - url: https://forge.ops.eblu.me - key: "{{HOMEPAGE_VAR_FORGEJO_API_KEY}}" - - Registry: - href: https://registry.ops.eblu.me - icon: zot-registry - description: Container registry - - Sifaka NAS: - href: https://nas.ops.eblu.me - icon: synology - description: NAS dashboard - widget: - type: prometheusmetric - url: https://prometheus.ops.eblu.me - metrics: - - label: Used - query: node_filesystem_size_bytes{mountpoint="/Volumes/backups"} - node_filesystem_avail_bytes{mountpoint="/Volumes/backups"} - format: - type: bytes - - label: Total - query: node_filesystem_size_bytes{mountpoint="/Volumes/backups"} - format: - type: bytes - - Borgmatic: - href: https://grafana.ops.eblu.me/d/borgmatic - icon: borgmatic - description: Backup system - widget: - type: prometheusmetric - url: https://prometheus.ops.eblu.me - metrics: - - label: Last backup - query: time() - borgmatic_last_archive_timestamp - format: - type: duration - - label: Archive size - query: borgmatic_repo_deduplicated_size_bytes - format: - type: bytes - - Jellyfin: - href: https://jellyfin.ops.eblu.me - icon: jellyfin - description: Media server - widget: - type: jellyfin - url: https://jellyfin.ops.eblu.me - key: "{{HOMEPAGE_VAR_JELLYFIN_API_KEY}}" - enableBlocks: true - enableNowPlaying: true - # TODO: Add Caddy widget when admin API is enabled (currently admin off) - # - Caddy: - # href: https://indri.tail8d86e.ts.net - # icon: caddy - # description: Reverse proxy - # widget: - # type: caddy - # url: http://indri.tail8d86e.ts.net:2019 - - Infrastructure: - - Authentik: - href: https://authentik.ops.eblu.me - icon: authentik - description: Identity provider - - NVR: - href: https://nvr.ops.eblu.me - icon: frigate.png - description: Network video recorder - - Ntfy: - href: https://ntfy.ops.eblu.me - icon: ntfy.png - description: Push notifications - - widgets.yaml: | - - greeting: - text_size: xl - text: Welcome to Blue Mops - - datetime: - text_size: lg - format: - dateStyle: long - timeStyle: short - hour12: true - - openweathermap: - label: Camano - latitude: 48.18235 - longitude: -122.52590 - units: imperial - provider: openweathermap - apiKey: "{{HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY}}" - cache: 15 - # TODO: Add UniFi widget when controller is set up - # - unifi_console: - # url: https://192.168.1.1 - # username: homepage - # password: "{{HOMEPAGE_VAR_UNIFI_PASSWORD}}" - # TODO: Add Glances widget when Glances is deployed - # - glances: - # url: http://indri.tail8d86e.ts.net:61208 - # metric: cpu - - kubernetes.yaml: | - mode: cluster - - docker.yaml: "" - - settings.yaml: | - title: BlumeOps - headerStyle: boxed - quicklaunch: - searchDescriptions: true - showSearchSuggestions: true - provider: custom - url: https://kagi.com/search?q= - suggestionUrl: https://kagisuggest.com/api/autosuggest?q= - layout: - Host Services: - style: column - Content: - style: column - Infrastructure: - style: column - Services: - style: column diff --git a/argocd/manifests/homepage/deployment.yaml b/argocd/manifests/homepage/deployment.yaml index f349399..203e06d 100644 --- a/argocd/manifests/homepage/deployment.yaml +++ b/argocd/manifests/homepage/deployment.yaml @@ -20,7 +20,7 @@ spec: fsGroup: 1000 containers: - name: homepage - image: registry.ops.eblu.me/blumeops/homepage:v1.10.1-a72a0d8 + image: registry.ops.eblu.me/blumeops/homepage securityContext: runAsNonRoot: true allowPrivilegeEscalation: false diff --git a/argocd/manifests/homepage/docker.yaml b/argocd/manifests/homepage/docker.yaml new file mode 100644 index 0000000..e69de29 diff --git a/argocd/manifests/homepage/kubernetes.yaml b/argocd/manifests/homepage/kubernetes.yaml new file mode 100644 index 0000000..0f259a0 --- /dev/null +++ b/argocd/manifests/homepage/kubernetes.yaml @@ -0,0 +1 @@ +mode: cluster diff --git a/argocd/manifests/homepage/kustomization.yaml b/argocd/manifests/homepage/kustomization.yaml index eb0d217..e66796a 100644 --- a/argocd/manifests/homepage/kustomization.yaml +++ b/argocd/manifests/homepage/kustomization.yaml @@ -5,7 +5,6 @@ resources: - serviceaccount.yaml - clusterrole.yaml - clusterrolebinding.yaml - - configmap.yaml - deployment.yaml - service.yaml - ingress-tailscale.yaml @@ -15,3 +14,17 @@ resources: - external-secret-grafana.yaml - external-secret-miniflux.yaml - external-secret-navidrome.yaml + +images: + - name: registry.ops.eblu.me/blumeops/homepage + newTag: v1.10.1-a72a0d8 + +configMapGenerator: + - name: homepage-config + files: + - bookmarks.yaml + - services.yaml + - widgets.yaml + - kubernetes.yaml + - docker.yaml + - settings.yaml diff --git a/argocd/manifests/homepage/services.yaml b/argocd/manifests/homepage/services.yaml new file mode 100644 index 0000000..0f35e9c --- /dev/null +++ b/argocd/manifests/homepage/services.yaml @@ -0,0 +1,76 @@ +- Host Services: + - Forgejo: + href: https://forge.ops.eblu.me + icon: forgejo + description: Git forge + widget: + type: gitea + url: https://forge.ops.eblu.me + key: "{{HOMEPAGE_VAR_FORGEJO_API_KEY}}" + - Registry: + href: https://registry.ops.eblu.me + icon: zot-registry + description: Container registry + - Sifaka NAS: + href: https://nas.ops.eblu.me + icon: synology + description: NAS dashboard + widget: + type: prometheusmetric + url: https://prometheus.ops.eblu.me + metrics: + - label: Used + query: node_filesystem_size_bytes{mountpoint="/Volumes/backups"} - node_filesystem_avail_bytes{mountpoint="/Volumes/backups"} + format: + type: bytes + - label: Total + query: node_filesystem_size_bytes{mountpoint="/Volumes/backups"} + format: + type: bytes + - Borgmatic: + href: https://grafana.ops.eblu.me/d/borgmatic + icon: borgmatic + description: Backup system + widget: + type: prometheusmetric + url: https://prometheus.ops.eblu.me + metrics: + - label: Last backup + query: time() - borgmatic_last_archive_timestamp + format: + type: duration + - label: Archive size + query: borgmatic_repo_deduplicated_size_bytes + format: + type: bytes + - Jellyfin: + href: https://jellyfin.ops.eblu.me + icon: jellyfin + description: Media server + widget: + type: jellyfin + url: https://jellyfin.ops.eblu.me + key: "{{HOMEPAGE_VAR_JELLYFIN_API_KEY}}" + enableBlocks: true + enableNowPlaying: true + # TODO: Add Caddy widget when admin API is enabled (currently admin off) + # - Caddy: + # href: https://indri.tail8d86e.ts.net + # icon: caddy + # description: Reverse proxy + # widget: + # type: caddy + # url: http://indri.tail8d86e.ts.net:2019 +- Infrastructure: + - Authentik: + href: https://authentik.ops.eblu.me + icon: authentik + description: Identity provider + - NVR: + href: https://nvr.ops.eblu.me + icon: frigate.png + description: Network video recorder + - Ntfy: + href: https://ntfy.ops.eblu.me + icon: ntfy.png + description: Push notifications diff --git a/argocd/manifests/homepage/settings.yaml b/argocd/manifests/homepage/settings.yaml new file mode 100644 index 0000000..f8c06fc --- /dev/null +++ b/argocd/manifests/homepage/settings.yaml @@ -0,0 +1,17 @@ +title: BlumeOps +headerStyle: boxed +quicklaunch: + searchDescriptions: true + showSearchSuggestions: true + provider: custom + url: https://kagi.com/search?q= + suggestionUrl: https://kagisuggest.com/api/autosuggest?q= +layout: + Host Services: + style: column + Content: + style: column + Infrastructure: + style: column + Services: + style: column diff --git a/argocd/manifests/homepage/widgets.yaml b/argocd/manifests/homepage/widgets.yaml new file mode 100644 index 0000000..097d806 --- /dev/null +++ b/argocd/manifests/homepage/widgets.yaml @@ -0,0 +1,26 @@ +- greeting: + text_size: xl + text: Welcome to Blue Mops +- datetime: + text_size: lg + format: + dateStyle: long + timeStyle: short + hour12: true +- openweathermap: + label: Camano + latitude: 48.18235 + longitude: -122.52590 + units: imperial + provider: openweathermap + apiKey: "{{HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY}}" + cache: 15 +# TODO: Add UniFi widget when controller is set up +# - unifi_console: +# url: https://192.168.1.1 +# username: homepage +# password: "{{HOMEPAGE_VAR_UNIFI_PASSWORD}}" +# TODO: Add Glances widget when Glances is deployed +# - glances: +# url: http://indri.tail8d86e.ts.net:61208 +# metric: cpu diff --git a/argocd/manifests/kiwix/configmap-sync-script.yaml b/argocd/manifests/kiwix/configmap-sync-script.yaml deleted file mode 100644 index 5be8cae..0000000 --- a/argocd/manifests/kiwix/configmap-sync-script.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: zim-torrent-sync-script - namespace: kiwix -data: - sync-zim-torrents.sh: | - #!/bin/bash - # Sync ZIM torrents from kiwix ConfigMap to Transmission - # Runs as a sidecar in the kiwix deployment - set -euo pipefail - - TORRENT_LIST="${TORRENT_LIST:-/config/torrents.txt}" - TRANSMISSION_HOST="${TRANSMISSION_HOST:-transmission.torrent.svc.cluster.local}" - TRANSMISSION_PORT="${TRANSMISSION_PORT:-9091}" - - echo "Syncing ZIM torrents to transmission at ${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" - - # Wait for transmission to be ready - # Transmission RPC returns 409 on first request (to provide session ID), which is fine - echo "Waiting for Transmission RPC..." - max_attempts=30 - attempt=0 - until curl -s -o /dev/null -w "%{http_code}" "http://${TRANSMISSION_HOST}:${TRANSMISSION_PORT}/transmission/rpc" | grep -qE "^(200|409)$"; do - attempt=$((attempt + 1)) - if [[ $attempt -ge $max_attempts ]]; then - echo "Transmission not ready after ${max_attempts} attempts, will retry next cycle" - exit 0 # Don't fail, just skip this sync - fi - sleep 10 - done - echo "Transmission is ready" - - # Get current torrents from transmission - # transmission-remote returns header + data + footer, extract just torrent names - current=$(transmission-remote "${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" -l 2>/dev/null | \ - tail -n +2 | head -n -1 | awk '{print $NF}' || true) - - added=0 - skipped=0 - - while IFS= read -r url || [[ -n "$url" ]]; do - # Skip empty lines and comments - [[ -z "$url" || "$url" =~ ^[[:space:]]*# ]] && continue - # Trim whitespace - url=$(echo "$url" | xargs) - [[ -z "$url" ]] && continue - - # Extract base name from URL (remove .torrent extension) - basename=$(basename "$url" .torrent) - # Also try without .zim in case transmission reports it differently - basename_no_zim="${basename%.zim}" - - # Check if already in transmission - if echo "$current" | grep -qF "$basename_no_zim"; then - ((skipped++)) || true - else - if transmission-remote "${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" -a "$url" 2>/dev/null; then - echo "Added: $basename" - ((added++)) || true - else - echo "Warning: Failed to add $url" >&2 - fi - fi - done < "$TORRENT_LIST" - - echo "Sync complete: $added added, $skipped already present" diff --git a/argocd/manifests/kiwix/configmap-zim-torrents.yaml b/argocd/manifests/kiwix/configmap-zim-torrents.yaml deleted file mode 100644 index c862f21..0000000 --- a/argocd/manifests/kiwix/configmap-zim-torrents.yaml +++ /dev/null @@ -1,69 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: kiwix-zim-torrents - namespace: kiwix -data: - torrents.txt: | - # Declarative ZIM archive torrent URLs - # These are synced to transmission automatically by the kiwix sidecar - # Format: one URL per line, comments start with # - # - # Users can also add ZIM torrents manually via torrent.tail8d86e.ts.net - # and kiwix will pick them up automatically. - - # Wikipedia - Top 1M English articles (43G) - https://download.kiwix.org/zim/wikipedia/wikipedia_en_top1m_maxi_2025-09.zim.torrent - - # Project Gutenberg - Public domain books (72G) - https://download.kiwix.org/zim/gutenberg/gutenberg_en_all_2023-08.zim.torrent - - # iFixit - Repair guides (3.3G) - https://download.kiwix.org/zim/ifixit/ifixit_en_all_2025-12.zim.torrent - - # Stack Exchange - https://download.kiwix.org/zim/stack_exchange/superuser.com_en_all_2025-12.zim.torrent - https://download.kiwix.org/zim/stack_exchange/math.stackexchange.com_en_all_2025-12.zim.torrent - - # LibreTexts - Open educational resources - https://download.kiwix.org/zim/libretexts/libretexts.org_en_bio_2025-01.zim.torrent - https://download.kiwix.org/zim/libretexts/libretexts.org_en_chem_2025-01.zim.torrent - https://download.kiwix.org/zim/libretexts/libretexts.org_en_eng_2025-01.zim.torrent - https://download.kiwix.org/zim/libretexts/libretexts.org_en_math_2025-01.zim.torrent - https://download.kiwix.org/zim/libretexts/libretexts.org_en_phys_2025-01.zim.torrent - https://download.kiwix.org/zim/libretexts/libretexts.org_en_human_2025-01.zim.torrent - - # DevDocs - Programming documentation - https://download.kiwix.org/zim/devdocs/devdocs_en_bash_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_c_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_click_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_cmake_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_cpp_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_css_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_django-rest-framework_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_django_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_docker_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_duckdb_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_fish_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_gcc_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_git_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_go_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_godot_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_hammerspoon_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_homebrew_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_javascript_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_kubectl_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_kubernetes_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_latex_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_lua_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_markdown_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_nginx_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_nix_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_postgresql_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_python_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_redis_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_sqlite_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_typescript_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_werkzeug_2026-01.zim.torrent - https://download.kiwix.org/zim/devdocs/devdocs_en_zig_2026-01.zim.torrent diff --git a/argocd/manifests/kiwix/cronjob-zim-watcher.yaml b/argocd/manifests/kiwix/cronjob-zim-watcher.yaml index fc8e76a..e6fc7ac 100644 --- a/argocd/manifests/kiwix/cronjob-zim-watcher.yaml +++ b/argocd/manifests/kiwix/cronjob-zim-watcher.yaml @@ -15,7 +15,7 @@ spec: serviceAccountName: zim-watcher containers: - name: watcher - image: registry.ops.eblu.me/blumeops/kubectl:v1.34.4-a72a0d8 + image: registry.ops.eblu.me/blumeops/kubectl command: ["/bin/bash", "-c"] args: - | diff --git a/argocd/manifests/kiwix/deployment.yaml b/argocd/manifests/kiwix/deployment.yaml index 0b51914..bf97f1c 100644 --- a/argocd/manifests/kiwix/deployment.yaml +++ b/argocd/manifests/kiwix/deployment.yaml @@ -20,7 +20,7 @@ spec: containers: # Main kiwix-serve container - name: kiwix-serve - image: registry.ops.eblu.me/blumeops/kiwix-serve:v3.8.1-ffa8727 + image: registry.ops.eblu.me/blumeops/kiwix-serve args: - "/bin/sh" - "-c" @@ -53,7 +53,7 @@ spec: # Sidecar: Syncs declarative ZIM torrents to transmission - name: torrent-sync - image: registry.ops.eblu.me/blumeops/transmission:v4.0.6-r4-ffa8727 + image: registry.ops.eblu.me/blumeops/transmission command: ["/bin/bash", "-c"] args: - | diff --git a/argocd/manifests/kiwix/kustomization.yaml b/argocd/manifests/kiwix/kustomization.yaml index d5e563d..0be5f03 100644 --- a/argocd/manifests/kiwix/kustomization.yaml +++ b/argocd/manifests/kiwix/kustomization.yaml @@ -3,9 +3,23 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: kiwix resources: - - configmap-zim-torrents.yaml - - configmap-sync-script.yaml - deployment.yaml - service.yaml - ingress-tailscale.yaml - cronjob-zim-watcher.yaml + +images: + - name: registry.ops.eblu.me/blumeops/kiwix-serve + newTag: v3.8.1-ffa8727 + - name: registry.ops.eblu.me/blumeops/transmission + newTag: v4.0.6-r4-ffa8727 + - name: registry.ops.eblu.me/blumeops/kubectl + newTag: v1.34.4-a72a0d8 + +configMapGenerator: + - name: kiwix-zim-torrents + files: + - torrents.txt + - name: zim-torrent-sync-script + files: + - sync-zim-torrents.sh diff --git a/argocd/manifests/kiwix/sync-zim-torrents.sh b/argocd/manifests/kiwix/sync-zim-torrents.sh new file mode 100644 index 0000000..df3e2b1 --- /dev/null +++ b/argocd/manifests/kiwix/sync-zim-torrents.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Sync ZIM torrents from kiwix ConfigMap to Transmission +# Runs as a sidecar in the kiwix deployment +set -euo pipefail + +TORRENT_LIST="${TORRENT_LIST:-/config/torrents.txt}" +TRANSMISSION_HOST="${TRANSMISSION_HOST:-transmission.torrent.svc.cluster.local}" +TRANSMISSION_PORT="${TRANSMISSION_PORT:-9091}" + +echo "Syncing ZIM torrents to transmission at ${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" + +# Wait for transmission to be ready +# Transmission RPC returns 409 on first request (to provide session ID), which is fine +echo "Waiting for Transmission RPC..." +max_attempts=30 +attempt=0 +until curl -s -o /dev/null -w "%{http_code}" "http://${TRANSMISSION_HOST}:${TRANSMISSION_PORT}/transmission/rpc" | grep -qE "^(200|409)$"; do + attempt=$((attempt + 1)) + if [[ $attempt -ge $max_attempts ]]; then + echo "Transmission not ready after ${max_attempts} attempts, will retry next cycle" + exit 0 # Don't fail, just skip this sync + fi + sleep 10 +done +echo "Transmission is ready" + +# Get current torrents from transmission +# transmission-remote returns header + data + footer, extract just torrent names +current=$(transmission-remote "${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" -l 2>/dev/null | \ + tail -n +2 | head -n -1 | awk '{print $NF}' || true) + +added=0 +skipped=0 + +while IFS= read -r url || [[ -n "$url" ]]; do + # Skip empty lines and comments + [[ -z "$url" || "$url" =~ ^[[:space:]]*# ]] && continue + # Trim whitespace + url=$(echo "$url" | xargs) + [[ -z "$url" ]] && continue + + # Extract base name from URL (remove .torrent extension) + basename=$(basename "$url" .torrent) + # Also try without .zim in case transmission reports it differently + basename_no_zim="${basename%.zim}" + + # Check if already in transmission + if echo "$current" | grep -qF "$basename_no_zim"; then + ((skipped++)) || true + else + if transmission-remote "${TRANSMISSION_HOST}:${TRANSMISSION_PORT}" -a "$url" 2>/dev/null; then + echo "Added: $basename" + ((added++)) || true + else + echo "Warning: Failed to add $url" >&2 + fi + fi +done < "$TORRENT_LIST" + +echo "Sync complete: $added added, $skipped already present" diff --git a/argocd/manifests/kiwix/torrents.txt b/argocd/manifests/kiwix/torrents.txt new file mode 100644 index 0000000..a7282a9 --- /dev/null +++ b/argocd/manifests/kiwix/torrents.txt @@ -0,0 +1,61 @@ +# Declarative ZIM archive torrent URLs +# These are synced to transmission automatically by the kiwix sidecar +# Format: one URL per line, comments start with # +# +# Users can also add ZIM torrents manually via torrent.tail8d86e.ts.net +# and kiwix will pick them up automatically. + +# Wikipedia - Top 1M English articles (43G) +https://download.kiwix.org/zim/wikipedia/wikipedia_en_top1m_maxi_2025-09.zim.torrent + +# Project Gutenberg - Public domain books (72G) +https://download.kiwix.org/zim/gutenberg/gutenberg_en_all_2023-08.zim.torrent + +# iFixit - Repair guides (3.3G) +https://download.kiwix.org/zim/ifixit/ifixit_en_all_2025-12.zim.torrent + +# Stack Exchange +https://download.kiwix.org/zim/stack_exchange/superuser.com_en_all_2025-12.zim.torrent +https://download.kiwix.org/zim/stack_exchange/math.stackexchange.com_en_all_2025-12.zim.torrent + +# LibreTexts - Open educational resources +https://download.kiwix.org/zim/libretexts/libretexts.org_en_bio_2025-01.zim.torrent +https://download.kiwix.org/zim/libretexts/libretexts.org_en_chem_2025-01.zim.torrent +https://download.kiwix.org/zim/libretexts/libretexts.org_en_eng_2025-01.zim.torrent +https://download.kiwix.org/zim/libretexts/libretexts.org_en_math_2025-01.zim.torrent +https://download.kiwix.org/zim/libretexts/libretexts.org_en_phys_2025-01.zim.torrent +https://download.kiwix.org/zim/libretexts/libretexts.org_en_human_2025-01.zim.torrent + +# DevDocs - Programming documentation +https://download.kiwix.org/zim/devdocs/devdocs_en_bash_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_c_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_click_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_cmake_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_cpp_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_css_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_django-rest-framework_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_django_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_docker_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_duckdb_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_fish_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_gcc_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_git_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_go_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_godot_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_hammerspoon_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_homebrew_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_javascript_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_kubectl_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_kubernetes_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_latex_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_lua_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_markdown_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_nginx_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_nix_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_postgresql_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_python_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_redis_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_sqlite_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_typescript_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_werkzeug_2026-01.zim.torrent +https://download.kiwix.org/zim/devdocs/devdocs_en_zig_2026-01.zim.torrent diff --git a/argocd/manifests/kube-state-metrics/deployment.yaml b/argocd/manifests/kube-state-metrics/deployment.yaml index 2ba12ba..cba8cac 100644 --- a/argocd/manifests/kube-state-metrics/deployment.yaml +++ b/argocd/manifests/kube-state-metrics/deployment.yaml @@ -18,7 +18,7 @@ spec: serviceAccountName: kube-state-metrics containers: - name: kube-state-metrics - image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0 + image: registry.k8s.io/kube-state-metrics/kube-state-metrics ports: - containerPort: 8080 name: http-metrics diff --git a/argocd/manifests/kube-state-metrics/kustomization.yaml b/argocd/manifests/kube-state-metrics/kustomization.yaml index bc60c0b..005cba8 100644 --- a/argocd/manifests/kube-state-metrics/kustomization.yaml +++ b/argocd/manifests/kube-state-metrics/kustomization.yaml @@ -4,3 +4,6 @@ resources: - rbac.yaml - deployment.yaml - service.yaml +images: + - name: registry.k8s.io/kube-state-metrics/kube-state-metrics + newTag: v2.18.0 diff --git a/argocd/manifests/loki/configmap.yaml b/argocd/manifests/loki/configmap.yaml deleted file mode 100644 index 19c516b..0000000 --- a/argocd/manifests/loki/configmap.yaml +++ /dev/null @@ -1,58 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: loki-config - namespace: monitoring -data: - loki-config.yaml: | - auth_enabled: false - - server: - http_listen_port: 3100 - http_listen_address: 0.0.0.0 - grpc_listen_port: 9096 - - common: - instance_addr: 127.0.0.1 - path_prefix: /loki - storage: - filesystem: - chunks_directory: /loki/chunks - rules_directory: /loki/rules - replication_factor: 1 - ring: - kvstore: - store: inmemory - - query_range: - results_cache: - cache: - embedded_cache: - enabled: true - max_size_mb: 100 - - schema_config: - configs: - - from: 2024-01-01 - store: tsdb - object_store: filesystem - schema: v13 - index: - prefix: index_ - period: 24h - - storage_config: - tsdb_shipper: - active_index_directory: /loki/tsdb-index - cache_location: /loki/tsdb-cache - - limits_config: - retention_period: 744h # 31 days - - compactor: - working_directory: /loki/compactor - compaction_interval: 10m - retention_enabled: true - retention_delete_delay: 2h - retention_delete_worker_count: 150 - delete_request_store: filesystem diff --git a/argocd/manifests/loki/kustomization.yaml b/argocd/manifests/loki/kustomization.yaml index 1c65acb..c305868 100644 --- a/argocd/manifests/loki/kustomization.yaml +++ b/argocd/manifests/loki/kustomization.yaml @@ -4,7 +4,15 @@ kind: Kustomization namespace: monitoring resources: - - configmap.yaml - statefulset.yaml - service.yaml - ingress-tailscale.yaml + +images: + - name: grafana/loki + newTag: "3.6.5" + +configMapGenerator: + - name: loki-config + files: + - loki-config.yaml diff --git a/argocd/manifests/loki/loki-config.yaml b/argocd/manifests/loki/loki-config.yaml new file mode 100644 index 0000000..5210f1b --- /dev/null +++ b/argocd/manifests/loki/loki-config.yaml @@ -0,0 +1,51 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + http_listen_address: 0.0.0.0 + grpc_listen_port: 9096 + +common: + instance_addr: 127.0.0.1 + path_prefix: /loki + storage: + filesystem: + chunks_directory: /loki/chunks + rules_directory: /loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +schema_config: + configs: + - from: 2024-01-01 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + tsdb_shipper: + active_index_directory: /loki/tsdb-index + cache_location: /loki/tsdb-cache + +limits_config: + retention_period: 744h # 31 days + +compactor: + working_directory: /loki/compactor + compaction_interval: 10m + retention_enabled: true + retention_delete_delay: 2h + retention_delete_worker_count: 150 + delete_request_store: filesystem diff --git a/argocd/manifests/loki/statefulset.yaml b/argocd/manifests/loki/statefulset.yaml index d3a75a7..5683eab 100644 --- a/argocd/manifests/loki/statefulset.yaml +++ b/argocd/manifests/loki/statefulset.yaml @@ -20,7 +20,7 @@ spec: runAsUser: 10001 containers: - name: loki - image: grafana/loki:3.6.5 + image: grafana/loki args: - -config.file=/etc/loki/loki-config.yaml ports: diff --git a/argocd/manifests/miniflux/deployment.yaml b/argocd/manifests/miniflux/deployment.yaml index 668471e..e139de3 100644 --- a/argocd/manifests/miniflux/deployment.yaml +++ b/argocd/manifests/miniflux/deployment.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: miniflux - image: registry.ops.eblu.me/blumeops/miniflux:v2.2.17-a72a0d8 + image: registry.ops.eblu.me/blumeops/miniflux ports: - containerPort: 8080 env: diff --git a/argocd/manifests/miniflux/kustomization.yaml b/argocd/manifests/miniflux/kustomization.yaml index 80927eb..992c8ae 100644 --- a/argocd/manifests/miniflux/kustomization.yaml +++ b/argocd/manifests/miniflux/kustomization.yaml @@ -7,3 +7,7 @@ resources: - deployment.yaml - service.yaml - ingress-tailscale.yaml + +images: + - name: registry.ops.eblu.me/blumeops/miniflux + newTag: v2.2.17-a72a0d8 diff --git a/argocd/manifests/mosquitto/configmap.yaml b/argocd/manifests/mosquitto/configmap.yaml deleted file mode 100644 index c0cd870..0000000 --- a/argocd/manifests/mosquitto/configmap.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: mosquitto-config - namespace: mqtt -data: - mosquitto.conf: | - listener 1883 - allow_anonymous true - persistence false diff --git a/argocd/manifests/mosquitto/deployment.yaml b/argocd/manifests/mosquitto/deployment.yaml index 55abf96..b58d926 100644 --- a/argocd/manifests/mosquitto/deployment.yaml +++ b/argocd/manifests/mosquitto/deployment.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: mosquitto - image: eclipse-mosquitto:2.0.22 + image: eclipse-mosquitto ports: - containerPort: 1883 name: mqtt diff --git a/argocd/manifests/mosquitto/kustomization.yaml b/argocd/manifests/mosquitto/kustomization.yaml index 5a9cfa1..e468120 100644 --- a/argocd/manifests/mosquitto/kustomization.yaml +++ b/argocd/manifests/mosquitto/kustomization.yaml @@ -3,6 +3,12 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: mqtt resources: - - configmap.yaml - deployment.yaml - service.yaml +images: + - name: eclipse-mosquitto + newTag: "2.0.22" +configMapGenerator: + - name: mosquitto-config + files: + - mosquitto.conf diff --git a/argocd/manifests/mosquitto/mosquitto.conf b/argocd/manifests/mosquitto/mosquitto.conf new file mode 100644 index 0000000..0e3b61e --- /dev/null +++ b/argocd/manifests/mosquitto/mosquitto.conf @@ -0,0 +1,3 @@ +listener 1883 +allow_anonymous true +persistence false diff --git a/argocd/manifests/navidrome/deployment.yaml b/argocd/manifests/navidrome/deployment.yaml index 28eaf2d..5da3948 100644 --- a/argocd/manifests/navidrome/deployment.yaml +++ b/argocd/manifests/navidrome/deployment.yaml @@ -20,7 +20,7 @@ spec: fsGroup: 1000 containers: - name: navidrome - image: registry.ops.eblu.me/blumeops/navidrome:v0.60.3-a72a0d8 + image: registry.ops.eblu.me/blumeops/navidrome securityContext: runAsNonRoot: true allowPrivilegeEscalation: false diff --git a/argocd/manifests/navidrome/kustomization.yaml b/argocd/manifests/navidrome/kustomization.yaml index bf62c5b..b625c82 100644 --- a/argocd/manifests/navidrome/kustomization.yaml +++ b/argocd/manifests/navidrome/kustomization.yaml @@ -9,3 +9,6 @@ resources: - deployment.yaml - service.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/navidrome + newTag: v0.60.3-a72a0d8 diff --git a/argocd/manifests/ntfy/configmap.yaml b/argocd/manifests/ntfy/configmap.yaml deleted file mode 100644 index 584eba1..0000000 --- a/argocd/manifests/ntfy/configmap.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: ntfy-config - namespace: ntfy -data: - server.yml: | - base-url: https://ntfy.ops.eblu.me - upstream-base-url: https://ntfy.sh - attachment-cache-dir: /var/cache/ntfy/attachments - attachment-total-size-limit: 1G - attachment-file-size-limit: 10M - attachment-expiry-duration: 24h diff --git a/argocd/manifests/ntfy/deployment.yaml b/argocd/manifests/ntfy/deployment.yaml index d471394..2a58604 100644 --- a/argocd/manifests/ntfy/deployment.yaml +++ b/argocd/manifests/ntfy/deployment.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: ntfy - image: registry.ops.eblu.me/blumeops/ntfy:v2.17.0-ffa8727-nix + image: registry.ops.eblu.me/blumeops/ntfy args: ["serve", "--config", "/etc/ntfy/server.yml"] ports: - containerPort: 80 diff --git a/argocd/manifests/ntfy/kustomization.yaml b/argocd/manifests/ntfy/kustomization.yaml index 851171f..a4852ac 100644 --- a/argocd/manifests/ntfy/kustomization.yaml +++ b/argocd/manifests/ntfy/kustomization.yaml @@ -3,7 +3,13 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: ntfy resources: - - configmap.yaml - deployment.yaml - service.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/ntfy + newTag: v2.17.0-ffa8727-nix +configMapGenerator: + - name: ntfy-config + files: + - server.yml diff --git a/argocd/manifests/ntfy/server.yml b/argocd/manifests/ntfy/server.yml new file mode 100644 index 0000000..0f53b0c --- /dev/null +++ b/argocd/manifests/ntfy/server.yml @@ -0,0 +1,6 @@ +base-url: https://ntfy.ops.eblu.me +upstream-base-url: https://ntfy.sh +attachment-cache-dir: /var/cache/ntfy/attachments +attachment-total-size-limit: 1G +attachment-file-size-limit: 10M +attachment-expiry-duration: 24h diff --git a/argocd/manifests/nvidia-device-plugin/daemonset.yaml b/argocd/manifests/nvidia-device-plugin/daemonset.yaml index 479d6e9..4c57a76 100644 --- a/argocd/manifests/nvidia-device-plugin/daemonset.yaml +++ b/argocd/manifests/nvidia-device-plugin/daemonset.yaml @@ -22,7 +22,7 @@ spec: priorityClassName: system-node-critical containers: - name: nvidia-device-plugin - image: nvcr.io/nvidia/k8s-device-plugin:v0.18.2 + image: nvcr.io/nvidia/k8s-device-plugin args: - --device-id-strategy=index env: diff --git a/argocd/manifests/nvidia-device-plugin/kustomization.yaml b/argocd/manifests/nvidia-device-plugin/kustomization.yaml new file mode 100644 index 0000000..4ffe2d9 --- /dev/null +++ b/argocd/manifests/nvidia-device-plugin/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: nvidia-device-plugin + +resources: + - daemonset.yaml + - runtime-class.yaml + +images: + - name: nvcr.io/nvidia/k8s-device-plugin + newTag: v0.18.2 diff --git a/argocd/manifests/prometheus/configmap.yaml b/argocd/manifests/prometheus/configmap.yaml deleted file mode 100644 index 09baf83..0000000 --- a/argocd/manifests/prometheus/configmap.yaml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: prometheus-config - namespace: monitoring -data: - prometheus.yml: | - global: - scrape_interval: 15s - evaluation_interval: 15s - - # Indri system metrics are pushed via Alloy remote_write - # K8s services are scraped directly - - scrape_configs: - # Sifaka NAS exporters (via Caddy L4 TCP proxy on indri) - - job_name: "node-exporter-sifaka" - static_configs: - - targets: ["nas.ops.eblu.me:9100"] - - - job_name: "smartctl-sifaka" - scrape_interval: 60s - static_configs: - - targets: ["nas.ops.eblu.me:9633"] - - # CNPG PostgreSQL metrics (k8s internal) - - job_name: "cnpg-postgres" - static_configs: - - targets: ["blumeops-pg-metrics-tailscale.databases.svc.cluster.local:9187"] - labels: - instance: "blumeops-pg" - - # Prometheus self-monitoring - - job_name: "prometheus" - static_configs: - - targets: ["localhost:9090"] - - # Loki metrics - - job_name: "loki" - static_configs: - - targets: ["loki.monitoring.svc.cluster.local:3100"] - - # Kubernetes state metrics (pods, deployments, resource usage, etc.) - - job_name: "kube-state-metrics" - static_configs: - - targets: ["kube-state-metrics.monitoring.svc.cluster.local:8080"] - - # Frigate NVR metrics (via Caddy on indri — Frigate runs on ringtail) - - job_name: "frigate" - scheme: https - static_configs: - - targets: ["nvr.ops.eblu.me"] - metrics_path: /api/metrics diff --git a/argocd/manifests/prometheus/kustomization.yaml b/argocd/manifests/prometheus/kustomization.yaml index 1c65acb..de9fdd2 100644 --- a/argocd/manifests/prometheus/kustomization.yaml +++ b/argocd/manifests/prometheus/kustomization.yaml @@ -4,7 +4,15 @@ kind: Kustomization namespace: monitoring resources: - - configmap.yaml - statefulset.yaml - service.yaml - ingress-tailscale.yaml + +images: + - name: registry.ops.eblu.me/blumeops/prometheus + newTag: v3.9.1-2ba5d8a + +configMapGenerator: + - name: prometheus-config + files: + - prometheus.yml diff --git a/argocd/manifests/prometheus/prometheus.yml b/argocd/manifests/prometheus/prometheus.yml new file mode 100644 index 0000000..09d1f4f --- /dev/null +++ b/argocd/manifests/prometheus/prometheus.yml @@ -0,0 +1,46 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +# Indri system metrics are pushed via Alloy remote_write +# K8s services are scraped directly + +scrape_configs: + # Sifaka NAS exporters (via Caddy L4 TCP proxy on indri) + - job_name: "node-exporter-sifaka" + static_configs: + - targets: ["nas.ops.eblu.me:9100"] + + - job_name: "smartctl-sifaka" + scrape_interval: 60s + static_configs: + - targets: ["nas.ops.eblu.me:9633"] + + # CNPG PostgreSQL metrics (k8s internal) + - job_name: "cnpg-postgres" + static_configs: + - targets: ["blumeops-pg-metrics-tailscale.databases.svc.cluster.local:9187"] + labels: + instance: "blumeops-pg" + + # Prometheus self-monitoring + - job_name: "prometheus" + static_configs: + - targets: ["localhost:9090"] + + # Loki metrics + - job_name: "loki" + static_configs: + - targets: ["loki.monitoring.svc.cluster.local:3100"] + + # Kubernetes state metrics (pods, deployments, resource usage, etc.) + - job_name: "kube-state-metrics" + static_configs: + - targets: ["kube-state-metrics.monitoring.svc.cluster.local:8080"] + + # Frigate NVR metrics (via Caddy on indri — Frigate runs on ringtail) + - job_name: "frigate" + scheme: https + static_configs: + - targets: ["nvr.ops.eblu.me"] + metrics_path: /api/metrics diff --git a/argocd/manifests/prometheus/statefulset.yaml b/argocd/manifests/prometheus/statefulset.yaml index ce2d810..24953e0 100644 --- a/argocd/manifests/prometheus/statefulset.yaml +++ b/argocd/manifests/prometheus/statefulset.yaml @@ -20,7 +20,7 @@ spec: runAsUser: 65534 containers: - name: prometheus - image: registry.ops.eblu.me/blumeops/prometheus:v3.9.1-2ba5d8a + image: registry.ops.eblu.me/blumeops/prometheus args: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus diff --git a/argocd/manifests/tailscale-operator-base/kustomization.yaml b/argocd/manifests/tailscale-operator-base/kustomization.yaml index 980b8e3..82ff89e 100644 --- a/argocd/manifests/tailscale-operator-base/kustomization.yaml +++ b/argocd/manifests/tailscale-operator-base/kustomization.yaml @@ -8,3 +8,7 @@ resources: - operator.yaml - proxyclass.yaml - dnsconfig.yaml + +images: + - name: docker.io/tailscale/k8s-operator + newTag: v1.94.2 diff --git a/argocd/manifests/tailscale-operator-base/operator.yaml b/argocd/manifests/tailscale-operator-base/operator.yaml index ad28d40..adfa1c2 100644 --- a/argocd/manifests/tailscale-operator-base/operator.yaml +++ b/argocd/manifests/tailscale-operator-base/operator.yaml @@ -5362,7 +5362,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.uid - image: docker.io/tailscale/k8s-operator:v1.94.2 + image: docker.io/tailscale/k8s-operator imagePullPolicy: Always name: operator volumeMounts: diff --git a/argocd/manifests/teslamate/deployment.yaml b/argocd/manifests/teslamate/deployment.yaml index b6757c4..c590185 100644 --- a/argocd/manifests/teslamate/deployment.yaml +++ b/argocd/manifests/teslamate/deployment.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: teslamate - image: registry.ops.eblu.me/blumeops/teslamate:v2.2.0-ffa8727 + image: registry.ops.eblu.me/blumeops/teslamate ports: - containerPort: 4000 env: diff --git a/argocd/manifests/teslamate/kustomization.yaml b/argocd/manifests/teslamate/kustomization.yaml index 3be2de4..3ea707c 100644 --- a/argocd/manifests/teslamate/kustomization.yaml +++ b/argocd/manifests/teslamate/kustomization.yaml @@ -9,3 +9,7 @@ resources: - ingress-tailscale.yaml - external-secret-db.yaml - external-secret-encryption-key.yaml + +images: + - name: registry.ops.eblu.me/blumeops/teslamate + newTag: v2.2.0-ffa8727 diff --git a/argocd/manifests/torrent/deployment.yaml b/argocd/manifests/torrent/deployment.yaml index 90ac128..bf1e8b1 100644 --- a/argocd/manifests/torrent/deployment.yaml +++ b/argocd/manifests/torrent/deployment.yaml @@ -16,7 +16,7 @@ spec: spec: containers: - name: transmission - image: registry.ops.eblu.me/blumeops/transmission:v4.0.6-r4-ffa8727 + image: registry.ops.eblu.me/blumeops/transmission env: - name: PUID value: "1000" diff --git a/argocd/manifests/torrent/kustomization.yaml b/argocd/manifests/torrent/kustomization.yaml index 57c6197..2bd029f 100644 --- a/argocd/manifests/torrent/kustomization.yaml +++ b/argocd/manifests/torrent/kustomization.yaml @@ -8,3 +8,6 @@ resources: - deployment.yaml - service.yaml - ingress-tailscale.yaml +images: + - name: registry.ops.eblu.me/blumeops/transmission + newTag: v4.0.6-r4-ffa8727 diff --git a/docs/changelog.d/feature-kustomize-images-configmapgen.infra.md b/docs/changelog.d/feature-kustomize-images-configmapgen.infra.md new file mode 100644 index 0000000..18eeab2 --- /dev/null +++ b/docs/changelog.d/feature-kustomize-images-configmapgen.infra.md @@ -0,0 +1 @@ +Move image tags to kustomize `images:` transformer across 22 services and replace hand-written ConfigMaps with `configMapGenerator:` in 12 services, enabling content-hash-based automatic rollouts on config changes.