#!/usr/bin/env bash set -euo pipefail # init_registry.sh # Purpose: # - Deploy ArgoCD into Kubernetes # - Deploy registry:2 into k3s clusters (default namespace) when enabled SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # Shared option parsing for common core scripts # shellcheck disable=SC1090 source "$SCRIPT_DIR/common_core_lib.sh" # Inject default config if not provided _has_config=0 for _arg in "$@"; do [[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1 done if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@" fi unset _has_config _arg common_core_preparse_config "$@" # Load environment and config via prole_cfg.sh # shellcheck disable=SC1090 source "$SCRIPT_DIR/prole_cfg.sh" if [[ -z "${PROLE_SERVICE:-}" ]]; then echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 exit 1 fi ACTION="" NAMESPACE_OVERRIDE="" REGISTRY_NAMESPACE_OVERRIDE="" if [[ -d "$SCRIPT_DIR/../k8s/argocd" ]]; then ARGOCD_MANIFEST_DIR="$SCRIPT_DIR/../k8s/argocd" elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/argocd" ]]; then ARGOCD_MANIFEST_DIR="$PROLE_HOME/k8s/argocd" else ARGOCD_MANIFEST_DIR="$SCRIPT_DIR/../k8s/argocd" fi ARGOCD_MANIFEST_FILE=${ARGOCD_MANIFEST_FILE:-"$ARGOCD_MANIFEST_DIR/install.yaml"} if [[ -d "$SCRIPT_DIR/../k8s/registry" ]]; then REGISTRY_MANIFEST_DIR="$SCRIPT_DIR/../k8s/registry" elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/registry" ]]; then REGISTRY_MANIFEST_DIR="$PROLE_HOME/k8s/registry" else REGISTRY_MANIFEST_DIR="$SCRIPT_DIR/../k8s/registry" fi REGISTRY_MANIFEST_FILE=${REGISTRY_MANIFEST_FILE:-"$REGISTRY_MANIFEST_DIR/deployment.yaml"} usage() { cat < Deploys ArgoCD into the target namespace and, for k3s, deploys registry:2 into the registry namespace (default: default). Note: The local Docker registry (port 5000) is managed separately for k3d. EOF } while [[ $# -gt 0 ]]; do case "$1" in -c|--config) shift # Already handled by common_core_preparse_config if we use common_core_lib.sh # but since we don't, we can just skip it here as prole_cfg.sh # will pick it up if we set PROLE_CONF? # Actually prole_cfg.sh uses PROLE_CONF. # Let's just consume it. shift ;; -m|--mode) shift prole_set_mode "${1:-}" ;; -m=*|--mode=*) prole_set_mode "${1#*=}" ;; -n|--namespace) shift if [[ -z "${1:-}" ]]; then echo "ERROR: -n/--namespace requires a value" >&2 exit 2 fi NAMESPACE_OVERRIDE="$1" ;; -n=*|--namespace=*) NAMESPACE_OVERRIDE="${1#*=}" ;; -r|--registry-namespace) shift if [[ -z "${1:-}" ]]; then echo "ERROR: -r/--registry-namespace requires a value" >&2 exit 2 fi REGISTRY_NAMESPACE_OVERRIDE="$1" ;; -r=*|--registry-namespace=*) REGISTRY_NAMESPACE_OVERRIDE="${1#*=}" ;; start|stop|status|restart|initialize|update|reload) ACTION="$1" ;; -h|--help) usage exit 0 ;; *) if [[ -z "$ACTION" && "$1" != -* ]]; then ACTION="$1" else echo "Unknown argument: $1" >&2 usage exit 2 fi ;; esac shift done if [[ -n "$NAMESPACE_OVERRIDE" ]]; then ARGOCD_NAMESPACE="$NAMESPACE_OVERRIDE" elif [[ -n "${ARGOCD_NAMESPACE:-}" ]]; then ARGOCD_NAMESPACE="$ARGOCD_NAMESPACE" else ARGOCD_NAMESPACE="argocd" fi if [[ -n "$REGISTRY_NAMESPACE_OVERRIDE" ]]; then REGISTRY_NAMESPACE="$REGISTRY_NAMESPACE_OVERRIDE" elif [[ -n "${REGISTRY_NAMESPACE:-}" ]]; then REGISTRY_NAMESPACE="$REGISTRY_NAMESPACE" else REGISTRY_NAMESPACE="default" fi export ARGOCD_NAMESPACE export REGISTRY_NAMESPACE export NAMESPACE="$ARGOCD_NAMESPACE" ARGOCD_LABEL_SELECTOR=${ARGOCD_LABEL_SELECTOR:-"app.kubernetes.io/part-of=argocd"} ARGOCD_SERVER_SERVICE=${ARGOCD_SERVER_SERVICE:-argocd-server} ARGOCD_PORT_FORWARD_LOCAL=${ARGOCD_PORT_FORWARD_LOCAL:-8081} ARGOCD_PORT_FORWARD_REMOTE=${ARGOCD_PORT_FORWARD_REMOTE:-80} ARGOCD_NODE_SELECTOR=${ARGOCD_NODE_SELECTOR:-} current_mode() { if command -v prole_normalize_mode >/dev/null 2>&1; then prole_normalize_mode "${PROLE_MODE:-${DEPLOYMENT_MODE:-}}" return 0 fi printf '%s' "${PROLE_MODE:-${DEPLOYMENT_MODE:-}}" } registry_enabled() { local mode mode=$(current_mode) [[ "$mode" == "k3s" || "$mode" == "k3d" ]] } resolve_argocd_node_selector() { if [[ -n "$ARGOCD_NODE_SELECTOR" ]]; then printf '%s' "$ARGOCD_NODE_SELECTOR" return 0 fi local node="" node=$(kubectl get nodes -l 'node-role.kubernetes.io/control-plane' -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true) if [[ -n "$node" ]]; then printf 'kubernetes.io/hostname=%s' "$node" fi } apply_argocd_node_selector() { local selector selector="$(resolve_argocd_node_selector)" if [[ -z "$selector" ]]; then return 0 fi local key="${selector%%=*}" local val="${selector#*=}" if [[ -z "$key" || -z "$val" ]]; then echo "WARN: invalid ARGOCD_NODE_SELECTOR '$selector' (expected key=value); skipping." >&2 return 0 fi for target in \ deployment/argocd-applicationset-controller \ deployment/argocd-dex-server \ deployment/argocd-notifications-controller \ deployment/argocd-repo-server \ deployment/argocd-server \ deployment/argocd-redis \ statefulset/argocd-application-controller; do kubectl -n "$ARGOCD_NAMESPACE" patch "$target" \ --type merge \ -p "{\"spec\":{\"template\":{\"spec\":{\"nodeSelector\":{\"$key\":\"$val\"}}}}}" >/dev/null 2>&1 || true done } ensure_tools() { command -v kubectl >/dev/null || { echo "Missing required tool: kubectl" >&2; exit 1; } } ensure_namespace() { local ns="$1" if [[ -z "$ns" ]]; then return 0 fi if ! kubectl get namespace "$ns" >/dev/null 2>&1; then echo "Creating namespace '$ns' ..." kubectl create namespace "$ns" >/dev/null 2>&1 || true fi } render_manifest() { if [[ ! -f "$ARGOCD_MANIFEST_FILE" ]]; then echo "ERROR: ArgoCD manifest not found: $ARGOCD_MANIFEST_FILE" >&2 exit 1 fi sed "s|\\${ARGOCD_NAMESPACE}|$ARGOCD_NAMESPACE|g" "$ARGOCD_MANIFEST_FILE" } render_registry_manifest() { if [[ ! -f "$REGISTRY_MANIFEST_FILE" ]]; then echo "ERROR: Registry manifest not found: $REGISTRY_MANIFEST_FILE" >&2 exit 1 fi prole_render_manifest "$REGISTRY_MANIFEST_FILE" } cleanup_registry_k8s() { render_registry_manifest | kubectl delete -n "$REGISTRY_NAMESPACE" -f - --ignore-not-found >/dev/null 2>&1 || true } apply_k8s() { echo "Applying ArgoCD manifest to namespace '$ARGOCD_NAMESPACE' ..." render_manifest | kubectl apply --server-side --force-conflicts --field-manager=prole-installer --validate=false -n "$ARGOCD_NAMESPACE" -f - apply_argocd_node_selector kubectl rollout status deploy/argocd-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status deploy/argocd-repo-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status deploy/argocd-dex-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status deploy/argocd-applicationset-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status deploy/argocd-notifications-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status deploy/argocd-redis -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true kubectl rollout status statefulset/argocd-application-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true } configure_k3d_insecure_registry() { local cluster_name="${1:-knoe-dev-cluster}" local reg_host="k3d-prole-registry" local reg_port="5000" local registries_yaml registries_yaml=$(cat < /etc/rancher/k3s/registries.yaml <<'EOF' ${registries_yaml} EOF" || { echo "WARN: failed to write registries.yaml to $node" >&2; continue; } # Restart k3s inside the node so it picks up the new mirror config. docker restart "$node" >/dev/null 2>&1 || true echo " configured $node" done # Wait briefly for nodes to come back up sleep 5 echo "Waiting for k3d nodes to become Ready ..." kubectl wait --for=condition=Ready nodes --all --timeout=${ROLLOUT_TIMEOUT:-300s} 2>/dev/null || true } apply_registry() { local mode mode=$(current_mode) if [[ "$mode" == "k3d" ]]; then if command -v k3d >/dev/null 2>&1; then echo "Ensuring k3d registry 'prole-registry' on port 5000 ..." if ! k3d registry list prole-registry >/dev/null 2>&1; then k3d registry create prole-registry --port 5000 || true fi local cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}" if k3d cluster list "$cluster_name" >/dev/null 2>&1; then echo "Connecting prole-registry to $cluster_name ..." # k3d v5+ uses 'k3d-NAME' as container name for registries local reg_container="k3d-prole-registry" local net_name="k3d-${cluster_name}" if command -v docker >/dev/null 2>&1; then if docker inspect "$reg_container" >/dev/null 2>&1 && docker network inspect "$net_name" >/dev/null 2>&1; then if ! docker inspect "$reg_container" -f '{{json .NetworkSettings.Networks}}' | grep -q "$net_name"; then docker network connect "$net_name" "$reg_container" || true fi fi fi fi # Configure k3d nodes to use the registry over plain HTTP (insecure). configure_k3d_insecure_registry "$cluster_name" # Ensure no k8s registry resources remain in k3d clusters. echo "Removing k8s registry resources (k3d uses k3d-prole-registry only) ..." cleanup_registry_k8s return 0 else echo "k3d not found; falling back to Docker for registry ..." fi fi # Fallback or k3s mode: use Docker container if k8s deploy fails or as alternative if [[ "$mode" == "k3d" ]] || [[ "${PROLE_REGISTRY_STANDALONE:-}" == "1" ]]; then if command -v docker >/dev/null 2>&1; then if ! docker ps --format '{{.Names}}' | grep -q "^prole-registry$"; then if docker ps -a --format '{{.Names}}' | grep -q "^prole-registry$"; then docker start prole-registry || true else docker run -d -p 5000:5000 --restart always --name prole-registry registry:2 || true fi fi if [[ "$mode" == "k3d" ]]; then echo "Removing k8s registry resources (k3d uses k3d-prole-registry only) ..." cleanup_registry_k8s fi return 0 fi fi echo "Applying registry manifest to namespace '$REGISTRY_NAMESPACE' ..." render_registry_manifest | kubectl apply --server-side --force-conflicts --field-manager=prole-installer --validate=false -n "$REGISTRY_NAMESPACE" -f - kubectl rollout status deploy/registry -n "$REGISTRY_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true } delete_k8s() { echo "Removing ArgoCD resources from namespace '$ARGOCD_NAMESPACE' ..." render_manifest | kubectl delete -n "$ARGOCD_NAMESPACE" -f - --ignore-not-found } delete_registry() { local mode mode=$(current_mode) if [[ "$mode" == "k3d" ]]; then if command -v k3d >/dev/null 2>&1; then echo "Removing k3d registry 'prole-registry' ..." k3d registry delete prole-registry || true fi echo "Removing registry resources from namespace '$REGISTRY_NAMESPACE' ..." render_registry_manifest | kubectl delete -n "$REGISTRY_NAMESPACE" -f - --ignore-not-found >/dev/null 2>&1 || true return 0 fi echo "Removing registry resources from namespace '$REGISTRY_NAMESPACE' ..." render_registry_manifest | kubectl delete -n "$REGISTRY_NAMESPACE" -f - --ignore-not-found } status_k8s() { kubectl -n "$ARGOCD_NAMESPACE" get deploy,statefulset,svc -l "$ARGOCD_LABEL_SELECTOR" 2>/dev/null || true kubectl -n "$ARGOCD_NAMESPACE" get pods -l "$ARGOCD_LABEL_SELECTOR" 2>/dev/null || true } status_registry() { kubectl -n "$REGISTRY_NAMESPACE" get deploy,svc -l "app=registry" 2>/dev/null || true kubectl -n "$REGISTRY_NAMESPACE" get pods -l "app=registry" 2>/dev/null || true } case "${ACTION:-}" in start|initialize|update|reload|restart) ensure_tools ensure_namespace "$ARGOCD_NAMESPACE" apply_k8s if registry_enabled; then ensure_namespace "$REGISTRY_NAMESPACE" apply_registry else echo "Skipping registry deploy (mode=$(current_mode))" fi prole_register_port_forward "argocd" "$ARGOCD_NAMESPACE" "svc/${ARGOCD_SERVER_SERVICE}" \ "$ARGOCD_PORT_FORWARD_LOCAL" "$ARGOCD_PORT_FORWARD_REMOTE" "0.0.0.0" "TCP" "ArgoCD" ;; stop) ensure_tools delete_k8s if registry_enabled; then delete_registry fi ;; status) ensure_tools status_k8s if registry_enabled; then status_registry fi ;; *) usage >&2 exit 1 ;; esac