#!/bin/bash # Methods library for buildkit_lite — sourced by run-*.sh scripts generated from manifest_plan. # SCRIPT_DIR must be set by the caller before sourcing. [ -f "${SCRIPT_DIR}/env-buildkit_lite" ] && source "${SCRIPT_DIR}/env-buildkit_lite" [ -f "${SCRIPT_DIR}/_credentials.env" ] && source "${SCRIPT_DIR}/_credentials.env" BUILDKIT_NAMESPACE="${BUILDKIT_NAMESPACE:-build-system}" BUILDKIT_DEPLOYMENT="${BUILDKIT_DEPLOYMENT:-buildkitd}" BUILDKIT_REGISTRY_SECRET="${BUILDKIT_REGISTRY_SECRET:-zot-credentials}" _kubectl() { if command -v kubectl >/dev/null 2>&1; then command kubectl "$@" elif command -v k0s >/dev/null 2>&1; then k0s kubectl "$@" else echo "ERROR: no kubectl or k0s binary found" >&2; exit 127 fi } # ── Manifest plan helpers ───────────────────────────────────────────────────── # Treat blank/whitespace-only manifests as no-ops — used by conditional templates # (e.g. service-vpn.yaml renders empty when vpn_service.enabled = false). _manifest_is_blank() { local path="$1" [ -f "$path" ] || return 0 ! grep -q '[^[:space:]]' "$path" } _plan_apply() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] $file.yaml not in manifests/"; return 0; } if _manifest_is_blank "$path"; then echo " [skip] $file.yaml is empty (template inactive)" return 0 fi _kubectl apply -f "$path" } _plan_apply_skip() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] $file.yaml not in manifests/"; return 0; } if _manifest_is_blank "$path"; then echo " [skip] $file.yaml is empty (template inactive)" return 0 fi if _kubectl get -f "$path" >/dev/null 2>&1; then echo " [skip] $file already exists in cluster" return 0 fi _kubectl apply -f "$path" } _plan_rollout_restart() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] $file.yaml not in manifests/"; return 0; } if _manifest_is_blank "$path"; then echo " [skip] $file.yaml is empty (template inactive)" return 0 fi _kubectl rollout restart -f "$path" } _plan_delete() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] $file.yaml not in manifests/"; return 0; } if _manifest_is_blank "$path"; then echo " [skip] $file.yaml is empty (template inactive)" return 0 fi _kubectl delete -f "$path" --ignore-not-found } # ── Component methods ───────────────────────────────────────────────────────── _method_create-credentials() { if _kubectl get secret "$BUILDKIT_REGISTRY_SECRET" \ --namespace "$BUILDKIT_NAMESPACE" >/dev/null 2>&1; then echo " [skip] secret $BUILDKIT_REGISTRY_SECRET already in $BUILDKIT_NAMESPACE" return 0 fi # Prefer credentials injected from SOPS at deploy time (REGISTRY_HOST/USER/PASS). if [ -n "${REGISTRY_USER:-}" ] && [ -n "${REGISTRY_PASS:-}" ]; then local host="${REGISTRY_HOST:-daoreg.librecloud.online}" local auth auth=$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASS" | base64 | tr -d '\n') local config config=$(printf '{"auths":{"%s":{"auth":"%s"}}}' "$host" "$auth") _kubectl create secret generic "$BUILDKIT_REGISTRY_SECRET" \ --namespace "$BUILDKIT_NAMESPACE" \ --from-literal=config.json="$config" \ --dry-run=client -o yaml | _kubectl apply -f - echo " Created $BUILDKIT_REGISTRY_SECRET in $BUILDKIT_NAMESPACE from injected credentials" return 0 fi # Fallback: copy pre-existing docker config secret from registry namespace. if _kubectl get secret "$BUILDKIT_REGISTRY_SECRET" \ --namespace registry >/dev/null 2>&1; then _kubectl get secret "$BUILDKIT_REGISTRY_SECRET" \ --namespace registry -o yaml \ | sed "s/namespace: registry/namespace: $BUILDKIT_NAMESPACE/" \ | _kubectl apply -f - echo " Copied $BUILDKIT_REGISTRY_SECRET from registry → $BUILDKIT_NAMESPACE" return 0 fi echo " WARNING: no registry credentials available — buildkitd will have no registry auth" >&2 } _method_wait-ready() { local timeout="${1:-120}" _kubectl rollout status deployment/"$BUILDKIT_DEPLOYMENT" \ --namespace "$BUILDKIT_NAMESPACE" \ --timeout="${timeout}s" } # ── mTLS client-cert minting (cert-manager-driven) ──────────────────────────── # # Source of truth: runtime-manifests/mtls-client-certificate.yaml.tmpl # This helper renders that template with envsubst → kubectl apply. # The resulting Certificate CR persists in cluster etcd (cert-manager rotates it # automatically); subsequent issue calls for the same name are idempotent. # # Preferred path for known consumers: declare a Certificate manifest in the # consumer's own catalog (e.g. lian_build/cluster/manifests/buildkit-client-cert.yaml.j2). # This helper is for ad-hoc operator clients (humans, debug sessions) — the # Certificate it creates still survives redeploys because it lives in etcd. _method_issue-client-cert() { local client_name="${1:?client name required}" local output_dir="${2:-./client-${client_name}}" local duration="${3:-${BUILDKIT_CLIENT_DURATION:-2160h}}" local renew="${4:-${BUILDKIT_CLIENT_RENEW:-360h}}" local ca_issuer_name="${BUILDKIT_MTLS_CA_ISSUER:-buildkit-ca-issuer}" local secret_name="buildkit-client-${client_name}-tls" local tmpl="${SCRIPT_DIR}/runtime-manifests/mtls-client-certificate.yaml.tmpl" if [ ! -f "$tmpl" ]; then echo "ERROR: client cert template missing: $tmpl" >&2 return 1 fi if ! _kubectl get issuer "$ca_issuer_name" --namespace "$BUILDKIT_NAMESPACE" >/dev/null 2>&1; then echo "ERROR: Issuer $ca_issuer_name not found in $BUILDKIT_NAMESPACE — enable mtls.cert_manager first" >&2 return 1 fi BUILDKIT_CLIENT_NAME="$client_name" \ BUILDKIT_NAMESPACE="$BUILDKIT_NAMESPACE" \ BUILDKIT_MTLS_CA_ISSUER="$ca_issuer_name" \ BUILDKIT_CLIENT_DURATION="$duration" \ BUILDKIT_CLIENT_RENEW="$renew" \ envsubst < "$tmpl" | _kubectl apply -f - _method_export-client-cert "$client_name" "$output_dir" } # Reads an existing buildkit-client--tls secret and dumps the contents # (ca.crt, tls.crt, tls.key) to $output_dir for buildctl consumption. # Waits up to 60s for cert-manager to populate the secret on first call. _method_export-client-cert() { local client_name="${1:?client name required}" local output_dir="${2:-./client-${client_name}}" local secret_name="buildkit-client-${client_name}-tls" mkdir -p "$output_dir" local attempts=30 while [ "$attempts" -gt 0 ]; do if _kubectl get secret "$secret_name" --namespace "$BUILDKIT_NAMESPACE" >/dev/null 2>&1; then break fi sleep 2 attempts=$((attempts - 1)) done if [ "$attempts" -eq 0 ]; then echo "ERROR: secret $secret_name not found in $BUILDKIT_NAMESPACE — issue the Certificate first" >&2 return 1 fi _kubectl get secret "$secret_name" --namespace "$BUILDKIT_NAMESPACE" \ -o jsonpath='{.data.ca\.crt}' | base64 -d > "$output_dir/ca.crt" _kubectl get secret "$secret_name" --namespace "$BUILDKIT_NAMESPACE" \ -o jsonpath='{.data.tls\.crt}' | base64 -d > "$output_dir/tls.crt" _kubectl get secret "$secret_name" --namespace "$BUILDKIT_NAMESPACE" \ -o jsonpath='{.data.tls\.key}' | base64 -d > "$output_dir/tls.key" chmod 0600 "$output_dir/tls.key" echo " Client cert for '${client_name}' exported to $output_dir" echo " Use with: buildctl --tlscacert=$output_dir/ca.crt --tlscert=$output_dir/tls.crt --tlskey=$output_dir/tls.key --addr tcp://build.librecloud.online:31234 ..." } # Deletes the Certificate CR (and its Secret). For Certificates declared in # a consumer's catalog (e.g. lian_build), prefer removing the manifest from the # catalog and re-running the consumer's install — this is for ad-hoc clients only. _method_revoke-client-cert() { local client_name="${1:?client name required}" local cert_name="buildkit-client-${client_name}" local secret_name="${cert_name}-tls" _kubectl delete certificate "$cert_name" --namespace "$BUILDKIT_NAMESPACE" --ignore-not-found _kubectl delete secret "$secret_name" --namespace "$BUILDKIT_NAMESPACE" --ignore-not-found echo " Revoked client cert for '${client_name}'" }