#!/bin/bash # Methods library for cert_manager — sourced by run-*.sh and install-cert_manager.sh. # SCRIPT_DIR must be set by the caller before sourcing. _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 } _resolve_kubeconfig() { if [ -n "${KUBECONFIG:-}" ] && [ -f "$KUBECONFIG" ]; then return fi for candidate in \ /var/lib/k0s/pki/admin.conf \ /etc/kubernetes/admin.conf \ /root/.kube/config; do if [ -f "$candidate" ]; then export KUBECONFIG="$candidate" return fi done if command -v k0s >/dev/null 2>&1; then local tmp tmp="$(mktemp)" k0s kubeconfig admin > "$tmp" 2>/dev/null && export KUBECONFIG="$tmp" && return rm -f "$tmp" fi echo "ERROR: kubeconfig not found — set KUBECONFIG explicitly" >&2; exit 1 } _require_env() { local var="$1" if [ -z "${!var:-}" ]; then echo "ERROR: required variable $var is not set" >&2; exit 1 fi } _plan_apply() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] ${file}.yaml not in manifests/"; return 0; } local content content=$(sed '/^\s*#/d; /^\s*$/d' "$path" | tr -d '[:space:]') [ -n "$content" ] || { echo " [skip] ${file}.yaml has no content"; return 0; } _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; } [ -s "$path" ] || { echo " [skip] ${file}.yaml is empty"; return 0; } 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_delete() { local file="$1" local path="$SCRIPT_DIR/manifests/${file}.yaml" [ -f "$path" ] || { echo " [skip] ${file}.yaml not in manifests/"; return 0; } [ -s "$path" ] || { echo " [skip] ${file}.yaml is empty"; return 0; } _kubectl delete -f "$path" --ignore-not-found } _method_apply_bundle() { local version="${CERT_MANAGER_VERSION:-v1.19.5}" local url="https://github.com/cert-manager/cert-manager/releases/download/${version}/cert-manager.yaml" local bundle="${SCRIPT_DIR}/cert-manager.yaml" if [ ! -f "$bundle" ]; then echo " [apply_bundle] downloading cert-manager ${version}" curl -fsSL "$url" -o "$bundle" else echo " [apply_bundle] bundle already present, skipping download" fi _kubectl apply --server-side -f "$bundle" } # Patch the cert-manager controller deployment to bypass cluster CoreDNS during # DNS-01 challenge verification. Without this, _acme-challenge.* lookups can hit # split-horizon zone blocks that lack `fallthrough`, returning SERVFAIL and # stalling the challenge indefinitely. _method_patch_recursive_dns() { local ns="${CERT_MANAGER_NAMESPACE:-cert-manager}" local ns_csv="${CERT_MANAGER_DNS01_RECURSIVE_NS:-1.1.1.1:53,8.8.8.8:53}" local only="${CERT_MANAGER_DNS01_RECURSIVE_ONLY:-true}" [ -n "$ns_csv" ] || { echo " [patch_recursive_dns] no nameservers configured, skipping"; return 0; } local args="[\"--dns01-recursive-nameservers=${ns_csv}\"" if [ "$only" = "true" ] || [ "$only" = "1" ]; then args="${args},\"--dns01-recursive-nameservers-only\"" fi args="${args}]" echo " [patch_recursive_dns] dns01 recursive nameservers: ${ns_csv} (only=${only})" local current current=$(_kubectl get deployment cert-manager -n "$ns" -o jsonpath='{.spec.template.spec.containers[0].args}' 2>/dev/null || true) local needs_ns_arg=true needs_only_arg=false patched=false [[ "$current" == *"--dns01-recursive-nameservers=${ns_csv}"* ]] && needs_ns_arg=false if [ "$only" = "true" ] || [ "$only" = "1" ]; then [[ "$current" != *"--dns01-recursive-nameservers-only"* ]] && needs_only_arg=true fi if $needs_ns_arg; then _kubectl patch deployment cert-manager \ --namespace "$ns" \ --type=json \ -p "[{\"op\":\"add\",\"path\":\"/spec/template/spec/containers/0/args/-\",\"value\":\"--dns01-recursive-nameservers=${ns_csv}\"}]" \ >/dev/null 2>&1 || true patched=true fi if $needs_only_arg; then _kubectl patch deployment cert-manager \ --namespace "$ns" \ --type=json \ -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--dns01-recursive-nameservers-only"}]' \ >/dev/null 2>&1 || true patched=true fi if $patched; then echo " [patch_recursive_dns] args added to deployment" else echo " [patch_recursive_dns] args already present — no change" fi _kubectl rollout status deployment cert-manager -n "$ns" --timeout=120s } _method_wait-ready() { local kind="${PLAN_PARAM_KIND:-deployment}" local ns="${PLAN_PARAM_NAMESPACE:-cert-manager}" local name="${PLAN_PARAM_NAME:-}" local timeout="${PLAN_PARAM_TIMEOUT:-120s}" [ -n "$name" ] || { echo "ERROR: PLAN_PARAM_NAME not set for wait-ready" >&2; exit 1; } _kubectl wait --for=condition=Available \ "${kind}/${name}" \ --namespace "$ns" \ --timeout="$timeout" }