|
| 1 | +# Makefile for Model Catalog Python Client |
| 2 | + |
| 3 | +all: install tidy |
| 4 | + |
| 5 | +.PHONY: help |
| 6 | +help: |
| 7 | + @echo "Model Catalog Python Client Targets:" |
| 8 | + @echo "" |
| 9 | + @echo " Setup & Build:" |
| 10 | + @echo " install - Generate OpenAPI client and install dependencies" |
| 11 | + @echo " generate - Regenerate catalog API client from OpenAPI spec" |
| 12 | + @echo " clean - Remove generated code and caches" |
| 13 | + @echo " build - Build the package" |
| 14 | + @echo "" |
| 15 | + @echo " Testing:" |
| 16 | + @echo " test-e2e - Run E2E tests (requires running catalog)" |
| 17 | + @echo " test-fuzz - Run fuzz tests (requires running catalog)" |
| 18 | + @echo "" |
| 19 | + @echo " Code Quality:" |
| 20 | + @echo " lint - Run linters (ruff + mypy)" |
| 21 | + @echo " tidy - Auto-fix code style issues" |
| 22 | + @echo "" |
| 23 | + @echo " Nox Sessions (multi-Python testing):" |
| 24 | + @echo " nox - Run default nox sessions" |
| 25 | + @echo " nox-lint - Run lint on all Python versions" |
| 26 | + @echo " nox-e2e - Run E2E tests on all Python versions" |
| 27 | + @echo "" |
| 28 | + @echo " Deployment (K8s with Kustomize):" |
| 29 | + @echo " deploy - Full local deployment (Kind + build + deploy)" |
| 30 | + @echo " deploy-kind - Create Kind cluster" |
| 31 | + @echo " deploy-build - Build Docker image" |
| 32 | + @echo " deploy-load - Load image into Kind cluster" |
| 33 | + @echo " deploy-k8s - Deploy to existing cluster (no Kind, no build)" |
| 34 | + @echo " deploy-apply - Apply kustomize manifests" |
| 35 | + @echo " deploy-forward - Start port-forward" |
| 36 | + @echo " deploy-restart - Rebuild and restart catalog (quick dev cycle)" |
| 37 | + @echo " deploy-cleanup - Remove deployment and Kind cluster" |
| 38 | + |
| 39 | +# Configuration (can be overridden: make deploy CATALOG_PORT=9090) |
| 40 | +export CATALOG_NAMESPACE ?= model-catalog |
| 41 | +export CATALOG_IMAGE ?= model-registry:catalog-test |
| 42 | +export CLUSTER_NAME ?= catalog-e2e |
| 43 | +export CATALOG_PORT ?= 8081 |
| 44 | + |
| 45 | +# Kustomize overlay for E2E testing |
| 46 | +KUSTOMIZE_E2E := ../../../manifests/kustomize/options/catalog/overlays/e2e |
| 47 | + |
| 48 | +.PHONY: install |
| 49 | +install: generate |
| 50 | + poetry install |
| 51 | + |
| 52 | +.PHONY: generate |
| 53 | +generate: |
| 54 | + @echo "Generating Catalog API client from OpenAPI spec..." |
| 55 | + @mkdir -p src/catalog_openapi |
| 56 | + ../../../bin/openapi-generator-cli generate \ |
| 57 | + -i ../../../api/openapi/catalog.yaml \ |
| 58 | + -g python \ |
| 59 | + -o src/ \ |
| 60 | + --package-name catalog_openapi \ |
| 61 | + --additional-properties=library=urllib3,generateSourceCodeOnly=true,useOneOfDiscriminatorLookup=true |
| 62 | + @# Remove generated test and docs |
| 63 | + @rm -rf src/catalog_openapi/test src/catalog_openapi/docs |
| 64 | + @rm -f src/catalog_openapi_README.md |
| 65 | + @if [ -d patches ] && [ -n "$$(ls -A patches/*.patch 2>/dev/null)" ]; then \ |
| 66 | + echo "Applying patches..."; \ |
| 67 | + git apply patches/*.patch; \ |
| 68 | + fi |
| 69 | + @echo "[OK] Generated catalog_openapi package" |
| 70 | + |
| 71 | +.PHONY: clean |
| 72 | +clean: |
| 73 | + @echo "Cleaning generated code and caches..." |
| 74 | + rm -rf src/catalog_openapi/ |
| 75 | + rm -rf .pytest_cache/ |
| 76 | + rm -rf .ruff_cache/ |
| 77 | + rm -rf .mypy_cache/ |
| 78 | + rm -rf .nox/ |
| 79 | + rm -rf .coverage |
| 80 | + rm -rf .coverage.* |
| 81 | + rm -rf htmlcov/ |
| 82 | + rm -rf dist/ |
| 83 | + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true |
| 84 | + find . -type f -name "*.pyc" -delete |
| 85 | + @echo "[OK] Cleaned" |
| 86 | + |
| 87 | +.PHONY: test-e2e |
| 88 | +test-e2e: |
| 89 | + CATALOG_URL=$${CATALOG_URL:-http://localhost:$(CATALOG_PORT)} poetry run pytest --e2e -v -rA |
| 90 | + |
| 91 | +.PHONY: test-fuzz |
| 92 | +test-fuzz: |
| 93 | + CATALOG_URL=$${CATALOG_URL:-http://localhost:$(CATALOG_PORT)} poetry run pytest --fuzz -v |
| 94 | + |
| 95 | +.PHONY: lint |
| 96 | +lint: |
| 97 | + poetry run ruff check . |
| 98 | + poetry run mypy . |
| 99 | + |
| 100 | +.PHONY: tidy |
| 101 | +tidy: |
| 102 | + @echo "Fixing code style issues..." |
| 103 | + -poetry run ruff check --fix-only . src/catalog_openapi 2>/dev/null |
| 104 | + -poetry run ruff format src tests 2>/dev/null |
| 105 | + |
| 106 | +.PHONY: build |
| 107 | +build: install tidy |
| 108 | + poetry build |
| 109 | + |
| 110 | +.PHONY: update |
| 111 | +update: |
| 112 | + poetry lock |
| 113 | + |
| 114 | +# Nox sessions for multi-Python version testing |
| 115 | +.PHONY: nox |
| 116 | +nox: |
| 117 | + poetry run nox |
| 118 | + |
| 119 | +.PHONY: nox-lint |
| 120 | +nox-lint: |
| 121 | + poetry run nox -s lint |
| 122 | + |
| 123 | +.PHONY: nox-e2e |
| 124 | +nox-e2e: |
| 125 | + poetry run nox -s e2e |
| 126 | + |
| 127 | +# ============================================================================= |
| 128 | +# Deployment Targets |
| 129 | +# ============================================================================= |
| 130 | + |
| 131 | +# Full local deployment: Kind cluster + build + deploy everything |
| 132 | +.PHONY: deploy |
| 133 | +deploy: deploy-kind deploy-build deploy-load deploy-k8s deploy-forward |
| 134 | + @echo "" |
| 135 | + @echo "========================================" |
| 136 | + @echo "[OK] Deployment complete!" |
| 137 | + @echo "========================================" |
| 138 | + @echo "" |
| 139 | + @echo "Catalog URL: http://localhost:$(CATALOG_PORT)" |
| 140 | + @echo "" |
| 141 | + @echo "Run tests: make test-e2e" |
| 142 | + @echo "View logs: kubectl logs -f deployment/model-catalog-server -n $(CATALOG_NAMESPACE)" |
| 143 | + @echo "Cleanup: make deploy-cleanup" |
| 144 | + |
| 145 | +# Deploy to existing cluster (no Kind, no build) |
| 146 | +.PHONY: deploy-k8s |
| 147 | +deploy-k8s: deploy-namespace deploy-apply |
| 148 | + |
| 149 | +# Create Kind cluster |
| 150 | +.PHONY: deploy-kind |
| 151 | +deploy-kind: |
| 152 | + @echo "Setting up Kind cluster: $(CLUSTER_NAME)" |
| 153 | + @if kind get clusters 2>/dev/null | grep -q "$(CLUSTER_NAME)"; then \ |
| 154 | + echo "Cluster $(CLUSTER_NAME) already exists, using it"; \ |
| 155 | + kubectl config use-context "kind-$(CLUSTER_NAME)"; \ |
| 156 | + else \ |
| 157 | + kind create cluster -n "$(CLUSTER_NAME)"; \ |
| 158 | + fi |
| 159 | + |
| 160 | +# Build Docker image |
| 161 | +.PHONY: deploy-build |
| 162 | +deploy-build: |
| 163 | + @echo "Building Docker image: $(CATALOG_IMAGE)" |
| 164 | + cd ../../.. && docker build --progress=plain -t "$(CATALOG_IMAGE)" -f Dockerfile . |
| 165 | + @echo "[OK] Build complete" |
| 166 | + |
| 167 | +# Load image into Kind cluster |
| 168 | +.PHONY: deploy-load |
| 169 | +deploy-load: |
| 170 | + @echo "Loading image into Kind cluster..." |
| 171 | + kind load docker-image -n "$(CLUSTER_NAME)" "$(CATALOG_IMAGE)" 2>&1 | tail -5 |
| 172 | + @# Adjust image name for podman if needed |
| 173 | + @if docker --version 2>&1 | grep -q "podman"; then \ |
| 174 | + echo "Using podman, image will be referenced as localhost/$(CATALOG_IMAGE)"; \ |
| 175 | + fi |
| 176 | + |
| 177 | +# Create namespace |
| 178 | +.PHONY: deploy-namespace |
| 179 | +deploy-namespace: |
| 180 | + @echo "Creating namespace: $(CATALOG_NAMESPACE)" |
| 181 | + kubectl create namespace "$(CATALOG_NAMESPACE)" --dry-run=client -o yaml | kubectl apply -f - |
| 182 | + |
| 183 | +# Apply kustomize manifests (includes postgres, secrets, configmaps, catalog) |
| 184 | +.PHONY: deploy-apply |
| 185 | +deploy-apply: deploy-namespace |
| 186 | + @echo "Applying kustomize manifests..." |
| 187 | + @# Set the image dynamically to support both Docker and Podman |
| 188 | + cd "$(KUSTOMIZE_E2E)" && kustomize edit set image "ghcr.io/kubeflow/model-registry/server=$(CATALOG_IMAGE)" |
| 189 | + kubectl apply -k "$(KUSTOMIZE_E2E)" |
| 190 | + @echo "Waiting for PostgreSQL to be ready..." |
| 191 | + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=postgres -n "$(CATALOG_NAMESPACE)" --timeout=120s |
| 192 | + @echo "[OK] PostgreSQL is ready" |
| 193 | + @echo "Waiting for Catalog to be ready..." |
| 194 | + kubectl wait --for=condition=available deployment/model-catalog-server -n "$(CATALOG_NAMESPACE)" --timeout=120s |
| 195 | + @echo "[OK] Catalog service is ready" |
| 196 | + |
| 197 | +# Start port-forward |
| 198 | +.PHONY: deploy-forward |
| 199 | +deploy-forward: |
| 200 | + @echo "Starting port-forward on port $(CATALOG_PORT)..." |
| 201 | + @# Stop any existing port-forward first to avoid stale PIDs |
| 202 | + @if [ -f .port-forward.pid ]; then \ |
| 203 | + kill $$(cat .port-forward.pid) 2>/dev/null || true; \ |
| 204 | + rm -f .port-forward.pid; \ |
| 205 | + fi |
| 206 | + @kubectl port-forward -n "$(CATALOG_NAMESPACE)" svc/model-catalog "$(CATALOG_PORT)":8080 > /dev/null 2>&1 & echo $$! > .port-forward.pid |
| 207 | + @sleep 3 |
| 208 | + @if curl -s "http://localhost:$(CATALOG_PORT)/api/model_catalog/v1alpha1/sources" > /dev/null 2>&1; then \ |
| 209 | + echo "[OK] Catalog accessible at http://localhost:$(CATALOG_PORT)"; \ |
| 210 | + else \ |
| 211 | + echo "[WARN] Port-forward may not be ready. Try: kubectl port-forward -n $(CATALOG_NAMESPACE) svc/model-catalog $(CATALOG_PORT):8080"; \ |
| 212 | + fi |
| 213 | + |
| 214 | +# Stop port-forward |
| 215 | +.PHONY: deploy-forward-stop |
| 216 | +deploy-forward-stop: |
| 217 | + @echo "Stopping port-forward..." |
| 218 | + @if [ -f .port-forward.pid ]; then \ |
| 219 | + kill $$(cat .port-forward.pid) 2>/dev/null || true; \ |
| 220 | + rm -f .port-forward.pid; \ |
| 221 | + fi |
| 222 | + |
| 223 | +# Cleanup everything |
| 224 | +.PHONY: deploy-cleanup |
| 225 | +deploy-cleanup: deploy-forward-stop |
| 226 | + @echo "Cleaning up deployment..." |
| 227 | + @if kubectl get namespace "$(CATALOG_NAMESPACE)" &>/dev/null; then \ |
| 228 | + kubectl delete namespace "$(CATALOG_NAMESPACE)" --timeout=60s || true; \ |
| 229 | + fi |
| 230 | + @if kind get clusters 2>/dev/null | grep -q "$(CLUSTER_NAME)"; then \ |
| 231 | + echo "Deleting Kind cluster: $(CLUSTER_NAME)"; \ |
| 232 | + kind delete cluster -n "$(CLUSTER_NAME)"; \ |
| 233 | + fi |
| 234 | + @echo "[OK] Cleanup complete" |
| 235 | + |
| 236 | +# Shortcut: redeploy catalog only (after code changes) |
| 237 | +.PHONY: deploy-restart |
| 238 | +deploy-restart: deploy-build deploy-load |
| 239 | + kubectl rollout restart deployment/model-catalog-server -n "$(CATALOG_NAMESPACE)" |
| 240 | + kubectl rollout status deployment/model-catalog-server -n "$(CATALOG_NAMESPACE)" |
| 241 | + @echo "[OK] Catalog restarted" |
0 commit comments