|
| 1 | + |
| 2 | +# Make expects SHELL to be a path (no args). Use bash directly for portability. |
| 3 | +SHELL := /bin/bash |
| 4 | +.ONESHELL: |
| 5 | +.SHELLFLAGS := -euo pipefail -c |
| 6 | +.SILENT: |
| 7 | + |
| 8 | +MAKEFLAGS += --no-print-directory |
| 9 | + |
| 10 | +# Defaults (override via environment or `make VAR=value ...`) |
| 11 | +LOCAL ?= True |
| 12 | +CLUSTER_NAME ?= aiidalab-demo-server-local |
| 13 | +NAMESPACE ?= local |
| 14 | +RELEASE_NAME ?= aiidalab-demo-server |
| 15 | +VALUES_TEMPLATE ?= basehub/values.yaml.j2 |
| 16 | +VALUES_FILE ?= ${VALUES_TEMPLATE:.j2=} |
| 17 | +CHART_LOCK_FILE ?= basehub/Chart.lock |
| 18 | +KIND_CONFIG_FILE ?= kind-config.yaml |
| 19 | +HELM_DEP_RETRIES ?= 5 |
| 20 | + |
| 21 | +help: ## Show available targets |
| 22 | + awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z0-9_.-]+:.*##/ {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) |
| 23 | + |
| 24 | +generate-values: ## Render basehub/values.yaml from .env + Jinja2 template |
| 25 | + if [[ ! -f "$(VALUES_TEMPLATE)" ]]; then |
| 26 | + echo "Template not found: $(VALUES_TEMPLATE) (skipping)" >&2 |
| 27 | + exit 0 |
| 28 | + fi |
| 29 | + if ! command -v jinja2 >/dev/null 2>&1; then |
| 30 | + echo "Missing required command: jinja2" >&2 |
| 31 | + exit 1 |
| 32 | + fi |
| 33 | + if [[ -f .env ]]; then |
| 34 | + set -a |
| 35 | + source .env |
| 36 | + set +a |
| 37 | + fi |
| 38 | + LOCAL=${LOCAL} jinja2 "$(VALUES_TEMPLATE)" -o "$(VALUES_FILE)" |
| 39 | + echo "Wrote $(VALUES_FILE)" |
| 40 | + |
| 41 | +up: _check-for-values ## Create kind cluster (if needed) and deploy helm release |
| 42 | + $(MAKE) _local_cmd CMD=up |
| 43 | + |
| 44 | +refresh: _check-for-values ## Re-deploy helm release (fast; no kind create) |
| 45 | + $(MAKE) _local_cmd CMD=refresh |
| 46 | + |
| 47 | +restart: ## Restart hub/proxy deployments (no helm upgrade) |
| 48 | + $(MAKE) _local_cmd CMD=restart |
| 49 | + |
| 50 | +port-forward: ## Forward http://localhost:8000 -> service/proxy-public |
| 51 | + $(MAKE) _local_cmd CMD=port-forward |
| 52 | + |
| 53 | +status: ## Show pods/services in the namespace |
| 54 | + $(MAKE) _local_cmd CMD=status |
| 55 | + |
| 56 | +down: ## Uninstall release and delete kind cluster |
| 57 | + $(MAKE) _local_cmd CMD=down |
| 58 | + |
| 59 | +clean: ## Remove generated files |
| 60 | + rm -f "$(VALUES_FILE)" |
| 61 | + rm -f "$(CHART_LOCK_FILE)" |
| 62 | + |
| 63 | +_check-for-values: |
| 64 | + if [[ ! -f "$(VALUES_FILE)" ]]; then |
| 65 | + echo "Values file not found: $(VALUES_FILE). Run 'make generate-values' first." >&2 |
| 66 | + exit 1 |
| 67 | + fi |
| 68 | + |
| 69 | +_local_cmd: |
| 70 | + require_cmd() { command -v "$$1" >/dev/null 2>&1 || { echo "Missing required command: $$1" >&2; exit 1; }; } |
| 71 | + has_cmd() { command -v "$$1" >/dev/null 2>&1; } |
| 72 | + |
| 73 | + ensure_helm_repos() { |
| 74 | + if ! helm repo list 2>/dev/null | awk 'NR>1 {print $$1}' | grep -qx "jupyterhub"; then |
| 75 | + helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/ >/dev/null |
| 76 | + fi |
| 77 | + helm repo update >/dev/null |
| 78 | + } |
| 79 | + |
| 80 | + retry() { |
| 81 | + local -r attempts="$$1"; shift |
| 82 | + local attempt=1 |
| 83 | + while true; do |
| 84 | + if "$$@"; then return 0; fi |
| 85 | + if [[ "$$attempt" -ge "$$attempts" ]]; then return 1; fi |
| 86 | + local sleep_s=$$((attempt * 5)) |
| 87 | + echo "Retrying ($$attempt/$$attempts) after $${sleep_s}s: $$*" >&2 |
| 88 | + sleep "$$sleep_s" |
| 89 | + attempt=$$((attempt + 1)) |
| 90 | + done |
| 91 | + } |
| 92 | + |
| 93 | + kind_cluster_exists() { kind get clusters 2>/dev/null | grep -qx "$(CLUSTER_NAME)"; } |
| 94 | + |
| 95 | + deploy_release() { |
| 96 | + echo "Deploying helm release '$(RELEASE_NAME)' into namespace '$(NAMESPACE)'" |
| 97 | + ensure_helm_repos |
| 98 | + retry "$(HELM_DEP_RETRIES)" helm dependency build basehub >/dev/null || true |
| 99 | + helm upgrade \ |
| 100 | + --install \ |
| 101 | + --cleanup-on-fail \ |
| 102 | + --create-namespace --namespace="$(NAMESPACE)" \ |
| 103 | + -f "$(VALUES_FILE)" \ |
| 104 | + "$(RELEASE_NAME)" basehub |
| 105 | + } |
| 106 | + |
| 107 | + wait_ready() { |
| 108 | + echo "Waiting for hub and proxy to be ready..." |
| 109 | + kubectl -n "$(NAMESPACE)" rollout status deploy/hub --timeout=300s |
| 110 | + kubectl -n "$(NAMESPACE)" rollout status deploy/proxy --timeout=300s |
| 111 | + } |
| 112 | + |
| 113 | + restart_pods() { |
| 114 | + echo "Restarting hub and proxy deployments..." |
| 115 | + kubectl -n "$(NAMESPACE)" rollout restart deploy/hub |
| 116 | + kubectl -n "$(NAMESPACE)" rollout restart deploy/proxy |
| 117 | + wait_ready |
| 118 | + } |
| 119 | + |
| 120 | + case "$${CMD:-}" in |
| 121 | + up) |
| 122 | + require_cmd kubectl |
| 123 | + require_cmd helm |
| 124 | + if has_cmd kind; then |
| 125 | + if ! kind_cluster_exists; then |
| 126 | + echo "Creating kind cluster: $(CLUSTER_NAME)" |
| 127 | + if [[ -f "$(KIND_CONFIG_FILE)" ]]; then |
| 128 | + kind create cluster --name "$(CLUSTER_NAME)" --config "$(KIND_CONFIG_FILE)" |
| 129 | + else |
| 130 | + kind create cluster --name "$(CLUSTER_NAME)" |
| 131 | + fi |
| 132 | + else |
| 133 | + echo "Using existing kind cluster: $(CLUSTER_NAME)" |
| 134 | + fi |
| 135 | + else |
| 136 | + echo "kind not found; deploying to current kubectl context." |
| 137 | + fi |
| 138 | + deploy_release |
| 139 | + restart_pods |
| 140 | + echo |
| 141 | + echo "Next: open http://localhost:8000" |
| 142 | + echo "- If your kind cluster has port mappings, it should work directly." |
| 143 | + echo "- Otherwise, run: make port-forward" |
| 144 | + echo "Login with any username and password: demo" |
| 145 | + ;; |
| 146 | + refresh) |
| 147 | + require_cmd kubectl |
| 148 | + require_cmd helm |
| 149 | + deploy_release |
| 150 | + restart_pods |
| 151 | + ;; |
| 152 | + restart) |
| 153 | + require_cmd kubectl |
| 154 | + restart_pods |
| 155 | + ;; |
| 156 | + port-forward) |
| 157 | + require_cmd kubectl |
| 158 | + echo "Forwarding service/proxy-public to http://localhost:8000 (Ctrl+C to stop)" |
| 159 | + kubectl -n "$(NAMESPACE)" port-forward svc/proxy-public 8000:80 |
| 160 | + ;; |
| 161 | + status) |
| 162 | + require_cmd kubectl |
| 163 | + kubectl -n "$(NAMESPACE)" get pods,svc |
| 164 | + ;; |
| 165 | + down) |
| 166 | + require_cmd helm |
| 167 | + require_cmd kubectl |
| 168 | + echo "Uninstalling helm release '$(RELEASE_NAME)' from namespace '$(NAMESPACE)' (if present)" |
| 169 | + helm -n "$(NAMESPACE)" uninstall "$(RELEASE_NAME)" 2>/dev/null || true |
| 170 | + echo "Deleting namespace '$(NAMESPACE)' (if present)" |
| 171 | + kubectl delete namespace "$(NAMESPACE)" 2>/dev/null || true |
| 172 | + if has_cmd kind && kind_cluster_exists; then |
| 173 | + echo "Deleting kind cluster: $(CLUSTER_NAME)" |
| 174 | + kind delete cluster --name "$(CLUSTER_NAME)" |
| 175 | + fi |
| 176 | + ;; |
| 177 | + *) |
| 178 | + echo "Unknown CMD='$${CMD:-}'. Try: make help" >&2 |
| 179 | + exit 2 &2>/dev/null |
| 180 | + ;; |
| 181 | + esac |
0 commit comments