#!/bin/bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # shellcheck source=/dev/null [ -f "${SCRIPT_DIR}/env-zot" ] && source "${SCRIPT_DIR}/env-zot" [ -f "${SCRIPT_DIR}/_credentials.env" ] && source "${SCRIPT_DIR}/_credentials.env" # env-zot rendered from zot.ncl by the orchestrator (run_taskserv). [ -f "${SCRIPT_DIR}/env-zot" ] && source "${SCRIPT_DIR}/env-zot" ZOT_NAMESPACE="${ZOT_NAMESPACE:-registry}" ZOT_IMAGE="${ZOT_IMAGE:-ghcr.io/project-zot/zot-linux-arm64:v2.1.18}" ZOT_PORT="${ZOT_PORT:-5000}" ZOT_DATA_DIR="${ZOT_DATA_DIR:-/var/lib/data/zot}" ZOT_STORAGE_CLASS="${ZOT_STORAGE_CLASS:-longhorn-retain}" ZOT_STORAGE_SIZE="${ZOT_STORAGE_SIZE:-5Gi}" ZOT_NODE_PORT="${ZOT_NODE_PORT:-30500}" # No hardcoded bucket fallback: absent/empty ZOT_S3_BUCKET means storage_backend.kind # = "local" (see zot.ncl) — a hardcoded default here would silently re-enable S3 for # every deploy that declares local storage, since env-zot sets this var to "" (not # unset) and `:-` treats empty the same as unset. That's exactly what happened # 2026-07-06: zot.ncl had said kind=local for a while, but this fallback kept # regenerating an S3 storageDriver config anyway, invisibly, until a routine # redeploy restarted the pod and it tried (and failed) to auth against a bucket # nothing had pointed at in months. ZOT_S3_BUCKET="${ZOT_S3_BUCKET:-}" ZOT_S3_REGION="${ZOT_S3_REGION:-eu-central-1}" ZOT_S3_ENDPOINT="${ZOT_S3_ENDPOINT:-}" ZOT_S3_PATH_STYLE="${ZOT_S3_PATH_STYLE:-true}" ZOT_S3_ACCESS_KEY="${ZOT_S3_ACCESS_KEY:-}" ZOT_S3_SECRET_KEY="${ZOT_S3_SECRET_KEY:-}" # htpasswd content — when set, creates zot-auth secret and enables auth in config ZOT_HTPASSWD="${ZOT_HTPASSWD:-}" # TLS: name of an existing k8s secret with tls.crt + tls.key — when set, enables TLS in config ZOT_TLS_SECRET="${ZOT_TLS_SECRET:-}" # Gateway API — two modes: # terminate (default) Gateway terminates TLS with ZOT_GATEWAY_CERT_SECRET in ZOT_TLS_NAMESPACE # (workload ns by default; ReferenceGrant authorizes the cross-ns read when # ZOT_TLS_NAMESPACE != ZOT_GATEWAY_NAMESPACE). # Zot runs plain HTTP. HTTPRoute routes HTTPS traffic. Cert rotation does not restart Zot. # Good when: you want path-level routing, WAF, rate limiting, or selective public exposure. # passthrough Gateway routes raw TLS by SNI. Zot terminates TLS via ZOT_TLS_SECRET. # Requires TLSRoute CRD (experimental channel): # kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.1/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml # Good when: e2e encryption is required or the gateway must remain opaque. ZOT_GATEWAY_ENABLED="${ZOT_GATEWAY_ENABLED:-false}" ZOT_GATEWAY_MODE="${ZOT_GATEWAY_MODE:-terminate}" ZOT_GATEWAY_IP="${ZOT_GATEWAY_IP:-}" # When set, the gateway shares the given IP with other gateways via Cilium LB IP sharing. # A dedicated CiliumLoadBalancerIPPool is NOT created; the existing pool for that IP is reused. # Use the infrastructure.annotations form (io.cilium/lb-ipam-ips + lbipam.cilium.io/sharing-key). ZOT_GATEWAY_SHARING_KEY="${ZOT_GATEWAY_SHARING_KEY:-}" ZOT_GATEWAY_CERT_SECRET="${ZOT_GATEWAY_CERT_SECRET:-}" ZOT_GATEWAY_NAMESPACE="${ZOT_GATEWAY_NAMESPACE:-kube-system}" # TLS resources (Certificate + Secret) live with the workload by default. # When ZOT_TLS_NAMESPACE != ZOT_GATEWAY_NAMESPACE, a ReferenceGrant is emitted # in ZOT_TLS_NAMESPACE granting Gateways in ZOT_GATEWAY_NAMESPACE permission to # read the secret cross-namespace (Gateway API contract). ZOT_TLS_NAMESPACE="${ZOT_TLS_NAMESPACE:-${ZOT_NAMESPACE}}" # Cloudflare DNS-01 token — required when cert-manager is used. Created as Secret # in cert-manager namespace, referenced by ClusterIssuer.spec.acme.solvers[0].dns01. ZOT_CF_DNS_TOKEN="${ZOT_CF_DNS_TOKEN:-}" ZOT_CF_DNS_SECRET_NAME="${ZOT_CF_DNS_SECRET_NAME:-dns-cloudflare}" ZOT_GATEWAY_HOSTNAME_UI="${ZOT_GATEWAY_HOSTNAME_UI:-reg.librecloud.online}" ZOT_GATEWAY_HOSTNAME_REG="${ZOT_GATEWAY_HOSTNAME_REG:-termas.librecloud.online}" ZOT_COSIGN_PUBKEY_PATH="${ZOT_COSIGN_PUBKEY_PATH:-}" # Path to a JSON file produced by the workspace's deploy recipe (nickel export of multi_tenant.policies). # Schema: { "/**": { read: [...], write: [...], anonymous_read: bool } } # When set, install-zot reads per-namespace ACL from this file instead of relying on defaults. ZOT_REPOSITORIES_FILE="${ZOT_REPOSITORIES_FILE:-}" ZOT_ADMIN_USER="${ZOT_ADMIN_USER:-regadm}" ZOT_ADMIN_PASS="${ZOT_ADMIN_PASS:-}" # Public gateway to patch with the registry listener. # When set, an HTTPS listener for ZOT_GATEWAY_HOSTNAME_REG is added to this shared gateway # (following the lbipam sharing pattern used by odoo/jesusperez tenants) and a separate # zot-public HTTPRoute is created so external clients reach Zot without VPN access. # Leave empty when registry exposure is private-only. ZOT_PUBLIC_GATEWAY_NAME="${ZOT_PUBLIC_GATEWAY_NAME:-}" ZOT_PUBLIC_GATEWAY_NS="${ZOT_PUBLIC_GATEWAY_NS:-kube-system}" # Upstream sync extension — optional. # When ZOT_SYNC_UPSTREAM is set, a sync.extensions block is injected into config.json # that mirrors the given prefixes from the upstream registry on first request. # ZOT_SYNC_UPSTREAM_PREFIXES is a comma-separated list of namespace prefixes (no wildcards). # Default prefixes: domains/**,modes/** — content-addressed OCI artifacts for ecosystem integration. ZOT_SYNC_UPSTREAM="${ZOT_SYNC_UPSTREAM:-}" ZOT_SYNC_UPSTREAM_TLS="${ZOT_SYNC_UPSTREAM_TLS:-true}" ZOT_SYNC_UPSTREAM_PREFIXES="${ZOT_SYNC_UPSTREAM_PREFIXES:-domains/**,modes/**}" ZOT_SYNC_ON_DEMAND="${ZOT_SYNC_ON_DEMAND:-true}" # Credentials for authenticating to the upstream sync registry. # Sourced from _credentials.env (SOPS-decrypted). Leave empty for anonymous upstreams. ZOT_SYNC_USERNAME="${ZOT_SYNC_USERNAME:-}" ZOT_SYNC_PASSWORD="${ZOT_SYNC_PASSWORD:-}" KUBECONFIG="${KUBECONFIG:-/etc/kubernetes/admin.conf}" export KUBECONFIG CMD_TSK="${CMD_TSK:-${1:-install}}" # shellcheck source=zot-lib.sh source "${SCRIPT_DIR}/zot-lib.sh" case "$CMD_TSK" in install) _do_install ;; update) _do_install ;; reinstall) _do_delete; _do_install ;; delete) _do_delete ;; health) _do_health ;; *) echo "ERROR: unknown CMD_TSK='$CMD_TSK'. Valid: install|update|reinstall|delete|health" >&2 exit 1 ;; esac