diff --git a/.gitignore b/.gitignore index fe42474a57..22159ba411 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,8 @@ istio-* venv/ .python-version -# Claude code +# AI agent tool configs (keep local, don't track) +.cursor/ .claude/ **/go.work diff --git a/AGENTS.md b/AGENTS.md index c9b1990fbe..4d65833b7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,13 +19,13 @@ AI agents should: - Include in the commit message: `Assisted-by: [Agent Name]`, or `Co-authored-by: [Agent Name]`. - Avoid automated responses/comments to the Pull Requests or Issues on GitHub. - Agents must NOT: - Bypass tests or linters -- Introduce dependencies without updating `go.mod` / `go.sum` +- Introduce dependencies without updating `go.mod` / `go.sum` (Go), `pyproject.toml` (Python), or `package.json` (TypeScript) - Generate or commit large autogenerated files (use the `gen/*` Make targets instead) - Modify OpenAPI specs (`api/openapi/`) without regenerating dependent code +- Modify CRD schemas or API versions without explicit instruction ### Context Awareness @@ -35,6 +35,10 @@ Before writing code, agents should: - Match import patterns and error-handling conventions from neighboring files - Preserve existing logging style (uses `golang/glog`) - Understand the module boundary — this is a Go workspace with multiple `go.mod` files +- Review OpenAPI specs in `api/openapi/` before changing API structures +- Call out any breaking changes introduced and follow the deprecation policy + +For additional context see the [Model Registry documentation](https://www.kubeflow.org/docs/components/model-registry/overview/). ## Repository Map @@ -48,7 +52,9 @@ catalog/ # Catalog service — federated model discovery ├── Makefile # Catalog-specific build/test targets clients/ # Client libraries and UI ├── python/ # Python client for model-registry API (Poetry) -└── ui/ # React UI + Go BFF (separate workflow, not covered here) +└── ui/ # React UI + Go BFF (separate workflow, see UI Development below) + ├── bff/ # Backend-for-Frontend (Go) + └── frontend/ # React/TypeScript frontend cmd/ # CLI entry points ├── controller/ # Kubernetes controller for ModelRegistry CRDs ├── csi/ # Container Storage Interface (storage-initializer) @@ -78,6 +84,7 @@ jobs/ # Background jobs manifests/kustomize/ # Kubernetes deployment manifests (Kustomize) scripts/ # Build and utility scripts devenv/ # Local development environment (Tilt-based) +docs/ # Documentation (see docs/dev_kind_environment.md for Kind setup) test/ # Integration/E2E test scripts ``` @@ -233,6 +240,24 @@ make compose/down # Stop services make compose/clean # Remove all volumes and networks ``` +### UI Development + +```bash +# From clients/ui/ +make dev-install-dependencies # Install all UI dependencies +make dev-start # Start BFF + frontend for standalone mode + +# Or individually: +cd clients/ui/bff && go run ./cmd --port=4000 --dev-mode --deployment-mode=standalone +cd clients/ui/frontend && DEPLOYMENT_MODE=standalone STYLE_THEME=patternfly npm run start:dev + +# UI tests +cd clients/ui/frontend && npm run test:unit +cd clients/ui/frontend && npm run test:type-check +``` + +For Kind-based UI development with Tilt, see [docs/dev_kind_environment.md](./docs/dev_kind_environment.md). + ## CI Checks (what runs on PRs) The following checks run automatically on pull requests: @@ -300,10 +325,23 @@ The following directories contain auto-generated code. Modify the sources and re - Use the `stretchr/testify` package for test assertions (already a project dependency) - Use Testcontainers for integration tests that need a real database +### TypeScript/React conventions + +- Follow existing component patterns in `clients/ui/frontend/src/` +- Use PatternFly components for UI elements +- Prefer functional components with hooks +- Write unit tests for new components + +### Python conventions + +- Follow existing patterns in `clients/python/` and `catalog/clients/python/` +- Use Poetry for dependency management +- Write tests with pytest + ### Testing requirements - Bug fixes MUST include a test that reproduces the bug -- Unit tests live alongside source files as `*_test.go` +- Unit tests live alongside source files as `*_test.go` (Go), `*.test.ts`/`*.test.tsx` (TypeScript), `*_test.py`/`test_*.py` (Python) - Integration tests use Testcontainers (MySQL and PostgreSQL) — see `internal/db/service/*_test.go` for patterns - Controller tests use `envtest` (kubebuilder test framework) — see `cmd/controller/` @@ -331,3 +369,11 @@ When modifying the REST API: 3. Validate: `make openapi/validate` 4. Regenerate server and client code: `make gen/openapi gen/openapi-server` 5. Update any affected handler logic in `internal/` + +## Additional Resources + +- [CONTRIBUTING.md](./CONTRIBUTING.md) — DCO, code of conduct, ARM/Mac setup, Kind deployment +- [clients/ui/CONTRIBUTING.md](./clients/ui/CONTRIBUTING.md) — UI-specific contribution guide +- [docs/dev_kind_environment.md](./docs/dev_kind_environment.md) — Kind local dev environment setup and troubleshooting +- [devenv/README.md](./devenv/README.md) — Tilt-based dev environment +- [Model Registry documentation](https://www.kubeflow.org/docs/components/model-registry/overview/) diff --git a/docs/dev_kind_environment.md b/docs/dev_kind_environment.md new file mode 100644 index 0000000000..7eca3be781 --- /dev/null +++ b/docs/dev_kind_environment.md @@ -0,0 +1,184 @@ +# Kind Dev Environment + +Local Kubernetes development environment for the model-registry UI using Kind and Tilt. + +## Prerequisites + +- A Docker-compatible runtime (e.g. Docker Desktop, Colima, Podman with `podman-docker`) +- [Kind](https://kind.sigs.k8s.io/) installed +- kubectl installed +- Go >= 1.25.7 +- Node.js >= 22.0.0 +- Tilt v0.33.22+ (auto-downloaded by `make tilt-up` if not present) + +## Architecture + +The dev environment runs three concurrent processes: + +| Component | Directory | Port | Purpose | +|-----------|-----------|------|---------| +| Infrastructure | `devenv/` | 10350 (Tilt) | Kind cluster + Tilt for deploying model-registry resources | +| BFF | `clients/ui/bff/` | 4000 | Go backend-for-frontend server | +| Frontend | `clients/ui/frontend/` | 9000 | React dev server (proxies to BFF) | + +## Quick Start + +### 1. Start Infrastructure + +```bash +# Start your Docker runtime (example with Colima on macOS): +colima start + +# Create the Kind cluster (if it doesn't exist): +kind get clusters | grep -q '^model-registry$' || kind create cluster --name model-registry +kubectl config use-context kind-model-registry + +# Start Tilt (auto-downloads if not installed): +cd devenv && make tilt-up +``` + +### 2. Start BFF + +```bash +cd clients/ui/bff +go run ./cmd --port=4000 --dev-mode \ + --dev-mode-model-registry-port=8080 \ + --dev-mode-catalog-port=8082 \ + --deployment-mode=standalone +``` + +Add `--mock-k8s-client` if you don't need a real cluster for basic UI testing. + +### 3. Start Frontend + +```bash +cd clients/ui/frontend +DEPLOYMENT_MODE=standalone STYLE_THEME=patternfly npm run start:dev +``` + +### 4. RBAC Setup (real K8s only) + +When using the real K8s client, the BFF's namespace registry access check uses SubjectAccessReview with only the `User` field (no groups), so group-based RoleBindings don't take effect. Create a ClusterRoleBinding that directly binds each namespace's default ServiceAccount: + +```bash +kubectl apply -f - </dev/null | grep -q "^${CLUSTER_NAME}$"; then + echo "Cluster ${CLUSTER_NAME} already exists." + kubectl config use-context "kind-${CLUSTER_NAME}" +else + kind create cluster --name "$CLUSTER_NAME" +fi + +echo "=== Creating namespace ${CATALOG_NAMESPACE} ===" +kubectl create namespace "$CATALOG_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - + +echo "=== Deploying Model Catalog with demo overlay (full performance data) ===" +kubectl apply -k manifests/kustomize/options/catalog/overlays/demo -n "$CATALOG_NAMESPACE" + +echo "=== Waiting for Model Catalog Postgres to be ready ===" +kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=postgres,app.kubernetes.io/part-of=model-catalog -n "$CATALOG_NAMESPACE" --timeout=120s 2>/dev/null || true +echo "=== Waiting for Model Catalog server (with perf data) to be available ===" +kubectl wait --for=condition=available deployment/model-catalog-server -n "$CATALOG_NAMESPACE" --timeout=5m + +echo "=== Done ===" +kubectl get pods -n "$CATALOG_NAMESPACE" +echo "" +echo "To access the Model Catalog API (with performance metrics):" +echo " kubectl port-forward -n $CATALOG_NAMESPACE svc/model-catalog-server 8080:8080" +echo "Then open http://localhost:8080 (or use the API with performance-metrics from /perf-data)." diff --git a/scripts/dev_teardown.sh b/scripts/dev_teardown.sh new file mode 100755 index 0000000000..9948a02dd6 --- /dev/null +++ b/scripts/dev_teardown.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Tear down the model-registry dev environment. +# Stops Frontend, BFF, and Tilt. Optionally deletes the Kind cluster. +# +# Usage: +# ./scripts/dev_teardown.sh # Stop everything, delete cluster +# ./scripts/dev_teardown.sh --keep-cluster # Stop processes, keep cluster +# +# Environment variables (override default ports): +# FRONTEND_PORT (default: 9000) +# BFF_PORT (default: 4000) + +set -e + +FRONTEND_PORT="${FRONTEND_PORT:-9000}" +BFF_PORT="${BFF_PORT:-4000}" +CLUSTER_NAME="${CLUSTER_NAME:-model-registry}" +KEEP_CLUSTER=false +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +for arg in "$@"; do + case "$arg" in + --keep-cluster) KEEP_CLUSTER=true ;; + esac +done + +cd "$REPO_ROOT" + +echo "=== Stopping Frontend (port ${FRONTEND_PORT}) ===" +lsof -ti:"${FRONTEND_PORT}" | xargs kill -9 2>/dev/null || true + +echo "=== Stopping BFF (port ${BFF_PORT}) ===" +lsof -ti:"${BFF_PORT}" | xargs kill -9 2>/dev/null || true + +echo "=== Stopping Tilt ===" +cd devenv && make tilt-down 2>/dev/null || true +cd "$REPO_ROOT" + +if [ "$KEEP_CLUSTER" = false ]; then + echo "=== Deleting Kind cluster '${CLUSTER_NAME}' ===" + kind delete cluster --name "$CLUSTER_NAME" + + if [ $(which colima 2>/dev/null) ]; then + echo "=== Stopping Colima ===" + colima stop 2>/dev/null + fi +else + echo "=== Keeping Kind cluster running ===" +fi + +echo "=== Done ==="