#!/usr/bin/env bash set -euo pipefail # deploy.sh — Deploy Harbor container registry to Kubernetes # Run from gremlin-1: bash ~/sources/kube/harbor/deploy.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" echo "=== Harbor Container Registry Deploy ===" echo "" # ------------------------------------------------------- # 1. Create namespace # ------------------------------------------------------- echo "--- Step 1: Creating namespace ---" kubectl create namespace harbor-service --dry-run=client -o yaml | kubectl apply -f - echo " ✓ harbor-service namespace ready" echo "" # ------------------------------------------------------- # 2. Proxy CA cert (for Squid SSL bump) # ------------------------------------------------------- echo "--- Step 2: Proxy CA cert ---" CA_CERT_PATH="/tmp/harbor-home.crt" if curl -sf http://192.168.42.1/home.crt -o "$CA_CERT_PATH" 2>/dev/null; then if ! kubectl get configmap proxy-ca-cert -n harbor-service > /dev/null 2>&1; then kubectl create configmap proxy-ca-cert --from-file=ca.crt="$CA_CERT_PATH" -n harbor-service echo " ✓ proxy-ca-cert created" else echo " ✓ proxy-ca-cert already exists" fi else echo " ⚠ Could not fetch CA cert (non-fatal)" fi echo "" # ------------------------------------------------------- # 3. Create NFS directories (requires root for mount) # ------------------------------------------------------- echo "--- Step 3: Ensuring NFS directories ---" if [ "$(id -u)" -eq 0 ]; then mkdir -p /tmp/harbor-nfs-init mount -t nfs 192.168.42.8:/volume1/Kubernetes/harbor /tmp/harbor-nfs-init 2>/dev/null || true mkdir -p /tmp/harbor-nfs-init/data/registry mkdir -p /tmp/harbor-nfs-init/data/redis mkdir -p /tmp/harbor-nfs-init/data/jobservice mkdir -p /tmp/harbor-nfs-init/data/trivy umount /tmp/harbor-nfs-init 2>/dev/null || true rmdir /tmp/harbor-nfs-init 2>/dev/null || true echo " ✓ NFS directories ready" else echo " ⚠ Not root — skipping NFS dir creation (dirs may already exist)" fi echo "" # ------------------------------------------------------- # 4. Apply PVs and PVCs # ------------------------------------------------------- echo "--- Step 4: Applying PVs and PVCs ---" kubectl apply -f "$SCRIPT_DIR/../pvs/harbor-pv.yaml" kubectl apply -f "$SCRIPT_DIR/pvcs.yaml" echo " ✓ PVs and PVCs applied" echo "" # ------------------------------------------------------- # 5. Remove old plain Docker Registry ingress # ------------------------------------------------------- echo "--- Step 5: Checking old registry ingress ---" if kubectl get ingress registry-ingress -n git-server > /dev/null 2>&1; then echo " Removing old registry ingress from git-server namespace..." kubectl delete ingress registry-ingress -n git-server echo " ✓ Old registry ingress removed" else echo " ✓ No old registry ingress found" fi echo "" # ------------------------------------------------------- # 6. Install/upgrade Harbor via Helm # ------------------------------------------------------- echo "--- Step 6: Installing Harbor ---" helm repo add harbor https://helm.goharbor.io 2>/dev/null || true helm repo update harbor 2>/dev/null || true HARBOR_EXISTS=$(helm list -n harbor-service -q 2>/dev/null | grep -c harbor || true) if [ "${HARBOR_EXISTS:-0}" -gt 0 ]; then echo " Harbor already installed — upgrading..." else echo " Fresh Harbor install..." fi helm upgrade --install harbor harbor/harbor \ --namespace harbor-service \ --values "$SCRIPT_DIR/values.yaml" \ --timeout 10m \ --wait echo "" echo " Waiting for Harbor core to be ready..." kubectl wait --for=condition=ready pod -l app=harbor,component=core -n harbor-service --timeout=180s || true echo "" # ------------------------------------------------------- # 7. Configure Harbor via API (idempotent) # ------------------------------------------------------- echo "--- Step 7: Configuring Harbor project and robot account ---" HARBOR_API="https://registry.celestium.life/api/v2.0" HARBOR_AUTH="admin:St0nks0racl3!" # Create stonks-oracle project if it doesn't exist if curl -sf -u "$HARBOR_AUTH" "$HARBOR_API/projects?name=stonks-oracle" | python3 -c "import sys,json; sys.exit(0 if json.load(sys.stdin) else 1)" 2>/dev/null; then echo " ✓ Project stonks-oracle already exists" else curl -sf -X POST -u "$HARBOR_AUTH" \ -H "Content-Type: application/json" \ "$HARBOR_API/projects" \ -d '{"project_name":"stonks-oracle","public":true,"metadata":{"auto_scan":"true","severity":"high"},"storage_limit":-1}' echo " ✓ Project stonks-oracle created (public, auto-scan enabled)" fi # Create CI robot account if it doesn't exist EXISTING_ROBOT=$(curl -sf -u "$HARBOR_AUTH" "$HARBOR_API/robots" 2>/dev/null | python3 -c " import sys, json robots = json.load(sys.stdin) for r in robots: if 'ci-push' in r.get('name', ''): print(r['name']) break " 2>/dev/null || true) if [ -n "$EXISTING_ROBOT" ]; then echo " ✓ Robot account already exists: $EXISTING_ROBOT" else ROBOT_RESP=$(curl -sf -X POST -u "$HARBOR_AUTH" \ -H "Content-Type: application/json" \ "$HARBOR_API/robots" \ -d '{ "name":"ci-push", "description":"CI/CD pipeline push account", "duration":-1, "level":"project", "permissions":[{ "namespace":"stonks-oracle", "kind":"project", "access":[ {"resource":"repository","action":"push"}, {"resource":"repository","action":"pull"}, {"resource":"tag","action":"create"}, {"resource":"tag","action":"list"}, {"resource":"artifact","action":"read"} ] }] }') ROBOT_NAME=$(echo "$ROBOT_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])") ROBOT_SECRET=$(echo "$ROBOT_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret'])") echo " ✓ Robot account created" echo " Username: $ROBOT_NAME" echo " Secret: $ROBOT_SECRET" echo " ⚠ Save these — add as HARBOR_USERNAME and HARBOR_PASSWORD in your CI secrets" fi # Set tag retention policy (idempotent — check if one exists first) PROJECT_ID=$(curl -sf -u "$HARBOR_AUTH" "$HARBOR_API/projects?name=stonks-oracle" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['project_id'])" 2>/dev/null || true) if [ -n "$PROJECT_ID" ]; then EXISTING_RETENTION=$(curl -sf -u "$HARBOR_AUTH" "$HARBOR_API/retentions" 2>/dev/null || true) if echo "$EXISTING_RETENTION" | python3 -c "import sys,json; d=json.load(sys.stdin); sys.exit(0 if isinstance(d,list) and len(d)>0 else 1)" 2>/dev/null; then echo " ✓ Tag retention policy already exists" else curl -sf -X POST -u "$HARBOR_AUTH" \ -H "Content-Type: application/json" \ "$HARBOR_API/retentions" \ -d "{ \"algorithm\":\"or\", \"scope\":{\"level\":\"project\",\"ref\":$PROJECT_ID}, \"trigger\":{\"kind\":\"Schedule\",\"settings\":{\"cron\":\"0 0 0 * * *\"}}, \"rules\":[ {\"disabled\":false,\"action\":\"retain\",\"scope_selectors\":{\"repository\":[{\"kind\":\"doublestar\",\"decoration\":\"repoMatches\",\"pattern\":\"**\"}]},\"tag_selectors\":[{\"kind\":\"doublestar\",\"decoration\":\"matches\",\"pattern\":\"latest\"}],\"params\":{}}, {\"disabled\":false,\"action\":\"retain\",\"scope_selectors\":{\"repository\":[{\"kind\":\"doublestar\",\"decoration\":\"repoMatches\",\"pattern\":\"**\"}]},\"tag_selectors\":[{\"kind\":\"doublestar\",\"decoration\":\"matches\",\"pattern\":\"**\"}],\"params\":{\"latestPushedK\":10}} ] }" 2>/dev/null && echo " ✓ Tag retention policy created (keep latest + last 10 tags, daily)" || echo " ⚠ Could not create retention policy (non-fatal)" fi fi echo "" echo "=== Harbor Deploy Complete ===" echo "" echo " URL: https://registry.celestium.life" echo " Login: admin / St0nks0racl3!" echo "" echo " Pods:" kubectl get pods -n harbor-service