From 85cea34fe8821290aa4219d568b286074036dc3d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:04:13 -0300 Subject: [PATCH 01/93] gitops-ui: cert + switch flux to feature/ci-gitops --- clusters/atlas/flux-system/gotk-sync.yaml | 2 +- services/gitops-ui/certificate.yaml | 13 +++++++++++++ services/gitops-ui/kustomization.yaml | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 services/gitops-ui/certificate.yaml diff --git a/clusters/atlas/flux-system/gotk-sync.yaml b/clusters/atlas/flux-system/gotk-sync.yaml index 26dc23f..006bdd3 100644 --- a/clusters/atlas/flux-system/gotk-sync.yaml +++ b/clusters/atlas/flux-system/gotk-sync.yaml @@ -8,7 +8,7 @@ metadata: spec: interval: 1m0s ref: - branch: feature/mailu + branch: feature/ci-gitops secretRef: name: flux-system-gitea url: ssh://git@scm.bstein.dev:2242/bstein/titan-iac.git diff --git a/services/gitops-ui/certificate.yaml b/services/gitops-ui/certificate.yaml new file mode 100644 index 0000000..d16a83a --- /dev/null +++ b/services/gitops-ui/certificate.yaml @@ -0,0 +1,13 @@ +# services/gitops-ui/certificate.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: gitops-ui-tls + namespace: flux-system +spec: + secretName: gitops-ui-tls + issuerRef: + kind: ClusterIssuer + name: letsencrypt-prod + dnsNames: + - cd.bstein.dev diff --git a/services/gitops-ui/kustomization.yaml b/services/gitops-ui/kustomization.yaml index 53a903e..fad837d 100644 --- a/services/gitops-ui/kustomization.yaml +++ b/services/gitops-ui/kustomization.yaml @@ -5,3 +5,4 @@ namespace: flux-system resources: - source.yaml - helmrelease.yaml + - certificate.yaml From 6993f51ef7ba95db1ccca9558e0e9a7a7aff1d6a Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:08:44 -0300 Subject: [PATCH 02/93] gitops-ui: allow acme solver ingress from traefik --- services/gitops-ui/kustomization.yaml | 1 + services/gitops-ui/networkpolicy-acme.yaml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 services/gitops-ui/networkpolicy-acme.yaml diff --git a/services/gitops-ui/kustomization.yaml b/services/gitops-ui/kustomization.yaml index fad837d..a86611a 100644 --- a/services/gitops-ui/kustomization.yaml +++ b/services/gitops-ui/kustomization.yaml @@ -6,3 +6,4 @@ resources: - source.yaml - helmrelease.yaml - certificate.yaml + - networkpolicy-acme.yaml diff --git a/services/gitops-ui/networkpolicy-acme.yaml b/services/gitops-ui/networkpolicy-acme.yaml new file mode 100644 index 0000000..a7a5063 --- /dev/null +++ b/services/gitops-ui/networkpolicy-acme.yaml @@ -0,0 +1,17 @@ +# services/gitops-ui/networkpolicy-acme.yaml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-acme-solver + namespace: flux-system +spec: + podSelector: + matchLabels: + acme.cert-manager.io/http01-solver: "true" + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: traefik From 694bb4d12e0062eb5c291522f7ac3f353a6d7b17 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:12:38 -0300 Subject: [PATCH 03/93] gitops-ui: allow acme solver from kube-system traefik --- services/gitops-ui/networkpolicy-acme.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/gitops-ui/networkpolicy-acme.yaml b/services/gitops-ui/networkpolicy-acme.yaml index a7a5063..c5b0f2d 100644 --- a/services/gitops-ui/networkpolicy-acme.yaml +++ b/services/gitops-ui/networkpolicy-acme.yaml @@ -15,3 +15,6 @@ spec: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: traefik + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system From b28e3935246365ea2b8ff9775e60ebaead96a7a3 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:14:11 -0300 Subject: [PATCH 04/93] gitops-ui: open ingress for acme solver --- services/gitops-ui/networkpolicy-acme.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/services/gitops-ui/networkpolicy-acme.yaml b/services/gitops-ui/networkpolicy-acme.yaml index c5b0f2d..246c14d 100644 --- a/services/gitops-ui/networkpolicy-acme.yaml +++ b/services/gitops-ui/networkpolicy-acme.yaml @@ -11,10 +11,4 @@ spec: policyTypes: - Ingress ingress: - - from: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: traefik - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: kube-system + - {} From 39d732d74d9e993411e3fbfb783236c47f7baf3f Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:39:27 -0300 Subject: [PATCH 05/93] git: ignore fixed --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 9bdfcf6..88b0632 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ -# Ignore markdown by default, but keep top-level docs *.md !README.md -!AGENTS.md -!**/NOTES.md From b575c64de1c87980211bf9ff2e08dd2f30747e7e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:43:06 -0300 Subject: [PATCH 06/93] chore: drop stray NOTES.md --- NOTES.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 NOTES.md diff --git a/NOTES.md b/NOTES.md deleted file mode 100644 index 8b8b8d2..0000000 --- a/NOTES.md +++ /dev/null @@ -1,3 +0,0 @@ -# Rotation reminders (temporary secrets set by automation) - -- Weave GitOps UI (`cd.bstein.dev`) admin: `admin` / `G1tOps!2025` — rotate immediately after first login. From ccfc4735215b098a09a01189c831722f0d36ab97 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:52:12 -0300 Subject: [PATCH 07/93] cleanup: stop tracking extra md files; switch gitops cert to letsencrypt --- AGENTS.md | 81 ----------------------------- docs/topology.md | 15 ------ hosts/styx/NOTES.md | 2 - services/gitops-ui/certificate.yaml | 2 +- services/gitops-ui/helmrelease.yaml | 2 +- services/keycloak/NOTES.md | 27 ---------- 6 files changed, 2 insertions(+), 127 deletions(-) delete mode 100644 AGENTS.md delete mode 100644 docs/topology.md delete mode 100644 hosts/styx/NOTES.md delete mode 100644 services/keycloak/NOTES.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 9dc36ac..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,81 +0,0 @@ - - -Repository Guidelines - -> Local-only note: apply changes through Flux-tracked manifests, not by manual kubectl edits in-cluster—manual tweaks will be reverted by Flux. - -## Project Structure & Module Organization -- `infrastructure/`: cluster-scoped building blocks (core, flux-system, traefik, longhorn). Add new platform features by mirroring this layout. -- `services/`: workload manifests per app (`services/gitea/`, etc.) with `kustomization.yaml` plus one file per kind; keep diffs small and focused. -- `dockerfiles/` hosts bespoke images, while `scripts/` stores operational Fish/Bash helpers—extend these directories instead of relying on ad-hoc commands. - -## Build, Test, and Development Commands -- `kustomize build services/` (or `kubectl kustomize ...`) renders manifests exactly as Flux will. -- `kubectl apply --server-side --dry-run=client -k services/` checks schema compatibility without touching the cluster. -- `flux reconcile kustomization --namespace flux-system --with-source` pulls the latest Git state after merges or hotfixes. -- `fish scripts/flux_hammer.fish --help` explains the recovery tool; read it before running against production workloads. - -## Coding Style & Naming Conventions -- YAML uses two-space indents; retain the leading path comment (e.g. `# services/gitea/deployment.yaml`) to speed code review. -- Keep resource names lowercase kebab-case, align labels/selectors, and mirror namespaces with directory names. -- List resources in `kustomization.yaml` from namespace/config, through storage, then workloads and networking for predictable diffs. -- Scripts start with `#!/usr/bin/env fish` or bash, stay executable, and follow snake_case names such as `flux_hammer.fish`. - -## Testing Guidelines -- Run `kustomize build` and the dry-run apply for every service you touch; capture failures before opening a PR. -- `flux diff kustomization --path services/` previews reconciliations—link notable output when behavior shifts. -- Docker edits: `docker build -f dockerfiles/Dockerfile.monerod .` (swap the file you changed) to verify image builds. - -## Commit & Pull Request Guidelines -- Keep commit subjects short, present-tense, and optionally scoped (`gpu(titan-24): add RuntimeClass`); squash fixups before review. -- Describe linked issues, affected services, and required operator steps (e.g. `flux reconcile kustomization services-gitea`) in the PR body. -- Focus each PR on one kustomization or service and update `infrastructure/flux-system` when Flux must track new folders. -- Record the validation you ran (dry-runs, diffs, builds) and add screenshots only when ingress or UI behavior changes. - -## Security & Configuration Tips -- Never commit credentials; use Vault workflows (`services/vault/`) or SOPS-encrypted manifests wired through `infrastructure/flux-system`. -- Node selectors and tolerations gate workloads to hardware like `hardware: rpi4`; confirm labels before scaling or renaming nodes. -- Pin external images by digest or rely on Flux image automation to follow approved tags and avoid drift. - -## Dashboard roadmap / context (2025-12-02) -- Atlas dashboards are generated via `scripts/dashboards_render_atlas.py --build`, which writes JSON under `services/monitoring/dashboards/` and ConfigMaps under `services/monitoring/`. Keep the Grafana manifests in sync by regenerating after edits. -- Atlas Overview panels are paired with internal dashboards (pods, nodes, storage, network, GPU). A new `atlas-gpu` internal dashboard holds the detailed GPU metrics that feed the overview share pie. -- Old Grafana folders (`Atlas Storage`, `Atlas SRE`, `Atlas Public`, `Atlas Nodes`) should be removed in Grafana UI when convenient; only `Atlas Overview` and `Atlas Internal` should remain provisioned. -- Future work: add a separate generator (e.g., `dashboards_render_oceanus.py`) for SUI/oceanus validation dashboards, mirroring the atlas pattern of internal dashboards feeding a public overview. - -## Monitoring state (2025-12-03) -- dcgm-exporter DaemonSet pulls `registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04` with nvidia runtime/imagePullSecret; titan-24 exports metrics, titan-22 remains NotReady. -- Atlas Overview is the Grafana home (1h range, 1m refresh), Overview folder UID `overview`, internal folder `atlas-internal` (oceanus-internal stub). -- Panels standardized via generator; hottest row compressed, worker/control rows taller, root disk row taller and top12 bar gauge with labels. GPU share pie uses 1h avg_over_time to persist idle activity. -- Internal dashboards are provisioned without Viewer role; if anonymous still sees them, restart Grafana and tighten auth if needed. -- GPU share panel updated (feature/sso) to use `max_over_time(…[$__range])`, so longer ranges (e.g., 12h) keep recent activity visible. Flux tracking `feature/sso`. - -## Upcoming priorities (SSO/storage/mail) -- Establish SSO (Keycloak or similar) and federate Grafana, Gitea, Zot, Nextcloud, Pegasus/Jellyfin; keep Vaultwarden separate until safe. -- Add Nextcloud (limit to rpi5 workers) with office suite; integrate with SSO; plan storage class and ingress. -- Plan mail: mostly self-hosted, relay through trusted provider for outbound; integrate with services (Nextcloud, Vaultwarden, etc.) for notifications and account flows. - -## SSO plan sketch (2025-12-03) -- IdP: use Keycloak (preferred) in a new `sso` namespace, Bitnami or codecentric chart with Postgres backing store (single PVC), ingress `sso.bstein.dev`, admin user bound to brad@bstein.dev; stick with local DB initially (no external IdP). -- Auth flow goals: Grafana (OIDC), Gitea (OAuth2/Keycloak), Zot (via Traefik forward-auth/oauth2-proxy), Jellyfin/Pegasus via Jellyfin OAuth/OpenID plugin (map existing usernames; run migration to pre-create users in Keycloak with same usernames/emails and temporary passwords), Pegasus keeps using Jellyfin tokens. -- Steps to implement: - 1) Add service folder `services/keycloak/` (namespace, PVC, HelmRelease, ingress, secret for admin creds). Verify with kustomize + Flux reconcile. - 2) Seed realm `atlas` with users (import CSV/realm). Create client for Grafana (public/implicit), Gitea (confidential), and a “jellyfin” client for the OAuth plugin; set email for brad@bstein.dev as admin. - 3) Reconfigure Grafana to OIDC (disable anonymous to internal folders, leave Overview public via folder permissions). Reconfigure Gitea to OIDC (app.ini). - 4) Add Traefik forward-auth (oauth2-proxy) in front of Zot and any other services needing headers-based auth. - 5) Deploy Jellyfin OpenID plugin; map Keycloak users to existing Jellyfin usernames; communicate password reset path. -- Migration caution: do not delete existing local creds until SSO validated; keep Pegasus working via Jellyfin tokens during transition. - -## Postgres centralization (2025-12-03) -- Prefer a shared in-cluster Postgres deployment with per-service databases to reduce resource sprawl on Pi nodes. Use it for services that can easily point at an external DB. -- Candidates to migrate to shared Postgres: Keycloak (realm DB), Gitea (git DB), Nextcloud (app DB), possibly Grafana (if persistence needed beyond current provisioner), Jitsi prosody/JVB state (if external DB supported). Keep tightly-coupled or lightweight embedded DBs as-is when migration is painful or not supported. - -## SSO integration snapshot (2025-12-08) -- Current blockers: Zot still prompts for basic auth/double-login; Vault still wants the token UI after Keycloak (previously 502/404 when vault-0 sealed). Forward-auth middleware on Zot Ingress likely still causing the 401/Found hop; Vault OIDC mount not completing UI flow unless unsealed and preferred login is set. -- Flux-only changes required: remove zot forward-auth middleware from Ingress (let oauth2-proxy handle redirect), ensure Vault OIDC mount is preferred UI login and bound to admin group; keep all edits in repo so Flux enforces them. -- Secrets present (per user): `zot-oidc-client` (client_secret only), `oauth2-proxy-zot-oidc`, `oauth2-proxy-vault-oidc`, `vault-oidc-admin-token`. Zot needs its regcred in the zot namespace if image pulls fail. -- Cluster validation blocked here: `kubectl get nodes` fails (403/permission) and DNS to `*.bstein.dev` fails in this session, so no live curl verification could be run. Re-test on a host with cluster/DNS access after Flux applies fixes. - -## Docs hygiene -- Do not add per-service `README.md` files; use `NOTES.md` if documentation is needed inside service folders. Keep only the top-level repo README. -- Keep comments succinct and in a human voice—no AI-sounding notes. Use `NOTES.md` for scratch notes instead of sprinkling reminders into code or extra READMEs. diff --git a/docs/topology.md b/docs/topology.md deleted file mode 100644 index 1e37235..0000000 --- a/docs/topology.md +++ /dev/null @@ -1,15 +0,0 @@ -# Titan Homelab Topology - -| Hostname | Role / Function | Managed By | Notes | -|------------|--------------------------------|---------------------|-------| -| titan-db | HA control plane database | Ansible | PostgreSQL / etcd backing services | -| titan-0a | Kubernetes control-plane | Flux (atlas cluster)| HA leader, tainted for control only | -| titan-0b | Kubernetes control-plane | Flux (atlas cluster)| Standby control node | -| titan-0c | Kubernetes control-plane | Flux (atlas cluster)| Standby control node | -| titan-04-19| Raspberry Pi workers | Flux (atlas cluster)| Workload nodes, labelled per hardware | -| titan-20&21| NVIDIA Jetson workers | Flux (atlas cluster)| Workload nodes, labelled per hardware | -| titan-22 | GPU mini-PC (Jellyfin) | Flux + Ansible | NVIDIA runtime managed via `modules/profiles/atlas-ha` | -| titan-23 | Dedicated SUI validator Oceanus| Manual + Ansible | Baremetal validator workloads, exposes metrics to atlas | -| titan-24 | Tethys hybrid node | Flux + Ansible | Runs SUI metrics via K8s, validator via Ansible | -| titan-jh | Jumphost & bastion & lesavka | Ansible | Entry point / future KVM services / custom kvm - lesavaka | -| styx | Air-gapped workstation | Manual / Scripts | Remains isolated, scripts tracked in `hosts/styx` | diff --git a/hosts/styx/NOTES.md b/hosts/styx/NOTES.md deleted file mode 100644 index 992bac5..0000000 --- a/hosts/styx/NOTES.md +++ /dev/null @@ -1,2 +0,0 @@ -# hosts/styx/README.md -Styx is air-gapped; provisioning scripts live under `scripts/`. diff --git a/services/gitops-ui/certificate.yaml b/services/gitops-ui/certificate.yaml index d16a83a..d2ea1fd 100644 --- a/services/gitops-ui/certificate.yaml +++ b/services/gitops-ui/certificate.yaml @@ -8,6 +8,6 @@ spec: secretName: gitops-ui-tls issuerRef: kind: ClusterIssuer - name: letsencrypt-prod + name: letsencrypt dnsNames: - cd.bstein.dev diff --git a/services/gitops-ui/helmrelease.yaml b/services/gitops-ui/helmrelease.yaml index 27b610d..974251c 100644 --- a/services/gitops-ui/helmrelease.yaml +++ b/services/gitops-ui/helmrelease.yaml @@ -34,7 +34,7 @@ spec: enabled: true className: traefik annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod + cert-manager.io/cluster-issuer: letsencrypt traefik.ingress.kubernetes.io/router.entrypoints: websecure hosts: - host: cd.bstein.dev diff --git a/services/keycloak/NOTES.md b/services/keycloak/NOTES.md deleted file mode 100644 index bf7c21b..0000000 --- a/services/keycloak/NOTES.md +++ /dev/null @@ -1,27 +0,0 @@ -# services/keycloak - -Keycloak is deployed via raw manifests and backed by the shared Postgres (`postgres-service.postgres.svc.cluster.local:5432`). Create these secrets before applying: - -```bash -# DB creds (per-service DB/user in shared Postgres) -kubectl -n sso create secret generic keycloak-db \ - --from-literal=username=keycloak \ - --from-literal=password='' \ - --from-literal=database=keycloak - -# Admin console creds (maps to KC admin user) -kubectl -n sso create secret generic keycloak-admin \ - --from-literal=username=brad@bstein.dev \ - --from-literal=password='' -``` - -Apply: - -```bash -kubectl apply -k services/keycloak -``` - -Notes -- Service: `keycloak.sso.svc:80` (Ingress `sso.bstein.dev`, TLS via cert-manager). -- Uses Postgres schema `public`; DB/user should be provisioned in the shared Postgres instance. -- Health endpoints on :9000 are wired for probes. From 4eff9ebcc1fa4f21e50c1cb1308f634ca68b14e9 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 15:57:42 -0300 Subject: [PATCH 08/93] jenkins: add helm release with ingress + astreae storage --- .../applications/jenkins/kustomization.yaml | 18 +++ .../applications/kustomization.yaml | 1 + infrastructure/sources/helm/jenkins.yaml | 9 ++ .../sources/helm/kustomization.yaml | 1 + services/jenkins/helmrelease.yaml | 139 ++++++++++++++++++ services/jenkins/kustomization.yaml | 7 + services/jenkins/namespace.yaml | 5 + 7 files changed, 180 insertions(+) create mode 100644 clusters/atlas/flux-system/applications/jenkins/kustomization.yaml create mode 100644 infrastructure/sources/helm/jenkins.yaml create mode 100644 services/jenkins/helmrelease.yaml create mode 100644 services/jenkins/kustomization.yaml create mode 100644 services/jenkins/namespace.yaml diff --git a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml new file mode 100644 index 0000000..4e5d616 --- /dev/null +++ b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml @@ -0,0 +1,18 @@ +# clusters/atlas/flux-system/applications/jenkins/kustomization.yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: jenkins + namespace: flux-system +spec: + interval: 10m + path: ./services/jenkins + prune: true + sourceRef: + kind: GitRepository + name: flux-system + targetNamespace: jenkins + dependsOn: + - name: helm + - name: traefik + wait: true diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index daf1c42..2ad7b14 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -16,3 +16,4 @@ resources: - keycloak/kustomization.yaml - oauth2-proxy/kustomization.yaml - mailu/kustomization.yaml + - jenkins/kustomization.yaml diff --git a/infrastructure/sources/helm/jenkins.yaml b/infrastructure/sources/helm/jenkins.yaml new file mode 100644 index 0000000..b746793 --- /dev/null +++ b/infrastructure/sources/helm/jenkins.yaml @@ -0,0 +1,9 @@ +# infrastructure/sources/helm/jenkins.yaml +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: jenkins + namespace: flux-system +spec: + interval: 1h + url: https://charts.jenkins.io diff --git a/infrastructure/sources/helm/kustomization.yaml b/infrastructure/sources/helm/kustomization.yaml index a0f55b0..cef76c2 100644 --- a/infrastructure/sources/helm/kustomization.yaml +++ b/infrastructure/sources/helm/kustomization.yaml @@ -5,6 +5,7 @@ resources: - grafana.yaml - hashicorp.yaml - jetstack.yaml + - jenkins.yaml - mailu.yaml - prometheus.yaml - victoria-metrics.yaml diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml new file mode 100644 index 0000000..002b4b6 --- /dev/null +++ b/services/jenkins/helmrelease.yaml @@ -0,0 +1,139 @@ +# services/jenkins/helmrelease.yaml +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: jenkins + namespace: jenkins +spec: + interval: 30m + chart: + spec: + chart: jenkins + version: 5.8.114 + sourceRef: + kind: HelmRepository + name: jenkins + namespace: flux-system + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + remediateLastFailure: true + cleanupOnFail: true + values: + controller: + jenkinsUrl: https://ci.bstein.dev + ingress: + enabled: true + hostName: ci.bstein.dev + ingressClassName: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt + traefik.ingress.kubernetes.io/router.entrypoints: websecure + tls: + - secretName: jenkins-tls + hosts: + - ci.bstein.dev + installPlugins: + - kubernetes:4334.v794a_7463eb_0c + - workflow-aggregator:596.v8c21c963d92d + - git:5.6.0 + - configuration-as-code:1835.v4296594051c5 + - oic-auth:1.30 + containerEnv: + - name: ENABLE_OIDC + value: "false" + - name: OIDC_CLIENT_ID + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: clientId + optional: true + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: clientSecret + optional: true + - name: OIDC_AUTH_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: authorizationUrl + optional: true + - name: OIDC_TOKEN_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: tokenUrl + optional: true + - name: OIDC_USERINFO_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: userInfoUrl + optional: true + - name: OIDC_LOGOUT_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: logoutUrl + optional: true + JCasC: + defaultConfig: true + configScripts: + 01-oidc.groovy: | + import jenkins.model.* + def env = System.getenv() + def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean() + if (!enable) { + println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") + return + } + def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL'] + if (!required.every { env[it] }) { + println("OIDC enabled but missing one or more env vars: ${required}") + return + } + try { + def realm = new org.jenkinsci.plugins.oic.OicSecurityRealm( + env['OIDC_CLIENT_ID'], + env['OIDC_CLIENT_SECRET'], + env['OIDC_TOKEN_URL'], + env['OIDC_AUTH_URL'], + env['OIDC_USERINFO_URL'], + true, // logout from provider + env['OIDC_LOGOUT_URL'] ?: "", + "", // postLogoutRedirectUrl + "openid email profile", + "", // prompt + "preferred_username", + "name", + "email", + false, // disableSslVerification + true, // escapeHatchEnabled + "admin", + "", // escapeHatchSecret + "", // escapeHatchGroup + true, // loadUserInfo + true, // validateScopes + false, // allowUnsignedIdTokens + false, // enforceValidIssuers + "", // issuer + false // disableUserInfoFetch + ) + def instance = Jenkins.get() + instance.setSecurityRealm(realm) + instance.save() + println("Configured OIDC security realm from env") + } catch (Exception e) { + println("Failed to configure OIDC realm: ${e}") + } + persistence: + enabled: true + storageClass: astreae + size: 50Gi + serviceAccount: + create: true diff --git a/services/jenkins/kustomization.yaml b/services/jenkins/kustomization.yaml new file mode 100644 index 0000000..b20b1d3 --- /dev/null +++ b/services/jenkins/kustomization.yaml @@ -0,0 +1,7 @@ +# services/jenkins/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: jenkins +resources: + - namespace.yaml + - helmrelease.yaml diff --git a/services/jenkins/namespace.yaml b/services/jenkins/namespace.yaml new file mode 100644 index 0000000..36e999b --- /dev/null +++ b/services/jenkins/namespace.yaml @@ -0,0 +1,5 @@ +# services/jenkins/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: jenkins From 6def1aa479e1c9b1774c33038992ea11171a3c63 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 16:00:45 -0300 Subject: [PATCH 09/93] jenkins: use latest plugin versions to avoid 404 --- services/jenkins/helmrelease.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 002b4b6..3d65a2c 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -37,11 +37,11 @@ spec: hosts: - ci.bstein.dev installPlugins: - - kubernetes:4334.v794a_7463eb_0c - - workflow-aggregator:596.v8c21c963d92d - - git:5.6.0 - - configuration-as-code:1835.v4296594051c5 - - oic-auth:1.30 + - kubernetes + - workflow-aggregator + - git + - configuration-as-code + - oic-auth containerEnv: - name: ENABLE_OIDC value: "false" From 90bf1f7d8e2bff5b73ec6b44ddef71ad4d05f0b0 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 16:02:05 -0300 Subject: [PATCH 10/93] jenkins: start without plugin installs to unblock bootstrap --- services/jenkins/helmrelease.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 3d65a2c..bf7c5e3 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -36,12 +36,7 @@ spec: - secretName: jenkins-tls hosts: - ci.bstein.dev - installPlugins: - - kubernetes - - workflow-aggregator - - git - - configuration-as-code - - oic-auth + installPlugins: [] containerEnv: - name: ENABLE_OIDC value: "false" From 0286f4f31747a389f5bac3c42e6265fcd3ffcec0 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 17:59:48 -0300 Subject: [PATCH 11/93] jenkins: restore plugin list without pinned versions --- services/jenkins/helmrelease.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index bf7c5e3..3d65a2c 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -36,7 +36,12 @@ spec: - secretName: jenkins-tls hosts: - ci.bstein.dev - installPlugins: [] + installPlugins: + - kubernetes + - workflow-aggregator + - git + - configuration-as-code + - oic-auth containerEnv: - name: ENABLE_OIDC value: "false" From fc0fa5998128937f77a64f47fc305f347699a46a Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 18:10:49 -0300 Subject: [PATCH 12/93] jenkins: drop JCasC OIDC script to unblock startup --- services/jenkins/helmrelease.yaml | 50 ------------------------------- 1 file changed, 50 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 3d65a2c..d4ccca5 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -81,56 +81,6 @@ spec: name: jenkins-oidc key: logoutUrl optional: true - JCasC: - defaultConfig: true - configScripts: - 01-oidc.groovy: | - import jenkins.model.* - def env = System.getenv() - def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean() - if (!enable) { - println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") - return - } - def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL'] - if (!required.every { env[it] }) { - println("OIDC enabled but missing one or more env vars: ${required}") - return - } - try { - def realm = new org.jenkinsci.plugins.oic.OicSecurityRealm( - env['OIDC_CLIENT_ID'], - env['OIDC_CLIENT_SECRET'], - env['OIDC_TOKEN_URL'], - env['OIDC_AUTH_URL'], - env['OIDC_USERINFO_URL'], - true, // logout from provider - env['OIDC_LOGOUT_URL'] ?: "", - "", // postLogoutRedirectUrl - "openid email profile", - "", // prompt - "preferred_username", - "name", - "email", - false, // disableSslVerification - true, // escapeHatchEnabled - "admin", - "", // escapeHatchSecret - "", // escapeHatchGroup - true, // loadUserInfo - true, // validateScopes - false, // allowUnsignedIdTokens - false, // enforceValidIssuers - "", // issuer - false // disableUserInfoFetch - ) - def instance = Jenkins.get() - instance.setSecurityRealm(realm) - instance.save() - println("Configured OIDC security realm from env") - } catch (Exception e) { - println("Failed to configure OIDC realm: ${e}") - } persistence: enabled: true storageClass: astreae From 04602a2914a0f21551d86461b81058bf3692ef9f Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 19:22:47 -0300 Subject: [PATCH 13/93] jenkins: auto-configure OIDC via init script --- services/jenkins/helmrelease.yaml | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index d4ccca5..2b8d3ac 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -45,6 +45,8 @@ spec: containerEnv: - name: ENABLE_OIDC value: "false" + - name: OIDC_ISSUER + value: "https://sso.bstein.dev/realms/atlas" - name: OIDC_CLIENT_ID valueFrom: secretKeyRef: @@ -81,6 +83,55 @@ spec: name: jenkins-oidc key: logoutUrl optional: true + initScripts: + oidc.groovy: | + import jenkins.model.Jenkins + import org.jenkinsci.plugins.oic.OicSecurityRealm + def env = System.getenv() + def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean() + if (!enable) { + println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") + return + } + def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL'] + if (!required.every { env[it] }) { + println("OIDC enabled but missing vars: ${required.findAll { !env[it] }}") + return + } + try { + def realm = new OicSecurityRealm( + env['OIDC_CLIENT_ID'], + env['OIDC_CLIENT_SECRET'], + env['OIDC_TOKEN_URL'], + env['OIDC_AUTH_URL'], + env['OIDC_USERINFO_URL'], + true, // logout from provider + env['OIDC_LOGOUT_URL'] ?: "", + "", // postLogoutRedirectUrl + "openid email profile", + "", // prompt + "preferred_username", + "name", + "email", + false, // disableSslVerification + true, // escapeHatchEnabled + "admin", + "", // escapeHatchSecret + "", // escapeHatchGroup + true, // loadUserInfo + true, // validateScopes + false, // allowUnsignedIdTokens + false, // enforceValidIssuers + env['OIDC_ISSUER'] ?: "", + false // disableUserInfoFetch + ) + def j = Jenkins.get() + j.setSecurityRealm(realm) + j.save() + println("Configured OIDC realm from init script") + } catch (Exception e) { + println("Failed to configure OIDC realm: ${e}") + } persistence: enabled: true storageClass: astreae From a46226bb0acaadf8367532a2d4895571d13c9116 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 20:58:57 -0300 Subject: [PATCH 14/93] ci: enable oidc for jenkins/gitops/gitea --- services/gitea/deployment.yaml | 6 ++ services/gitea/kustomization.yaml | 1 + services/gitea/oidc-job.yaml | 85 +++++++++++++++++++++++++++ services/gitops-ui/helmrelease.yaml | 11 ++-- services/gitops-ui/kustomization.yaml | 1 + services/gitops-ui/rbac.yaml | 15 +++++ services/jenkins/helmrelease.yaml | 51 ++++++++-------- 7 files changed, 136 insertions(+), 34 deletions(-) create mode 100644 services/gitea/oidc-job.yaml create mode 100644 services/gitops-ui/rbac.yaml diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 5dc0cee..b5c4ca9 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -55,6 +55,12 @@ spec: value: "master" - name: ROOT_URL value: "https://scm.bstein.dev" + - name: GITEA__service__ENABLE_OPENID_SIGNIN + value: "true" + - name: GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION + value: "true" + - name: GITEA__service__DISABLE_REGISTRATION + value: "false" - name: DB_TYPE value: "postgres" - name: DB_HOST diff --git a/services/gitea/kustomization.yaml b/services/gitea/kustomization.yaml index 36d6c23..9731b76 100644 --- a/services/gitea/kustomization.yaml +++ b/services/gitea/kustomization.yaml @@ -7,3 +7,4 @@ resources: - service.yaml - pvc.yaml - ingress.yaml + - oidc-job.yaml diff --git a/services/gitea/oidc-job.yaml b/services/gitea/oidc-job.yaml new file mode 100644 index 0000000..652d40f --- /dev/null +++ b/services/gitea/oidc-job.yaml @@ -0,0 +1,85 @@ +# services/gitea/oidc-job.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: gitea-oidc-bootstrap + namespace: gitea +spec: + ttlSecondsAfterFinished: 1800 + backoffLimit: 1 + template: + metadata: + labels: + app: gitea + job: gitea-oidc-bootstrap + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: gitea + topologyKey: kubernetes.io/hostname + restartPolicy: OnFailure + volumes: + - name: gitea-data + persistentVolumeClaim: + claimName: gitea-data + containers: + - name: gitea-oidc-bootstrap + image: gitea/gitea:1.23 + imagePullPolicy: IfNotPresent + volumeMounts: + - name: gitea-data + mountPath: /data + env: + - name: CLIENT_ID + valueFrom: + secretKeyRef: + name: gitea-oidc + key: client_id + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: gitea-oidc + key: client_secret + - name: DISCOVERY_URL + valueFrom: + secretKeyRef: + name: gitea-oidc + key: openid_auto_discovery_url + command: + - /bin/bash + - -c + - | + set -euo pipefail + APPINI=/data/gitea/conf/app.ini + BIN=/usr/local/bin/gitea + list="$($BIN -c "$APPINI" admin auth list)" + id=$(echo "$list" | awk '$2=="keycloak"{print $1}') + if [ -n "$id" ]; then + echo "Updating existing auth source id=$id" + $BIN -c "$APPINI" admin auth update-oauth \ + --id "$id" \ + --name keycloak \ + --provider openidConnect \ + --key "$CLIENT_ID" \ + --secret "$CLIENT_SECRET" \ + --auto-discover-url "$DISCOVERY_URL" \ + --scopes "openid profile email" \ + --group-claim-name groups + else + echo "Creating keycloak auth source" + $BIN -c "$APPINI" admin auth add-oauth \ + --name keycloak \ + --provider openidConnect \ + --key "$CLIENT_ID" \ + --secret "$CLIENT_SECRET" \ + --auto-discover-url "$DISCOVERY_URL" \ + --scopes "openid profile email" \ + --group-claim-name groups + fi diff --git a/services/gitops-ui/helmrelease.yaml b/services/gitops-ui/helmrelease.yaml index 974251c..86ae327 100644 --- a/services/gitops-ui/helmrelease.yaml +++ b/services/gitops-ui/helmrelease.yaml @@ -23,13 +23,10 @@ spec: remediateLastFailure: true cleanupOnFail: true values: + additionalArgs: + - --auth-methods=oidc adminUser: - create: true - createClusterRole: true - createSecret: true - username: admin - # bcrypt hash for temporary password "G1tOps!2025" (rotate after login) - passwordHash: "$2y$12$wDEOzR1Gc2dbvNSJ3ZXNdOBVFEjC6YASIxnZmHIbO.W1m0fie/QVi" + create: false ingress: enabled: true className: traefik @@ -45,5 +42,7 @@ spec: - secretName: gitops-ui-tls hosts: - cd.bstein.dev + oidcSecret: + create: false metrics: enabled: true diff --git a/services/gitops-ui/kustomization.yaml b/services/gitops-ui/kustomization.yaml index a86611a..6d8b8a7 100644 --- a/services/gitops-ui/kustomization.yaml +++ b/services/gitops-ui/kustomization.yaml @@ -7,3 +7,4 @@ resources: - helmrelease.yaml - certificate.yaml - networkpolicy-acme.yaml + - rbac.yaml diff --git a/services/gitops-ui/rbac.yaml b/services/gitops-ui/rbac.yaml new file mode 100644 index 0000000..492d0e6 --- /dev/null +++ b/services/gitops-ui/rbac.yaml @@ -0,0 +1,15 @@ +# services/gitops-ui/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: gitops-admins + labels: + app.kubernetes.io/name: weave-gitops +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: Group + name: admin + apiGroup: rbac.authorization.k8s.io diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 2b8d3ac..d591b9f 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -44,7 +44,7 @@ spec: - oic-auth containerEnv: - name: ENABLE_OIDC - value: "false" + value: "true" - name: OIDC_ISSUER value: "https://sso.bstein.dev/realms/atlas" - name: OIDC_CLIENT_ID @@ -85,50 +85,45 @@ spec: optional: true initScripts: oidc.groovy: | + import hudson.util.Secret + import jenkins.model.IdStrategy import jenkins.model.Jenkins import org.jenkinsci.plugins.oic.OicSecurityRealm + import org.jenkinsci.plugins.oic.OicServerWellKnownConfiguration def env = System.getenv() - def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean() - if (!enable) { + if (!(env['ENABLE_OIDC'] ?: 'false').toBoolean()) { println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") return } - def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL'] + def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_ISSUER'] if (!required.every { env[it] }) { println("OIDC enabled but missing vars: ${required.findAll { !env[it] }}") return } try { + def wellKnown = "${env['OIDC_ISSUER']}/.well-known/openid-configuration" + def serverCfg = new OicServerWellKnownConfiguration(wellKnown) + serverCfg.setScopesOverride('openid profile email') def realm = new OicSecurityRealm( env['OIDC_CLIENT_ID'], - env['OIDC_CLIENT_SECRET'], - env['OIDC_TOKEN_URL'], - env['OIDC_AUTH_URL'], - env['OIDC_USERINFO_URL'], - true, // logout from provider - env['OIDC_LOGOUT_URL'] ?: "", - "", // postLogoutRedirectUrl - "openid email profile", - "", // prompt - "preferred_username", - "name", - "email", - false, // disableSslVerification - true, // escapeHatchEnabled - "admin", - "", // escapeHatchSecret - "", // escapeHatchGroup - true, // loadUserInfo - true, // validateScopes - false, // allowUnsignedIdTokens - false, // enforceValidIssuers - env['OIDC_ISSUER'] ?: "", - false // disableUserInfoFetch + Secret.fromString(env['OIDC_CLIENT_SECRET']), + serverCfg, + false, + IdStrategy.CASE_INSENSITIVE, + IdStrategy.CASE_INSENSITIVE ) + realm.setLogoutFromOpenidProvider(true) + realm.setPostLogoutRedirectUrl('https://ci.bstein.dev') + realm.setUserNameField('preferred_username') + realm.setFullNameFieldName('name') + realm.setEmailFieldName('email') + realm.setGroupsFieldName('groups') + realm.setRootURLFromRequest(true) + realm.setSendScopesInTokenRequest(true) def j = Jenkins.get() j.setSecurityRealm(realm) j.save() - println("Configured OIDC realm from init script") + println("Configured OIDC realm from init script (well-known)") } catch (Exception e) { println("Failed to configure OIDC realm: ${e}") } From 37761fa11828b5cb0e4131cdd8a9e74717f23e25 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:23:15 -0300 Subject: [PATCH 15/93] jenkins: fix OIDC retriever null --- services/jenkins/helmrelease.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index d591b9f..44d9846 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -112,6 +112,7 @@ spec: IdStrategy.CASE_INSENSITIVE, IdStrategy.CASE_INSENSITIVE ) + realm.createProxyAwareResourceRetriver() realm.setLogoutFromOpenidProvider(true) realm.setPostLogoutRedirectUrl('https://ci.bstein.dev') realm.setUserNameField('preferred_username') From 2ffc906487d4d9038fb4be5616fc118207b1a2dd Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:38:32 -0300 Subject: [PATCH 16/93] gitea: enable debug logging for oauth --- services/gitea/deployment.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index b5c4ca9..c0d2134 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -61,6 +61,8 @@ spec: value: "true" - name: GITEA__service__DISABLE_REGISTRATION value: "false" + - name: GITEA__log__LEVEL + value: "debug" - name: DB_TYPE value: "postgres" - name: DB_HOST From 0d93929e3db8155d003143161c91ed4188967586 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:42:08 -0300 Subject: [PATCH 17/93] gitea: relax required signin, set admin group+skip 2fa --- services/gitea/deployment.yaml | 2 ++ services/gitea/oidc-job.yaml | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index c0d2134..c769a6f 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -63,6 +63,8 @@ spec: value: "false" - name: GITEA__log__LEVEL value: "debug" + - name: GITEA__service__REQUIRE_SIGNIN_VIEW + value: "false" - name: DB_TYPE value: "postgres" - name: DB_HOST diff --git a/services/gitea/oidc-job.yaml b/services/gitea/oidc-job.yaml index 652d40f..32c4949 100644 --- a/services/gitea/oidc-job.yaml +++ b/services/gitea/oidc-job.yaml @@ -71,7 +71,9 @@ spec: --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ --scopes "openid profile email" \ - --group-claim-name groups + --group-claim-name groups \ + --admin-group admin \ + --skip-local-2fa else echo "Creating keycloak auth source" $BIN -c "$APPINI" admin auth add-oauth \ @@ -81,5 +83,7 @@ spec: --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ --scopes "openid profile email" \ - --group-claim-name groups + --group-claim-name groups \ + --admin-group admin \ + --skip-local-2fa fi From d8dab08cd8ad460180f56237c9a494d148ddb5a0 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:44:43 -0300 Subject: [PATCH 18/93] gitea: set trace logging for oidc --- services/gitea/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index c769a6f..5f66347 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -62,7 +62,7 @@ spec: - name: GITEA__service__DISABLE_REGISTRATION value: "false" - name: GITEA__log__LEVEL - value: "debug" + value: "trace" - name: GITEA__service__REQUIRE_SIGNIN_VIEW value: "false" - name: DB_TYPE From a174e451d90d624307aacd63f6cc93f074219f5d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:47:50 -0300 Subject: [PATCH 19/93] gitea: fix bootstrap job immutability --- services/gitea/oidc-job.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/gitea/oidc-job.yaml b/services/gitea/oidc-job.yaml index 32c4949..73aacbb 100644 --- a/services/gitea/oidc-job.yaml +++ b/services/gitea/oidc-job.yaml @@ -1,6 +1,15 @@ # services/gitea/oidc-job.yaml apiVersion: batch/v1 kind: Job +metadata: + name: gitea-oidc-bootstrap + namespace: gitea +spec: + # clean and recreate when the manifest changes to avoid immutable pod fields + template: {} +--- +apiVersion: batch/v1 +kind: Job metadata: name: gitea-oidc-bootstrap namespace: gitea From 9b994111cbb1052752a4375286872f4d57c32e28 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:49:07 -0300 Subject: [PATCH 20/93] gitea: remove bootstrap job (immutable error) --- services/gitea/kustomization.yaml | 1 - services/gitea/oidc-job.yaml | 98 ------------------------------- 2 files changed, 99 deletions(-) delete mode 100644 services/gitea/oidc-job.yaml diff --git a/services/gitea/kustomization.yaml b/services/gitea/kustomization.yaml index 9731b76..36d6c23 100644 --- a/services/gitea/kustomization.yaml +++ b/services/gitea/kustomization.yaml @@ -7,4 +7,3 @@ resources: - service.yaml - pvc.yaml - ingress.yaml - - oidc-job.yaml diff --git a/services/gitea/oidc-job.yaml b/services/gitea/oidc-job.yaml deleted file mode 100644 index 73aacbb..0000000 --- a/services/gitea/oidc-job.yaml +++ /dev/null @@ -1,98 +0,0 @@ -# services/gitea/oidc-job.yaml -apiVersion: batch/v1 -kind: Job -metadata: - name: gitea-oidc-bootstrap - namespace: gitea -spec: - # clean and recreate when the manifest changes to avoid immutable pod fields - template: {} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: gitea-oidc-bootstrap - namespace: gitea -spec: - ttlSecondsAfterFinished: 1800 - backoffLimit: 1 - template: - metadata: - labels: - app: gitea - job: gitea-oidc-bootstrap - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - affinity: - podAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app: gitea - topologyKey: kubernetes.io/hostname - restartPolicy: OnFailure - volumes: - - name: gitea-data - persistentVolumeClaim: - claimName: gitea-data - containers: - - name: gitea-oidc-bootstrap - image: gitea/gitea:1.23 - imagePullPolicy: IfNotPresent - volumeMounts: - - name: gitea-data - mountPath: /data - env: - - name: CLIENT_ID - valueFrom: - secretKeyRef: - name: gitea-oidc - key: client_id - - name: CLIENT_SECRET - valueFrom: - secretKeyRef: - name: gitea-oidc - key: client_secret - - name: DISCOVERY_URL - valueFrom: - secretKeyRef: - name: gitea-oidc - key: openid_auto_discovery_url - command: - - /bin/bash - - -c - - | - set -euo pipefail - APPINI=/data/gitea/conf/app.ini - BIN=/usr/local/bin/gitea - list="$($BIN -c "$APPINI" admin auth list)" - id=$(echo "$list" | awk '$2=="keycloak"{print $1}') - if [ -n "$id" ]; then - echo "Updating existing auth source id=$id" - $BIN -c "$APPINI" admin auth update-oauth \ - --id "$id" \ - --name keycloak \ - --provider openidConnect \ - --key "$CLIENT_ID" \ - --secret "$CLIENT_SECRET" \ - --auto-discover-url "$DISCOVERY_URL" \ - --scopes "openid profile email" \ - --group-claim-name groups \ - --admin-group admin \ - --skip-local-2fa - else - echo "Creating keycloak auth source" - $BIN -c "$APPINI" admin auth add-oauth \ - --name keycloak \ - --provider openidConnect \ - --key "$CLIENT_ID" \ - --secret "$CLIENT_SECRET" \ - --auto-discover-url "$DISCOVERY_URL" \ - --scopes "openid profile email" \ - --group-claim-name groups \ - --admin-group admin \ - --skip-local-2fa - fi From c8b89c312089c11694214436c381a8117c0a297a Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:54:18 -0300 Subject: [PATCH 21/93] gitea: enforce keycloak auth source via init container --- services/gitea/deployment.yaml | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 5f66347..f5b550e 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -21,6 +21,68 @@ spec: labels: app: gitea spec: + initContainers: + - name: configure-oidc + image: gitea/gitea:1.23 + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + env: + - name: CLIENT_ID + valueFrom: + secretKeyRef: + name: gitea-oidc + key: client_id + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + name: gitea-oidc + key: client_secret + - name: DISCOVERY_URL + valueFrom: + secretKeyRef: + name: gitea-oidc + key: openid_auto_discovery_url + command: + - /bin/bash + - -c + - | + set -euo pipefail + APPINI=/data/gitea/conf/app.ini + BIN=/usr/local/bin/gitea + + list="$($BIN -c "$APPINI" admin auth list)" + id=$(echo "$list" | awk '$2=="keycloak"{print $1}') + + if [ -n "$id" ]; then + echo "Updating existing auth source id=$id" + $BIN -c "$APPINI" admin auth update-oauth \ + --id "$id" \ + --name keycloak \ + --provider openidConnect \ + --key "$CLIENT_ID" \ + --secret "$CLIENT_SECRET" \ + --auto-discover-url "$DISCOVERY_URL" \ + --scopes "openid profile email" \ + --group-claim-name groups \ + --admin-group admin \ + --skip-local-2fa + else + echo "Creating keycloak auth source" + $BIN -c "$APPINI" admin auth add-oauth \ + --name keycloak \ + --provider openidConnect \ + --key "$CLIENT_ID" \ + --secret "$CLIENT_SECRET" \ + --auto-discover-url "$DISCOVERY_URL" \ + --scopes "openid profile email" \ + --group-claim-name groups \ + --admin-group admin \ + --skip-local-2fa + fi + volumeMounts: + - name: gitea-data + mountPath: /data nodeSelector: node-role.kubernetes.io/worker: "true" affinity: From fc5b0cccf809bcea21d378be17b56b1da851e176 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 21:58:36 -0300 Subject: [PATCH 22/93] gitea: drop required claim constraint on keycloak auth --- services/gitea/deployment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index f5b550e..4568972 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -64,6 +64,8 @@ spec: --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ --scopes "openid profile email" \ + --required-claim-name "" \ + --required-claim-value "" \ --group-claim-name groups \ --admin-group admin \ --skip-local-2fa @@ -76,6 +78,8 @@ spec: --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ --scopes "openid profile email" \ + --required-claim-name "" \ + --required-claim-value "" \ --group-claim-name groups \ --admin-group admin \ --skip-local-2fa From 29da4be557f90b95afba3a3f54f62d40b7c0912c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 22:06:25 -0300 Subject: [PATCH 23/93] gitea: pin secret/internal token and include secret manifest --- services/gitea/deployment.yaml | 10 ++++++++++ services/gitea/kustomization.yaml | 1 + services/gitea/secret.yaml | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 services/gitea/secret.yaml diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 4568972..66670a9 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -131,6 +131,16 @@ spec: value: "trace" - name: GITEA__service__REQUIRE_SIGNIN_VIEW value: "false" + - name: GITEA__security__SECRET_KEY + valueFrom: + secretKeyRef: + name: gitea-secret + key: SECRET_KEY + - name: GITEA__security__INTERNAL_TOKEN + valueFrom: + secretKeyRef: + name: gitea-secret + key: INTERNAL_TOKEN - name: DB_TYPE value: "postgres" - name: DB_HOST diff --git a/services/gitea/kustomization.yaml b/services/gitea/kustomization.yaml index 36d6c23..1ea9af4 100644 --- a/services/gitea/kustomization.yaml +++ b/services/gitea/kustomization.yaml @@ -3,6 +3,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - namespace.yaml + - secret.yaml - deployment.yaml - service.yaml - pvc.yaml diff --git a/services/gitea/secret.yaml b/services/gitea/secret.yaml new file mode 100644 index 0000000..11a533d --- /dev/null +++ b/services/gitea/secret.yaml @@ -0,0 +1,10 @@ +# services/gitea/secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: gitea-secret + namespace: gitea +type: Opaque +stringData: + SECRET_KEY: "QVOarq1Tb8Lxm2esuB7MoWeK7wkNGpdePFRDyBhj1Rc" + INTERNAL_TOKEN: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3NTQ1NzU3Mzd9.QVOarq1Tb8Lxm2esuB7MoWeK7wkNGpdePFRDyBhj1Rc" From c3a8c7ddae73e80b27434bdb6c7d1d0e44a81259 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 22:10:13 -0300 Subject: [PATCH 24/93] gitea: remove committed secret and env refs --- services/gitea/deployment.yaml | 10 ---------- services/gitea/kustomization.yaml | 1 - 2 files changed, 11 deletions(-) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 66670a9..4568972 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -131,16 +131,6 @@ spec: value: "trace" - name: GITEA__service__REQUIRE_SIGNIN_VIEW value: "false" - - name: GITEA__security__SECRET_KEY - valueFrom: - secretKeyRef: - name: gitea-secret - key: SECRET_KEY - - name: GITEA__security__INTERNAL_TOKEN - valueFrom: - secretKeyRef: - name: gitea-secret - key: INTERNAL_TOKEN - name: DB_TYPE value: "postgres" - name: DB_HOST diff --git a/services/gitea/kustomization.yaml b/services/gitea/kustomization.yaml index 1ea9af4..36d6c23 100644 --- a/services/gitea/kustomization.yaml +++ b/services/gitea/kustomization.yaml @@ -3,7 +3,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - namespace.yaml - - secret.yaml - deployment.yaml - service.yaml - pvc.yaml From 294542e7186543c0cff1da9c350523871db7ebac Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 22:16:49 -0300 Subject: [PATCH 25/93] gitea: reference secret via env; remove secret file --- services/gitea/deployment.yaml | 10 ++++++++++ services/gitea/secret.yaml | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 services/gitea/secret.yaml diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 4568972..66670a9 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -131,6 +131,16 @@ spec: value: "trace" - name: GITEA__service__REQUIRE_SIGNIN_VIEW value: "false" + - name: GITEA__security__SECRET_KEY + valueFrom: + secretKeyRef: + name: gitea-secret + key: SECRET_KEY + - name: GITEA__security__INTERNAL_TOKEN + valueFrom: + secretKeyRef: + name: gitea-secret + key: INTERNAL_TOKEN - name: DB_TYPE value: "postgres" - name: DB_HOST diff --git a/services/gitea/secret.yaml b/services/gitea/secret.yaml deleted file mode 100644 index 11a533d..0000000 --- a/services/gitea/secret.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# services/gitea/secret.yaml -apiVersion: v1 -kind: Secret -metadata: - name: gitea-secret - namespace: gitea -type: Opaque -stringData: - SECRET_KEY: "QVOarq1Tb8Lxm2esuB7MoWeK7wkNGpdePFRDyBhj1Rc" - INTERNAL_TOKEN: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3NTQ1NzU3Mzd9.QVOarq1Tb8Lxm2esuB7MoWeK7wkNGpdePFRDyBhj1Rc" From 7a1f3bfc3f827876feba669e2f0b6c034c51eef8 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 22:25:46 -0300 Subject: [PATCH 26/93] gitea: add proxy/session headers for OIDC --- services/gitea/deployment.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 66670a9..4fd47c2 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -131,6 +131,14 @@ spec: value: "trace" - name: GITEA__service__REQUIRE_SIGNIN_VIEW value: "false" + - name: GITEA__server__PROXY_HEADERS + value: "X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host" + - name: GITEA__session__COOKIE_SECURE + value: "true" + - name: GITEA__session__DOMAIN + value: "scm.bstein.dev" + - name: GITEA__session__SAME_SITE + value: "lax" - name: GITEA__security__SECRET_KEY valueFrom: secretKeyRef: From 828f66d18cf3b9728ce97727e7c808e13cc0c607 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 14 Dec 2025 23:08:38 -0300 Subject: [PATCH 27/93] gitea: enable OIDC auto-registration --- services/gitea/deployment.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 4fd47c2..0508f57 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -123,6 +123,8 @@ spec: value: "https://scm.bstein.dev" - name: GITEA__service__ENABLE_OPENID_SIGNIN value: "true" + - name: GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION + value: "true" - name: GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION value: "true" - name: GITEA__service__DISABLE_REGISTRATION From b87f06f6ff2b638025d60a5aea8b07d50a544eff Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 12:57:02 -0300 Subject: [PATCH 28/93] zot: add oauth proxy and user sync scripts --- scripts/crypto_wallet_monero_setup.fish | 5 +- scripts/crypto_wallet_sui_setup.fish | 2 +- scripts/gitea_cred_sync.sh | 92 ++++++++++++++++++++++ scripts/gitops_cred_sync.sh | 87 +++++++++++++++++++++ scripts/jenkins_cred_sync.sh | 94 +++++++++++++++++++++++ scripts/longhorn_volume_usage.fish | 2 +- scripts/nextcloud-maintenance.sh | 2 +- scripts/zot_cred_sync.sh | 76 ++++++++++++++++++ services/crypto/monerod/deployment.yaml | 2 +- services/crypto/xmr-miner/deployment.yaml | 2 +- services/gitea/deployment.yaml | 4 +- services/keycloak/deployment.yaml | 2 +- services/monitoring/dcgm-exporter.yaml | 2 +- services/oauth2-proxy/middleware.yaml | 2 +- services/pegasus/deployment.yaml | 2 +- services/pegasus/image.yaml | 2 +- services/zot/configmap.yaml | 4 +- services/zot/deployment.yaml | 34 +++++++- services/zot/ingress.yaml | 35 ++++++++- services/zot/kustomization.yaml | 3 + services/zot/middleware-ui.yaml | 10 +++ services/zot/oauth2-proxy-deployment.yaml | 83 ++++++++++++++++++++ services/zot/oauth2-proxy-service.yaml | 14 ++++ 23 files changed, 538 insertions(+), 23 deletions(-) create mode 100755 scripts/gitea_cred_sync.sh create mode 100755 scripts/gitops_cred_sync.sh create mode 100755 scripts/jenkins_cred_sync.sh create mode 100755 scripts/zot_cred_sync.sh create mode 100644 services/zot/middleware-ui.yaml create mode 100644 services/zot/oauth2-proxy-deployment.yaml create mode 100644 services/zot/oauth2-proxy-service.yaml diff --git a/scripts/crypto_wallet_monero_setup.fish b/scripts/crypto_wallet_monero_setup.fish index 86c746a..fc1bc16 100644 --- a/scripts/crypto_wallet_monero_setup.fish +++ b/scripts/crypto_wallet_monero_setup.fish @@ -372,8 +372,8 @@ function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-r end # Use your private image by default (in Zot) - read -P "Container image for wallet RPC [registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image - if test -z "$image"; set image registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end + read -P "Container image for wallet RPC [cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image + if test -z "$image"; set image cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end _require "Container image" $image; or return 1 # --- Secrets (defaults: RPC user=wallet name, passwords auto if missing) @@ -1375,4 +1375,3 @@ function xmrwallet_help_detailed echo " Probes it via a temporary port-forward so it works from your workstation." echo " Set xmrwallet_SKIP_DAEMON_CHECK=1 to bypass the daemon probe (not recommended)." end - diff --git a/scripts/crypto_wallet_sui_setup.fish b/scripts/crypto_wallet_sui_setup.fish index 5883a7c..544ceaf 100644 --- a/scripts/crypto_wallet_sui_setup.fish +++ b/scripts/crypto_wallet_sui_setup.fish @@ -23,7 +23,7 @@ end # Default image chooser (you should override with your own multi-arch image) function _sui_default_image -a NET - echo registry.bstein.dev/infra/sui-tools:1.53.2 + echo cli.registry.bstein.dev/infra/sui-tools:1.53.2 end # Convert any string to a k8s-safe name (RFC-1123 label-ish) diff --git a/scripts/gitea_cred_sync.sh b/scripts/gitea_cred_sync.sh new file mode 100755 index 0000000..445a6b2 --- /dev/null +++ b/scripts/gitea_cred_sync.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Sync Keycloak users into Gitea local accounts (for CLI + tokens). +# Requires: curl, jq, kubectl. Expects a Keycloak client with realm-management +# permissions (manage-users) and a Gitea admin token stored in a secret. + +set -euo pipefail + +require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } +require curl; require jq; require kubectl + +: "${KEYCLOAK_URL:=https://sso.bstein.dev}" +: "${KEYCLOAK_REALM:=atlas}" +: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" +: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" +: "${GITEA_BASE_URL:=https://scm.bstein.dev}" +: "${GITEA_NAMESPACE:=gitea}" +: "${GITEA_TOKEN_SECRET_NAME:=gitea-admin-token}" +: "${GITEA_TOKEN_SECRET_KEY:=token}" +: "${DEFAULT_PASSWORD:=TempSsoPass!2025}" + +fetch_token() { + curl -fsS -X POST \ + -d "grant_type=client_credentials" \ + -d "client_id=${KEYCLOAK_CLIENT_ID}" \ + -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ + "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ + | jq -r '.access_token' +} + +pull_users() { + local token="$1" + curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ + | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.username, (.email // ""), (.firstName // ""), (.lastName // "")] | @tsv' +} + +get_gitea_token() { + if [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then + echo "${GITEA_ADMIN_TOKEN}" + return + fi + kubectl -n "${GITEA_NAMESPACE}" get secret "${GITEA_TOKEN_SECRET_NAME}" -o "jsonpath={.data.${GITEA_TOKEN_SECRET_KEY}}" \ + | base64 -d +} + +user_exists() { + local token="$1" username="$2" + local code + code=$(curl -s -o /dev/null -w '%{http_code}' \ + -H "Authorization: token ${token}" \ + "${GITEA_BASE_URL}/api/v1/admin/users/${username}") + [[ "${code}" == "200" ]] +} + +create_user() { + local token="$1" username="$2" email="$3" fname="$4" lname="$5" + local body status fullname + fullname="$(echo "${fname} ${lname}" | xargs)" + if [[ -z "${email}" ]]; then + email="${username}@example.local" + fi + body=$(jq -n --arg u "${username}" --arg e "${email}" --arg p "${DEFAULT_PASSWORD}" \ + --arg fn "${fullname}" '{username:$u, email:$e, password:$p, must_change_password:false, full_name:$fn}') + status=$(curl -s -o /dev/null -w '%{http_code}' \ + -H "Authorization: token ${token}" \ + -H "Content-Type: application/json" \ + -X POST \ + -d "${body}" \ + "${GITEA_BASE_URL}/api/v1/admin/users") + if [[ "${status}" == "201" ]]; then + echo "created gitea user ${username}" + elif [[ "${status}" == "409" ]]; then + echo "gitea user ${username} already exists (409)" >&2 + else + echo "failed to create gitea user ${username} (status ${status})" >&2 + fi +} + +main() { + local kc_token gitea_token + kc_token="$(fetch_token)" + gitea_token="$(get_gitea_token)" + + while IFS=$'\t' read -r username email fname lname; do + if user_exists "${gitea_token}" "${username}"; then + continue + fi + create_user "${gitea_token}" "${username}" "${email}" "${fname}" "${lname}" + done < <(pull_users "${kc_token}") +} + +main "$@" diff --git a/scripts/gitops_cred_sync.sh b/scripts/gitops_cred_sync.sh new file mode 100755 index 0000000..8f85caa --- /dev/null +++ b/scripts/gitops_cred_sync.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Ensure Keycloak users are in the GitOps admin group used by weave-gitops (cd.bstein.dev). +# Weave GitOps relies on OIDC; membership in the "admin" group maps to cluster-admin via RBAC. +# Requires: curl, jq. Needs a Keycloak client with realm-management (manage-users/groups). + +set -euo pipefail + +require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } +require curl; require jq + +: "${KEYCLOAK_URL:=https://sso.bstein.dev}" +: "${KEYCLOAK_REALM:=atlas}" +: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" +: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" +: "${GITOPS_GROUP:=admin}" +# Comma-separated usernames to sync; set SYNC_ALL_USERS=true to include all Keycloak users. +: "${TARGET_USERNAMES:=bstein}" +: "${SYNC_ALL_USERS:=false}" + +fetch_token() { + curl -fsS -X POST \ + -d "grant_type=client_credentials" \ + -d "client_id=${KEYCLOAK_CLIENT_ID}" \ + -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ + "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ + | jq -r '.access_token' +} + +ensure_group() { + local token="$1" group="$2" id + id=$(curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \ + | jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1) + if [[ -n "${id}" ]]; then + echo "${id}" + return + fi + curl -fsS -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"${group}\"}" \ + -X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups" + # Fetch again to get id + curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \ + | jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1 +} + +user_id_by_name() { + local token="$1" username="$2" + curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=${username}" \ + | jq -r '.[0].id' +} + +add_user_to_group() { + local token="$1" user_id="$2" group_id="$3" username="$4" + if [[ -z "${user_id}" ]]; then + echo "user ${username} not found in Keycloak; skip" >&2 + return + fi + curl -fsS -o /dev/null -w '%{http_code}' \ + -H "Authorization: Bearer ${token}" \ + -X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${user_id}/groups/${group_id}" \ + | grep -qE '^(204|409)$' || echo "failed adding ${username} to group" >&2 +} + +main() { + local token group_id users=() + token="$(fetch_token)" + group_id="$(ensure_group "${token}" "${GITOPS_GROUP}")" + + if [[ "${SYNC_ALL_USERS}" == "true" ]]; then + readarray -t users < <(curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ + | jq -r '.[] | select(.enabled==true) | .username') + else + IFS=',' read -ra users <<< "${TARGET_USERNAMES}" + fi + + for user in "${users[@]}"; do + user="$(echo "${user}" | xargs)" + [[ -z "${user}" ]] && continue + add_user_to_group "${token}" "$(user_id_by_name "${token}" "${user}")" "${group_id}" "${user}" + done +} + +main "$@" diff --git a/scripts/jenkins_cred_sync.sh b/scripts/jenkins_cred_sync.sh new file mode 100755 index 0000000..d4919a1 --- /dev/null +++ b/scripts/jenkins_cred_sync.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Sync Keycloak users into Jenkins local accounts (for CLI/API use). +# Jenkins is OIDC-enabled, but local users can still be provisioned for tokens. +# Requires: curl, jq, kubectl. Needs Jenkins admin user+API token. + +set -euo pipefail + +require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } +require curl; require jq; require kubectl + +: "${KEYCLOAK_URL:=https://sso.bstein.dev}" +: "${KEYCLOAK_REALM:=atlas}" +: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" +: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" +: "${JENKINS_URL:=https://ci.bstein.dev}" +: "${JENKINS_NAMESPACE:=jenkins}" +: "${JENKINS_ADMIN_SECRET_NAME:=jenkins-admin-token}" +: "${JENKINS_ADMIN_USER_KEY:=username}" +: "${JENKINS_ADMIN_TOKEN_KEY:=token}" +: "${DEFAULT_PASSWORD:=TempSsoPass!2025}" + +fetch_token() { + curl -fsS -X POST \ + -d "grant_type=client_credentials" \ + -d "client_id=${KEYCLOAK_CLIENT_ID}" \ + -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ + "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ + | jq -r '.access_token' +} + +pull_users() { + local token="$1" + curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ + | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.id, .username, (.email // "")] | @tsv' +} + +get_admin_auth() { + local user token + if [[ -n "${JENKINS_ADMIN_USER:-}" && -n "${JENKINS_ADMIN_TOKEN:-}" ]]; then + echo "${JENKINS_ADMIN_USER}:${JENKINS_ADMIN_TOKEN}" + return + fi + user=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_USER_KEY}}" | base64 -d) + token=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_TOKEN_KEY}}" | base64 -d) + echo "${user}:${token}" +} + +get_crumb() { + local auth="$1" + curl -fsS -u "${auth}" "${JENKINS_URL}/crumbIssuer/api/json" | jq -r .crumb +} + +user_exists() { + local auth="$1" user="$2" + local code + code=$(curl -s -o /dev/null -w '%{http_code}' -u "${auth}" "${JENKINS_URL}/user/${user}/api/json") + [[ "${code}" == "200" ]] +} + +create_user() { + local auth="$1" crumb="$2" username="$3" email="$4" + local status + status=$(curl -s -o /dev/null -w '%{http_code}' \ + -u "${auth}" \ + -H "Jenkins-Crumb: ${crumb}" \ + -X POST \ + --data "username=${username}&password1=${DEFAULT_PASSWORD}&password2=${DEFAULT_PASSWORD}&fullname=${username}&email=${email}" \ + "${JENKINS_URL}/securityRealm/createAccountByAdmin") + + if [[ "${status}" == "200" || "${status}" == "302" ]]; then + echo "created jenkins user ${username}" + elif [[ "${status}" == "400" ]]; then + echo "jenkins user ${username} already exists (400)" >&2 + else + echo "failed to create jenkins user ${username} (status ${status})" >&2 + fi +} + +main() { + local kc_token auth crumb + kc_token="$(fetch_token)" + auth="$(get_admin_auth)" + crumb="$(get_crumb "${auth}")" + + while IFS=$'\t' read -r _ uid email; do + if user_exists "${auth}" "${uid}"; then + continue + fi + create_user "${auth}" "${crumb}" "${uid}" "${email}" + done < <(pull_users "${kc_token}") +} + +main "$@" diff --git a/scripts/longhorn_volume_usage.fish b/scripts/longhorn_volume_usage.fish index b2f9f7b..1d32ccd 100755 --- a/scripts/longhorn_volume_usage.fish +++ b/scripts/longhorn_volume_usage.fish @@ -1,6 +1,6 @@ #!/usr/bin/env fish -function pvc-usage --description "Show Longhorn PVC usage (human-readable) mapped to namespace/name" +function pvc-usage --description "Show Longhorn PVC usage mapped to namespace/name" begin kubectl -n longhorn-system get volumes.longhorn.io -o json \ | jq -r '.items[] | "\(.metadata.name)\t\(.status.actualSize)\t\(.spec.size)"' \ diff --git a/scripts/nextcloud-maintenance.sh b/scripts/nextcloud-maintenance.sh index e8ea18c..3dd3763 100755 --- a/scripts/nextcloud-maintenance.sh +++ b/scripts/nextcloud-maintenance.sh @@ -39,7 +39,7 @@ SITES=( "Jellyfin|https://stream.bstein.dev" "Gitea|https://scm.bstein.dev" "Jenkins|https://ci.bstein.dev" - "Zot|https://registry.bstein.dev" + "Zot|https://web.registry.bstein.dev" "Vault|https://secret.bstein.dev" "Jitsi|https://meet.bstein.dev" "Grafana|https://metrics.bstein.dev" diff --git a/scripts/zot_cred_sync.sh b/scripts/zot_cred_sync.sh new file mode 100755 index 0000000..e33c379 --- /dev/null +++ b/scripts/zot_cred_sync.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Sync Keycloak users into Zot htpasswd. +# Intended to be rendered into a ConfigMap/Job; keep real secrets out of git. + +set -euo pipefail + +require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } +require curl; require jq; require kubectl; require htpasswd + +: "${KEYCLOAK_URL:=https://sso.bstein.dev}" +: "${KEYCLOAK_REALM:=atlas}" +: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" +: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" +: "${ZOT_NAMESPACE:=zot}" +: "${HTPASSWD_SECRET_NAME:=zot-htpasswd}" +: "${DEFAULT_PASSWORD:=TempSsoPass!2025}" +: "${UI_PROXY_USER:=zot-ui-proxy}" +: "${UI_PROXY_PASSWORD:=TempSsoUiPass!2025}" + +fetch_token() { + curl -fsS -X POST \ + -d "grant_type=client_credentials" \ + -d "client_id=${KEYCLOAK_CLIENT_ID}" \ + -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ + "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ + | jq -r '.access_token' +} + +pull_users() { + local token="$1" + curl -fsS -H "Authorization: Bearer ${token}" \ + "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ + | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | .username' +} + +decode_secret_file() { + local ns="$1" name="$2" key="$3" out="$4" + if kubectl -n "${ns}" get secret "${name}" >/dev/null 2>&1; then + kubectl -n "${ns}" get secret "${name}" -o "jsonpath={.data.${key}}" | base64 -d > "${out}" || true + else + : > "${out}" + fi +} + +ensure_htpasswd_line() { + local user="$1" password="$2" file="$3" + if ! grep -q "^${user}:" "${file}" 2>/dev/null; then + htpasswd -nbB "${user}" "${password}" >> "${file}" + echo "added user ${user} to htpasswd" + fi +} + +main() { + local token tmp existing + tmp="$(mktemp)" + existing="$(mktemp)" + trap 'rm -f "${tmp}" "${existing}"' EXIT + + decode_secret_file "${ZOT_NAMESPACE}" "${HTPASSWD_SECRET_NAME}" htpasswd "${existing}" + cp "${existing}" "${tmp}" + + ensure_htpasswd_line "${UI_PROXY_USER}" "${UI_PROXY_PASSWORD}" "${tmp}" + + token="$(fetch_token)" + readarray -t users < <(pull_users "${token}") + for user in "${users[@]}"; do + ensure_htpasswd_line "${user}" "${DEFAULT_PASSWORD}" "${tmp}" + done + + kubectl create secret generic "${HTPASSWD_SECRET_NAME}" \ + --namespace "${ZOT_NAMESPACE}" \ + --from-file=htpasswd="${tmp}" \ + --dry-run=client -o yaml | kubectl apply -f - +} + +main "$@" diff --git a/services/crypto/monerod/deployment.yaml b/services/crypto/monerod/deployment.yaml index 1b98e47..8a913d1 100644 --- a/services/crypto/monerod/deployment.yaml +++ b/services/crypto/monerod/deployment.yaml @@ -35,7 +35,7 @@ spec: values: ["rpi4"] containers: - name: monerod - image: registry.bstein.dev/infra/monerod:0.18.4.1 + image: cli.registry.bstein.dev/infra/monerod:0.18.4.1 command: ["/opt/monero/monerod"] args: - --data-dir=/data diff --git a/services/crypto/xmr-miner/deployment.yaml b/services/crypto/xmr-miner/deployment.yaml index 178f2b2..dc72499 100644 --- a/services/crypto/xmr-miner/deployment.yaml +++ b/services/crypto/xmr-miner/deployment.yaml @@ -32,7 +32,7 @@ spec: values: ["rpi4"] containers: - name: monero-p2pool - image: registry.bstein.dev/infra/monero-p2pool:4.9 + image: cli.registry.bstein.dev/infra/monero-p2pool:4.9 imagePullPolicy: Always command: ["p2pool"] args: diff --git a/services/gitea/deployment.yaml b/services/gitea/deployment.yaml index 0508f57..d17a007 100644 --- a/services/gitea/deployment.yaml +++ b/services/gitea/deployment.yaml @@ -63,7 +63,7 @@ spec: --key "$CLIENT_ID" \ --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ - --scopes "openid profile email" \ + --scopes "openid profile email groups" \ --required-claim-name "" \ --required-claim-value "" \ --group-claim-name groups \ @@ -77,7 +77,7 @@ spec: --key "$CLIENT_ID" \ --secret "$CLIENT_SECRET" \ --auto-discover-url "$DISCOVERY_URL" \ - --scopes "openid profile email" \ + --scopes "openid profile email groups" \ --required-claim-name "" \ --required-claim-value "" \ --group-claim-name groups \ diff --git a/services/keycloak/deployment.yaml b/services/keycloak/deployment.yaml index c4ffcda..3acb187 100644 --- a/services/keycloak/deployment.yaml +++ b/services/keycloak/deployment.yaml @@ -52,7 +52,7 @@ spec: - name: zot-regcred initContainers: - name: mailu-http-listener - image: registry.bstein.dev/sso/mailu-http-listener:0.1.0 + image: cli.registry.bstein.dev/sso/mailu-http-listener:0.1.0 imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c"] args: diff --git a/services/monitoring/dcgm-exporter.yaml b/services/monitoring/dcgm-exporter.yaml index 06152e7..b30e810 100644 --- a/services/monitoring/dcgm-exporter.yaml +++ b/services/monitoring/dcgm-exporter.yaml @@ -39,7 +39,7 @@ spec: - operator: Exists containers: - name: dcgm-exporter - image: registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04 + image: cli.registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04 imagePullPolicy: Always ports: - name: metrics diff --git a/services/oauth2-proxy/middleware.yaml b/services/oauth2-proxy/middleware.yaml index db5f3a4..0adaff9 100644 --- a/services/oauth2-proxy/middleware.yaml +++ b/services/oauth2-proxy/middleware.yaml @@ -6,7 +6,7 @@ metadata: namespace: sso spec: forwardAuth: - address: http://oauth2-proxy.sso.svc.cluster.local:4180/oauth2/auth + address: http://oauth2-proxy.sso.svc.cluster.local/oauth2/auth trustForwardHeader: true authResponseHeaders: - Authorization diff --git a/services/pegasus/deployment.yaml b/services/pegasus/deployment.yaml index d9fa0be..fcd2b63 100644 --- a/services/pegasus/deployment.yaml +++ b/services/pegasus/deployment.yaml @@ -58,7 +58,7 @@ spec: containers: - name: pegasus - image: registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} + image: cli.registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} imagePullPolicy: Always command: ["/pegasus"] env: diff --git a/services/pegasus/image.yaml b/services/pegasus/image.yaml index 1d891ff..d5acf53 100644 --- a/services/pegasus/image.yaml +++ b/services/pegasus/image.yaml @@ -5,7 +5,7 @@ metadata: name: pegasus namespace: jellyfin spec: - image: registry.bstein.dev/pegasus + image: cli.registry.bstein.dev/pegasus interval: 1m0s secretRef: name: zot-regcred diff --git a/services/zot/configmap.yaml b/services/zot/configmap.yaml index 0261fc1..3046d78 100644 --- a/services/zot/configmap.yaml +++ b/services/zot/configmap.yaml @@ -26,14 +26,14 @@ data: "repositories": { "**": { "policies": [ - { "users": ["bstein"], "actions": ["read", "create", "update", "delete"] } + { "users": ["bstein", "zot-ui-proxy"], "actions": ["read", "create", "update", "delete"] } ], "defaultPolicy": [], "anonymousPolicy": [] } }, "adminPolicy": { - "users": ["bstein"], + "users": ["bstein", "zot-ui-proxy"], "actions": ["read", "create", "update", "delete"] } } diff --git a/services/zot/deployment.yaml b/services/zot/deployment.yaml index e4fdc1f..ef6ade9 100644 --- a/services/zot/deployment.yaml +++ b/services/zot/deployment.yaml @@ -35,6 +35,9 @@ spec: image: ghcr.io/project-zot/zot-linux-arm64:v2.1.8 imagePullPolicy: IfNotPresent args: ["serve", "/etc/zot/config.json"] + env: + - name: UI_PROXY_HTPASSWD + value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y" ports: - { name: http, containerPort: 5000 } volumeMounts: @@ -45,7 +48,6 @@ spec: - name: htpasswd mountPath: /etc/zot/htpasswd subPath: htpasswd - readOnly: true - name: zot-data mountPath: /var/lib/registry readinessProbe: @@ -60,13 +62,41 @@ spec: periodSeconds: 10 resources: requests: { cpu: "50m", memory: "64Mi" } + initContainers: + - name: merge-htpasswd + image: busybox:1.36 + command: + - sh + - -c + - | + set -e + if [ -f /src/htpasswd ]; then + cp /src/htpasswd /merged/htpasswd + else + touch /merged/htpasswd + fi + if [ -n "${UI_PROXY_HTPASSWD}" ]; then + echo "${UI_PROXY_HTPASSWD}" >> /merged/htpasswd + fi + env: + - name: UI_PROXY_HTPASSWD + value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y" + volumeMounts: + - name: htpasswd-source + mountPath: /src + readOnly: true + - name: htpasswd + mountPath: /merged volumes: - name: cfg configMap: name: zot-config - - name: htpasswd + - name: htpasswd-source secret: secretName: zot-htpasswd + optional: true + - name: htpasswd + emptyDir: {} - name: zot-data persistentVolumeClaim: claimName: zot-data diff --git a/services/zot/ingress.yaml b/services/zot/ingress.yaml index 12f6c60..86747c6 100644 --- a/services/zot/ingress.yaml +++ b/services/zot/ingress.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: zot + name: zot-cli namespace: zot annotations: cert-manager.io/cluster-issuer: letsencrypt @@ -12,10 +12,10 @@ metadata: spec: ingressClassName: traefik tls: - - hosts: [ "registry.bstein.dev" ] - secretName: registry-bstein-dev-tls + - hosts: [ "cli.registry.bstein.dev" ] + secretName: cli-registry-bstein-dev-tls rules: - - host: registry.bstein.dev + - host: cli.registry.bstein.dev http: paths: - path: / @@ -25,3 +25,30 @@ spec: name: zot port: number: 5000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: zot-ui + namespace: zot + annotations: + cert-manager.io/cluster-issuer: letsencrypt + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + traefik.ingress.kubernetes.io/router.middlewares: zot-zot-ui-auth-header@kubernetescrd, zot-zot-resp-headers@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: [ "web.registry.bstein.dev" ] + secretName: web-registry-bstein-dev-tls + rules: + - host: web.registry.bstein.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: zot-oauth2-proxy + port: + number: 80 diff --git a/services/zot/kustomization.yaml b/services/zot/kustomization.yaml index 73a097c..0795b7e 100644 --- a/services/zot/kustomization.yaml +++ b/services/zot/kustomization.yaml @@ -7,5 +7,8 @@ resources: - deployment.yaml - configmap.yaml - service.yaml + - oauth2-proxy-deployment.yaml + - oauth2-proxy-service.yaml - ingress.yaml - middleware.yaml + - middleware-ui.yaml diff --git a/services/zot/middleware-ui.yaml b/services/zot/middleware-ui.yaml new file mode 100644 index 0000000..7feaf53 --- /dev/null +++ b/services/zot/middleware-ui.yaml @@ -0,0 +1,10 @@ +# services/zot/middleware-ui.yaml +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: zot-ui-auth-header + namespace: zot +spec: + headers: + customRequestHeaders: + Authorization: "Basic em90LXVpLXByb3h5OlRlbXBTc29VaVBhc3MhMjAyNQ==" diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml new file mode 100644 index 0000000..b071f9a --- /dev/null +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -0,0 +1,83 @@ +# services/zot/oauth2-proxy-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zot-oauth2-proxy + namespace: zot + labels: { app: zot-oauth2-proxy } +spec: + replicas: 1 + selector: + matchLabels: { app: zot-oauth2-proxy } + template: + metadata: + labels: { app: zot-oauth2-proxy } + spec: + nodeSelector: + node-role.kubernetes.io/worker: "true" + affinity: + nodeAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 50 + preference: + matchExpressions: + - key: hardware + operator: In + values: ["rpi4","rpi5"] + containers: + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 + imagePullPolicy: IfNotPresent + args: + - --provider=oidc + - --redirect-url=https://web.registry.bstein.dev/oauth2/callback + - --oidc-issuer-url=https://sso.bstein.dev/realms/atlas + - --scope=openid profile email + - --email-domain=* + - --cookie-domain=web.registry.bstein.dev + - --cookie-name=_zot_ui_oauth + - --set-xauthrequest=true + - --set-authorization-header=false + - --pass-authorization-header=false + - --pass-access-token=false + - --cookie-secure=true + - --cookie-samesite=lax + - --cookie-refresh=20m + - --cookie-expire=168h + - --upstream=http://zot:5000 + - --http-address=0.0.0.0:4180 + - --skip-provider-button=true + - --skip-jwt-bearer-tokens=true + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: zot-oidc + key: client_id + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: zot-oidc + key: client_secret + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: zot-oidc + key: client_secret + ports: + - containerPort: 4180 + name: http + readinessProbe: + httpGet: + path: /ping + port: 4180 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /ping + port: 4180 + initialDelaySeconds: 20 + periodSeconds: 20 + resources: + requests: { cpu: "25m", memory: "64Mi" } diff --git a/services/zot/oauth2-proxy-service.yaml b/services/zot/oauth2-proxy-service.yaml new file mode 100644 index 0000000..4a7e96a --- /dev/null +++ b/services/zot/oauth2-proxy-service.yaml @@ -0,0 +1,14 @@ +# services/zot/oauth2-proxy-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: zot-oauth2-proxy + namespace: zot + labels: { app: zot-oauth2-proxy } +spec: + type: ClusterIP + selector: { app: zot-oauth2-proxy } + ports: + - name: http + port: 80 + targetPort: 4180 From 0416493f4965ebc4227e679accb69551b3d8ec3b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 13:00:51 -0300 Subject: [PATCH 29/93] zot: fix htpasswd volume to avoid type conflict --- services/zot/deployment.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/zot/deployment.yaml b/services/zot/deployment.yaml index ef6ade9..14dc724 100644 --- a/services/zot/deployment.yaml +++ b/services/zot/deployment.yaml @@ -45,7 +45,7 @@ spec: mountPath: /etc/zot/config.json subPath: config.json readOnly: true - - name: htpasswd + - name: htpasswd-merged mountPath: /etc/zot/htpasswd subPath: htpasswd - name: zot-data @@ -85,7 +85,7 @@ spec: - name: htpasswd-source mountPath: /src readOnly: true - - name: htpasswd + - name: htpasswd-merged mountPath: /merged volumes: - name: cfg @@ -95,7 +95,7 @@ spec: secret: secretName: zot-htpasswd optional: true - - name: htpasswd + - name: htpasswd-merged emptyDir: {} - name: zot-data persistentVolumeClaim: From 1899bb767777dff5fb46fe0a00d92a9a8d616f64 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 13:53:53 -0300 Subject: [PATCH 30/93] zot: move basic auth to oauth2-proxy upstream --- services/zot/ingress.yaml | 2 +- services/zot/kustomization.yaml | 1 - services/zot/middleware-ui.yaml | 10 ---------- services/zot/oauth2-proxy-deployment.yaml | 2 +- 4 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 services/zot/middleware-ui.yaml diff --git a/services/zot/ingress.yaml b/services/zot/ingress.yaml index 86747c6..6c23709 100644 --- a/services/zot/ingress.yaml +++ b/services/zot/ingress.yaml @@ -35,7 +35,7 @@ metadata: cert-manager.io/cluster-issuer: letsencrypt traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" - traefik.ingress.kubernetes.io/router.middlewares: zot-zot-ui-auth-header@kubernetescrd, zot-zot-resp-headers@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: zot-zot-resp-headers@kubernetescrd spec: ingressClassName: traefik tls: diff --git a/services/zot/kustomization.yaml b/services/zot/kustomization.yaml index 0795b7e..22d76ae 100644 --- a/services/zot/kustomization.yaml +++ b/services/zot/kustomization.yaml @@ -11,4 +11,3 @@ resources: - oauth2-proxy-service.yaml - ingress.yaml - middleware.yaml - - middleware-ui.yaml diff --git a/services/zot/middleware-ui.yaml b/services/zot/middleware-ui.yaml deleted file mode 100644 index 7feaf53..0000000 --- a/services/zot/middleware-ui.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# services/zot/middleware-ui.yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: zot-ui-auth-header - namespace: zot -spec: - headers: - customRequestHeaders: - Authorization: "Basic em90LXVpLXByb3h5OlRlbXBTc29VaVBhc3MhMjAyNQ==" diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml index b071f9a..f3eef52 100644 --- a/services/zot/oauth2-proxy-deployment.yaml +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -44,7 +44,7 @@ spec: - --cookie-samesite=lax - --cookie-refresh=20m - --cookie-expire=168h - - --upstream=http://zot:5000 + - --upstream=http://zot-ui-proxy:TempSsoUiPass%212025@zot:5000 - --http-address=0.0.0.0:4180 - --skip-provider-button=true - --skip-jwt-bearer-tokens=true From 54eb9e1ac513a44037621bfa2a3744d9aa25f388 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 14:01:18 -0300 Subject: [PATCH 31/93] zot: restore UI basic header middleware --- services/zot/ingress.yaml | 2 +- services/zot/kustomization.yaml | 1 + services/zot/middleware-ui.yaml | 10 ++++++++++ services/zot/oauth2-proxy-deployment.yaml | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 services/zot/middleware-ui.yaml diff --git a/services/zot/ingress.yaml b/services/zot/ingress.yaml index 6c23709..86747c6 100644 --- a/services/zot/ingress.yaml +++ b/services/zot/ingress.yaml @@ -35,7 +35,7 @@ metadata: cert-manager.io/cluster-issuer: letsencrypt traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" - traefik.ingress.kubernetes.io/router.middlewares: zot-zot-resp-headers@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: zot-zot-ui-auth-header@kubernetescrd, zot-zot-resp-headers@kubernetescrd spec: ingressClassName: traefik tls: diff --git a/services/zot/kustomization.yaml b/services/zot/kustomization.yaml index 22d76ae..0795b7e 100644 --- a/services/zot/kustomization.yaml +++ b/services/zot/kustomization.yaml @@ -11,3 +11,4 @@ resources: - oauth2-proxy-service.yaml - ingress.yaml - middleware.yaml + - middleware-ui.yaml diff --git a/services/zot/middleware-ui.yaml b/services/zot/middleware-ui.yaml new file mode 100644 index 0000000..7feaf53 --- /dev/null +++ b/services/zot/middleware-ui.yaml @@ -0,0 +1,10 @@ +# services/zot/middleware-ui.yaml +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: zot-ui-auth-header + namespace: zot +spec: + headers: + customRequestHeaders: + Authorization: "Basic em90LXVpLXByb3h5OlRlbXBTc29VaVBhc3MhMjAyNQ==" diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml index f3eef52..b071f9a 100644 --- a/services/zot/oauth2-proxy-deployment.yaml +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -44,7 +44,7 @@ spec: - --cookie-samesite=lax - --cookie-refresh=20m - --cookie-expire=168h - - --upstream=http://zot-ui-proxy:TempSsoUiPass%212025@zot:5000 + - --upstream=http://zot:5000 - --http-address=0.0.0.0:4180 - --skip-provider-button=true - --skip-jwt-bearer-tokens=true From caef505677d6b40257307d931122023a252619ce Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 14:08:18 -0300 Subject: [PATCH 32/93] zot ui: send basic creds from oauth2-proxy, remove traefik header --- services/zot/ingress.yaml | 2 +- services/zot/kustomization.yaml | 1 - services/zot/middleware-ui.yaml | 10 ---------- services/zot/oauth2-proxy-deployment.yaml | 2 +- 4 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 services/zot/middleware-ui.yaml diff --git a/services/zot/ingress.yaml b/services/zot/ingress.yaml index 86747c6..6c23709 100644 --- a/services/zot/ingress.yaml +++ b/services/zot/ingress.yaml @@ -35,7 +35,7 @@ metadata: cert-manager.io/cluster-issuer: letsencrypt traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" - traefik.ingress.kubernetes.io/router.middlewares: zot-zot-ui-auth-header@kubernetescrd, zot-zot-resp-headers@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: zot-zot-resp-headers@kubernetescrd spec: ingressClassName: traefik tls: diff --git a/services/zot/kustomization.yaml b/services/zot/kustomization.yaml index 0795b7e..22d76ae 100644 --- a/services/zot/kustomization.yaml +++ b/services/zot/kustomization.yaml @@ -11,4 +11,3 @@ resources: - oauth2-proxy-service.yaml - ingress.yaml - middleware.yaml - - middleware-ui.yaml diff --git a/services/zot/middleware-ui.yaml b/services/zot/middleware-ui.yaml deleted file mode 100644 index 7feaf53..0000000 --- a/services/zot/middleware-ui.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# services/zot/middleware-ui.yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: zot-ui-auth-header - namespace: zot -spec: - headers: - customRequestHeaders: - Authorization: "Basic em90LXVpLXByb3h5OlRlbXBTc29VaVBhc3MhMjAyNQ==" diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml index b071f9a..f3eef52 100644 --- a/services/zot/oauth2-proxy-deployment.yaml +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -44,7 +44,7 @@ spec: - --cookie-samesite=lax - --cookie-refresh=20m - --cookie-expire=168h - - --upstream=http://zot:5000 + - --upstream=http://zot-ui-proxy:TempSsoUiPass%212025@zot:5000 - --http-address=0.0.0.0:4180 - --skip-provider-button=true - --skip-jwt-bearer-tokens=true From 54406661f2f15216e4a29ea684d5c0c822a8264a Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 14:14:49 -0300 Subject: [PATCH 33/93] zot: forward authorization header to ui --- services/zot/oauth2-proxy-deployment.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml index f3eef52..16b0854 100644 --- a/services/zot/oauth2-proxy-deployment.yaml +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -38,8 +38,9 @@ spec: - --cookie-name=_zot_ui_oauth - --set-xauthrequest=true - --set-authorization-header=false - - --pass-authorization-header=false + - --pass-authorization-header=true - --pass-access-token=false + - --pass-basic-auth=false - --cookie-secure=true - --cookie-samesite=lax - --cookie-refresh=20m From aa2bb09873a627f1a311f81408a54d262cf8f3f7 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 14:22:48 -0300 Subject: [PATCH 34/93] zot: allow upstream basic auth from oauth2-proxy --- services/zot/oauth2-proxy-deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml index 16b0854..9761e6e 100644 --- a/services/zot/oauth2-proxy-deployment.yaml +++ b/services/zot/oauth2-proxy-deployment.yaml @@ -38,9 +38,9 @@ spec: - --cookie-name=_zot_ui_oauth - --set-xauthrequest=true - --set-authorization-header=false - - --pass-authorization-header=true + - --pass-authorization-header=false - --pass-access-token=false - - --pass-basic-auth=false + - --pass-basic-auth=true - --cookie-secure=true - --cookie-samesite=lax - --cookie-refresh=20m From aa0df1f62b6676901428ea50abd8fe055036ea6c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:05:32 -0300 Subject: [PATCH 35/93] harbor: add helm repo and deploy via helmrelease --- clusters/atlas/flux-system/applications/kustomization.yaml | 1 + infrastructure/sources/helm/kustomization.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index 2ad7b14..9b388c8 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -10,6 +10,7 @@ resources: - monerod/kustomization.yaml - pegasus/kustomization.yaml - pegasus/image-automation.yaml + - harbor/kustomization.yaml - jellyfin/kustomization.yaml - xmr-miner/kustomization.yaml - sui-metrics/kustomization.yaml diff --git a/infrastructure/sources/helm/kustomization.yaml b/infrastructure/sources/helm/kustomization.yaml index cef76c2..3ded0f1 100644 --- a/infrastructure/sources/helm/kustomization.yaml +++ b/infrastructure/sources/helm/kustomization.yaml @@ -7,5 +7,6 @@ resources: - jetstack.yaml - jenkins.yaml - mailu.yaml + - harbor.yaml - prometheus.yaml - victoria-metrics.yaml From dba7cf00a4554439ebc69ac2f39d262dc46694a7 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:05:40 -0300 Subject: [PATCH 36/93] harbor: deploy chart via flux --- .../applications/harbor/kustomization.yaml | 18 +++++ infrastructure/sources/helm/harbor.yaml | 9 +++ services/harbor/certificate.yaml | 12 ++++ services/harbor/helmrelease.yaml | 71 +++++++++++++++++++ services/harbor/kustomization.yaml | 9 +++ services/harbor/namespace.yaml | 5 ++ services/harbor/pvc.yaml | 24 +++++++ 7 files changed, 148 insertions(+) create mode 100644 clusters/atlas/flux-system/applications/harbor/kustomization.yaml create mode 100644 infrastructure/sources/helm/harbor.yaml create mode 100644 services/harbor/certificate.yaml create mode 100644 services/harbor/helmrelease.yaml create mode 100644 services/harbor/kustomization.yaml create mode 100644 services/harbor/namespace.yaml create mode 100644 services/harbor/pvc.yaml diff --git a/clusters/atlas/flux-system/applications/harbor/kustomization.yaml b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml new file mode 100644 index 0000000..62bcdd1 --- /dev/null +++ b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml @@ -0,0 +1,18 @@ +# clusters/atlas/flux-system/applications/harbor/kustomization.yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: harbor + namespace: flux-system +spec: + interval: 10m + path: ./services/harbor + targetNamespace: harbor + prune: false + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + wait: true + dependsOn: + - name: core diff --git a/infrastructure/sources/helm/harbor.yaml b/infrastructure/sources/helm/harbor.yaml new file mode 100644 index 0000000..575136c --- /dev/null +++ b/infrastructure/sources/helm/harbor.yaml @@ -0,0 +1,9 @@ +# infrastructure/sources/helm/harbor.yaml +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: harbor + namespace: flux-system +spec: + interval: 10m + url: https://helm.goharbor.io diff --git a/services/harbor/certificate.yaml b/services/harbor/certificate.yaml new file mode 100644 index 0000000..ba879a4 --- /dev/null +++ b/services/harbor/certificate.yaml @@ -0,0 +1,12 @@ +# services/harbor/certificate.yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: registry-bstein-dev + namespace: harbor +spec: + secretName: registry-bstein-dev-tls + dnsNames: [ "registry.bstein.dev" ] + issuerRef: + name: letsencrypt + kind: ClusterIssuer diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml new file mode 100644 index 0000000..ed7c34e --- /dev/null +++ b/services/harbor/helmrelease.yaml @@ -0,0 +1,71 @@ +# services/harbor/helmrelease.yaml +apiVersion: helm.toolkit.fluxcd.io/v2beta2 +kind: HelmRelease +metadata: + name: harbor + namespace: harbor +spec: + interval: 10m + chart: + spec: + chart: harbor + version: 1.18.1 + sourceRef: + kind: HelmRepository + name: harbor + namespace: flux-system + values: + externalURL: https://registry.bstein.dev + expose: + type: ingress + tls: + enabled: true + certSource: secret + secret: + secretName: registry-bstein-dev-tls + ingress: + className: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + hosts: + core: registry.bstein.dev + persistence: + enabled: true + resourcePolicy: keep + persistentVolumeClaim: + registry: + existingClaim: harbor-registry + accessMode: ReadWriteOnce + size: 50Gi + jobservice: + jobLog: + existingClaim: harbor-jobservice-logs + accessMode: ReadWriteOnce + size: 5Gi + imageChartStorage: + type: filesystem + filesystem: + rootdirectory: /storage + database: + type: external + external: + host: postgres-service.postgres.svc.cluster.local + port: "5432" + username: harbor + coreDatabase: harbor + existingSecret: harbor-db + sslmode: disable + redis: + type: internal + trivy: + enabled: false + metrics: + enabled: false + cache: + enabled: false + existingSecretAdminPassword: harbor-core + existingSecretAdminPasswordKey: HARBOR_ADMIN_PASSWORD + updateStrategy: + type: Recreate diff --git a/services/harbor/kustomization.yaml b/services/harbor/kustomization.yaml new file mode 100644 index 0000000..eb27a25 --- /dev/null +++ b/services/harbor/kustomization.yaml @@ -0,0 +1,9 @@ +# services/harbor/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: harbor +resources: + - namespace.yaml + - pvc.yaml + - certificate.yaml + - helmrelease.yaml diff --git a/services/harbor/namespace.yaml b/services/harbor/namespace.yaml new file mode 100644 index 0000000..47b7daf --- /dev/null +++ b/services/harbor/namespace.yaml @@ -0,0 +1,5 @@ +# services/harbor/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: harbor diff --git a/services/harbor/pvc.yaml b/services/harbor/pvc.yaml new file mode 100644 index 0000000..e985416 --- /dev/null +++ b/services/harbor/pvc.yaml @@ -0,0 +1,24 @@ +# services/harbor/pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: harbor-registry + namespace: harbor +spec: + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 50Gi + storageClassName: asteria +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: harbor-jobservice-logs + namespace: harbor +spec: + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 5Gi + storageClassName: asteria From 252743e416ee9b3564971932a50b5cfa6a302429 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:08:52 -0300 Subject: [PATCH 37/93] harbor: use existing secrets and correct admin key --- services/harbor/helmrelease.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index ed7c34e..32686a9 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -66,6 +66,11 @@ spec: cache: enabled: false existingSecretAdminPassword: harbor-core - existingSecretAdminPasswordKey: HARBOR_ADMIN_PASSWORD + existingSecretAdminPasswordKey: harbor_admin_password + existingSecretSecretKey: harbor-core + core: + existingSecret: harbor-core + existingXsrfSecret: harbor-core + existingXsrfSecretKey: CSRF_KEY updateStrategy: type: Recreate From 0f49849761e832cb7b1d6b7e8edfdf58fc73de19 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:14:26 -0300 Subject: [PATCH 38/93] Regenerate dashboards after availability thresholds tweak --- scripts/dashboards_render_atlas.py | 7 ++++--- services/monitoring/dashboards/atlas-overview.json | 8 ++++++-- services/monitoring/grafana-dashboard-overview.yaml | 8 ++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/scripts/dashboards_render_atlas.py b/scripts/dashboards_render_atlas.py index 894edb9..a14f396 100644 --- a/scripts/dashboards_render_atlas.py +++ b/scripts/dashboards_render_atlas.py @@ -241,9 +241,10 @@ UPTIME_PERCENT_THRESHOLDS = { "mode": "absolute", "steps": [ {"color": "red", "value": None}, - {"color": "orange", "value": 0.999}, - {"color": "yellow", "value": 0.9999}, - {"color": "green", "value": 0.99999}, + {"color": "orange", "value": 0.99}, + {"color": "yellow", "value": 0.999}, + {"color": "green", "value": 0.9999}, + {"color": "blue", "value": 0.99999}, ], } PROBLEM_TABLE_EXPR = ( diff --git a/services/monitoring/dashboards/atlas-overview.json b/services/monitoring/dashboards/atlas-overview.json index 93ee927..9088ea1 100644 --- a/services/monitoring/dashboards/atlas-overview.json +++ b/services/monitoring/dashboards/atlas-overview.json @@ -245,14 +245,18 @@ }, { "color": "orange", - "value": 0.999 + "value": 0.99 }, { "color": "yellow", - "value": 0.9999 + "value": 0.999 }, { "color": "green", + "value": 0.9999 + }, + { + "color": "blue", "value": 0.99999 } ] diff --git a/services/monitoring/grafana-dashboard-overview.yaml b/services/monitoring/grafana-dashboard-overview.yaml index f4165bd..5fe8b6f 100644 --- a/services/monitoring/grafana-dashboard-overview.yaml +++ b/services/monitoring/grafana-dashboard-overview.yaml @@ -254,14 +254,18 @@ data: }, { "color": "orange", - "value": 0.999 + "value": 0.99 }, { "color": "yellow", - "value": 0.9999 + "value": 0.999 }, { "color": "green", + "value": 0.9999 + }, + { + "color": "blue", "value": 0.99999 } ] From 9cda32c0bfc6af3d089c7d3d73c291748fe5fd2a Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:22:48 -0300 Subject: [PATCH 39/93] harbor: use astreae storageclass for pvc --- services/harbor/pvc.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/harbor/pvc.yaml b/services/harbor/pvc.yaml index e985416..ff2db47 100644 --- a/services/harbor/pvc.yaml +++ b/services/harbor/pvc.yaml @@ -9,7 +9,7 @@ spec: resources: requests: storage: 50Gi - storageClassName: asteria + storageClassName: astreae --- apiVersion: v1 kind: PersistentVolumeClaim @@ -21,4 +21,4 @@ spec: resources: requests: storage: 5Gi - storageClassName: asteria + storageClassName: astreae From 3e3cab684510c3cc66b4556b87d4f8d6402dac8e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 22:32:29 -0300 Subject: [PATCH 40/93] harbor: increase helm timeout --- services/harbor/helmrelease.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 32686a9..d60e7e6 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -6,6 +6,7 @@ metadata: namespace: harbor spec: interval: 10m + timeout: 10m chart: spec: chart: harbor From d51a19cab98437e68c07854d7984e487699741e6 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 23:00:11 -0300 Subject: [PATCH 41/93] harbor: prefer rpi nodes --- services/harbor/helmrelease.yaml | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index d60e7e6..0e84fa5 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -60,6 +60,27 @@ spec: sslmode: disable redis: type: internal + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + - weight: 10 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] trivy: enabled: false metrics: @@ -73,5 +94,92 @@ spec: existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + - weight: 10 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + jobservice: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + - weight: 10 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + portal: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + - weight: 10 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + registry: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + - weight: 10 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] updateStrategy: type: Recreate From a8bde2edc745c5399dbf7215e47b43d98c3cb432 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 23:02:58 -0300 Subject: [PATCH 42/93] harbor: pin to amd64, prefer titan-22 --- services/harbor/helmrelease.yaml | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 0e84fa5..a5552c5 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -65,22 +65,22 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware + - key: kubernetes.io/arch operator: In - values: [ "rpi4", "rpi5" ] + values: [ "amd64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi4" ] + values: [ "titan-22" ] - weight: 10 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi5" ] + values: [ "titan-24" ] trivy: enabled: false metrics: @@ -99,87 +99,87 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware + - key: kubernetes.io/arch operator: In - values: [ "rpi4", "rpi5" ] + values: [ "amd64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi4" ] + values: [ "titan-22" ] - weight: 10 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi5" ] + values: [ "titan-24" ] jobservice: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware + - key: kubernetes.io/arch operator: In - values: [ "rpi4", "rpi5" ] + values: [ "amd64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi4" ] + values: [ "titan-22" ] - weight: 10 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi5" ] + values: [ "titan-24" ] portal: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware + - key: kubernetes.io/arch operator: In - values: [ "rpi4", "rpi5" ] + values: [ "amd64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi4" ] + values: [ "titan-22" ] - weight: 10 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi5" ] + values: [ "titan-24" ] registry: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware + - key: kubernetes.io/arch operator: In - values: [ "rpi4", "rpi5" ] + values: [ "amd64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi4" ] + values: [ "titan-22" ] - weight: 10 preference: matchExpressions: - - key: hardware + - key: kubernetes.io/hostname operator: In - values: [ "rpi5" ] + values: [ "titan-24" ] updateStrategy: type: Recreate From 22b611f8ea7ba271cef6970b80979daad9342f9f Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 15 Dec 2025 23:14:26 -0300 Subject: [PATCH 43/93] harbor: set redis affinity to amd64 titan-22 first --- services/harbor/helmrelease.yaml | 43 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index a5552c5..ddb8dab 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -60,27 +60,28 @@ spec: sslmode: disable redis: type: internal - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: [ "amd64" ] - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 90 - preference: - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: [ "titan-22" ] - - weight: 10 - preference: - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: [ "titan-24" ] + internal: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: [ "amd64" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: [ "titan-22" ] + - weight: 10 + preference: + matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: [ "titan-24" ] trivy: enabled: false metrics: From bd64a36165b4f192f262d9fa303d83eec354d6eb Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 00:08:11 -0300 Subject: [PATCH 44/93] registry: point workloads to harbor --- scripts/crypto_wallet_monero_setup.fish | 4 ++-- scripts/crypto_wallet_sui_setup.fish | 2 +- scripts/nextcloud-maintenance.sh | 2 +- services/crypto/monerod/deployment.yaml | 2 +- services/crypto/xmr-miner/deployment.yaml | 2 +- services/keycloak/deployment.yaml | 4 +--- services/monitoring/dcgm-exporter.yaml | 4 +--- services/pegasus/deployment.yaml | 4 +--- services/pegasus/image.yaml | 4 +--- 9 files changed, 10 insertions(+), 18 deletions(-) diff --git a/scripts/crypto_wallet_monero_setup.fish b/scripts/crypto_wallet_monero_setup.fish index fc1bc16..864a667 100644 --- a/scripts/crypto_wallet_monero_setup.fish +++ b/scripts/crypto_wallet_monero_setup.fish @@ -372,8 +372,8 @@ function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-r end # Use your private image by default (in Zot) - read -P "Container image for wallet RPC [cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image - if test -z "$image"; set image cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end + read -P "Container image for wallet RPC [registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image + if test -z "$image"; set image registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end _require "Container image" $image; or return 1 # --- Secrets (defaults: RPC user=wallet name, passwords auto if missing) diff --git a/scripts/crypto_wallet_sui_setup.fish b/scripts/crypto_wallet_sui_setup.fish index 544ceaf..5883a7c 100644 --- a/scripts/crypto_wallet_sui_setup.fish +++ b/scripts/crypto_wallet_sui_setup.fish @@ -23,7 +23,7 @@ end # Default image chooser (you should override with your own multi-arch image) function _sui_default_image -a NET - echo cli.registry.bstein.dev/infra/sui-tools:1.53.2 + echo registry.bstein.dev/infra/sui-tools:1.53.2 end # Convert any string to a k8s-safe name (RFC-1123 label-ish) diff --git a/scripts/nextcloud-maintenance.sh b/scripts/nextcloud-maintenance.sh index 3dd3763..af1694c 100755 --- a/scripts/nextcloud-maintenance.sh +++ b/scripts/nextcloud-maintenance.sh @@ -39,7 +39,7 @@ SITES=( "Jellyfin|https://stream.bstein.dev" "Gitea|https://scm.bstein.dev" "Jenkins|https://ci.bstein.dev" - "Zot|https://web.registry.bstein.dev" + "Harbor|https://registry.bstein.dev" "Vault|https://secret.bstein.dev" "Jitsi|https://meet.bstein.dev" "Grafana|https://metrics.bstein.dev" diff --git a/services/crypto/monerod/deployment.yaml b/services/crypto/monerod/deployment.yaml index 8a913d1..1b98e47 100644 --- a/services/crypto/monerod/deployment.yaml +++ b/services/crypto/monerod/deployment.yaml @@ -35,7 +35,7 @@ spec: values: ["rpi4"] containers: - name: monerod - image: cli.registry.bstein.dev/infra/monerod:0.18.4.1 + image: registry.bstein.dev/infra/monerod:0.18.4.1 command: ["/opt/monero/monerod"] args: - --data-dir=/data diff --git a/services/crypto/xmr-miner/deployment.yaml b/services/crypto/xmr-miner/deployment.yaml index dc72499..178f2b2 100644 --- a/services/crypto/xmr-miner/deployment.yaml +++ b/services/crypto/xmr-miner/deployment.yaml @@ -32,7 +32,7 @@ spec: values: ["rpi4"] containers: - name: monero-p2pool - image: cli.registry.bstein.dev/infra/monero-p2pool:4.9 + image: registry.bstein.dev/infra/monero-p2pool:4.9 imagePullPolicy: Always command: ["p2pool"] args: diff --git a/services/keycloak/deployment.yaml b/services/keycloak/deployment.yaml index 3acb187..9336bd9 100644 --- a/services/keycloak/deployment.yaml +++ b/services/keycloak/deployment.yaml @@ -48,11 +48,9 @@ spec: runAsGroup: 0 fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch - imagePullSecrets: - - name: zot-regcred initContainers: - name: mailu-http-listener - image: cli.registry.bstein.dev/sso/mailu-http-listener:0.1.0 + image: registry.bstein.dev/sso/mailu-http-listener:0.1.0 imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c"] args: diff --git a/services/monitoring/dcgm-exporter.yaml b/services/monitoring/dcgm-exporter.yaml index b30e810..cd37b7b 100644 --- a/services/monitoring/dcgm-exporter.yaml +++ b/services/monitoring/dcgm-exporter.yaml @@ -39,7 +39,7 @@ spec: - operator: Exists containers: - name: dcgm-exporter - image: cli.registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04 + image: registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04 imagePullPolicy: Always ports: - name: metrics @@ -56,8 +56,6 @@ spec: volumeMounts: - name: pod-resources mountPath: /var/lib/kubelet/pod-resources - imagePullSecrets: - - name: zot-regcred volumes: - name: pod-resources hostPath: diff --git a/services/pegasus/deployment.yaml b/services/pegasus/deployment.yaml index fcd2b63..72d383d 100644 --- a/services/pegasus/deployment.yaml +++ b/services/pegasus/deployment.yaml @@ -17,8 +17,6 @@ spec: spec: nodeSelector: kubernetes.io/arch: amd64 - imagePullSecrets: - - name: zot-regcred securityContext: runAsNonRoot: true runAsUser: 65532 @@ -58,7 +56,7 @@ spec: containers: - name: pegasus - image: cli.registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} + image: registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} imagePullPolicy: Always command: ["/pegasus"] env: diff --git a/services/pegasus/image.yaml b/services/pegasus/image.yaml index d5acf53..ee0e7de 100644 --- a/services/pegasus/image.yaml +++ b/services/pegasus/image.yaml @@ -5,10 +5,8 @@ metadata: name: pegasus namespace: jellyfin spec: - image: cli.registry.bstein.dev/pegasus + image: registry.bstein.dev/pegasus interval: 1m0s - secretRef: - name: zot-regcred --- From 144a860a886ed5c264c0517588ac48b506d7da69 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 00:15:22 -0300 Subject: [PATCH 45/93] harbor: use project paths for crypto/pegasus images --- scripts/crypto_wallet_monero_setup.fish | 4 ++-- scripts/crypto_wallet_sui_setup.fish | 2 +- services/crypto/monerod/deployment.yaml | 2 +- services/crypto/xmr-miner/deployment.yaml | 2 +- services/pegasus/deployment.yaml | 2 +- services/pegasus/image.yaml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/crypto_wallet_monero_setup.fish b/scripts/crypto_wallet_monero_setup.fish index 864a667..7a4c96b 100644 --- a/scripts/crypto_wallet_monero_setup.fish +++ b/scripts/crypto_wallet_monero_setup.fish @@ -372,8 +372,8 @@ function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-r end # Use your private image by default (in Zot) - read -P "Container image for wallet RPC [registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image - if test -z "$image"; set image registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end + read -P "Container image for wallet RPC [registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1]: " image + if test -z "$image"; set image registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1; end _require "Container image" $image; or return 1 # --- Secrets (defaults: RPC user=wallet name, passwords auto if missing) diff --git a/scripts/crypto_wallet_sui_setup.fish b/scripts/crypto_wallet_sui_setup.fish index 5883a7c..09d81e1 100644 --- a/scripts/crypto_wallet_sui_setup.fish +++ b/scripts/crypto_wallet_sui_setup.fish @@ -23,7 +23,7 @@ end # Default image chooser (you should override with your own multi-arch image) function _sui_default_image -a NET - echo registry.bstein.dev/infra/sui-tools:1.53.2 + echo registry.bstein.dev/crypto/sui-tools:1.53.2 end # Convert any string to a k8s-safe name (RFC-1123 label-ish) diff --git a/services/crypto/monerod/deployment.yaml b/services/crypto/monerod/deployment.yaml index 1b98e47..1c20ff8 100644 --- a/services/crypto/monerod/deployment.yaml +++ b/services/crypto/monerod/deployment.yaml @@ -35,7 +35,7 @@ spec: values: ["rpi4"] containers: - name: monerod - image: registry.bstein.dev/infra/monerod:0.18.4.1 + image: registry.bstein.dev/crypto/monerod:0.18.4.1 command: ["/opt/monero/monerod"] args: - --data-dir=/data diff --git a/services/crypto/xmr-miner/deployment.yaml b/services/crypto/xmr-miner/deployment.yaml index 178f2b2..dc24828 100644 --- a/services/crypto/xmr-miner/deployment.yaml +++ b/services/crypto/xmr-miner/deployment.yaml @@ -32,7 +32,7 @@ spec: values: ["rpi4"] containers: - name: monero-p2pool - image: registry.bstein.dev/infra/monero-p2pool:4.9 + image: registry.bstein.dev/crypto/monero-p2pool:4.9 imagePullPolicy: Always command: ["p2pool"] args: diff --git a/services/pegasus/deployment.yaml b/services/pegasus/deployment.yaml index 72d383d..3a10dfe 100644 --- a/services/pegasus/deployment.yaml +++ b/services/pegasus/deployment.yaml @@ -56,7 +56,7 @@ spec: containers: - name: pegasus - image: registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} + image: registry.bstein.dev/streaming/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"} imagePullPolicy: Always command: ["/pegasus"] env: diff --git a/services/pegasus/image.yaml b/services/pegasus/image.yaml index ee0e7de..682ec83 100644 --- a/services/pegasus/image.yaml +++ b/services/pegasus/image.yaml @@ -5,7 +5,7 @@ metadata: name: pegasus namespace: jellyfin spec: - image: registry.bstein.dev/pegasus + image: registry.bstein.dev/streaming/pegasus interval: 1m0s --- From c661658a12cfa7e6d94494ed1395e79c2e171c4e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 01:45:04 -0300 Subject: [PATCH 46/93] Add AC Infinity ingestion plan --- .gitignore | 3 +++ NOTES.md | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 NOTES.md diff --git a/.gitignore b/.gitignore index 88b0632..3f1a193 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.md !README.md +!AGENTS.md +!**/NOTES.md +!NOTES.md diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..c409499 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,39 @@ +AC Infinity metrics → Grafana plan +================================== + +Goal: expose tent ambient temperature and fan speeds (inlet indoor/outdoor, outlet, internal) on metrics.bstein.dev overview. Keep the footprint minimal; avoid full Home Assistant if possible. + +Option A (slim exporter; recommended) +- Use the community AC Infinity Python client (from the Home Assistant custom component) wrapped in a tiny Prometheus exporter. +- Flow: + 1) Deploy a `Deployment` in `monitoring` (e.g., `acinfinity-exporter`) that: + - Reads AC Infinity cloud creds from a `Secret` (populated via Vault). + - Polls the AC Infinity cloud API on an interval (e.g., 30–60s) using the client to fetch current sensor state. + - Exposes `/metrics` with gauges: `acinfinity_temp_c`, `acinfinity_humidity_percent`, `acinfinity_fan_speed_percent{fan="inlet_indoor|inlet_outdoor|outlet|internal"}`, `acinfinity_mode`, `acinfinity_alarm`, etc. + 2) Add a `Service` + `ServiceMonitor` to scrape the exporter. Metric relabel to friendly names if needed. + 3) Update `scripts/dashboards_render_atlas.py` to add: + - Ambient temp gauge (°C/°F) on Overview. + - Fan speed gauges (3–4 panels) for inlet/outlet/internal. + 4) Regenerate dashboards (`python3 scripts/dashboards_render_atlas.py --build`), commit, push, reconcile `monitoring`. +- Pros: minimal footprint, no HA. Cons: need to vendor the client library and keep it in sync if AC Infinity changes their API. + +Option B (minimal Home Assistant) +- Run a stripped-down Home Assistant in `monitoring` with only the AC Infinity custom integration and Prometheus exporter enabled. +- Flow: + 1) HA `Deployment` + PVC or emptyDir for config; `Secret` for AC Infinity creds; HA API password in Secret. + 2) Prometheus scrapes `http://ha-acinfinity:8123/api/prometheus?api_password=...` via `ServiceMonitor`. + 3) Same dashboard steps as above after metric relabeling to `acinfinity_*`. +- Pros: reuse maintained HA integration. Cons: heavier than exporter, HA maintenance overhead. + +Secrets and ops +- Store AC Infinity username/password in Vault; template into a Secret consumed by the exporter/HA. +- Network: allow outbound HTTPS to AC Infinity cloud from the Pod. +- Interval: 30–60s polling is usually enough; avoid hammering the API. + +Implementation sketch (Option A) +- New image `monitoring/acinfinity-exporter` (Python + prometheus_client + AC Infinity client). +- Deployment (namespace `monitoring`): env AC_INFINITY_USER/PASS, POLL_INTERVAL, LISTEN_ADDR. +- Service: `port: 9100` (or similar). +- ServiceMonitor: scrape `/metrics` every 30–60s, metricRelabel to normalize names/labels. +- Dashboard panels: add to Overview top/mid rows; regenerate JSONs; push; reconcile `monitoring`. + From 759a77c745736b3e553814457154232b4f1fb52f Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 03:22:01 -0300 Subject: [PATCH 47/93] harbor: run arm64 images on rpi workers --- services/harbor/helmrelease.yaml | 133 ++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 30 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index ddb8dab..887e97f 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -17,6 +17,7 @@ spec: namespace: flux-system values: externalURL: https://registry.bstein.dev + imagePullPolicy: IfNotPresent expose: type: ingress tls: @@ -59,6 +60,9 @@ spec: existingSecret: harbor-db sslmode: disable redis: + image: + repository: registry.bstein.dev/infra/harbor-redis + tag: v2.14.1-arm64 type: internal internal: affinity: @@ -68,20 +72,20 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "amd64" ] + values: [ "arm64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-22" ] - - weight: 10 + values: [ "rpi5" ] + - weight: 50 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-24" ] + values: [ "rpi4" ] trivy: enabled: false metrics: @@ -92,6 +96,9 @@ spec: existingSecretAdminPasswordKey: harbor_admin_password existingSecretSecretKey: harbor-core core: + image: + repository: registry.bstein.dev/infra/harbor-core + tag: v2.14.1-arm64 existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY @@ -102,21 +109,24 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "amd64" ] + values: [ "arm64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-22" ] - - weight: 10 + values: [ "rpi5" ] + - weight: 50 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-24" ] + values: [ "rpi4" ] jobservice: + image: + repository: registry.bstein.dev/infra/harbor-jobservice + tag: v2.14.1-arm64 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -124,21 +134,24 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "amd64" ] + values: [ "arm64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-22" ] - - weight: 10 + values: [ "rpi5" ] + - weight: 50 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-24" ] + values: [ "rpi4" ] portal: + image: + repository: registry.bstein.dev/infra/harbor-portal + tag: v2.14.1-arm64 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -146,21 +159,27 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "amd64" ] + values: [ "arm64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-22" ] - - weight: 10 + values: [ "rpi5" ] + - weight: 50 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-24" ] + values: [ "rpi4" ] registry: + image: + repository: registry.bstein.dev/infra/harbor-registry + tag: v2.14.1-arm64 + controllerImage: + repository: registry.bstein.dev/infra/harbor-registryctl + tag: v2.14.1-arm64 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -168,19 +187,73 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "amd64" ] + values: [ "arm64" ] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-22" ] - - weight: 10 + values: [ "rpi5" ] + - weight: 50 preference: matchExpressions: - - key: kubernetes.io/hostname + - key: hardware operator: In - values: [ "titan-24" ] + values: [ "rpi4" ] + log: + image: + repository: registry.bstein.dev/infra/harbor-log + tag: v2.14.1-arm64 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: [ "arm64" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + - weight: 50 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + nginx: + image: + repository: registry.bstein.dev/infra/harbor-nginx + tag: v2.14.1-arm64 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: [ "arm64" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + - weight: 50 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] + prepare: + image: + repository: registry.bstein.dev/infra/harbor-prepare + tag: v2.14.1-arm64 updateStrategy: type: Recreate From 3066db793da69c6cdc472ea6e2eb178f37e99c0e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 11:16:34 -0300 Subject: [PATCH 48/93] harbor: bootstrap arm64 images on titan-05 --- services/harbor/helmrelease.yaml | 57 +++++++++++++------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 887e97f..3ce3a91 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -60,11 +60,13 @@ spec: existingSecret: harbor-db sslmode: disable redis: - image: - repository: registry.bstein.dev/infra/harbor-redis - tag: v2.14.1-arm64 type: internal internal: + image: + repository: registry.bstein.dev/infra/harbor-redis + tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -99,6 +101,8 @@ spec: image: repository: registry.bstein.dev/infra/harbor-core tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY @@ -127,6 +131,8 @@ spec: image: repository: registry.bstein.dev/infra/harbor-jobservice tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -152,6 +158,8 @@ spec: image: repository: registry.bstein.dev/infra/harbor-portal tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -174,37 +182,16 @@ spec: operator: In values: [ "rpi4" ] registry: - image: - repository: registry.bstein.dev/infra/harbor-registry - tag: v2.14.1-arm64 - controllerImage: - repository: registry.bstein.dev/infra/harbor-registryctl - tag: v2.14.1-arm64 - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: [ "arm64" ] - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 90 - preference: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi5" ] - - weight: 50 - preference: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4" ] - log: - image: - repository: registry.bstein.dev/infra/harbor-log - tag: v2.14.1-arm64 + registry: + image: + repository: registry.bstein.dev/infra/harbor-registry + tag: v2.14.1-arm64 + controller: + image: + repository: registry.bstein.dev/infra/harbor-registryctl + tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -230,6 +217,8 @@ spec: image: repository: registry.bstein.dev/infra/harbor-nginx tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: From 8b9fc8ff1c8b877d19f3e58c670bb6e91417e775 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 14:10:04 -0300 Subject: [PATCH 49/93] chore: remove zot stack --- .../atlas/applications/kustomization.yaml | 1 - .../applications/kustomization.yaml | 1 - .../applications/zot/kustomization.yaml | 18 ---- scripts/zot_cred_sync.sh | 76 ------------- services/zot/configmap.yaml | 47 -------- services/zot/deployment.yaml | 102 ------------------ services/zot/ingress.yaml | 54 ---------- services/zot/kustomization.yaml | 13 --- services/zot/middleware.yaml | 26 ----- services/zot/namespace.yaml | 5 - services/zot/oauth2-proxy-deployment.yaml | 84 --------------- services/zot/oauth2-proxy-service.yaml | 14 --- services/zot/pvc.yaml | 13 --- services/zot/service.yaml | 14 --- 14 files changed, 468 deletions(-) delete mode 100644 clusters/atlas/flux-system/applications/zot/kustomization.yaml delete mode 100755 scripts/zot_cred_sync.sh delete mode 100644 services/zot/configmap.yaml delete mode 100644 services/zot/deployment.yaml delete mode 100644 services/zot/ingress.yaml delete mode 100644 services/zot/kustomization.yaml delete mode 100644 services/zot/middleware.yaml delete mode 100644 services/zot/namespace.yaml delete mode 100644 services/zot/oauth2-proxy-deployment.yaml delete mode 100644 services/zot/oauth2-proxy-service.yaml delete mode 100644 services/zot/pvc.yaml delete mode 100644 services/zot/service.yaml diff --git a/clusters/atlas/applications/kustomization.yaml b/clusters/atlas/applications/kustomization.yaml index c29dbb7..209790b 100644 --- a/clusters/atlas/applications/kustomization.yaml +++ b/clusters/atlas/applications/kustomization.yaml @@ -9,4 +9,3 @@ resources: - ../../services/monitoring - ../../services/pegasus - ../../services/vault - - ../../services/zot diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index 9b388c8..93e10bf 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -2,7 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - zot/kustomization.yaml - gitea/kustomization.yaml - vault/kustomization.yaml - jitsi/kustomization.yaml diff --git a/clusters/atlas/flux-system/applications/zot/kustomization.yaml b/clusters/atlas/flux-system/applications/zot/kustomization.yaml deleted file mode 100644 index c60ae5b..0000000 --- a/clusters/atlas/flux-system/applications/zot/kustomization.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# clusters/atlas/flux-system/applications/zot/kustomization.yaml -apiVersion: kustomize.toolkit.fluxcd.io/v1 -kind: Kustomization -metadata: - name: zot - namespace: flux-system -spec: - interval: 10m - path: ./services/zot - targetNamespace: zot - prune: false - sourceRef: - kind: GitRepository - name: flux-system - namespace: flux-system - wait: true - dependsOn: - - name: core diff --git a/scripts/zot_cred_sync.sh b/scripts/zot_cred_sync.sh deleted file mode 100755 index e33c379..0000000 --- a/scripts/zot_cred_sync.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -# Sync Keycloak users into Zot htpasswd. -# Intended to be rendered into a ConfigMap/Job; keep real secrets out of git. - -set -euo pipefail - -require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; } -require curl; require jq; require kubectl; require htpasswd - -: "${KEYCLOAK_URL:=https://sso.bstein.dev}" -: "${KEYCLOAK_REALM:=atlas}" -: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}" -: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}" -: "${ZOT_NAMESPACE:=zot}" -: "${HTPASSWD_SECRET_NAME:=zot-htpasswd}" -: "${DEFAULT_PASSWORD:=TempSsoPass!2025}" -: "${UI_PROXY_USER:=zot-ui-proxy}" -: "${UI_PROXY_PASSWORD:=TempSsoUiPass!2025}" - -fetch_token() { - curl -fsS -X POST \ - -d "grant_type=client_credentials" \ - -d "client_id=${KEYCLOAK_CLIENT_ID}" \ - -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \ - "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \ - | jq -r '.access_token' -} - -pull_users() { - local token="$1" - curl -fsS -H "Authorization: Bearer ${token}" \ - "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \ - | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | .username' -} - -decode_secret_file() { - local ns="$1" name="$2" key="$3" out="$4" - if kubectl -n "${ns}" get secret "${name}" >/dev/null 2>&1; then - kubectl -n "${ns}" get secret "${name}" -o "jsonpath={.data.${key}}" | base64 -d > "${out}" || true - else - : > "${out}" - fi -} - -ensure_htpasswd_line() { - local user="$1" password="$2" file="$3" - if ! grep -q "^${user}:" "${file}" 2>/dev/null; then - htpasswd -nbB "${user}" "${password}" >> "${file}" - echo "added user ${user} to htpasswd" - fi -} - -main() { - local token tmp existing - tmp="$(mktemp)" - existing="$(mktemp)" - trap 'rm -f "${tmp}" "${existing}"' EXIT - - decode_secret_file "${ZOT_NAMESPACE}" "${HTPASSWD_SECRET_NAME}" htpasswd "${existing}" - cp "${existing}" "${tmp}" - - ensure_htpasswd_line "${UI_PROXY_USER}" "${UI_PROXY_PASSWORD}" "${tmp}" - - token="$(fetch_token)" - readarray -t users < <(pull_users "${token}") - for user in "${users[@]}"; do - ensure_htpasswd_line "${user}" "${DEFAULT_PASSWORD}" "${tmp}" - done - - kubectl create secret generic "${HTPASSWD_SECRET_NAME}" \ - --namespace "${ZOT_NAMESPACE}" \ - --from-file=htpasswd="${tmp}" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -main "$@" diff --git a/services/zot/configmap.yaml b/services/zot/configmap.yaml deleted file mode 100644 index 3046d78..0000000 --- a/services/zot/configmap.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# services/zot/config.map -apiVersion: v1 -kind: ConfigMap -metadata: - name: zot-config - namespace: zot -data: - config.json: | - { - "storage": { - "rootDirectory": "/var/lib/registry", - "dedupe": true, - "gc": true, - "gcDelay": "1h", - "gcInterval": "1h" - }, - "http": { - "address": "0.0.0.0", - "port": "5000", - "realm": "zot-registry", - "compat": ["docker2s2"], - "auth": { - "htpasswd": { "path": "/etc/zot/htpasswd" } - }, - "accessControl": { - "repositories": { - "**": { - "policies": [ - { "users": ["bstein", "zot-ui-proxy"], "actions": ["read", "create", "update", "delete"] } - ], - "defaultPolicy": [], - "anonymousPolicy": [] - } - }, - "adminPolicy": { - "users": ["bstein", "zot-ui-proxy"], - "actions": ["read", "create", "update", "delete"] - } - } - }, - "log": { "level": "info" }, - "extensions": { - "ui": { "enable": true }, - "search": { "enable": true }, - "metrics": { "enable": true } - } - } diff --git a/services/zot/deployment.yaml b/services/zot/deployment.yaml deleted file mode 100644 index 14dc724..0000000 --- a/services/zot/deployment.yaml +++ /dev/null @@ -1,102 +0,0 @@ -# services/zot/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: zot - namespace: zot - labels: { app: zot } -spec: - replicas: 1 - selector: - matchLabels: { app: zot } - template: - metadata: - labels: { app: zot } - spec: - nodeSelector: - node-role.kubernetes.io/worker: "true" - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: hardware - operator: In - values: ["rpi4","rpi5"] - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 50 - preference: - matchExpressions: - - key: hardware - operator: In - values: ["rpi4"] - containers: - - name: zot - image: ghcr.io/project-zot/zot-linux-arm64:v2.1.8 - imagePullPolicy: IfNotPresent - args: ["serve", "/etc/zot/config.json"] - env: - - name: UI_PROXY_HTPASSWD - value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y" - ports: - - { name: http, containerPort: 5000 } - volumeMounts: - - name: cfg - mountPath: /etc/zot/config.json - subPath: config.json - readOnly: true - - name: htpasswd-merged - mountPath: /etc/zot/htpasswd - subPath: htpasswd - - name: zot-data - mountPath: /var/lib/registry - readinessProbe: - tcpSocket: - port: 5000 - initialDelaySeconds: 2 - periodSeconds: 5 - livenessProbe: - tcpSocket: - port: 5000 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - requests: { cpu: "50m", memory: "64Mi" } - initContainers: - - name: merge-htpasswd - image: busybox:1.36 - command: - - sh - - -c - - | - set -e - if [ -f /src/htpasswd ]; then - cp /src/htpasswd /merged/htpasswd - else - touch /merged/htpasswd - fi - if [ -n "${UI_PROXY_HTPASSWD}" ]; then - echo "${UI_PROXY_HTPASSWD}" >> /merged/htpasswd - fi - env: - - name: UI_PROXY_HTPASSWD - value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y" - volumeMounts: - - name: htpasswd-source - mountPath: /src - readOnly: true - - name: htpasswd-merged - mountPath: /merged - volumes: - - name: cfg - configMap: - name: zot-config - - name: htpasswd-source - secret: - secretName: zot-htpasswd - optional: true - - name: htpasswd-merged - emptyDir: {} - - name: zot-data - persistentVolumeClaim: - claimName: zot-data diff --git a/services/zot/ingress.yaml b/services/zot/ingress.yaml deleted file mode 100644 index 6c23709..0000000 --- a/services/zot/ingress.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# services/zot/ingress.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: zot-cli - namespace: zot - annotations: - cert-manager.io/cluster-issuer: letsencrypt - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.tls: "true" - traefik.ingress.kubernetes.io/router.middlewares: zot-zot-resp-headers@kubernetescrd -spec: - ingressClassName: traefik - tls: - - hosts: [ "cli.registry.bstein.dev" ] - secretName: cli-registry-bstein-dev-tls - rules: - - host: cli.registry.bstein.dev - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: zot - port: - number: 5000 ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: zot-ui - namespace: zot - annotations: - cert-manager.io/cluster-issuer: letsencrypt - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.tls: "true" - traefik.ingress.kubernetes.io/router.middlewares: zot-zot-resp-headers@kubernetescrd -spec: - ingressClassName: traefik - tls: - - hosts: [ "web.registry.bstein.dev" ] - secretName: web-registry-bstein-dev-tls - rules: - - host: web.registry.bstein.dev - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: zot-oauth2-proxy - port: - number: 80 diff --git a/services/zot/kustomization.yaml b/services/zot/kustomization.yaml deleted file mode 100644 index 22d76ae..0000000 --- a/services/zot/kustomization.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# services/zot/kustomization.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - namespace.yaml - - pvc.yaml - - deployment.yaml - - configmap.yaml - - service.yaml - - oauth2-proxy-deployment.yaml - - oauth2-proxy-service.yaml - - ingress.yaml - - middleware.yaml diff --git a/services/zot/middleware.yaml b/services/zot/middleware.yaml deleted file mode 100644 index 166b070..0000000 --- a/services/zot/middleware.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# services/zot/middleware.yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: zot-resp-headers - namespace: zot -spec: - headers: - customResponseHeaders: - Docker-Distribution-Api-Version: "registry/2.0" - accessControlAllowOriginList: - - "*" - accessControlAllowCredentials: true - accessControlAllowHeaders: - - Authorization - - Content-Type - - Docker-Distribution-Api-Version - - X-Registry-Auth - accessControlAllowMethods: - - GET - - HEAD - - OPTIONS - - POST - - PUT - - PATCH - - DELETE diff --git a/services/zot/namespace.yaml b/services/zot/namespace.yaml deleted file mode 100644 index b91de10..0000000 --- a/services/zot/namespace.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# services/zot/namespace.yaml -apiVersion: v1 -kind: Namespace -metadata: - name: zot diff --git a/services/zot/oauth2-proxy-deployment.yaml b/services/zot/oauth2-proxy-deployment.yaml deleted file mode 100644 index 9761e6e..0000000 --- a/services/zot/oauth2-proxy-deployment.yaml +++ /dev/null @@ -1,84 +0,0 @@ -# services/zot/oauth2-proxy-deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: zot-oauth2-proxy - namespace: zot - labels: { app: zot-oauth2-proxy } -spec: - replicas: 1 - selector: - matchLabels: { app: zot-oauth2-proxy } - template: - metadata: - labels: { app: zot-oauth2-proxy } - spec: - nodeSelector: - node-role.kubernetes.io/worker: "true" - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 50 - preference: - matchExpressions: - - key: hardware - operator: In - values: ["rpi4","rpi5"] - containers: - - name: oauth2-proxy - image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 - imagePullPolicy: IfNotPresent - args: - - --provider=oidc - - --redirect-url=https://web.registry.bstein.dev/oauth2/callback - - --oidc-issuer-url=https://sso.bstein.dev/realms/atlas - - --scope=openid profile email - - --email-domain=* - - --cookie-domain=web.registry.bstein.dev - - --cookie-name=_zot_ui_oauth - - --set-xauthrequest=true - - --set-authorization-header=false - - --pass-authorization-header=false - - --pass-access-token=false - - --pass-basic-auth=true - - --cookie-secure=true - - --cookie-samesite=lax - - --cookie-refresh=20m - - --cookie-expire=168h - - --upstream=http://zot-ui-proxy:TempSsoUiPass%212025@zot:5000 - - --http-address=0.0.0.0:4180 - - --skip-provider-button=true - - --skip-jwt-bearer-tokens=true - env: - - name: OAUTH2_PROXY_CLIENT_ID - valueFrom: - secretKeyRef: - name: zot-oidc - key: client_id - - name: OAUTH2_PROXY_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: zot-oidc - key: client_secret - - name: OAUTH2_PROXY_COOKIE_SECRET - valueFrom: - secretKeyRef: - name: zot-oidc - key: client_secret - ports: - - containerPort: 4180 - name: http - readinessProbe: - httpGet: - path: /ping - port: 4180 - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /ping - port: 4180 - initialDelaySeconds: 20 - periodSeconds: 20 - resources: - requests: { cpu: "25m", memory: "64Mi" } diff --git a/services/zot/oauth2-proxy-service.yaml b/services/zot/oauth2-proxy-service.yaml deleted file mode 100644 index 4a7e96a..0000000 --- a/services/zot/oauth2-proxy-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# services/zot/oauth2-proxy-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: zot-oauth2-proxy - namespace: zot - labels: { app: zot-oauth2-proxy } -spec: - type: ClusterIP - selector: { app: zot-oauth2-proxy } - ports: - - name: http - port: 80 - targetPort: 4180 diff --git a/services/zot/pvc.yaml b/services/zot/pvc.yaml deleted file mode 100644 index b3af86c..0000000 --- a/services/zot/pvc.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# services/zot/pvc.yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: zot-data - namespace: zot -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 25Gi - storageClassName: asteria diff --git a/services/zot/service.yaml b/services/zot/service.yaml deleted file mode 100644 index e41c8d4..0000000 --- a/services/zot/service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# services/zot/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: zot - namespace: zot - labels: { app: zot } -spec: - type: ClusterIP - selector: { app: zot } - ports: - - name: http - port: 5000 - targetPort: 5000 From fc858fc8df000f4c724b55dab01e9ddc8d02c4eb Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 19:26:46 -0300 Subject: [PATCH 50/93] ci: seed harbor-arm-build pipeline in Jenkins --- services/jenkins/helmrelease.yaml | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 44d9846..d750b15 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -128,6 +128,46 @@ spec: } catch (Exception e) { println("Failed to configure OIDC realm: ${e}") } + JCasC: + configScripts: + creds.yaml: | + credentials: + system: + domainCredentials: + - credentials: + - usernamePassword: + scope: GLOBAL + id: gitea-pat + username: "bstein" + password: "4693a39ee3f0ebb58e7d1795ab98add6df44ef12" + description: "Gitea PAT for harbor-arm-build" + - usernamePassword: + scope: GLOBAL + id: harbor-robot + username: "robot$infra+robotuser-pipeline" + password: "ouuvMheoTxOQtFSbWnO1OKVujORMPfO7" + description: "Harbor robot for pipeline push" + jobs.yaml: | + jobs: + - script: | + pipelineJob('harbor-arm-build') { + definition { + cpsScm { + scm { + git { + remote { + url('https://scm.bstein.dev/bstein/harbor-arm-build.git') + credentials('gitea-pat') + } + branches('*/master') + } + } + } + } + triggers { + scm('H/5 * * * *') + } + } persistence: enabled: true storageClass: astreae From 162fe3339f97816fe4cc8418cf56bdf471aed219 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:04:21 -0300 Subject: [PATCH 51/93] fix: pin Jenkins OIDC realm via JCasC --- services/jenkins/helmrelease.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index d750b15..9d2e450 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -130,6 +130,23 @@ spec: } JCasC: configScripts: + security.yaml: | + jenkins: + securityRealm: + oic: + clientId: "${OIDC_CLIENT_ID}" + clientSecret: "${OIDC_CLIENT_SECRET}" + wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration" + scopes: "openid profile email" + userNameField: "preferred_username" + fullNameFieldName: "name" + emailFieldName: "email" + groupsFieldName: "groups" + logoutFromOpenidProvider: true + rootURLFromRequest: true + authorizationStrategy: + loggedInUsersCanDoAnything: + allowAnonymousRead: false creds.yaml: | credentials: system: From cfa7bd8198507c15e4dbd118e0054cbc98ecbeef Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:13:52 -0300 Subject: [PATCH 52/93] fix: jenkins casc OIDC using explicit endpoints --- services/jenkins/helmrelease.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 9d2e450..ac6b8b6 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -136,8 +136,10 @@ spec: oic: clientId: "${OIDC_CLIENT_ID}" clientSecret: "${OIDC_CLIENT_SECRET}" - wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration" - scopes: "openid profile email" + tokenServerUrl: "${OIDC_TOKEN_URL}" + authorizationServerUrl: "${OIDC_AUTH_URL}" + userInfoServerUrl: "${OIDC_USERINFO_URL}" + logoutUrl: "${OIDC_LOGOUT_URL}" userNameField: "preferred_username" fullNameFieldName: "name" emailFieldName: "email" From b951058dc66162b95cdde400cbd4910d6eaab8e4 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:16:18 -0300 Subject: [PATCH 53/93] fix: enforce Jenkins OIDC via init groovy only --- services/jenkins/helmrelease.yaml | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index ac6b8b6..4778219 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -90,6 +90,7 @@ spec: import jenkins.model.Jenkins import org.jenkinsci.plugins.oic.OicSecurityRealm import org.jenkinsci.plugins.oic.OicServerWellKnownConfiguration + import hudson.security.GlobalMatrixAuthorizationStrategy def env = System.getenv() if (!(env['ENABLE_OIDC'] ?: 'false').toBoolean()) { println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") @@ -123,6 +124,9 @@ spec: realm.setSendScopesInTokenRequest(true) def j = Jenkins.get() j.setSecurityRealm(realm) + def auth = new GlobalMatrixAuthorizationStrategy() + auth.add(Jenkins.ADMINISTER, "authenticated") + j.setAuthorizationStrategy(auth) j.save() println("Configured OIDC realm from init script (well-known)") } catch (Exception e) { @@ -130,25 +134,6 @@ spec: } JCasC: configScripts: - security.yaml: | - jenkins: - securityRealm: - oic: - clientId: "${OIDC_CLIENT_ID}" - clientSecret: "${OIDC_CLIENT_SECRET}" - tokenServerUrl: "${OIDC_TOKEN_URL}" - authorizationServerUrl: "${OIDC_AUTH_URL}" - userInfoServerUrl: "${OIDC_USERINFO_URL}" - logoutUrl: "${OIDC_LOGOUT_URL}" - userNameField: "preferred_username" - fullNameFieldName: "name" - emailFieldName: "email" - groupsFieldName: "groups" - logoutFromOpenidProvider: true - rootURLFromRequest: true - authorizationStrategy: - loggedInUsersCanDoAnything: - allowAnonymousRead: false creds.yaml: | credentials: system: From 3e4a49e7fb34605346c6eef03e9f0c80d1e7d357 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:21:33 -0300 Subject: [PATCH 54/93] fix: add job-dsl plugin for JCasC jobs --- services/jenkins/helmrelease.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 4778219..ce24588 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -42,6 +42,7 @@ spec: - git - configuration-as-code - oic-auth + - job-dsl containerEnv: - name: ENABLE_OIDC value: "true" From 6759871b43a1bad2d4e684ae7110c664eee5c339 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:27:41 -0300 Subject: [PATCH 55/93] fix: add casc support plugin --- services/jenkins/helmrelease.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index ce24588..de4bda9 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -43,6 +43,7 @@ spec: - configuration-as-code - oic-auth - job-dsl + - configuration-as-code-support containerEnv: - name: ENABLE_OIDC value: "true" From 0385a653af327a357990af80b55fa11696251241 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 20:33:03 -0300 Subject: [PATCH 56/93] fix: use FullControlOnceLoggedIn auth strategy --- services/jenkins/helmrelease.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index de4bda9..78e9382 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -92,7 +92,7 @@ spec: import jenkins.model.Jenkins import org.jenkinsci.plugins.oic.OicSecurityRealm import org.jenkinsci.plugins.oic.OicServerWellKnownConfiguration - import hudson.security.GlobalMatrixAuthorizationStrategy + import hudson.security.FullControlOnceLoggedInAuthorizationStrategy def env = System.getenv() if (!(env['ENABLE_OIDC'] ?: 'false').toBoolean()) { println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") @@ -126,8 +126,8 @@ spec: realm.setSendScopesInTokenRequest(true) def j = Jenkins.get() j.setSecurityRealm(realm) - def auth = new GlobalMatrixAuthorizationStrategy() - auth.add(Jenkins.ADMINISTER, "authenticated") + def auth = new FullControlOnceLoggedInAuthorizationStrategy() + auth.setAllowAnonymousRead(false) j.setAuthorizationStrategy(auth) j.save() println("Configured OIDC realm from init script (well-known)") From f3335028b12d0fe2eeb04a31d54ab42c7a43c191 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 23:12:27 -0300 Subject: [PATCH 57/93] jenkins: disable scm trigger for harbor arm build --- services/jenkins/helmrelease.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 78e9382..9ae7d39 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -170,9 +170,6 @@ spec: } } } - triggers { - scm('H/5 * * * *') - } } persistence: enabled: true From ce7631f896e00849b216bc727cd33cbc44b7194c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 16 Dec 2025 23:38:08 -0300 Subject: [PATCH 58/93] jenkins: enforce OIDC via JCasC and pin to arm64 --- services/jenkins/helmrelease.yaml | 47 +++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 9ae7d39..6118f35 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -54,37 +54,54 @@ spec: secretKeyRef: name: jenkins-oidc key: clientId - optional: true - name: OIDC_CLIENT_SECRET valueFrom: secretKeyRef: name: jenkins-oidc key: clientSecret - optional: true - name: OIDC_AUTH_URL valueFrom: secretKeyRef: name: jenkins-oidc key: authorizationUrl - optional: true - name: OIDC_TOKEN_URL valueFrom: secretKeyRef: name: jenkins-oidc key: tokenUrl - optional: true - name: OIDC_USERINFO_URL valueFrom: secretKeyRef: name: jenkins-oidc key: userInfoUrl - optional: true - name: OIDC_LOGOUT_URL valueFrom: secretKeyRef: name: jenkins-oidc key: logoutUrl - optional: true + nodeSelector: + kubernetes.io/arch: arm64 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: [ "arm64" ] + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 90 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi5" ] + - weight: 50 + preference: + matchExpressions: + - key: hardware + operator: In + values: [ "rpi4" ] initScripts: oidc.groovy: | import hudson.util.Secret @@ -136,6 +153,24 @@ spec: } JCasC: configScripts: + security.yaml: | + jenkins: + securityRealm: + oic: + clientId: "${OIDC_CLIENT_ID}" + clientSecret: "${OIDC_CLIENT_SECRET}" + wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration" + logoutFromOpenidProvider: true + postLogoutRedirectUrl: "https://ci.bstein.dev" + scopes: "openid profile email" + userNameField: "preferred_username" + fullNameFieldName: "name" + emailFieldName: "email" + groupsFieldName: "groups" + disableSslVerification: false + authorizationStrategy: + fullControlOnceLoggedIn: + allowAnonymousRead: false creds.yaml: | credentials: system: From 60a8192f612962f63fcb089cb24a7972de89efe8 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 00:23:15 -0300 Subject: [PATCH 59/93] jenkins: enforce OIDC via JCasC (no node move) --- services/jenkins/helmrelease.yaml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 6118f35..451d0bc 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -79,29 +79,6 @@ spec: secretKeyRef: name: jenkins-oidc key: logoutUrl - nodeSelector: - kubernetes.io/arch: arm64 - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: [ "arm64" ] - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 90 - preference: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi5" ] - - weight: 50 - preference: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4" ] initScripts: oidc.groovy: | import hudson.util.Secret From 30048a9ae5d20a31b25baa888bcfc7fb3158c4db Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 00:28:52 -0300 Subject: [PATCH 60/93] jenkins: drop invalid JCasC OIDC realm (use init script) --- services/jenkins/helmrelease.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 451d0bc..5bb9255 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -130,24 +130,6 @@ spec: } JCasC: configScripts: - security.yaml: | - jenkins: - securityRealm: - oic: - clientId: "${OIDC_CLIENT_ID}" - clientSecret: "${OIDC_CLIENT_SECRET}" - wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration" - logoutFromOpenidProvider: true - postLogoutRedirectUrl: "https://ci.bstein.dev" - scopes: "openid profile email" - userNameField: "preferred_username" - fullNameFieldName: "name" - emailFieldName: "email" - groupsFieldName: "groups" - disableSslVerification: false - authorizationStrategy: - fullControlOnceLoggedIn: - allowAnonymousRead: false creds.yaml: | credentials: system: From 1f98a5be129f699e7a97a80e786c39313560f752 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 00:37:37 -0300 Subject: [PATCH 61/93] jenkins: clean stale JCasC files on startup --- services/jenkins/helmrelease.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 5bb9255..75fb8b6 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -79,6 +79,21 @@ spec: secretKeyRef: name: jenkins-oidc key: logoutUrl + customInitContainers: + - name: clean-jcasc-stale + image: alpine:3.20 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + set -euo pipefail + rm -f /var/jenkins_home/casc_configs/*.yaml || true + securityContext: + runAsUser: 0 + volumeMounts: + - name: jenkins-home + mountPath: /var/jenkins_home initScripts: oidc.groovy: | import hudson.util.Secret From 4e479147ec0093184b75a395d4a9d5dca1404c5b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 00:43:55 -0300 Subject: [PATCH 62/93] jenkins: run jcasc cleanup initcontainer as jenkins user --- services/jenkins/helmrelease.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 75fb8b6..d22842f 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -90,7 +90,8 @@ spec: set -euo pipefail rm -f /var/jenkins_home/casc_configs/*.yaml || true securityContext: - runAsUser: 0 + runAsUser: 1000 + runAsGroup: 1000 volumeMounts: - name: jenkins-home mountPath: /var/jenkins_home From f512e0fa29218ae97514c6754dd51ba340428cb7 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 01:11:07 -0300 Subject: [PATCH 63/93] jenkins: harden oidc and timeouts --- services/jenkins/helmrelease.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index d22842f..21f8821 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -15,13 +15,17 @@ spec: name: jenkins namespace: flux-system install: + timeout: 15m remediation: retries: 3 upgrade: + timeout: 15m remediation: retries: 3 remediateLastFailure: true cleanupOnFail: true + rollback: + timeout: 15m values: controller: jenkinsUrl: https://ci.bstein.dev @@ -90,6 +94,7 @@ spec: set -euo pipefail rm -f /var/jenkins_home/casc_configs/*.yaml || true securityContext: + runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 volumeMounts: @@ -110,8 +115,7 @@ spec: } def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_ISSUER'] if (!required.every { env[it] }) { - println("OIDC enabled but missing vars: ${required.findAll { !env[it] }}") - return + throw new IllegalStateException("OIDC enabled but missing vars: ${required.findAll { !env[it] }}") } try { def wellKnown = "${env['OIDC_ISSUER']}/.well-known/openid-configuration" @@ -143,6 +147,7 @@ spec: println("Configured OIDC realm from init script (well-known)") } catch (Exception e) { println("Failed to configure OIDC realm: ${e}") + throw e } JCasC: configScripts: From 5e6f9c6c834cedcc0dc81bdf2b275f7ff041209d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 01:29:48 -0300 Subject: [PATCH 64/93] chore: stop tracking NOTES.md --- .gitignore | 3 --- NOTES.md | 39 --------------------------------------- 2 files changed, 42 deletions(-) delete mode 100644 NOTES.md diff --git a/.gitignore b/.gitignore index 3f1a193..88b0632 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ *.md !README.md -!AGENTS.md -!**/NOTES.md -!NOTES.md diff --git a/NOTES.md b/NOTES.md deleted file mode 100644 index c409499..0000000 --- a/NOTES.md +++ /dev/null @@ -1,39 +0,0 @@ -AC Infinity metrics → Grafana plan -================================== - -Goal: expose tent ambient temperature and fan speeds (inlet indoor/outdoor, outlet, internal) on metrics.bstein.dev overview. Keep the footprint minimal; avoid full Home Assistant if possible. - -Option A (slim exporter; recommended) -- Use the community AC Infinity Python client (from the Home Assistant custom component) wrapped in a tiny Prometheus exporter. -- Flow: - 1) Deploy a `Deployment` in `monitoring` (e.g., `acinfinity-exporter`) that: - - Reads AC Infinity cloud creds from a `Secret` (populated via Vault). - - Polls the AC Infinity cloud API on an interval (e.g., 30–60s) using the client to fetch current sensor state. - - Exposes `/metrics` with gauges: `acinfinity_temp_c`, `acinfinity_humidity_percent`, `acinfinity_fan_speed_percent{fan="inlet_indoor|inlet_outdoor|outlet|internal"}`, `acinfinity_mode`, `acinfinity_alarm`, etc. - 2) Add a `Service` + `ServiceMonitor` to scrape the exporter. Metric relabel to friendly names if needed. - 3) Update `scripts/dashboards_render_atlas.py` to add: - - Ambient temp gauge (°C/°F) on Overview. - - Fan speed gauges (3–4 panels) for inlet/outlet/internal. - 4) Regenerate dashboards (`python3 scripts/dashboards_render_atlas.py --build`), commit, push, reconcile `monitoring`. -- Pros: minimal footprint, no HA. Cons: need to vendor the client library and keep it in sync if AC Infinity changes their API. - -Option B (minimal Home Assistant) -- Run a stripped-down Home Assistant in `monitoring` with only the AC Infinity custom integration and Prometheus exporter enabled. -- Flow: - 1) HA `Deployment` + PVC or emptyDir for config; `Secret` for AC Infinity creds; HA API password in Secret. - 2) Prometheus scrapes `http://ha-acinfinity:8123/api/prometheus?api_password=...` via `ServiceMonitor`. - 3) Same dashboard steps as above after metric relabeling to `acinfinity_*`. -- Pros: reuse maintained HA integration. Cons: heavier than exporter, HA maintenance overhead. - -Secrets and ops -- Store AC Infinity username/password in Vault; template into a Secret consumed by the exporter/HA. -- Network: allow outbound HTTPS to AC Infinity cloud from the Pod. -- Interval: 30–60s polling is usually enough; avoid hammering the API. - -Implementation sketch (Option A) -- New image `monitoring/acinfinity-exporter` (Python + prometheus_client + AC Infinity client). -- Deployment (namespace `monitoring`): env AC_INFINITY_USER/PASS, POLL_INTERVAL, LISTEN_ADDR. -- Service: `port: 9100` (or similar). -- ServiceMonitor: scrape `/metrics` every 30–60s, metricRelabel to normalize names/labels. -- Dashboard panels: add to Overview top/mid rows; regenerate JSONs; push; reconcile `monitoring`. - From cd1b9b57b078f9ffe866ef304e4a5d7085361179 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 01:39:49 -0300 Subject: [PATCH 65/93] harbor: add helm remediation and timeouts --- services/harbor/helmrelease.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 3ce3a91..8af9f46 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -1,12 +1,23 @@ # services/harbor/helmrelease.yaml -apiVersion: helm.toolkit.fluxcd.io/v2beta2 +apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: harbor namespace: harbor spec: interval: 10m - timeout: 10m + install: + timeout: 20m + remediation: + retries: 3 + upgrade: + timeout: 20m + remediation: + retries: 3 + remediateLastFailure: true + cleanupOnFail: true + rollback: + timeout: 20m chart: spec: chart: harbor From a52b811e5b5149a215a0842259c035a81577b734 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 01:47:33 -0300 Subject: [PATCH 66/93] jenkins: source pipeline creds from secrets --- services/jenkins/helmrelease.yaml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 21f8821..4d7bb2b 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -83,6 +83,16 @@ spec: secretKeyRef: name: jenkins-oidc key: logoutUrl + - name: GITEA_PAT_USERNAME + valueFrom: + secretKeyRef: + name: gitea-pat + key: username + - name: GITEA_PAT_TOKEN + valueFrom: + secretKeyRef: + name: gitea-pat + key: token customInitContainers: - name: clean-jcasc-stale image: alpine:3.20 @@ -159,15 +169,9 @@ spec: - usernamePassword: scope: GLOBAL id: gitea-pat - username: "bstein" - password: "4693a39ee3f0ebb58e7d1795ab98add6df44ef12" - description: "Gitea PAT for harbor-arm-build" - - usernamePassword: - scope: GLOBAL - id: harbor-robot - username: "robot$infra+robotuser-pipeline" - password: "ouuvMheoTxOQtFSbWnO1OKVujORMPfO7" - description: "Harbor robot for pipeline push" + username: "${GITEA_PAT_USERNAME}" + password: "${GITEA_PAT_TOKEN}" + description: "Gitea PAT for pipelines" jobs.yaml: | jobs: - script: | From d3aa456bee32aeb56d73a0b80ceab2152edcf572 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 01:58:10 -0300 Subject: [PATCH 67/93] jenkins: poll harbor-arm-build scm --- services/jenkins/helmrelease.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 4d7bb2b..226ae4e 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -176,6 +176,9 @@ spec: jobs: - script: | pipelineJob('harbor-arm-build') { + triggers { + scm('H/5 * * * *') + } definition { cpsScm { scm { From 7dcfd5f6cfe039571370f28016c797a4d81a1183 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 02:23:54 -0300 Subject: [PATCH 68/93] jenkins: stop JCasC resetting OIDC --- services/jenkins/helmrelease.yaml | 73 ++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 226ae4e..38115f9 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -102,7 +102,7 @@ spec: - -c - | set -euo pipefail - rm -f /var/jenkins_home/casc_configs/*.yaml || true + rm -f /var/jenkins_home/casc_configs/* || true securityContext: runAsNonRoot: true runAsUser: 1000 @@ -160,7 +160,78 @@ spec: throw e } JCasC: + defaultConfig: false configScripts: + base.yaml: | + jenkins: + disableRememberMe: false + mode: NORMAL + numExecutors: 0 + labelString: "" + projectNamingStrategy: "standard" + markupFormatter: + plainText + clouds: + - kubernetes: + containerCapStr: "10" + defaultsProviderTemplate: "" + connectTimeout: "5" + readTimeout: "15" + jenkinsUrl: "http://jenkins.jenkins.svc.cluster.local:8080" + jenkinsTunnel: "jenkins-agent.jenkins.svc.cluster.local:50000" + skipTlsVerify: false + usageRestricted: false + maxRequestsPerHostStr: "32" + retentionTimeout: "5" + waitForPodSec: "600" + name: "kubernetes" + namespace: "jenkins" + restrictedPssSecurityContext: false + serverUrl: "https://kubernetes.default" + credentialsId: "" + podLabels: + - key: "jenkins/jenkins-jenkins-agent" + value: "true" + templates: + - name: "default" + namespace: "jenkins" + id: a23c9bbcd21e360a77d51b426f05bd7b8032d8fdedd6ffb97c436883ce6c5ffa + containers: + - name: "jnlp" + alwaysPullImage: false + args: "^${computer.jnlpmac} ^${computer.name}" + envVars: + - envVar: + key: "JENKINS_URL" + value: "http://jenkins.jenkins.svc.cluster.local:8080/" + image: "jenkins/inbound-agent:3355.v388858a_47b_33-3" + privileged: "false" + resourceLimitCpu: 512m + resourceLimitMemory: 512Mi + resourceRequestCpu: 512m + resourceRequestMemory: 512Mi + ttyEnabled: false + workingDir: /home/jenkins/agent + idleMinutes: 0 + instanceCap: 2147483647 + label: "jenkins-jenkins-agent " + nodeUsageMode: "NORMAL" + podRetention: Never + showRawYaml: true + serviceAccount: "default" + slaveConnectTimeoutStr: "100" + yamlMergeStrategy: override + inheritYamlMergeStrategy: false + slaveAgentPort: 50000 + crumbIssuer: + standard: + excludeClientIPFromCrumb: true + security: + apiToken: + creationOfLegacyTokenEnabled: false + tokenGenerationOnCreationEnabled: false + usageStatisticsEnabled: true + unclassified: creds.yaml: | credentials: system: From d93d24d5ef12088600ffe02cbecb85df0abe82da Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 02:30:41 -0300 Subject: [PATCH 69/93] jenkins: disable chart local auth realm --- services/jenkins/helmrelease.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 38115f9..4b3dfb1 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -161,6 +161,8 @@ spec: } JCasC: defaultConfig: false + securityRealm: "" + authorizationStrategy: "" configScripts: base.yaml: | jenkins: From 8d04f6c6c78712bbe2138a0530477b871a2f08e0 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 02:53:23 -0300 Subject: [PATCH 70/93] jenkins: pin controller to rpi4 --- services/jenkins/helmrelease.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 4b3dfb1..90e9f32 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -28,6 +28,8 @@ spec: timeout: 15m values: controller: + nodeSelector: + hardware: rpi4 jenkinsUrl: https://ci.bstein.dev ingress: enabled: true From f28d5680f2f9a7f4d8ed43bf432e9b0b41f298f8 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 03:21:35 -0300 Subject: [PATCH 71/93] harbor: add image automation --- .../applications/harbor/image-automation.yaml | 20 +++ .../applications/kustomization.yaml | 1 + services/harbor/helmrelease.yaml | 66 ++++---- services/harbor/image.yaml | 144 ++++++++++++++++++ services/harbor/kustomization.yaml | 1 + 5 files changed, 202 insertions(+), 30 deletions(-) create mode 100644 clusters/atlas/flux-system/applications/harbor/image-automation.yaml create mode 100644 services/harbor/image.yaml diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml new file mode 100644 index 0000000..0040c6f --- /dev/null +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -0,0 +1,20 @@ +# clusters/atlas/flux-system/applications/harbor/image-automation.yaml +apiVersion: image.toolkit.fluxcd.io/v1beta1 +kind: ImageUpdateAutomation +metadata: + name: harbor + namespace: flux-system +spec: + interval: 5m0s + sourceRef: + kind: GitRepository + name: flux-system + git: + commit: + author: + email: ops@bstein.dev + name: flux-bot + messageTemplate: "chore(harbor): update images to {{range .Updated.Images}}{{.}}{{end}}" + update: + strategy: Setters + path: ./services/harbor diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index 93e10bf..50ca611 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -10,6 +10,7 @@ resources: - pegasus/kustomization.yaml - pegasus/image-automation.yaml - harbor/kustomization.yaml + - harbor/image-automation.yaml - jellyfin/kustomization.yaml - xmr-miner/kustomization.yaml - sui-metrics/kustomization.yaml diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 8af9f46..0ccf82c 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -75,14 +75,15 @@ spec: internal: image: repository: registry.bstein.dev/infra/harbor-redis - tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis"} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -92,13 +93,13 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] trivy: enabled: false metrics: @@ -111,9 +112,7 @@ spec: core: image: repository: registry.bstein.dev/infra/harbor-core - tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core"} existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY @@ -122,6 +121,9 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -131,24 +133,25 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice - tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice"} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -158,24 +161,25 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] portal: image: repository: registry.bstein.dev/infra/harbor-portal - tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal"} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -185,29 +189,30 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] registry: registry: image: repository: registry.bstein.dev/infra/harbor-registry - tag: v2.14.1-arm64 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry"} controller: image: repository: registry.bstein.dev/infra/harbor-registryctl - tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl"} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -217,24 +222,25 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] nginx: image: repository: registry.bstein.dev/infra/harbor-nginx tag: v2.14.1-arm64 - nodeSelector: - kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: + - key: hardware + operator: In + values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -244,13 +250,13 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] prepare: image: repository: registry.bstein.dev/infra/harbor-prepare diff --git a/services/harbor/image.yaml b/services/harbor/image.yaml new file mode 100644 index 0000000..732f9e1 --- /dev/null +++ b/services/harbor/image.yaml @@ -0,0 +1,144 @@ +# services/harbor/image.yaml +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-core + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-core + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-core + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-core + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-jobservice + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-jobservice + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-jobservice + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-jobservice + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-portal + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-portal + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-portal + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-portal + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-registry + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-registry + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-registry + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-registry + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-registryctl + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-registryctl + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-registryctl + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-registryctl + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-redis + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-redis + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-redis + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-redis + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" diff --git a/services/harbor/kustomization.yaml b/services/harbor/kustomization.yaml index eb27a25..7da3d50 100644 --- a/services/harbor/kustomization.yaml +++ b/services/harbor/kustomization.yaml @@ -7,3 +7,4 @@ resources: - pvc.yaml - certificate.yaml - helmrelease.yaml + - image.yaml From 1a8c6857e7bfe5381858646a328002be64b6cb52 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 03:30:31 -0300 Subject: [PATCH 72/93] harbor: re-pin workloads to titan-05 --- services/harbor/helmrelease.yaml | 54 ++++++++++++++------------------ 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 0ccf82c..d6d4042 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -76,14 +76,13 @@ spec: image: repository: registry.bstein.dev/infra/harbor-redis tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis"} + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -93,13 +92,13 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] trivy: enabled: false metrics: @@ -113,6 +112,8 @@ spec: image: repository: registry.bstein.dev/infra/harbor-core tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core"} + nodeSelector: + kubernetes.io/hostname: titan-05 existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY @@ -121,9 +122,6 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -133,25 +131,24 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice"} + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -161,25 +158,24 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] portal: image: repository: registry.bstein.dev/infra/harbor-portal tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal"} + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -189,13 +185,13 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] registry: registry: image: @@ -205,14 +201,13 @@ spec: image: repository: registry.bstein.dev/infra/harbor-registryctl tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl"} + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -222,25 +217,24 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] nginx: image: repository: registry.bstein.dev/infra/harbor-nginx tag: v2.14.1-arm64 + nodeSelector: + kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: hardware - operator: In - values: [ "rpi4", "rpi5" ] - key: kubernetes.io/arch operator: In values: [ "arm64" ] @@ -250,13 +244,13 @@ spec: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: [ "rpi5" ] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: [ "rpi4" ] prepare: image: repository: registry.bstein.dev/infra/harbor-prepare From f55d3fd956bb0f077965f642e77bdf1d7c4a75dd Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 04:11:26 -0300 Subject: [PATCH 73/93] flux(atlas): limit kustomization health checks --- .../atlas/flux-system/applications/harbor/kustomization.yaml | 5 +++++ .../flux-system/applications/jenkins/kustomization.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/clusters/atlas/flux-system/applications/harbor/kustomization.yaml b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml index 62bcdd1..361018c 100644 --- a/clusters/atlas/flux-system/applications/harbor/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml @@ -14,5 +14,10 @@ spec: name: flux-system namespace: flux-system wait: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: harbor + namespace: harbor dependsOn: - name: core diff --git a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml index 4e5d616..9fe9bd3 100644 --- a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml @@ -16,3 +16,8 @@ spec: - name: helm - name: traefik wait: true + healthChecks: + - apiVersion: helm.toolkit.fluxcd.io/v2 + kind: HelmRelease + name: jenkins + namespace: jenkins From 2f66afd97017ed1e31dc6866b89cbcd64f98f062 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 04:47:12 -0300 Subject: [PATCH 74/93] flux(atlas): use scoped health checks --- .../atlas/flux-system/applications/harbor/kustomization.yaml | 2 +- .../atlas/flux-system/applications/jenkins/kustomization.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clusters/atlas/flux-system/applications/harbor/kustomization.yaml b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml index 361018c..06baf26 100644 --- a/clusters/atlas/flux-system/applications/harbor/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/harbor/kustomization.yaml @@ -13,11 +13,11 @@ spec: kind: GitRepository name: flux-system namespace: flux-system - wait: true healthChecks: - apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease name: harbor namespace: harbor + wait: false dependsOn: - name: core diff --git a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml index 9fe9bd3..98a7211 100644 --- a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml @@ -15,9 +15,9 @@ spec: dependsOn: - name: helm - name: traefik - wait: true healthChecks: - apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease name: jenkins namespace: jenkins + wait: false From efa6d92b694afe06ed806a425001f306c421d9ba Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 13:14:31 -0300 Subject: [PATCH 75/93] harbor: automate nginx and prepare --- services/harbor/helmrelease.yaml | 4 +-- services/harbor/image.yaml | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index d6d4042..d7e94db 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -227,7 +227,7 @@ spec: nginx: image: repository: registry.bstein.dev/infra/harbor-nginx - tag: v2.14.1-arm64 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -254,6 +254,6 @@ spec: prepare: image: repository: registry.bstein.dev/infra/harbor-prepare - tag: v2.14.1-arm64 + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare"} updateStrategy: type: Recreate diff --git a/services/harbor/image.yaml b/services/harbor/image.yaml index 732f9e1..800b648 100644 --- a/services/harbor/image.yaml +++ b/services/harbor/image.yaml @@ -142,3 +142,51 @@ spec: policy: semver: range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-nginx + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-nginx + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-nginx + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-nginx + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: harbor-prepare + namespace: harbor +spec: + image: registry.bstein.dev/infra/harbor-prepare + interval: 5m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: harbor-prepare + namespace: harbor +spec: + imageRepositoryRef: + name: harbor-prepare + filterTags: + pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + extract: '$version' + policy: + semver: + range: ">=2.14.0-0 <2.15.0-0" From 543f2a9ccd87d77d4ac13d7fb482be797818a8b5 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 13:16:57 -0300 Subject: [PATCH 76/93] harbor: fix image policy tag regex --- services/harbor/image.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/harbor/image.yaml b/services/harbor/image.yaml index 800b648..2b25875 100644 --- a/services/harbor/image.yaml +++ b/services/harbor/image.yaml @@ -17,7 +17,7 @@ spec: imageRepositoryRef: name: harbor-core filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -41,7 +41,7 @@ spec: imageRepositoryRef: name: harbor-jobservice filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -65,7 +65,7 @@ spec: imageRepositoryRef: name: harbor-portal filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -89,7 +89,7 @@ spec: imageRepositoryRef: name: harbor-registry filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -113,7 +113,7 @@ spec: imageRepositoryRef: name: harbor-registryctl filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -137,7 +137,7 @@ spec: imageRepositoryRef: name: harbor-redis filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -161,7 +161,7 @@ spec: imageRepositoryRef: name: harbor-nginx filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: @@ -185,7 +185,7 @@ spec: imageRepositoryRef: name: harbor-prepare filterTags: - pattern: '^v(?P\\d+\\.\\d+\\.\\d+-arm64(\\.\\d+)?)$' + pattern: '^v(?P\d+\.\d+\.\d+-arm64(\.\d+)?)$' extract: '$version' policy: semver: From cde135c59e16023c56434e3bc813fb87a18f169b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 16:17:07 -0300 Subject: [PATCH 77/93] harbor: enable image automation push --- .../applications/harbor/image-automation.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml index 0040c6f..74334ba 100644 --- a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -1,5 +1,5 @@ # clusters/atlas/flux-system/applications/harbor/image-automation.yaml -apiVersion: image.toolkit.fluxcd.io/v1beta1 +apiVersion: image.toolkit.fluxcd.io/v1beta2 kind: ImageUpdateAutomation metadata: name: harbor @@ -10,11 +10,18 @@ spec: kind: GitRepository name: flux-system git: + checkout: + ref: + branch: feature/ci-gitops commit: author: email: ops@bstein.dev name: flux-bot messageTemplate: "chore(harbor): update images to {{range .Updated.Images}}{{.}}{{end}}" + push: + branch: feature/ci-gitops + secretRef: + name: flux-system-gitea update: strategy: Setters path: ./services/harbor From 37a50622a21b5a543845bb05d9f621cfe598b645 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 16:25:16 -0300 Subject: [PATCH 78/93] harbor: fix image automation push schema --- .../atlas/flux-system/applications/harbor/image-automation.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml index 74334ba..5a0311d 100644 --- a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -20,8 +20,6 @@ spec: messageTemplate: "chore(harbor): update images to {{range .Updated.Images}}{{.}}{{end}}" push: branch: feature/ci-gitops - secretRef: - name: flux-system-gitea update: strategy: Setters path: ./services/harbor From 1d788a5dc4513093722e5cee9d3e975630a7bb51 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 16:32:42 -0300 Subject: [PATCH 79/93] harbor: fix imagepolicy tag setters --- services/harbor/helmrelease.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index d7e94db..4da021b 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -75,7 +75,7 @@ spec: internal: image: repository: registry.bstein.dev/infra/harbor-redis - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -111,7 +111,7 @@ spec: core: image: repository: registry.bstein.dev/infra/harbor-core - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core:tag"} nodeSelector: kubernetes.io/hostname: titan-05 existingSecret: harbor-core @@ -141,7 +141,7 @@ spec: jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -168,7 +168,7 @@ spec: portal: image: repository: registry.bstein.dev/infra/harbor-portal - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -196,11 +196,11 @@ spec: registry: image: repository: registry.bstein.dev/infra/harbor-registry - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry:tag"} controller: image: repository: registry.bstein.dev/infra/harbor-registryctl - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -227,7 +227,7 @@ spec: nginx: image: repository: registry.bstein.dev/infra/harbor-nginx - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -254,6 +254,6 @@ spec: prepare: image: repository: registry.bstein.dev/infra/harbor-prepare - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare:tag"} updateStrategy: type: Recreate From bb8de41cdbd066a34e826327ebed87d0508616f8 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 16:38:37 -0300 Subject: [PATCH 80/93] harbor: run image automation in harbor ns --- .../flux-system/applications/harbor/image-automation.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml index 5a0311d..4fa18ec 100644 --- a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -3,12 +3,13 @@ apiVersion: image.toolkit.fluxcd.io/v1beta2 kind: ImageUpdateAutomation metadata: name: harbor - namespace: flux-system + namespace: harbor spec: interval: 5m0s sourceRef: kind: GitRepository name: flux-system + namespace: flux-system git: checkout: ref: From b7709b3f40dc206a9dc25d74939e41a7b27a7733 Mon Sep 17 00:00:00 2001 From: flux-bot Date: Wed, 17 Dec 2025 19:38:57 +0000 Subject: [PATCH 81/93] chore(harbor): update images to registry.bstein.dev/infra/harbor-redis:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-core:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-jobservice:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-portal:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-registry:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-registryctl:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-nginx:v2.14.1-arm64.14registry.bstein.dev/infra/harbor-prepare:v2.14.1-arm64.14 --- services/harbor/helmrelease.yaml | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 4da021b..e11c7c3 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -75,7 +75,7 @@ spec: internal: image: repository: registry.bstein.dev/infra/harbor-redis - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-redis:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -85,20 +85,20 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] trivy: enabled: false metrics: @@ -111,7 +111,7 @@ spec: core: image: repository: registry.bstein.dev/infra/harbor-core - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-core:tag"} nodeSelector: kubernetes.io/hostname: titan-05 existingSecret: harbor-core @@ -124,24 +124,24 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -151,24 +151,24 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] portal: image: repository: registry.bstein.dev/infra/harbor-portal - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-portal:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -178,29 +178,29 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] registry: registry: image: repository: registry.bstein.dev/infra/harbor-registry - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-registry:tag"} controller: image: repository: registry.bstein.dev/infra/harbor-registryctl - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -210,24 +210,24 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] nginx: image: repository: registry.bstein.dev/infra/harbor-nginx - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-nginx:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -237,23 +237,23 @@ spec: - matchExpressions: - key: kubernetes.io/arch operator: In - values: [ "arm64" ] + values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi5" ] + values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In - values: [ "rpi4" ] + values: ["rpi4"] prepare: image: repository: registry.bstein.dev/infra/harbor-prepare - tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare:tag"} + tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-prepare:tag"} updateStrategy: type: Recreate From b7246f58350dfb05ae45610792d9dbead9864334 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 17:29:03 -0300 Subject: [PATCH 82/93] harbor: suspend automation, pin redis --- .../atlas/flux-system/applications/harbor/image-automation.yaml | 1 + services/harbor/helmrelease.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml index 4fa18ec..e010ea8 100644 --- a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -5,6 +5,7 @@ metadata: name: harbor namespace: harbor spec: + suspend: true interval: 5m0s sourceRef: kind: GitRepository diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index e11c7c3..1ac29a9 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -75,7 +75,7 @@ spec: internal: image: repository: registry.bstein.dev/infra/harbor-redis - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-redis:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: From 0a422895169cdd3568820234fc276d74f7b53927 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 17:54:50 -0300 Subject: [PATCH 83/93] harbor: pin components to v2.14.1-arm64 --- services/harbor/helmrelease.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/services/harbor/helmrelease.yaml b/services/harbor/helmrelease.yaml index 1ac29a9..75f8be3 100644 --- a/services/harbor/helmrelease.yaml +++ b/services/harbor/helmrelease.yaml @@ -111,7 +111,7 @@ spec: core: image: repository: registry.bstein.dev/infra/harbor-core - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-core:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core:tag"} nodeSelector: kubernetes.io/hostname: titan-05 existingSecret: harbor-core @@ -141,7 +141,7 @@ spec: jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -168,7 +168,7 @@ spec: portal: image: repository: registry.bstein.dev/infra/harbor-portal - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-portal:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -196,11 +196,11 @@ spec: registry: image: repository: registry.bstein.dev/infra/harbor-registry - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-registry:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry:tag"} controller: image: repository: registry.bstein.dev/infra/harbor-registryctl - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -227,7 +227,7 @@ spec: nginx: image: repository: registry.bstein.dev/infra/harbor-nginx - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-nginx:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: @@ -254,6 +254,6 @@ spec: prepare: image: repository: registry.bstein.dev/infra/harbor-prepare - tag: v2.14.1-arm64.14 # {"$imagepolicy": "harbor:harbor-prepare:tag"} + tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare:tag"} updateStrategy: type: Recreate From daa354e2cd7b275b2f0a15d5370421ed668ba8f4 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 19:18:29 -0300 Subject: [PATCH 84/93] ci-demo: add flux image automation --- .../ci-demo/image-automation.yaml | 27 ++++++++++++++++ .../applications/ci-demo/kustomization.yaml | 19 ++++++++++++ .../applications/kustomization.yaml | 2 ++ scripts/crypto_wallet_monero_setup.fish | 2 +- services/ci-demo/deployment.yaml | 31 +++++++++++++++++++ services/ci-demo/image.yaml | 25 +++++++++++++++ services/ci-demo/kustomization.yaml | 11 +++++++ services/ci-demo/namespace.yaml | 6 ++++ services/ci-demo/service.yaml | 14 +++++++++ 9 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml create mode 100644 clusters/atlas/flux-system/applications/ci-demo/kustomization.yaml create mode 100644 services/ci-demo/deployment.yaml create mode 100644 services/ci-demo/image.yaml create mode 100644 services/ci-demo/kustomization.yaml create mode 100644 services/ci-demo/namespace.yaml create mode 100644 services/ci-demo/service.yaml diff --git a/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml new file mode 100644 index 0000000..0050a88 --- /dev/null +++ b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml @@ -0,0 +1,27 @@ +# clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageUpdateAutomation +metadata: + name: ci-demo + namespace: flux-system +spec: + interval: 1m0s + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + git: + checkout: + ref: + branch: feature/ci-gitops + commit: + author: + email: ops@bstein.dev + name: flux-bot + messageTemplate: "chore(ci-demo): update image to {{range .Updated.Images}}{{.}}{{end}}" + push: + branch: feature/ci-gitops + update: + strategy: Setters + path: ./services/ci-demo + diff --git a/clusters/atlas/flux-system/applications/ci-demo/kustomization.yaml b/clusters/atlas/flux-system/applications/ci-demo/kustomization.yaml new file mode 100644 index 0000000..eb6903f --- /dev/null +++ b/clusters/atlas/flux-system/applications/ci-demo/kustomization.yaml @@ -0,0 +1,19 @@ +# clusters/atlas/flux-system/applications/ci-demo/kustomization.yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: ci-demo + namespace: flux-system +spec: + interval: 10m + path: ./services/ci-demo + prune: true + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + targetNamespace: ci-demo + dependsOn: + - name: core + wait: false + diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index 50ca611..62caed7 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -18,3 +18,5 @@ resources: - oauth2-proxy/kustomization.yaml - mailu/kustomization.yaml - jenkins/kustomization.yaml + - ci-demo/kustomization.yaml + - ci-demo/image-automation.yaml diff --git a/scripts/crypto_wallet_monero_setup.fish b/scripts/crypto_wallet_monero_setup.fish index 7a4c96b..0b1f43a 100644 --- a/scripts/crypto_wallet_monero_setup.fish +++ b/scripts/crypto_wallet_monero_setup.fish @@ -371,7 +371,7 @@ function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-r echo "Skipping daemon probe due to xmrwallet_SKIP_DAEMON_CHECK=1" end - # Use your private image by default (in Zot) + # Use your private image by default (in Harbor) read -P "Container image for wallet RPC [registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1]: " image if test -z "$image"; set image registry.bstein.dev/crypto/monero-wallet-rpc:0.18.4.1; end _require "Container image" $image; or return 1 diff --git a/services/ci-demo/deployment.yaml b/services/ci-demo/deployment.yaml new file mode 100644 index 0000000..df882f5 --- /dev/null +++ b/services/ci-demo/deployment.yaml @@ -0,0 +1,31 @@ +# services/ci-demo/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ci-demo + namespace: ci-demo +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: ci-demo + template: + metadata: + labels: + app.kubernetes.io/name: ci-demo + spec: + nodeSelector: + hardware: rpi4 + containers: + - name: ci-demo + image: registry.bstein.dev/infra/ci-demo:latest + ports: + - name: http + containerPort: 8080 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 2 + periodSeconds: 5 + diff --git a/services/ci-demo/image.yaml b/services/ci-demo/image.yaml new file mode 100644 index 0000000..aa2db71 --- /dev/null +++ b/services/ci-demo/image.yaml @@ -0,0 +1,25 @@ +# services/ci-demo/image.yaml +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: ci-demo + namespace: ci-demo +spec: + image: registry.bstein.dev/infra/ci-demo + interval: 1m0s +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: ci-demo + namespace: ci-demo +spec: + imageRepositoryRef: + name: ci-demo + filterTags: + pattern: '^v(?P0\\.0\\.0-\\d+)$' + extract: '$version' + policy: + semver: + range: ">=0.0.0-0" + diff --git a/services/ci-demo/kustomization.yaml b/services/ci-demo/kustomization.yaml new file mode 100644 index 0000000..5a34b1d --- /dev/null +++ b/services/ci-demo/kustomization.yaml @@ -0,0 +1,11 @@ +# services/ci-demo/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - image.yaml + - deployment.yaml + - service.yaml +images: + - name: registry.bstein.dev/infra/ci-demo + newTag: v0.0.0-0 # {"$imagepolicy": "ci-demo:ci-demo:tag"} diff --git a/services/ci-demo/namespace.yaml b/services/ci-demo/namespace.yaml new file mode 100644 index 0000000..e661fc1 --- /dev/null +++ b/services/ci-demo/namespace.yaml @@ -0,0 +1,6 @@ +# services/ci-demo/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: ci-demo + diff --git a/services/ci-demo/service.yaml b/services/ci-demo/service.yaml new file mode 100644 index 0000000..c094387 --- /dev/null +++ b/services/ci-demo/service.yaml @@ -0,0 +1,14 @@ +# services/ci-demo/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: ci-demo + namespace: ci-demo +spec: + selector: + app.kubernetes.io/name: ci-demo + ports: + - name: http + port: 80 + targetPort: http + From bbb84c1182340732cbc66e6bfd3e58ce9093c2ad Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 19:27:23 -0300 Subject: [PATCH 85/93] jenkins: add ci-demo job --- services/jenkins/helmrelease.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml index 90e9f32..a9bba37 100644 --- a/services/jenkins/helmrelease.yaml +++ b/services/jenkins/helmrelease.yaml @@ -268,6 +268,25 @@ spec: } } } + pipelineJob('ci-demo') { + triggers { + scm('H/1 * * * *') + } + definition { + cpsScm { + scm { + git { + remote { + url('https://scm.bstein.dev/bstein/ci-demo.git') + credentials('gitea-pat') + } + branches('*/master') + } + } + scriptPath('Jenkinsfile') + } + } + } persistence: enabled: true storageClass: astreae From 9635100675b860d6a95bc569e11d989a1180fd8d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 19:45:15 -0300 Subject: [PATCH 86/93] ci-demo: fix imagepolicy tag regex --- services/ci-demo/image.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/ci-demo/image.yaml b/services/ci-demo/image.yaml index aa2db71..60f2f42 100644 --- a/services/ci-demo/image.yaml +++ b/services/ci-demo/image.yaml @@ -17,9 +17,8 @@ spec: imageRepositoryRef: name: ci-demo filterTags: - pattern: '^v(?P0\\.0\\.0-\\d+)$' + pattern: '^v(?P0\.0\.0-\d+)$' extract: '$version' policy: semver: range: ">=0.0.0-0" - From 39275db74ee626303402d7ba3d8b8f95d0eefc5d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 19:49:53 -0300 Subject: [PATCH 87/93] ci-demo: set tag v0.0.0-1 --- services/ci-demo/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/ci-demo/kustomization.yaml b/services/ci-demo/kustomization.yaml index 5a34b1d..f24037f 100644 --- a/services/ci-demo/kustomization.yaml +++ b/services/ci-demo/kustomization.yaml @@ -8,4 +8,4 @@ resources: - service.yaml images: - name: registry.bstein.dev/infra/ci-demo - newTag: v0.0.0-0 # {"$imagepolicy": "ci-demo:ci-demo:tag"} + newTag: v0.0.0-1 # {"$imagepolicy": "ci-demo:ci-demo:tag"} From 9816354d0fe59b7072ee0e19395daaa8b6692b46 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 17 Dec 2025 23:12:03 -0300 Subject: [PATCH 88/93] ci-demo: bump to v0.0.0-2 --- .../flux-system/applications/ci-demo/image-automation.yaml | 3 +-- services/ci-demo/kustomization.yaml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml index 0050a88..8fa4282 100644 --- a/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml @@ -23,5 +23,4 @@ spec: branch: feature/ci-gitops update: strategy: Setters - path: ./services/ci-demo - + path: services/ci-demo diff --git a/services/ci-demo/kustomization.yaml b/services/ci-demo/kustomization.yaml index f24037f..9280678 100644 --- a/services/ci-demo/kustomization.yaml +++ b/services/ci-demo/kustomization.yaml @@ -8,4 +8,4 @@ resources: - service.yaml images: - name: registry.bstein.dev/infra/ci-demo - newTag: v0.0.0-1 # {"$imagepolicy": "ci-demo:ci-demo:tag"} + newTag: v0.0.0-2 # {"$imagepolicy": "ci-demo:ci-demo:tag"} From 609347991e60ebf9bd3ec6285802c0047efaa1ec Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 18 Dec 2025 00:38:32 -0300 Subject: [PATCH 89/93] flux: upgrade controllers to v2.7.5 --- .../atlas/flux-system/gotk-components.yaml | 6978 ++++++++--------- 1 file changed, 3417 insertions(+), 3561 deletions(-) diff --git a/clusters/atlas/flux-system/gotk-components.yaml b/clusters/atlas/flux-system/gotk-components.yaml index c0ec238..bb53b3d 100644 --- a/clusters/atlas/flux-system/gotk-components.yaml +++ b/clusters/atlas/flux-system/gotk-components.yaml @@ -1,14 +1,14 @@ --- # This manifest was generated by flux. DO NOT EDIT. -# Flux Version: v2.5.1f reconzaq1= zaq1= aq1= 1= w2cile kustomization flux-system --namespace flux-system --with-source -# Components: source-controller,kustomize-controller,helm-controller,notification-controller +# Flux Version: v2.7.5 +# Components: source-controller,kustomize-controller,helm-controller,notification-controller,image-reflector-controller,image-automation-controller apiVersion: v1 kind: Namespace metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 pod-security.kubernetes.io/warn: restricted pod-security.kubernetes.io/warn-version: latest name: flux-system @@ -19,7 +19,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: allow-egress namespace: flux-system spec: @@ -39,7 +39,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: allow-scraping namespace: flux-system spec: @@ -59,7 +59,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: allow-webhooks namespace: flux-system spec: @@ -78,7 +78,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: critical-pods-flux-system namespace: flux-system spec: @@ -98,7 +98,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: crd-controller-flux-system rules: - apiGroups: @@ -131,6 +131,12 @@ rules: - '*' verbs: - '*' +- apiGroups: + - source.extensions.fluxcd.io + resources: + - '*' + verbs: + - '*' - apiGroups: - "" resources: @@ -181,6 +187,12 @@ rules: - update - patch - delete +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create - nonResourceURLs: - /livez/ping verbs: @@ -192,7 +204,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 rbac.authorization.k8s.io/aggregate-to-admin: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" name: flux-edit-flux-system @@ -200,6 +212,7 @@ rules: - apiGroups: - notification.toolkit.fluxcd.io - source.toolkit.fluxcd.io + - source.extensions.fluxcd.io - helm.toolkit.fluxcd.io - image.toolkit.fluxcd.io - kustomize.toolkit.fluxcd.io @@ -218,7 +231,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 rbac.authorization.k8s.io/aggregate-to-admin: "true" rbac.authorization.k8s.io/aggregate-to-edit: "true" rbac.authorization.k8s.io/aggregate-to-view: "true" @@ -227,6 +240,7 @@ rules: - apiGroups: - notification.toolkit.fluxcd.io - source.toolkit.fluxcd.io + - source.extensions.fluxcd.io - helm.toolkit.fluxcd.io - image.toolkit.fluxcd.io - kustomize.toolkit.fluxcd.io @@ -243,7 +257,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: cluster-reconciler-flux-system roleRef: apiGroup: rbac.authorization.k8s.io @@ -263,7 +277,7 @@ metadata: labels: app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: crd-controller-flux-system roleRef: apiGroup: rbac.authorization.k8s.io @@ -288,17 +302,20 @@ subjects: - kind: ServiceAccount name: image-automation-controller namespace: flux-system +- kind: ServiceAccount + name: source-watcher + namespace: flux-system --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: buckets.source.toolkit.fluxcd.io spec: group: source.toolkit.fluxcd.io @@ -437,6 +454,13 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the bucket. This field is only supported for the 'gcp' and 'aws' providers. + For more information about workload identity: + https://fluxcd.io/flux/components/source/buckets/#workload-identity + type: string sts: description: |- STS specifies the required configuration to use a Security Token @@ -527,6 +551,11 @@ spec: rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' - message: spec.sts.certSecretRef is not required for the 'aws' STS provider rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' + - message: ServiceAccountName is not supported for the 'generic' Bucket + provider + rule: self.provider != 'generic' || !has(self.serviceAccountName) + - message: cannot set both .spec.secretRef and .spec.serviceAccountName + rule: '!has(self.secretRef) || !has(self.serviceAccountName)' status: default: observedGeneration: -1 @@ -572,6 +601,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -662,238 +692,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta1 Bucket is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: BucketSpec defines the desired state of an S3 compatible - bucket - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: The bucket name. - type: string - endpoint: - description: The bucket endpoint address. - type: string - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. - type: boolean - interval: - description: The interval at which to check for bucket updates. - type: string - provider: - default: generic - description: The S3 compatible storage provider name, default ('generic'). - enum: - - generic - - aws - - gcp - type: string - region: - description: The bucket region. - type: string - secretRef: - description: |- - The name of the secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for download operations, defaults to 60s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - default: - observedGeneration: -1 - description: BucketStatus defines the observed state of a bucket - properties: - artifact: - description: Artifact represents the output of the last successful - Bucket sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of this - artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: |- - Revision is a human readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm - chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - lastUpdateTime - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last Bucket sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .spec.endpoint name: Endpoint @@ -1187,6 +985,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -1282,12 +1081,208 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 + name: externalartifacts.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: ExternalArtifact + listKind: ExternalArtifactList + plural: externalartifacts + singular: externalartifact + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .spec.sourceRef.name + name: Source + type: string + name: v1 + schema: + openAPIV3Schema: + description: ExternalArtifact is the Schema for the external artifacts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExternalArtifactSpec defines the desired state of ExternalArtifact + properties: + sourceRef: + description: |- + SourceRef points to the Kubernetes custom resource for + which the artifact is generated. + properties: + apiVersion: + description: API version of the referent, if not specified the + Kubernetes preferred version will be used. + type: string + kind: + description: Kind of the referent. + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - kind + - name + type: object + type: object + status: + description: ExternalArtifactStatus defines the observed state of ExternalArtifact + properties: + artifact: + description: Artifact represents the output of an ExternalArtifact + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the ExternalArtifact. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/component: source-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 name: gitrepositories.source.toolkit.fluxcd.io spec: group: source.toolkit.fluxcd.io @@ -1458,6 +1453,19 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to + authenticate to the GitRepository. This field is only supported for 'azure' provider. + type: string + sparseCheckout: + description: |- + SparseCheckout specifies a list of directories to checkout when cloning + the repository. If specified, only these directories are included in the + Artifact produced for this GitRepository. + items: + type: string + type: array suspend: description: |- Suspend tells the controller to suspend the reconciliation of this @@ -1511,6 +1519,10 @@ spec: - interval - url type: object + x-kubernetes-validations: + - message: serviceAccountName can only be set when provider is 'azure' + rule: '!has(self.serviceAccountName) || (has(self.provider) && self.provider + == ''azure'')' status: default: observedGeneration: -1 @@ -1557,6 +1569,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -1665,6 +1678,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -1727,6 +1741,13 @@ spec: ObservedRecurseSubmodules is the observed resource submodules configuration used to produce the current Artifact. type: boolean + observedSparseCheckout: + description: |- + ObservedSparseCheckout is the observed list of directories used to + produce the current Artifact. + items: + type: string + type: array sourceVerificationMode: description: |- SourceVerificationMode is the last used verification mode indicating @@ -1738,343 +1759,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta1 GitRepository is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: GitRepositorySpec defines the desired state of a Git repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: |- - Determines which git client library to use. - Defaults to go-git, valid values are ('go-git', 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - include: - description: Extra git repositories to map into the repository - items: - description: GitRepositoryInclude defines a source with a from and - to path. - properties: - fromPath: - description: The path to copy contents from, defaults to the - root directory. - type: string - repository: - description: Reference to a GitRepository to include. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: The path to copy contents to, defaults to the name - of the source ref. - type: string - required: - - repository - type: object - type: array - interval: - description: The interval at which to check for repository updates. - type: string - recurseSubmodules: - description: |- - When enabled, after the clone is created, initializes all submodules within, - using their default settings. - This option is available only when using the 'go-git' GitImplementation. - type: boolean - ref: - description: |- - The Git reference to checkout and monitor for changes, defaults to - master branch. - properties: - branch: - description: The Git branch to checkout, defaults to master. - type: string - commit: - description: The Git commit SHA to checkout, if specified Tag - filters will be ignored. - type: string - semver: - description: The Git tag semver expression, takes precedence over - Tag. - type: string - tag: - description: The Git tag to checkout, takes precedence over Branch. - type: string - type: object - secretRef: - description: |- - The secret name containing the Git credentials. - For HTTPS repositories the secret must contain username and password - fields. - For SSH repositories the secret must contain identity and known_hosts - fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote Git operations like cloning, defaults - to 60s. - type: string - url: - description: The repository URL, can be a HTTP/S or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: Verify OpenPGP signature for the Git commit HEAD points - to. - properties: - mode: - description: Mode describes what git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: The secret name containing the public keys of all - trusted Git authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus defines the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of this - artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: |- - Revision is a human readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm - chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - lastUpdateTime - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - includedArtifacts: - description: IncludedArtifacts represents the included artifacts from - the last successful repository sync. - items: - description: Artifact represents the output of a source synchronisation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of this - artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: |- - Revision is a human readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm - chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - lastUpdateTime - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: |- - URL is the download link for the artifact output of the last repository - sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .spec.url name: URL @@ -2343,6 +2027,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -2466,6 +2151,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -2545,12 +2231,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: helmcharts.source.toolkit.fluxcd.io spec: group: source.toolkit.fluxcd.io @@ -2791,6 +2477,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -2895,263 +2582,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta1 HelmChart is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmChartSpec defines the desired state of a Helm chart. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: The name or path the Helm chart is available at in the - SourceRef. - type: string - interval: - description: The interval at which to check the Source for updates. - type: string - reconcileStrategy: - default: ChartVersion - description: |- - Determines what enables the creation of a new artifact. Valid values are - ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: |- - Kind of the referent, valid values are ('HelmRepository', 'GitRepository', - 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: |- - Alternative values file to use as the default chart values, expected to - be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, - for backwards compatibility the file defined here is merged before the - ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: |- - Alternative list of values files to use as the chart values (values.yaml - is not included by default), expected to be a relative path in the SourceRef. - Values files are merged in the order of this list with the last file overriding - the first. Ignored when omitted. - items: - type: string - type: array - version: - default: '*' - description: |- - The chart version semver expression, ignored for charts from GitRepository - and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus defines the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - chart sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of this - artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: |- - Revision is a human readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm - chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - lastUpdateTime - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last chart pulled. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .spec.chart name: Chart @@ -3417,6 +2847,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -3526,12 +2957,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: helmrepositories.source.toolkit.fluxcd.io spec: group: source.toolkit.fluxcd.io @@ -3760,6 +3191,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -3846,226 +3278,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta1 HelmRepository is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec defines the reference to a Helm repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - interval: - description: The interval at which to check the upstream for updates. - type: string - passCredentials: - description: |- - PassCredentials allows the credentials from the SecretRef to be passed on to - a host that does not match the host as defined in URL. - This may be required if the host of the advertised chart URLs in the index - differ from the defined URL. - Enabling this should be done with caution, as it can potentially result in - credentials getting stolen in a MITM-attack. - type: boolean - secretRef: - description: |- - The name of the secret containing authentication credentials for the Helm - repository. - For HTTP/S basic auth the secret must contain username and - password fields. - For TLS the secret must contain a certFile and keyFile, and/or - caFile fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout of index downloading, defaults to 60s. - type: string - url: - description: The Helm repository URL, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus defines the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of this - artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: |- - Revision is a human readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm - chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - lastUpdateTime - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last index fetched. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .spec.url name: URL @@ -4284,6 +3496,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -4375,12 +3588,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: ocirepositories.source.toolkit.fluxcd.io spec: group: source.toolkit.fluxcd.io @@ -4406,6 +3619,401 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date + name: v1 + schema: + openAPIV3Schema: + description: OCIRepository is the Schema for the ocirepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OCIRepositorySpec defines the desired state of OCIRepository + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container + registry. + type: boolean + interval: + description: |- + Interval at which the OCIRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + layerSelector: + description: |- + LayerSelector specifies which layer should be extracted from the OCI artifact. + When not specified, the first layer found in the artifact is selected. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ref: + description: |- + The OCI reference to pull and monitor for changes, + defaults to the latest tag. + properties: + digest: + description: |- + Digest is the image digest to pull, takes precedence over SemVer. + The value should be in the format 'sha256:'. + type: string + semver: + description: |- + SemVer is the range of tags to pull selecting the latest within + the range, takes precedence over Tag. + type: string + semverFilter: + description: SemverFilter is a regex pattern to filter the tags + within the SemVer range. + type: string + tag: + description: Tag is the image tag to pull, defaults to latest. + type: string + type: object + secretRef: + description: |- + SecretRef contains the secret name containing the registry login + credentials to resolve image metadata. + The secret must be of type kubernetes.io/dockerconfigjson. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote OCI Repository operations like + pulling, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: |- + URL is a reference to an OCI artifact repository hosted + on a remote container registry. + pattern: ^oci://.*$ + type: string + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the + OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: OCIRepositoryStatus defines the observed state of OCIRepository + properties: + artifact: + description: Artifact represents the output of the last successful + OCI Repository sync. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the OCIRepository. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedLayerSelector: + description: |- + ObservedLayerSelector is the observed layer selector used for constructing + the source artifact. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + url: + description: URL is the download link for the artifact output of the + last OCI Repository sync. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 OCIRepository is deprecated, upgrade to v1 name: v1beta2 schema: openAPIV3Schema: @@ -4679,6 +4287,7 @@ spec: consumption, e.g. by another controller applying the Artifact contents. type: string required: + - digest - lastUpdateTime - path - revision @@ -4799,7 +4408,7 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} --- @@ -4810,7 +4419,7 @@ metadata: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: source-controller namespace: flux-system --- @@ -4821,7 +4430,7 @@ metadata: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: source-controller namespace: flux-system @@ -4842,7 +4451,7 @@ metadata: app.kubernetes.io/component: source-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: source-controller namespace: flux-system @@ -4860,12 +4469,16 @@ spec: prometheus.io/scrape: "true" labels: app: source-controller + app.kubernetes.io/component: source-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 spec: containers: - args: - - --events-addr=http://notification-controller.flux-system.svc.cluster.local./ + - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election - --storage-path=/data @@ -4877,17 +4490,12 @@ spec: fieldPath: metadata.namespace - name: TUF_ROOT value: /tmp/.sigstore - - name: GOMAXPROCS - valueFrom: - resourceFieldRef: - containerName: manager - resource: limits.cpu - name: GOMEMLIMIT valueFrom: resourceFieldRef: containerName: manager resource: limits.memory - image: ghcr.io/fluxcd/source-controller:v1.5.0 + image: ghcr.io/fluxcd/source-controller:v1.7.4 imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -4946,12 +4554,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: kustomize-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: kustomizations.kustomize.toolkit.fluxcd.io spec: group: kustomize.toolkit.fluxcd.io @@ -5019,8 +4627,7 @@ spec: type: object type: object components: - description: Components specifies relative paths to specifications - of other Components. + description: Components specifies relative paths to kustomize Components. items: type: string type: array @@ -5034,8 +4641,11 @@ spec: - sops type: string secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. + description: |- + The secret name containing the private OpenPGP keys used for decryption. + A static credential for a cloud provider defined inside the Secret + takes priority to secret-less authentication with the ServiceAccountName + field. properties: name: description: Name of the referent. @@ -5043,6 +4653,14 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the service account used to + authenticate with KMS services from cloud providers. If a + static credential for a given cloud provider is defined + inside the Secret referenced by SecretRef, that static + credential takes priority. + type: string required: - provider type: object @@ -5050,29 +4668,38 @@ spec: description: |- DeletionPolicy can be used to control garbage collection when this Kustomization is deleted. Valid values are ('MirrorPrune', 'Delete', - 'Orphan'). 'MirrorPrune' mirrors the Prune field (orphan if false, - delete if true). Defaults to 'MirrorPrune'. + 'WaitForTermination', 'Orphan'). 'MirrorPrune' mirrors the Prune field + (orphan if false, delete if true). Defaults to 'MirrorPrune'. enum: - MirrorPrune - Delete + - WaitForTermination - Orphan type: string dependsOn: description: |- - DependsOn may contain a meta.NamespacedObjectReference slice + DependsOn may contain a DependencyReference slice with references to Kustomization resources that must be ready before this Kustomization can be reconciled. items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. + description: DependencyReference defines a Kustomization dependency + on another Kustomization resource. properties: name: description: Name of the referent. type: string namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. + description: |- + Namespace of the referent, defaults to the namespace of the Kustomization + resource object that contains the reference. + type: string + readyExpr: + description: |- + ReadyExpr is a CEL expression that can be used to assess the readiness + of a dependency. When specified, the built-in readiness check + is replaced by the logic defined in the CEL expression. + To make the CEL expression additive to the built-in readiness check, + the feature gate `AdditiveCELDependencyCheck` must be set to `true`. type: string required: - name @@ -5146,6 +4773,12 @@ spec: - name type: object type: array + ignoreMissingComponents: + description: |- + IgnoreMissingComponents instructs the controller to ignore Components paths + not found in source by removing them from the generated kustomization.yaml + before running kustomize build. + type: boolean images: description: |- Images is a list of (image name, new name, new tag or digest) @@ -5192,16 +4825,54 @@ spec: a controller level fallback for when KustomizationSpec.ServiceAccountName is empty. properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object secretRef: description: |- - SecretRef holds the name of a secret that contains a key with + SecretRef holds an optional name of a secret that contains a key with the kubeconfig file as the value. If no key is set, the key will default - to 'value'. + to 'value'. Mutually exclusive with ConfigMapRef. It is recommended that the kubeconfig is self-contained, and the secret is regularly updated if credentials such as a cloud-access-token expire. Cloud specific `cmd-path` auth helpers will not function without adding binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. + Kubernetes resources. Supported only for the generic provider. properties: key: description: Key in the Secret, when not specified an implementation-specific @@ -5213,9 +4884,14 @@ spec: required: - name type: object - required: - - secretRef type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' namePrefix: description: NamePrefix will prefix the names of all managed resources. maxLength: 200 @@ -5374,6 +5050,7 @@ spec: - OCIRepository - GitRepository - Bucket + - ExternalArtifact type: string name: description: Name of the referent. @@ -5476,6 +5153,57 @@ spec: - type type: object type: array + history: + description: |- + History contains a set of snapshots of the last reconciliation attempts + tracking the revision, the state and the duration of each attempt. + items: + description: |- + Snapshot represents a point-in-time record of a group of resources reconciliation, + including timing information, status, and a unique digest identifier. + properties: + digest: + description: Digest is the checksum in the format `:` + of the resources in this snapshot. + type: string + firstReconciled: + description: FirstReconciled is the time when this revision + was first reconciled to the cluster. + format: date-time + type: string + lastReconciled: + description: LastReconciled is the time when this revision was + last reconciled to the cluster. + format: date-time + type: string + lastReconciledDuration: + description: LastReconciledDuration is time it took to reconcile + the resources in this revision. + type: string + lastReconciledStatus: + description: LastReconciledStatus is the status of the last + reconciliation. + type: string + metadata: + additionalProperties: + type: string + description: Metadata contains additional information about + the snapshot. + type: object + totalReconciliations: + description: TotalReconciliations is the total number of reconciliations + that have occurred for this snapshot. + format: int64 + type: integer + required: + - digest + - firstReconciled + - lastReconciled + - lastReconciledDuration + - lastReconciledStatus + - totalReconciliations + type: object + type: array inventory: description: |- Inventory contains the list of Kubernetes resource object references that @@ -5537,574 +5265,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta1 Kustomization is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: KustomizationSpec defines the desired state of a kustomization. - properties: - decryption: - description: Decrypt Kubernetes secrets before applying them on the - cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - dependsOn: - description: |- - DependsOn may contain a meta.NamespacedObjectReference slice - with references to Kustomization resources that must be ready before this - Kustomization can be reconciled. - items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - force: - default: false - description: |- - Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: |- - NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object - in any namespace. - properties: - apiVersion: - description: API version of the referent, if not specified the - Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: array - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag - or digest, which will replace the original name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original - name. - type: string - newTag: - description: NewTag is the value used to replace the original - tag. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the Kustomization. - type: string - kubeConfig: - description: |- - The KubeConfig for reconciling the Kustomization on a remote cluster. - When specified, KubeConfig takes precedence over ServiceAccountName. - properties: - secretRef: - description: |- - SecretRef holds the name to a secret that contains a 'value' key with - the kubeconfig file as the value. It must be in the same namespace as - the Kustomization. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - the Kustomization. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - secretRef - type: object - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - patchesJson6902: - description: JSON 6902 patches, defined as inline YAML objects. - items: - description: JSON6902Patch contains a JSON6902 patch and the target - the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with - an array of operation objects. - items: - description: |- - JSON6902 is a JSON6902 operation object. - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: |- - From contains a JSON-pointer value that references a location within the target document where the operation is - performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. - type: string - op: - description: |- - Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or - "test". - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: |- - Path contains the JSON-pointer value that references a location within the target document where the operation - is performed. The meaning of the value depends on the value of Op. - type: string - value: - description: |- - Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into - account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: Strategic merge patches, defined as inline YAML objects. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - path: - description: |- - Path to the directory containing the kustomization.yaml file, or the - set of plain YAMLs a kustomization.yaml should be generated for. - Defaults to 'None', which translates to the root path of the SourceRef. - type: string - postBuild: - description: |- - PostBuild describes which actions to perform on the YAML manifest - generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: |- - Substitute holds a map of key/value pairs. - The variables defined in your YAML manifests - that match any of the keys defined in the map - will be substituted with the set value. - Includes support for bash string replacement functions - e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: |- - SubstituteFrom holds references to ConfigMaps and Secrets containing - the variables and their values to be substituted in the YAML manifests. - The ConfigMap and the Secret data keys represent the var names and they - must match the vars declared in the manifests for the substitution to happen. - items: - description: |- - SubstituteReference contains a reference to a resource containing - the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are - ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: |- - The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - type: string - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file - is. - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - GitRepository - - Bucket - type: string - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent, defaults to the Kustomization - namespace - type: string - required: - - kind - - name - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent kustomize executions, - it does not apply to already started executions. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: |- - Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - type: string - validation: - description: |- - Validate the Kubernetes objects before applying them on the cluster. - The validation strategy can be 'client' (local dry-run), 'server' - (APIServer dry-run) or 'none'. - When 'Force' is 'true', validation will fallback to 'client' if set to - 'server' because server-side validation is not supported in this scenario. - enum: - - none - - client - - server - type: string - required: - - interval - - prune - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAppliedRevision: - description: |- - The last successfully applied revision. - The revision format for Git sources is /. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - snapshot: - description: The last successfully applied revision metadata. - properties: - checksum: - description: The manifests sha1 checksum. - type: string - entries: - description: A list of Kubernetes kinds grouped by namespace. - items: - description: |- - Snapshot holds the metadata of namespaced - Kubernetes objects - properties: - kinds: - additionalProperties: - type: string - description: The list of Kubernetes kinds. - type: object - namespace: - description: The namespace of this entry. - type: string - required: - - kinds - type: object - type: array - required: - - checksum - - entries - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -6283,16 +5443,54 @@ spec: a controller level fallback for when KustomizationSpec.ServiceAccountName is empty. properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object secretRef: description: |- - SecretRef holds the name of a secret that contains a key with + SecretRef holds an optional name of a secret that contains a key with the kubeconfig file as the value. If no key is set, the key will default - to 'value'. + to 'value'. Mutually exclusive with ConfigMapRef. It is recommended that the kubeconfig is self-contained, and the secret is regularly updated if credentials such as a cloud-access-token expire. Cloud specific `cmd-path` auth helpers will not function without adding binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. + Kubernetes resources. Supported only for the generic provider. properties: key: description: Key in the Secret, when not specified an implementation-specific @@ -6304,9 +5502,14 @@ spec: required: - name type: object - required: - - secretRef type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' patches: description: |- Strategic merge and JSON patches, defined as inline YAML objects, @@ -6726,7 +5929,7 @@ metadata: app.kubernetes.io/component: kustomize-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: kustomize-controller namespace: flux-system --- @@ -6737,7 +5940,7 @@ metadata: app.kubernetes.io/component: kustomize-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: kustomize-controller namespace: flux-system @@ -6753,10 +5956,14 @@ spec: prometheus.io/scrape: "true" labels: app: kustomize-controller + app.kubernetes.io/component: kustomize-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 spec: containers: - args: - - --events-addr=http://notification-controller.flux-system.svc.cluster.local./ + - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - --log-level=info - --log-encoding=json @@ -6766,17 +5973,12 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - - name: GOMAXPROCS - valueFrom: - resourceFieldRef: - containerName: manager - resource: limits.cpu - name: GOMEMLIMIT valueFrom: resourceFieldRef: containerName: manager resource: limits.memory - image: ghcr.io/fluxcd/kustomize-controller:v1.5.1 + image: ghcr.io/fluxcd/kustomize-controller:v1.7.3 imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -6828,12 +6030,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: helm-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: helmreleases.helm.toolkit.fluxcd.io spec: group: helm.toolkit.fluxcd.io @@ -7033,6 +6235,7 @@ spec: enum: - OCIRepository - HelmChart + - ExternalArtifact type: string name: description: Name of the referent. @@ -7050,22 +6253,47 @@ spec: - kind - name type: object + commonMetadata: + description: |- + CommonMetadata specifies the common labels and annotations that are + applied to all resources. Any existing label or annotation will be + overridden if its key matches a common one. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to be added to the object's metadata. + type: object + labels: + additionalProperties: + type: string + description: Labels to be added to the object's metadata. + type: object + type: object dependsOn: description: |- - DependsOn may contain a meta.NamespacedObjectReference slice with + DependsOn may contain a DependencyReference slice with references to HelmRelease resources that must be ready before this HelmRelease can be reconciled. items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. + description: DependencyReference defines a HelmRelease dependency + on another HelmRelease resource. properties: name: description: Name of the referent. type: string namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. + description: |- + Namespace of the referent, defaults to the namespace of the HelmRelease + resource object that contains the reference. + type: string + readyExpr: + description: |- + ReadyExpr is a CEL expression that can be used to assess the readiness + of a dependency. When specified, the built-in readiness check + is replaced by the logic defined in the CEL expression. + To make the CEL expression additive to the built-in readiness check, + the feature gate `AdditiveCELDependencyCheck` must be set to `true`. type: string required: - name @@ -7250,6 +6478,30 @@ spec: Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. type: boolean + strategy: + description: |- + Strategy defines the install strategy to use for this HelmRelease. + Defaults to 'RemediateOnFailure'. + properties: + name: + description: Name of the install strategy. + enum: + - RemediateOnFailure + - RetryOnFailure + type: string + retryInterval: + description: |- + RetryInterval is the interval at which to retry a failed install. + Can be used only when Name is set to RetryOnFailure. + Defaults to '5m'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: .retryInterval cannot be set when .name is 'RemediateOnFailure' + rule: '!has(self.retryInterval) || self.name != ''RemediateOnFailure''' timeout: description: |- Timeout is the time to wait for any individual Kubernetes operation (like @@ -7272,16 +6524,54 @@ spec: a controller level fallback for when HelmReleaseSpec.ServiceAccountName is empty. properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object secretRef: description: |- - SecretRef holds the name of a secret that contains a key with + SecretRef holds an optional name of a secret that contains a key with the kubeconfig file as the value. If no key is set, the key will default - to 'value'. + to 'value'. Mutually exclusive with ConfigMapRef. It is recommended that the kubeconfig is self-contained, and the secret is regularly updated if credentials such as a cloud-access-token expire. Cloud specific `cmd-path` auth helpers will not function without adding binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. + Kubernetes resources. Supported only for the generic provider. properties: key: description: Key in the Secret, when not specified an implementation-specific @@ -7293,9 +6583,14 @@ spec: required: - name type: object - required: - - secretRef type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' maxHistory: description: |- MaxHistory is the number of revisions saved by Helm for this HelmRelease. @@ -7674,6 +6969,30 @@ spec: - uninstall type: string type: object + strategy: + description: |- + Strategy defines the upgrade strategy to use for this HelmRelease. + Defaults to 'RemediateOnFailure'. + properties: + name: + description: Name of the upgrade strategy. + enum: + - RemediateOnFailure + - RetryOnFailure + type: string + retryInterval: + description: |- + RetryInterval is the interval at which to retry a failed upgrade. + Can be used only when Name is set to RetryOnFailure. + Defaults to '5m'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: .retryInterval can only be set when .name is 'RetryOnFailure' + rule: '!has(self.retryInterval) || self.name == ''RetryOnFailure''' timeout: description: |- Timeout is the time to wait for any individual Kubernetes operation (like @@ -7940,11 +7259,17 @@ spec: lastAttemptedReleaseAction: description: |- LastAttemptedReleaseAction is the last release action performed for this - HelmRelease. It is used to determine the active remediation strategy. + HelmRelease. It is used to determine the active retry or remediation + strategy. enum: - install - upgrade type: string + lastAttemptedReleaseActionDuration: + description: |- + LastAttemptedReleaseActionDuration is the duration of the last + release action performed for this HelmRelease. + type: string lastAttemptedRevision: description: |- LastAttemptedRevision is the Source revision of the last reconciliation @@ -7960,12 +7285,14 @@ spec: description: |- LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last reconciliation attempt. + Deprecated: Use LastAttemptedConfigDigest instead. type: string lastHandledForceAt: description: |- - LastHandledForceAt holds the value of the most recent force request - value, so a change of the annotation value can be detected. + LastHandledForceAt holds the value of the most recent + force request value, so a change of the annotation value + can be detected. type: string lastHandledReconcileAt: description: |- @@ -7981,8 +7308,14 @@ spec: lastReleaseRevision: description: |- LastReleaseRevision is the revision of the last successful Helm release. + Deprecated: Use History instead. type: integer + observedCommonMetadataDigest: + description: |- + ObservedCommonMetadataDigest is the digest for the common metadata of + the last successful reconciliation attempt. + type: string observedGeneration: description: ObservedGeneration is the last observed generation. format: int64 @@ -8011,1249 +7344,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v2beta1 HelmRelease is deprecated, upgrade to v2 - name: v2beta1 - schema: - openAPIV3Schema: - description: HelmRelease is the Schema for the helmreleases API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmReleaseSpec defines the desired state of a Helm release. - properties: - chart: - description: |- - Chart defines the template of the v1beta2.HelmChart that should be created - for this HelmRelease. - properties: - metadata: - description: ObjectMeta holds the template for metadata like labels - and annotations. - properties: - annotations: - additionalProperties: - type: string - description: |- - Annotations is an unstructured key value map stored with a resource that may be - set by external tools to store and retrieve arbitrary metadata. They are not - queryable and should be preserved when modifying objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - type: object - labels: - additionalProperties: - type: string - description: |- - Map of string keys and values that can be used to organize and categorize - (scope and select) objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - type: object - type: object - spec: - description: Spec holds the template for the v1beta2.HelmChartSpec - for this HelmRelease. - properties: - chart: - description: The name or path the Helm chart is available - at in the SourceRef. - type: string - interval: - description: |- - Interval at which to check the v1beta2.Source for updates. Defaults to - 'HelmReleaseSpec.Interval'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - reconcileStrategy: - default: ChartVersion - description: |- - Determines what enables the creation of a new artifact. Valid values are - ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The name and namespace of the v1beta2.Source - the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - valuesFile: - description: |- - Alternative values file to use as the default chart values, expected to - be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, - for backwards compatibility the file defined here is merged before the - ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: |- - Alternative list of values files to use as the chart values (values.yaml - is not included by default), expected to be a relative path in the SourceRef. - Values files are merged in the order of this list with the last file overriding - the first. Ignored when omitted. - items: - type: string - type: array - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - This field is only supported for OCI sources. - Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. - properties: - provider: - default: cosign - description: Provider specifies the technology used to - sign the OCI Helm chart. - enum: - - cosign - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - version: - default: '*' - description: |- - Version semver expression, ignored for charts from v1beta2.GitRepository and - v1beta2.Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - sourceRef - type: object - required: - - spec - type: object - chartRef: - description: |- - ChartRef holds a reference to a source controller resource containing the - Helm chart artifact. - - Note: this field is provisional to the v2 API, and not actively used - by v2beta1 HelmReleases. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - HelmChart - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the Kubernetes - resource object that contains the reference. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - dependsOn: - description: |- - DependsOn may contain a meta.NamespacedObjectReference slice with - references to HelmRelease resources that must be ready before this HelmRelease - can be reconciled. - items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - driftDetection: - description: |- - DriftDetection holds the configuration for detecting and handling - differences between the manifest in the Helm storage and the resources - currently existing in the cluster. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - properties: - ignore: - description: |- - Ignore contains a list of rules for specifying which changes to ignore - during diffing. - items: - description: |- - IgnoreRule defines a rule to selectively disregard specific changes during - the drift detection process. - properties: - paths: - description: |- - Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from - consideration in a Kubernetes object. - items: - type: string - type: array - target: - description: |- - Target is a selector for specifying Kubernetes objects to which this - rule applies. - If Target is not set, the Paths will be ignored for all Kubernetes - objects within the manifest of the Helm release. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - paths - type: object - type: array - mode: - description: |- - Mode defines how differences should be handled between the Helm manifest - and the manifest currently applied to the cluster. - If not explicitly set, it defaults to DiffModeDisabled. - enum: - - enabled - - warn - - disabled - type: string - type: object - install: - description: Install holds the configuration for Helm install actions - for this HelmRelease. - properties: - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Create` and if omitted - CRDs are installed but not updated. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are applied (installed) during Helm install action. - With this option users can opt-in to CRD replace existing CRDs on Helm - install actions, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - createNamespace: - description: |- - CreateNamespace tells the Helm install action to create the - HelmReleaseSpec.TargetNamespace if it does not exist yet. - On uninstall, the namespace will not be garbage collected. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm install action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm install action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - install has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - install has been performed. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm install - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an install action but fail. Defaults to - 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false'. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using an uninstall, is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - type: object - replace: - description: |- - Replace tells the Helm install action to re-use the 'ReleaseName', but only - if that name is a deleted release which remains in the history. - type: boolean - skipCRDs: - description: |- - SkipCRDs tells the Helm install action to not install any CRDs. By default, - CRDs are installed if not already present. - - Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm install action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - interval: - description: |- - Interval at which to reconcile the Helm release. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: |- - KubeConfig for reconciling the HelmRelease on a remote cluster. - When used in combination with HelmReleaseSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at the - target cluster. - If the --default-service-account flag is set, its value will be used as - a controller level fallback for when HelmReleaseSpec.ServiceAccountName - is empty. - properties: - secretRef: - description: |- - SecretRef holds the name of a secret that contains a key with - the kubeconfig file as the value. If no key is set, the key will default - to 'value'. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific - default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - required: - - secretRef - type: object - maxHistory: - description: |- - MaxHistory is the number of revisions saved by Helm for this HelmRelease. - Use '0' for an unlimited number of revisions; defaults to '10'. - type: integer - persistentClient: - description: |- - PersistentClient tells the controller to use a persistent Kubernetes - client for this release. When enabled, the client will be reused for the - duration of the reconciliation, instead of being created and destroyed - for each (step of a) Helm action. - - This can improve performance, but may cause issues with some Helm charts - that for example do create Custom Resource Definitions during installation - outside Helm's CRD lifecycle hooks, which are then not observed to be - available by e.g. post-install hooks. - - If not set, it defaults to true. - type: boolean - postRenderers: - description: |- - PostRenderers holds an array of Helm PostRenderers, which will be applied in order - of their definition. - items: - description: PostRenderer contains a Helm PostRenderer specification. - properties: - kustomize: - description: Kustomization to apply as PostRenderer. - properties: - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, - a new tag or digest, which will replace the original - name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace - the original name. - type: string - newTag: - description: NewTag is the value used to replace the - original tag. - type: string - required: - - name - type: object - type: array - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the - patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - patchesJson6902: - description: JSON 6902 patches, defined as inline YAML objects. - items: - description: JSON6902Patch contains a JSON6902 patch and - the target the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document - with an array of operation objects. - items: - description: |- - JSON6902 is a JSON6902 operation object. - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: |- - From contains a JSON-pointer value that references a location within the target document where the operation is - performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. - type: string - op: - description: |- - Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or - "test". - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: |- - Path contains the JSON-pointer value that references a location within the target document where the operation - is performed. The meaning of the value depends on the value of Op. - type: string - value: - description: |- - Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into - account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the - patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: Strategic merge patches, defined as inline - YAML objects. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - type: object - type: object - type: array - releaseName: - description: |- - ReleaseName used for the Helm release. Defaults to a composition of - '[TargetNamespace-]Name'. - maxLength: 53 - minLength: 1 - type: string - rollback: - description: Rollback holds the configuration for Helm rollback actions - for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - rollback action when it fails. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - rollback has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - rollback has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement - strategy. - type: boolean - recreate: - description: Recreate performs pod restarts for the resource if - applicable. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm rollback action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this HelmRelease. - type: string - storageNamespace: - description: |- - StorageNamespace used for the Helm storage. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - suspend: - description: |- - Suspend tells the controller to suspend reconciliation for this HelmRelease, - it does not apply to already started reconciliations. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace to target when performing operations for the HelmRelease. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - test: - description: Test holds the configuration for Helm test actions for - this HelmRelease. - properties: - enable: - description: |- - Enable enables Helm test actions for this HelmRelease after an Helm install - or upgrade action has been performed. - type: boolean - ignoreFailures: - description: |- - IgnoreFailures tells the controller to skip remediation when the Helm tests - are run but fail. Can be overwritten for tests run after install or upgrade - actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation during - the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like Jobs - for hooks) during the performance of a Helm action. Defaults to '5m0s'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - uninstall: - description: Uninstall holds the configuration for Helm uninstall - actions for this HelmRelease. - properties: - deletionPropagation: - default: background - description: |- - DeletionPropagation specifies the deletion propagation policy when - a Helm uninstall is performed. - enum: - - background - - foreground - - orphan - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables waiting for all the resources to be deleted after - a Helm uninstall is performed. - type: boolean - keepHistory: - description: |- - KeepHistory tells Helm to remove all associated resources and mark the - release as deleted, but retain the release history. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm uninstall action. Defaults - to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - upgrade: - description: Upgrade holds the configuration for Helm upgrade actions - for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - upgrade action when it fails. - type: boolean - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Skip` and if omitted - CRDs are neither installed nor upgraded. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are not applied during Helm upgrade action. With this - option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm upgrade action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm upgrade action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - upgrade has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - upgrade has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement - strategy. - type: boolean - preserveValues: - description: |- - PreserveValues will make Helm reuse the last release's values and merge in - overrides from 'Values'. Setting this flag makes the HelmRelease - non-declarative. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm upgrade - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an upgrade action but fail. - Defaults to 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using 'Strategy', is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - strategy: - description: Strategy to use for failure remediation. Defaults - to 'rollback'. - enum: - - rollback - - uninstall - type: string - type: object - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm upgrade action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - values: - description: Values holds the values for this Helm release. - x-kubernetes-preserve-unknown-fields: true - valuesFrom: - description: |- - ValuesFrom holds references to resources containing Helm values for this HelmRelease, - and information about how they should be merged. - items: - description: |- - ValuesReference contains a reference to a resource containing Helm values, - and optionally the key they can be found at. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', - 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - description: |- - Optional marks this ValuesReference as optional. When set, a not found error - for the values reference is ignored, but any ValuesKey, TargetPath or - transient error will still result in a reconciliation failure. - type: boolean - targetPath: - description: |- - TargetPath is the YAML dot notation path the value should be merged at. When - set, the ValuesKey is expected to be a single flat value. Defaults to 'None', - which results in the values getting merged at the root. - maxLength: 250 - pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ - type: string - valuesKey: - description: |- - ValuesKey is the data key where the values.yaml or a specific value can be - found at. Defaults to 'values.yaml'. - When set, must be a valid Data Key, consisting of alphanumeric characters, - '-', '_' or '.'. - maxLength: 253 - pattern: ^[\-._a-zA-Z0-9]+$ - type: string - required: - - kind - - name - type: object - type: array - required: - - chart - - interval - type: object - status: - default: - observedGeneration: -1 - description: HelmReleaseStatus defines the observed state of a HelmRelease. - properties: - conditions: - description: Conditions holds the conditions for the HelmRelease. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - failures: - description: |- - Failures is the reconciliation failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - helmChart: - description: |- - HelmChart is the namespaced name of the HelmChart resource created by - the controller for the HelmRelease. - type: string - history: - description: |- - History holds the history of Helm releases performed for this HelmRelease - up to the last successfully completed release. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - items: - description: |- - Snapshot captures a point-in-time copy of the status information for a Helm release, - as managed by the controller. - properties: - apiVersion: - description: |- - APIVersion is the API version of the Snapshot. - Provisional: when the calculation method of the Digest field is changed, - this field will be used to distinguish between the old and new methods. - type: string - appVersion: - description: AppVersion is the chart app version of the release - object in storage. - type: string - chartName: - description: ChartName is the chart name of the release object - in storage. - type: string - chartVersion: - description: |- - ChartVersion is the chart version of the release object in - storage. - type: string - configDigest: - description: |- - ConfigDigest is the checksum of the config (better known as - "values") of the release object in storage. - It has the format of `:`. - type: string - deleted: - description: Deleted is when the release was deleted. - format: date-time - type: string - digest: - description: |- - Digest is the checksum of the release object in storage. - It has the format of `:`. - type: string - firstDeployed: - description: FirstDeployed is when the release was first deployed. - format: date-time - type: string - lastDeployed: - description: LastDeployed is when the release was last deployed. - format: date-time - type: string - name: - description: Name is the name of the release. - type: string - namespace: - description: Namespace is the namespace the release is deployed - to. - type: string - ociDigest: - description: OCIDigest is the digest of the OCI artifact associated - with the release. - type: string - status: - description: Status is the current state of the release. - type: string - testHooks: - additionalProperties: - description: |- - TestHookStatus holds the status information for a test hook as observed - to be run by the controller. - properties: - lastCompleted: - description: LastCompleted is the time the test hook last - completed. - format: date-time - type: string - lastStarted: - description: LastStarted is the time the test hook was - last started. - format: date-time - type: string - phase: - description: Phase the test hook was observed to be in. - type: string - type: object - description: |- - TestHooks is the list of test hooks for the release as observed to be - run by the controller. - type: object - version: - description: Version is the version of the release object in - storage. - type: integer - required: - - chartName - - chartVersion - - configDigest - - digest - - firstDeployed - - lastDeployed - - name - - namespace - - status - - version - type: object - type: array - installFailures: - description: |- - InstallFailures is the install failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - lastAppliedRevision: - description: LastAppliedRevision is the revision of the last successfully - applied source. - type: string - lastAttemptedConfigDigest: - description: |- - LastAttemptedConfigDigest is the digest for the config (better known as - "values") of the last reconciliation attempt. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - type: string - lastAttemptedGeneration: - description: |- - LastAttemptedGeneration is the last generation the controller attempted - to reconcile. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - format: int64 - type: integer - lastAttemptedReleaseAction: - description: |- - LastAttemptedReleaseAction is the last release action performed for this - HelmRelease. It is used to determine the active remediation strategy. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastAttemptedValuesChecksum: - description: |- - LastAttemptedValuesChecksum is the SHA1 checksum of the values of the last - reconciliation attempt. - type: string - lastHandledForceAt: - description: |- - LastHandledForceAt holds the value of the most recent force request - value, so a change of the annotation value can be detected. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastHandledResetAt: - description: |- - LastHandledResetAt holds the value of the most recent reset request - value, so a change of the annotation value can be detected. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - type: string - lastReleaseRevision: - description: LastReleaseRevision is the revision of the last successful - Helm release. - type: integer - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - observedPostRenderersDigest: - description: |- - ObservedPostRenderersDigest is the digest for the post-renderers of - the last successful reconciliation attempt. - type: string - storageNamespace: - description: |- - StorageNamespace is the namespace of the Helm release storage for the - current release. - - Note: this field is provisional to the v2beta2 API, and not actively used - by v2beta1 HelmReleases. - type: string - upgradeFailures: - description: |- - UpgradeFailures is the upgrade failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -9682,16 +7772,54 @@ spec: a controller level fallback for when HelmReleaseSpec.ServiceAccountName is empty. properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object secretRef: description: |- - SecretRef holds the name of a secret that contains a key with + SecretRef holds an optional name of a secret that contains a key with the kubeconfig file as the value. If no key is set, the key will default - to 'value'. + to 'value'. Mutually exclusive with ConfigMapRef. It is recommended that the kubeconfig is self-contained, and the secret is regularly updated if credentials such as a cloud-access-token expire. Cloud specific `cmd-path` auth helpers will not function without adding binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. + Kubernetes resources. Supported only for the generic provider. properties: key: description: Key in the Secret, when not specified an implementation-specific @@ -9703,9 +7831,14 @@ spec: required: - name type: object - required: - - secretRef type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef + must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' maxHistory: description: |- MaxHistory is the number of revisions saved by Helm for this HelmRelease. @@ -9828,6 +7961,7 @@ spec: patchesJson6902: description: |- JSON 6902 patches, defined as inline YAML objects. + Deprecated: use Patches instead. items: description: JSON6902Patch contains a JSON6902 patch and @@ -9924,6 +8058,7 @@ spec: patchesStrategicMerge: description: |- Strategic merge patches, defined as inline YAML objects. + Deprecated: use Patches instead. items: x-kubernetes-preserve-unknown-fields: true @@ -10433,6 +8568,7 @@ spec: description: |- LastAppliedRevision is the revision of the last successfully applied source. + Deprecated: the revision can now be found in the History. type: string lastAttemptedConfigDigest: @@ -10469,6 +8605,7 @@ spec: description: |- LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last reconciliation attempt. + Deprecated: Use LastAttemptedConfigDigest instead. type: string lastHandledForceAt: @@ -10490,6 +8627,7 @@ spec: lastReleaseRevision: description: |- LastReleaseRevision is the revision of the last successful Helm release. + Deprecated: Use History instead. type: integer observedGeneration: @@ -10528,7 +8666,7 @@ metadata: app.kubernetes.io/component: helm-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: helm-controller namespace: flux-system --- @@ -10539,7 +8677,7 @@ metadata: app.kubernetes.io/component: helm-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: helm-controller namespace: flux-system @@ -10555,10 +8693,14 @@ spec: prometheus.io/scrape: "true" labels: app: helm-controller + app.kubernetes.io/component: helm-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 spec: containers: - args: - - --events-addr=http://notification-controller.flux-system.svc.cluster.local./ + - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - --log-level=info - --log-encoding=json @@ -10568,17 +8710,12 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - - name: GOMAXPROCS - valueFrom: - resourceFieldRef: - containerName: manager - resource: limits.cpu - name: GOMEMLIMIT valueFrom: resourceFieldRef: containerName: manager resource: limits.memory - image: ghcr.io/fluxcd/helm-controller:v1.2.0 + image: ghcr.io/fluxcd/helm-controller:v1.4.5 imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -10630,12 +8767,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: alerts.notification.toolkit.fluxcd.io spec: group: notification.toolkit.fluxcd.io @@ -10646,198 +8783,6 @@ spec: singular: alert scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta1 Alert is deprecated, upgrade to v1beta3 - name: v1beta1 - schema: - openAPIV3Schema: - description: Alert is the Schema for the alerts API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: AlertSpec defines an alerting rule for events involving a - list of objects - properties: - eventSeverity: - default: info - description: |- - Filter events based on severity, defaults to ('info'). - If set to 'info' no events will be filtered. - enum: - - info - - error - type: string - eventSources: - description: Filter events based on the involved objects. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - name: - description: Name of the referent - maxLength: 53 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 53 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - exclusionList: - description: A list of Golang regular expressions to be used for excluding - messages. - items: - type: string - type: array - providerRef: - description: Send events using this provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - summary: - description: Short description of the impact and affected cluster. - type: string - suspend: - description: |- - This flag tells the controller to suspend subsequent events dispatching. - Defaults to false. - type: boolean - required: - - eventSources - - providerRef - type: object - status: - default: - observedGeneration: -1 - description: AlertStatus defines the observed state of Alert - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -10934,12 +8879,12 @@ spec: description: |- Name of the referent If multiple resources are targeted `*` may be set. - maxLength: 53 + maxLength: 253 minLength: 1 type: string namespace: description: Namespace of the referent - maxLength: 53 + maxLength: 253 minLength: 1 type: string required: @@ -11151,12 +9096,12 @@ spec: description: |- Name of the referent If multiple resources are targeted `*` may be set. - maxLength: 53 + maxLength: 253 minLength: 1 type: string namespace: description: Namespace of the referent - maxLength: 53 + maxLength: 253 minLength: 1 type: string required: @@ -11212,12 +9157,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: providers.notification.toolkit.fluxcd.io spec: group: notification.toolkit.fluxcd.io @@ -11228,187 +9173,6 @@ spec: singular: provider scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta1 Provider is deprecated, upgrade to v1beta3 - name: v1beta1 - schema: - openAPIV3Schema: - description: Provider is the Schema for the providers API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ProviderSpec defines the desired state of Provider - properties: - address: - description: HTTP/S webhook address of this provider - pattern: ^(http|https):// - type: string - certSecretRef: - description: |- - CertSecretRef can be given the name of a secret containing - a PEM-encoded CA certificate (`caFile`) - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - channel: - description: Alert channel for this provider - type: string - proxy: - description: HTTP/S address of the proxy - pattern: ^(http|https):// - type: string - secretRef: - description: |- - Secret reference containing the provider webhook URL - using "address" as data key - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent events handling. - Defaults to false. - type: boolean - timeout: - description: Timeout for sending alerts to the provider. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - type: - description: Type of provider - enum: - - slack - - discord - - msteams - - rocket - - generic - - generic-hmac - - github - - gitlab - - bitbucket - - azuredevops - - googlechat - - webex - - sentry - - azureeventhub - - telegram - - lark - - matrix - - opsgenie - - alertmanager - - grafana - - githubdispatch - type: string - username: - description: Bot username for this provider - type: string - required: - - type - type: object - status: - default: - observedGeneration: -1 - description: ProviderStatus defines the observed state of Provider - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -11657,11 +9421,15 @@ spec: type: string certSecretRef: description: |- - CertSecretRef specifies the Secret containing - a PEM-encoded CA certificate (in the `ca.crt` key). + CertSecretRef specifies the Secret containing TLS certificates + for secure communication. - Note: Support for the `caFile` key has - been deprecated. + Supported configurations: + - CA-only: Server authentication (provide ca.crt only) + - mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key) + - Client-only: Client authentication with system CA (provide tls.crt + tls.key only) + + Legacy keys "caFile", "certFile", "keyFile" are supported but deprecated. Use "ca.crt", "tls.crt", "tls.key" instead. properties: name: description: Name of the referent. @@ -11674,6 +9442,14 @@ spec: should be posted. maxLength: 2048 type: string + commitStatusExpr: + description: |- + CommitStatusExpr is a CEL expression that evaluates to a string value + that can be used to generate a custom commit status message for use + with eligible Provider types (github, gitlab, gitea, bitbucketserver, + bitbucket, azuredevops). Supported variables are: event, provider, + and alert. + type: string interval: description: |- Interval at which to reconcile the Provider with its Secret references. @@ -11681,10 +9457,25 @@ spec: pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ type: string proxy: - description: Proxy the HTTP/S address of the proxy server. + description: |- + Proxy the HTTP/S address of the proxy server. + Deprecated: Use ProxySecretRef instead. Will be removed in v1. maxLength: 2048 pattern: ^(http|https)://.*$ type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + for this Provider. The Secret should contain an 'address' key with the + HTTP/S address of the proxy server. Optional 'username' and 'password' + keys can be provided for proxy authentication. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object secretRef: description: |- SecretRef specifies the Secret containing the authentication @@ -11696,6 +9487,24 @@ spec: required: - name type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to + authenticate with cloud provider services through workload identity. + This enables multi-tenant authentication without storing static credentials. + + Supported provider types: azureeventhub, azuredevops, googlepubsub + + When specified, the controller will: + 1. Create an OIDC token for the specified ServiceAccount + 2. Exchange it for cloud provider credentials via STS + 3. Use the obtained credentials for API authentication + + When unspecified, controller-level authentication is used (single-tenant). + + An error is thrown if static credentials are also defined in SecretRef. + This field requires the ObjectLevelWorkloadIdentity feature gate to be enabled. + type: string suspend: description: |- Suspend tells the controller to suspend subsequent @@ -11735,6 +9544,8 @@ spec: - pagerduty - datadog - nats + - zulip + - otel type: string username: description: Username specifies the name under which events are posted. @@ -11743,6 +9554,12 @@ spec: required: - type type: object + x-kubernetes-validations: + - message: spec.commitStatusExpr is only supported for the 'github', 'gitlab', + 'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types + rule: self.type == 'github' || self.type == 'gitlab' || self.type == + 'gitea' || self.type == 'bitbucketserver' || self.type == 'bitbucket' + || self.type == 'azuredevops' || !has(self.commitStatusExpr) type: object served: true storage: true @@ -11752,12 +9569,12 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 labels: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: receivers.notification.toolkit.fluxcd.io spec: group: notification.toolkit.fluxcd.io @@ -11863,12 +9680,12 @@ spec: description: |- Name of the referent If multiple resources are targeted `*` may be set. - maxLength: 53 + maxLength: 253 minLength: 1 type: string namespace: description: Namespace of the referent - maxLength: 53 + maxLength: 253 minLength: 1 type: string required: @@ -11999,211 +9816,6 @@ spec: storage: true subresources: status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta1 Receiver is deprecated, upgrade to v1 - name: v1beta1 - schema: - openAPIV3Schema: - description: Receiver is the Schema for the receivers API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ReceiverSpec defines the desired state of Receiver - properties: - events: - description: |- - A list of events to handle, - e.g. 'push' for GitHub or 'Push Hook' for GitLab. - items: - type: string - type: array - resources: - description: A list of resources to be notified about changes. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - name: - description: Name of the referent - maxLength: 53 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 53 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - secretRef: - description: |- - Secret reference containing the token used - to validate the payload authenticity - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent events handling. - Defaults to false. - type: boolean - type: - description: |- - Type of webhook sender, used to determine - the validation procedure and payload deserialization. - enum: - - generic - - generic-hmac - - github - - gitlab - - bitbucket - - harbor - - dockerhub - - quay - - gcr - - nexus - - acr - type: string - required: - - resources - - secretRef - - type - type: object - status: - default: - observedGeneration: -1 - description: ReceiverStatus defines the observed state of Receiver - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: |- - Generated webhook URL in the format - of '/hook/sha256sum(token+name+namespace)'. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -12290,12 +9902,12 @@ spec: description: |- Name of the referent If multiple resources are targeted `*` may be set. - maxLength: 53 + maxLength: 253 minLength: 1 type: string namespace: description: Namespace of the referent - maxLength: 53 + maxLength: 253 minLength: 1 type: string required: @@ -12439,7 +10051,7 @@ metadata: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 name: notification-controller namespace: flux-system --- @@ -12450,7 +10062,7 @@ metadata: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: notification-controller namespace: flux-system @@ -12471,7 +10083,7 @@ metadata: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: webhook-receiver namespace: flux-system @@ -12492,7 +10104,7 @@ metadata: app.kubernetes.io/component: notification-controller app.kubernetes.io/instance: flux-system app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.5.1 + app.kubernetes.io/version: v2.7.5 control-plane: controller name: notification-controller namespace: flux-system @@ -12508,6 +10120,10 @@ spec: prometheus.io/scrape: "true" labels: app: notification-controller + app.kubernetes.io/component: notification-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 spec: containers: - args: @@ -12520,17 +10136,12 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - - name: GOMAXPROCS - valueFrom: - resourceFieldRef: - containerName: manager - resource: limits.cpu - name: GOMEMLIMIT valueFrom: resourceFieldRef: containerName: manager resource: limits.memory - image: ghcr.io/fluxcd/notification-controller:v1.5.0 + image: ghcr.io/fluxcd/notification-controller:v1.7.5 imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -12582,3 +10193,2248 @@ spec: volumes: - emptyDir: {} name: temp +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/component: image-reflector-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + name: imagepolicies.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImagePolicy + listKind: ImagePolicyList + plural: imagepolicies + shortNames: + - imgpol + - imagepol + singular: imagepolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.latestRef.name + name: Image + type: string + - jsonPath: .status.latestRef.tag + name: Tag + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImagePolicy is the Schema for the imagepolicies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImagePolicySpec defines the parameters for calculating the + ImagePolicy. + properties: + digestReflectionPolicy: + default: Never + description: |- + DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. + + Never: The digest field will always be set to the empty string. + + IfNotPresent: The digest field will be set to the digest of the elected + latest image if the field is empty and the image did not change. + + Always: The digest field will always be set to the digest of the elected + latest image. + + Default: Never. + enum: + - Always + - IfNotPresent + - Never + type: string + filterTags: + description: |- + FilterTags enables filtering for only a subset of tags based on a set of + rules. If no rules are provided, all the tags from the repository will be + ordered and compared. + properties: + extract: + description: |- + Extract allows a capture group to be extracted from the specified regular + expression pattern, useful before tag evaluation. + type: string + pattern: + description: |- + Pattern specifies a regular expression pattern used to filter for image + tags. + type: string + type: object + imageRepositoryRef: + description: |- + ImageRepositoryRef points at the object specifying the image + being scanned + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + interval: + description: |- + Interval is the length of time to wait between + refreshing the digest of the latest tag when the + reflection policy is set to "Always". + + Defaults to 10m. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policy: + description: |- + Policy gives the particulars of the policy to be followed in + selecting the most recent image + properties: + alphabetical: + description: Alphabetical set of rules to use for alphabetical + ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the letters of the + alphabet as tags, ascending order would select Z, and descending order + would select A. + enum: + - asc + - desc + type: string + type: object + numerical: + description: Numerical set of rules to use for numerical ordering + of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the integer values + from 0 to 9 as tags, ascending order would select 9, and descending order + would select 0. + enum: + - asc + - desc + type: string + type: object + semver: + description: |- + SemVer gives a semantic version range to check against the tags + available. + properties: + range: + description: |- + Range gives a semver range for the image tag; the highest + version within the range that's a tag yields the latest image. + type: string + required: + - range + type: object + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent policy reconciliations. + It does not apply to already started reconciliations. Defaults to false. + type: boolean + required: + - imageRepositoryRef + - policy + type: object + x-kubernetes-validations: + - message: spec.interval is only accepted when spec.digestReflectionPolicy + is set to 'Always' + rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy + == ''Always'')' + - message: spec.interval must be set when spec.digestReflectionPolicy + is set to 'Always' + rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy + != 'Always' + status: + default: + observedGeneration: -1 + description: ImagePolicyStatus defines the observed state of ImagePolicy + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + latestRef: + description: |- + LatestRef gives the first in the list of images scanned by + the image repository, when filtered and ordered according + to the policy. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + observedGeneration: + format: int64 + type: integer + observedPreviousRef: + description: |- + ObservedPreviousRef is the observed previous LatestRef. It is used + to keep track of the previous and current images. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.latestRef.name + name: Image + type: string + - jsonPath: .status.latestRef.tag + name: Tag + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImagePolicy is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImagePolicy is the Schema for the imagepolicies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImagePolicySpec defines the parameters for calculating the + ImagePolicy. + properties: + digestReflectionPolicy: + default: Never + description: |- + DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. + + Never: The digest field will always be set to the empty string. + + IfNotPresent: The digest field will be set to the digest of the elected + latest image if the field is empty and the image did not change. + + Always: The digest field will always be set to the digest of the elected + latest image. + + Default: Never. + enum: + - Always + - IfNotPresent + - Never + type: string + filterTags: + description: |- + FilterTags enables filtering for only a subset of tags based on a set of + rules. If no rules are provided, all the tags from the repository will be + ordered and compared. + properties: + extract: + description: |- + Extract allows a capture group to be extracted from the specified regular + expression pattern, useful before tag evaluation. + type: string + pattern: + description: |- + Pattern specifies a regular expression pattern used to filter for image + tags. + type: string + type: object + imageRepositoryRef: + description: |- + ImageRepositoryRef points at the object specifying the image + being scanned + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + interval: + description: |- + Interval is the length of time to wait between + refreshing the digest of the latest tag when the + reflection policy is set to "Always". + + Defaults to 10m. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policy: + description: |- + Policy gives the particulars of the policy to be followed in + selecting the most recent image + properties: + alphabetical: + description: Alphabetical set of rules to use for alphabetical + ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the letters of the + alphabet as tags, ascending order would select Z, and descending order + would select A. + enum: + - asc + - desc + type: string + type: object + numerical: + description: Numerical set of rules to use for numerical ordering + of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the integer values + from 0 to 9 as tags, ascending order would select 9, and descending order + would select 0. + enum: + - asc + - desc + type: string + type: object + semver: + description: |- + SemVer gives a semantic version range to check against the tags + available. + properties: + range: + description: |- + Range gives a semver range for the image tag; the highest + version within the range that's a tag yields the latest image. + type: string + required: + - range + type: object + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent policy reconciliations. + It does not apply to already started reconciliations. Defaults to false. + type: boolean + required: + - imageRepositoryRef + - policy + type: object + x-kubernetes-validations: + - message: spec.interval is only accepted when spec.digestReflectionPolicy + is set to 'Always' + rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy + == ''Always'')' + - message: spec.interval must be set when spec.digestReflectionPolicy + is set to 'Always' + rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy + != 'Always' + status: + default: + observedGeneration: -1 + description: ImagePolicyStatus defines the observed state of ImagePolicy + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + latestRef: + description: |- + LatestRef gives the first in the list of images scanned by + the image repository, when filtered and ordered according + to the policy. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + observedGeneration: + format: int64 + type: integer + observedPreviousRef: + description: |- + ObservedPreviousRef is the observed previous LatestRef. It is used + to keep track of the previous and current images. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/component: image-reflector-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + name: imagerepositories.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImageRepository + listKind: ImageRepositoryList + plural: imagerepositories + shortNames: + - imgrepo + - imagerepo + singular: imagerepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.image + name: Image + type: string + - jsonPath: .status.lastScanResult.tagCount + name: Tags + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastScanResult.scanTime + name: Last scan + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImageRepository is the Schema for the imagerepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageRepositorySpec defines the parameters for scanning an image + repository, e.g., `fluxcd/flux`. + properties: + accessFrom: + description: |- + AccessFrom defines an ACL for allowing cross-namespace references + to the ImageRepository object based on the caller's namespace labels. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + Note: Support for the `caFile`, `certFile` and `keyFile` keys has + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + exclusionList: + default: + - ^.*\.sig$ + description: |- + ExclusionList is a list of regex strings used to exclude certain tags + from being stored in the database. + items: + type: string + maxItems: 25 + type: array + image: + description: Image is the name of the image repository + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container + registry. + type: boolean + interval: + description: |- + Interval is the length of time to wait between + scans of the image repository. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef can be given the name of a secret containing + credentials to use for the image registry. The secret should be + created with `kubectl create secret docker-registry`, or the + equivalent. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. + maxLength: 253 + type: string + suspend: + description: |- + This flag tells the controller to suspend subsequent image scans. + It does not apply to already started scans. Defaults to false. + type: boolean + timeout: + description: |- + Timeout for image scanning. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - image + - interval + type: object + status: + default: + observedGeneration: -1 + description: ImageRepositoryStatus defines the observed state of ImageRepository + properties: + canonicalImageName: + description: |- + CanonicalName is the name of the image repository with all the + implied bits made explicit; e.g., `docker.io/library/alpine` + rather than `alpine`. + type: string + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastScanResult: + description: LastScanResult contains the number of fetched tags. + properties: + latestTags: + description: |- + LatestTags is a small sample of the tags found in the last scan. + It's the first 10 tags when sorting all the tags in descending + alphabetical order. + items: + type: string + type: array + revision: + description: Revision is a stable hash of the scanned tags. + type: string + scanTime: + description: ScanTime is the time when the last scan was performed. + format: date-time + type: string + tagCount: + description: TagCount is the number of tags found in the last + scan. + type: integer + required: + - tagCount + type: object + observedExclusionList: + description: |- + ObservedExclusionList is a list of observed exclusion list. It reflects + the exclusion rules used for the observed scan result in + spec.lastScanResult. + items: + type: string + type: array + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.image + name: Image + type: string + - jsonPath: .status.lastScanResult.tagCount + name: Tags + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastScanResult.scanTime + name: Last scan + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImageRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImageRepository is the Schema for the imagerepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageRepositorySpec defines the parameters for scanning an image + repository, e.g., `fluxcd/flux`. + properties: + accessFrom: + description: |- + AccessFrom defines an ACL for allowing cross-namespace references + to the ImageRepository object based on the caller's namespace labels. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + Note: Support for the `caFile`, `certFile` and `keyFile` keys has + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + exclusionList: + default: + - ^.*\.sig$ + description: |- + ExclusionList is a list of regex strings used to exclude certain tags + from being stored in the database. + items: + type: string + maxItems: 25 + type: array + image: + description: Image is the name of the image repository + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container + registry. + type: boolean + interval: + description: |- + Interval is the length of time to wait between + scans of the image repository. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef can be given the name of a secret containing + credentials to use for the image registry. The secret should be + created with `kubectl create secret docker-registry`, or the + equivalent. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. + maxLength: 253 + type: string + suspend: + description: |- + This flag tells the controller to suspend subsequent image scans. + It does not apply to already started scans. Defaults to false. + type: boolean + timeout: + description: |- + Timeout for image scanning. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - image + - interval + type: object + status: + default: + observedGeneration: -1 + description: ImageRepositoryStatus defines the observed state of ImageRepository + properties: + canonicalImageName: + description: |- + CanonicalName is the name of the image repository with all the + implied bits made explicit; e.g., `docker.io/library/alpine` + rather than `alpine`. + type: string + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastScanResult: + description: LastScanResult contains the number of fetched tags. + properties: + latestTags: + description: |- + LatestTags is a small sample of the tags found in the last scan. + It's the first 10 tags when sorting all the tags in descending + alphabetical order. + items: + type: string + type: array + revision: + description: Revision is a stable hash of the scanned tags. + type: string + scanTime: + description: ScanTime is the time when the last scan was performed. + format: date-time + type: string + tagCount: + description: TagCount is the number of tags found in the last + scan. + type: integer + required: + - tagCount + type: object + observedExclusionList: + description: |- + ObservedExclusionList is a list of observed exclusion list. It reflects + the exclusion rules used for the observed scan result in + spec.lastScanResult. + items: + type: string + type: array + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: image-reflector-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + name: image-reflector-controller + namespace: flux-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: image-reflector-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + control-plane: controller + name: image-reflector-controller + namespace: flux-system +spec: + replicas: 1 + selector: + matchLabels: + app: image-reflector-controller + template: + metadata: + annotations: + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + labels: + app: image-reflector-controller + app.kubernetes.io/component: image-reflector-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + spec: + containers: + - args: + - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ + - --watch-all-namespaces=true + - --log-level=info + - --log-encoding=json + - --enable-leader-election + env: + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.memory + image: ghcr.io/fluxcd/image-reflector-controller:v1.0.4 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz + name: manager + ports: + - containerPort: 8080 + name: http-prom + protocol: TCP + - containerPort: 9440 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: temp + - mountPath: /data + name: data + nodeSelector: + kubernetes.io/os: linux + securityContext: + fsGroup: 1337 + serviceAccountName: image-reflector-controller + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: temp + - emptyDir: {} + name: data +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/component: image-automation-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + name: imageupdateautomations.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImageUpdateAutomation + listKind: ImageUpdateAutomationList + plural: imageupdateautomations + shortNames: + - iua + - imgupd + - imgauto + singular: imageupdateautomation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastAutomationRunTime + name: Last run + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImageUpdateAutomation is the Schema for the imageupdateautomations + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation + properties: + git: + description: |- + GitSpec contains all the git-specific definitions. This is + technically optional, but in practice mandatory until there are + other kinds of source allowed. + properties: + checkout: + description: |- + Checkout gives the parameters for cloning the git repository, + ready to make changes. If not present, the `spec.ref` field from the + referenced `GitRepository` or its default will be used. + properties: + ref: + description: |- + Reference gives a branch, tag or commit to clone from the Git + repository. + properties: + branch: + description: Branch to check out, defaults to 'master' + if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes + precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + required: + - ref + type: object + commit: + description: Commit specifies how to commit to the git repository. + properties: + author: + description: |- + Author gives the email and optionally the name to use as the + author of commits. + properties: + email: + description: Email gives the email to provide when making + a commit. + type: string + name: + description: Name gives the name to provide when making + a commit. + type: string + required: + - email + type: object + messageTemplate: + description: |- + MessageTemplate provides a template for the commit message, + into which will be interpolated the details of the change made. + Note: The `Updated` template field has been removed. Use `Changed` instead. + type: string + messageTemplateValues: + additionalProperties: + type: string + description: |- + MessageTemplateValues provides additional values to be available to the + templating rendering. + type: object + signingKey: + description: SigningKey provides the option to sign commits + with a GPG key + properties: + secretRef: + description: |- + SecretRef holds the name to a secret that contains a 'git.asc' key + corresponding to the ASCII Armored file containing the GPG signing + keypair as the value. It must be in the same namespace as the + ImageUpdateAutomation. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - author + type: object + push: + description: |- + Push specifies how and where to push commits made by the + automation. If missing, commits are pushed (back) to + `.spec.checkout.branch` or its default. + properties: + branch: + description: |- + Branch specifies that commits should be pushed to the branch + named. The branch is created using `.spec.checkout.branch` as the + starting point, if it doesn't already exist. + type: string + options: + additionalProperties: + type: string + description: |- + Options specifies the push options that are sent to the Git + server when performing a push operation. For details, see: + https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt + type: object + refspec: + description: |- + Refspec specifies the Git Refspec to use for a push operation. + If both Branch and Refspec are provided, then the commit is pushed + to the branch and also using the specified refspec. + For more details about Git Refspecs, see: + https://git-scm.com/book/en/v2/Git-Internals-The-Refspec + type: string + type: object + required: + - commit + type: object + interval: + description: |- + Interval gives an lower bound for how often the automation + run should be attempted. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policySelector: + description: |- + PolicySelector allows to filter applied policies based on labels. + By default includes all policies in namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + sourceRef: + description: |- + SourceRef refers to the resource giving access details + to a git repository. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + default: GitRepository + description: Kind of the referent. + enum: + - GitRepository + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace + of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to not run this automation, until + it is unset (or set to false). Defaults to false. + type: boolean + update: + default: + strategy: Setters + description: |- + Update gives the specification for how to update the files in + the repository. This can be left empty, to use the default + value. + properties: + path: + description: |- + Path to the directory containing the manifests to be updated. + Defaults to 'None', which translates to the root path + of the GitRepositoryRef. + type: string + strategy: + default: Setters + description: Strategy names the strategy to be used. + enum: + - Setters + type: string + type: object + required: + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: ImageUpdateAutomationStatus defines the observed state of + ImageUpdateAutomation + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAutomationRunTime: + description: |- + LastAutomationRunTime records the last time the controller ran + this automation through to completion (even if no updates were + made). + format: date-time + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastPushCommit: + description: |- + LastPushCommit records the SHA1 of the last commit made by the + controller, for this automation object + type: string + lastPushTime: + description: LastPushTime records the time of the last pushed change. + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + observedPolicies: + additionalProperties: + description: ImageRef represents an image reference. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + description: |- + ObservedPolicies is the list of observed ImagePolicies that were + considered by the ImageUpdateAutomation update process. + type: object + observedSourceRevision: + description: |- + ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` + ObservedSourceRevision is the last observed source revision. This can be + used to determine if the source has been updated since last observation. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastAutomationRunTime + name: Last run + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImageUpdateAutomation is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImageUpdateAutomation is the Schema for the imageupdateautomations + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation + properties: + git: + description: |- + GitSpec contains all the git-specific definitions. This is + technically optional, but in practice mandatory until there are + other kinds of source allowed. + properties: + checkout: + description: |- + Checkout gives the parameters for cloning the git repository, + ready to make changes. If not present, the `spec.ref` field from the + referenced `GitRepository` or its default will be used. + properties: + ref: + description: |- + Reference gives a branch, tag or commit to clone from the Git + repository. + properties: + branch: + description: Branch to check out, defaults to 'master' + if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes + precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + required: + - ref + type: object + commit: + description: Commit specifies how to commit to the git repository. + properties: + author: + description: |- + Author gives the email and optionally the name to use as the + author of commits. + properties: + email: + description: Email gives the email to provide when making + a commit. + type: string + name: + description: Name gives the name to provide when making + a commit. + type: string + required: + - email + type: object + messageTemplate: + description: |- + MessageTemplate provides a template for the commit message, + into which will be interpolated the details of the change made. + Note: The `Updated` template field has been removed. Use `Changed` instead. + type: string + messageTemplateValues: + additionalProperties: + type: string + description: |- + MessageTemplateValues provides additional values to be available to the + templating rendering. + type: object + signingKey: + description: SigningKey provides the option to sign commits + with a GPG key + properties: + secretRef: + description: |- + SecretRef holds the name to a secret that contains a 'git.asc' key + corresponding to the ASCII Armored file containing the GPG signing + keypair as the value. It must be in the same namespace as the + ImageUpdateAutomation. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - author + type: object + push: + description: |- + Push specifies how and where to push commits made by the + automation. If missing, commits are pushed (back) to + `.spec.checkout.branch` or its default. + properties: + branch: + description: |- + Branch specifies that commits should be pushed to the branch + named. The branch is created using `.spec.checkout.branch` as the + starting point, if it doesn't already exist. + type: string + options: + additionalProperties: + type: string + description: |- + Options specifies the push options that are sent to the Git + server when performing a push operation. For details, see: + https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt + type: object + refspec: + description: |- + Refspec specifies the Git Refspec to use for a push operation. + If both Branch and Refspec are provided, then the commit is pushed + to the branch and also using the specified refspec. + For more details about Git Refspecs, see: + https://git-scm.com/book/en/v2/Git-Internals-The-Refspec + type: string + type: object + required: + - commit + type: object + interval: + description: |- + Interval gives an lower bound for how often the automation + run should be attempted. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policySelector: + description: |- + PolicySelector allows to filter applied policies based on labels. + By default includes all policies in namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + sourceRef: + description: |- + SourceRef refers to the resource giving access details + to a git repository. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + default: GitRepository + description: Kind of the referent. + enum: + - GitRepository + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace + of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to not run this automation, until + it is unset (or set to false). Defaults to false. + type: boolean + update: + default: + strategy: Setters + description: |- + Update gives the specification for how to update the files in + the repository. This can be left empty, to use the default + value. + properties: + path: + description: |- + Path to the directory containing the manifests to be updated. + Defaults to 'None', which translates to the root path + of the GitRepositoryRef. + type: string + strategy: + default: Setters + description: Strategy names the strategy to be used. + enum: + - Setters + type: string + type: object + required: + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: ImageUpdateAutomationStatus defines the observed state of + ImageUpdateAutomation + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAutomationRunTime: + description: |- + LastAutomationRunTime records the last time the controller ran + this automation through to completion (even if no updates were + made). + format: date-time + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastPushCommit: + description: |- + LastPushCommit records the SHA1 of the last commit made by the + controller, for this automation object + type: string + lastPushTime: + description: LastPushTime records the time of the last pushed change. + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + observedPolicies: + additionalProperties: + description: ImageRef represents an image reference. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + description: |- + ObservedPolicies is the list of observed ImagePolicies that were + considered by the ImageUpdateAutomation update process. + type: object + observedSourceRevision: + description: |- + ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` + ObservedSourceRevision is the last observed source revision. This can be + used to determine if the source has been updated since last observation. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: image-automation-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + name: image-automation-controller + namespace: flux-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: image-automation-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + control-plane: controller + name: image-automation-controller + namespace: flux-system +spec: + replicas: 1 + selector: + matchLabels: + app: image-automation-controller + template: + metadata: + annotations: + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + labels: + app: image-automation-controller + app.kubernetes.io/component: image-automation-controller + app.kubernetes.io/instance: flux-system + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.5 + spec: + containers: + - args: + - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ + - --watch-all-namespaces=true + - --log-level=info + - --log-encoding=json + - --enable-leader-election + env: + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.memory + image: ghcr.io/fluxcd/image-automation-controller:v1.0.4 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz + name: manager + ports: + - containerPort: 8080 + name: http-prom + protocol: TCP + - containerPort: 9440 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: temp + nodeSelector: + kubernetes.io/os: linux + securityContext: + fsGroup: 1337 + serviceAccountName: image-automation-controller + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: temp From 61d9f05fef1e66af5113966adee3915859cbfe26 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 18 Dec 2025 00:39:39 -0300 Subject: [PATCH 90/93] flux: update pegasus image automation api --- .../applications/pegasus/image-automation.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml b/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml index 27c50b8..36feabd 100644 --- a/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml @@ -1,5 +1,5 @@ # clusters/atlas/flux-system/applications/pegasus/image-automation.yaml -apiVersion: image.toolkit.fluxcd.io/v1beta1 +apiVersion: image.toolkit.fluxcd.io/v1beta2 kind: ImageUpdateAutomation metadata: name: pegasus @@ -9,12 +9,18 @@ spec: sourceRef: kind: GitRepository name: flux-system + namespace: flux-system git: + checkout: + ref: + branch: feature/ci-gitops commit: author: email: ops@bstein.dev name: flux-bot messageTemplate: "chore(pegasus): update image to {{range .Updated.Images}}{{.}}{{end}}" + push: + branch: feature/ci-gitops update: strategy: Setters - path: ./services/pegasus + path: services/pegasus From 5d4a0814c1950dcd926b7238af7fb090286ed0f1 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 18 Dec 2025 00:40:55 -0300 Subject: [PATCH 91/93] flux: enable debug logging for image automation --- clusters/atlas/flux-system/gotk-components.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clusters/atlas/flux-system/gotk-components.yaml b/clusters/atlas/flux-system/gotk-components.yaml index bb53b3d..9357d1e 100644 --- a/clusters/atlas/flux-system/gotk-components.yaml +++ b/clusters/atlas/flux-system/gotk-components.yaml @@ -5965,7 +5965,7 @@ spec: - args: - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election env: From c04a38fac521eeac95e9e57a835b95dfc612f801 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 18 Dec 2025 00:44:11 -0300 Subject: [PATCH 92/93] flux: enable debug logging for controllers --- clusters/atlas/flux-system/gotk-components.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clusters/atlas/flux-system/gotk-components.yaml b/clusters/atlas/flux-system/gotk-components.yaml index 9357d1e..6501f93 100644 --- a/clusters/atlas/flux-system/gotk-components.yaml +++ b/clusters/atlas/flux-system/gotk-components.yaml @@ -8702,7 +8702,7 @@ spec: - args: - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election env: @@ -10128,7 +10128,7 @@ spec: containers: - args: - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election env: @@ -11441,7 +11441,7 @@ spec: - args: - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election env: @@ -12379,7 +12379,7 @@ spec: - args: - --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./ - --watch-all-namespaces=true - - --log-level=info + - --log-level=debug - --log-encoding=json - --enable-leader-election env: From 5f300c47a5fc110663484b1092d6ae9db3432368 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 18 Dec 2025 00:46:25 -0300 Subject: [PATCH 93/93] flux: bump image automation api to v1 --- .../flux-system/applications/ci-demo/image-automation.yaml | 2 +- .../atlas/flux-system/applications/harbor/image-automation.yaml | 2 +- .../flux-system/applications/pegasus/image-automation.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml index 8fa4282..b86869b 100644 --- a/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml @@ -1,5 +1,5 @@ # clusters/atlas/flux-system/applications/ci-demo/image-automation.yaml -apiVersion: image.toolkit.fluxcd.io/v1beta2 +apiVersion: image.toolkit.fluxcd.io/v1 kind: ImageUpdateAutomation metadata: name: ci-demo diff --git a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml index e010ea8..0189a60 100644 --- a/clusters/atlas/flux-system/applications/harbor/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/harbor/image-automation.yaml @@ -1,5 +1,5 @@ # clusters/atlas/flux-system/applications/harbor/image-automation.yaml -apiVersion: image.toolkit.fluxcd.io/v1beta2 +apiVersion: image.toolkit.fluxcd.io/v1 kind: ImageUpdateAutomation metadata: name: harbor diff --git a/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml b/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml index 36feabd..eca4a6c 100644 --- a/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml +++ b/clusters/atlas/flux-system/applications/pegasus/image-automation.yaml @@ -1,5 +1,5 @@ # clusters/atlas/flux-system/applications/pegasus/image-automation.yaml -apiVersion: image.toolkit.fluxcd.io/v1beta2 +apiVersion: image.toolkit.fluxcd.io/v1 kind: ImageUpdateAutomation metadata: name: pegasus