diff --git a/docs/cli/reference.mdx b/docs/cli/reference.mdx index 42011f745e..d25445b563 100644 --- a/docs/cli/reference.mdx +++ b/docs/cli/reference.mdx @@ -145,7 +145,7 @@ nemo services run [OPTIONS] Start platform services in the background. -Detaches the process, polls /health/ready, then returns. +Detaches the process, polls /status, then returns. **Examples:** diff --git a/e2e/conftest.py b/e2e/conftest.py index 543c39efd3..feb0a7efcd 100644 --- a/e2e/conftest.py +++ b/e2e/conftest.py @@ -13,7 +13,7 @@ When ``NMP_BASE_URL`` is set the harness skips service startup/shutdown and connects to the given URL. Otherwise it spawns ``nemo services run`` as a -child process on a free port, polls ``/health/ready`` until ready, and +child process on a free port, polls ``/status`` until ready, and terminates the process after the session. """ @@ -162,11 +162,11 @@ def _find_free_port() -> int: def _wait_for_healthy(url: str, timeout: float = _HEALTH_TIMEOUT) -> bool: - """Poll /health/ready until it returns 200 or timeout expires.""" + """Poll /status until it returns 200 or timeout expires.""" deadline = time.monotonic() + timeout while time.monotonic() < deadline: try: - resp = httpx.get(f"{url}/health/ready", timeout=2.0) + resp = httpx.get(f"{url}/status", timeout=2.0) if resp.status_code == 200: return True except httpx.RequestError: diff --git a/e2e/k8s/scripts/install_helm_e2e.sh b/e2e/k8s/scripts/install_helm_e2e.sh index c5c0869dc0..07f4f34baf 100755 --- a/e2e/k8s/scripts/install_helm_e2e.sh +++ b/e2e/k8s/scripts/install_helm_e2e.sh @@ -18,6 +18,7 @@ HELM_VALUES="${HELM_VALUES:-${HELM_VALUES_FILE:-${REPO_ROOT}/e2e/k8s/values/defa HELM_EXTRA_ARGS="${HELM_EXTRA_ARGS:-}" NMP_E2E_REGISTRY="${NMP_E2E_REGISTRY:-}" NMP_E2E_TAG="${NMP_E2E_TAG:-}" +NMP_E2E_PULL_POLICY="${NMP_E2E_PULL_POLICY:-}" REQUIRE_NMP_E2E_IMAGES="${REQUIRE_NMP_E2E_IMAGES:-false}" POSTGRES_IMAGE="${POSTGRES_IMAGE:-docker.io/library/postgres}" BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/library/busybox}" @@ -231,6 +232,13 @@ if [ -n "${NMP_E2E_TAG}" ]; then ) fi +if [ -n "${NMP_E2E_PULL_POLICY}" ]; then + HELM_ARGS+=( + --set api.image.pullPolicy="${NMP_E2E_PULL_POLICY}" + --set core.image.pullPolicy="${NMP_E2E_PULL_POLICY}" + ) +fi + log_info "Helm install inputs:" printf ' release: %s\n' "${HELM_RELEASE_NAME}" printf ' namespace: %s\n' "${NAMESPACE}" diff --git a/e2e/k8s/scripts/local_build_and_upgrade.sh b/e2e/k8s/scripts/local_build_and_upgrade.sh index 70785b78a3..87ad05d541 100755 --- a/e2e/k8s/scripts/local_build_and_upgrade.sh +++ b/e2e/k8s/scripts/local_build_and_upgrade.sh @@ -1,61 +1,65 @@ #!/usr/bin/env bash +# Build Docker images locally and deploy to minikube via Helm. +# +# This script handles the build step, then delegates the Helm install to +# install_helm_e2e.sh so install logic lives in one place. +# +# Environment variables: +# MINIKUBE_PROFILE - minikube profile name (default: minikube) +# NMP_REGISTRY - image registry (default: docker.io/my-registry) +# IMAGE_TAG - image tag (default: local-) +# BUILD_ARCH - target platform (default: auto-detected from host) +# HELM_VALUES - values file (default: e2e/k8s/values/minikube.yaml) + set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" +MINIKUBE_PROFILE="${MINIKUBE_PROFILE:-minikube}" + # Check if minikube is running -if ! minikube status &>/dev/null; then - echo "Minikube is not running. Starting minikube..." - # Use the setup_local_minikube_gpu.sh script to start minikube, - # and ensure the script is in the same directory as this script. - - "$SCRIPT_DIR/setup_local_minikube_gpu.sh" +if ! minikube status -p "${MINIKUBE_PROFILE}" &>/dev/null; then + echo "Minikube profile ${MINIKUBE_PROFILE} is not running. Starting..." + MINIKUBE_PROFILE="${MINIKUBE_PROFILE}" "$SCRIPT_DIR/setup_local_minikube_cpu.sh" fi # Wait for minikube to be ready -minikube status +minikube status -p "${MINIKUBE_PROFILE}" -# Build the images with a local tag and then load them -# Use epoch seconds (date +%s) so each run gets a unique tag and upgrades pick up new images +# Use epoch seconds so each run gets a unique tag and upgrades pick up new images IMAGE_TAG="${IMAGE_TAG:-local-$(date +%s)}" # Detect platform for build (match host arch) BUILD_ARCH="${BUILD_ARCH:-linux/$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')}" GIT_SHA=$(git -C "${REPO_ROOT}" rev-parse HEAD) +NMP_REGISTRY="${NMP_REGISTRY:-docker.io/my-registry}" + echo "Building docker-cpu images with tag $IMAGE_TAG (platform=$BUILD_ARCH)..." -# Allow building directly into minikube's docker daemon -eval "$(minikube docker-env)" +# Build directly into minikube's docker daemon +eval "$(minikube -p "${MINIKUBE_PROFILE}" docker-env)" -# Set the image tag to the git sha ( cd "${REPO_ROOT}" CI_COMMIT_SHA="$GIT_SHA" \ BAKE_TAG="$IMAGE_TAG" \ - IMAGE_REGISTRY="docker.io/my-registry" \ + IMAGE_REGISTRY="${NMP_REGISTRY}" \ BUILD_ARCH="$BUILD_ARCH" \ docker buildx bake docker-cpu --set "*.platform=$BUILD_ARCH" ) - -# Echo the image tags and an example script to run end-to-end tests -echo "Image tags:" -echo " nmp-api: $IMAGE_TAG" -echo " nmp-cpu-tasks: $IMAGE_TAG" -echo " platform: $IMAGE_TAG" -echo "----------------------------------------" -echo "Example script to run end-to-end jobs tests:" -echo " NMP_E2E_INTERNAL_HOST=nemo-platform-api:8080 NMP_E2E_REGISTRY=docker.io/my-registry NMP_E2E_TAG=$IMAGE_TAG uv run pytest e2e --kubernetes --cluster-url=http://localhost:80" echo "----------------------------------------" -echo "To rerun the helm install/upgrade, run:" -echo " helm upgrade --install nemo-platform k8s/helm/ -f e2e/k8s/values/local.yaml --set \"api.image.tag=$IMAGE_TAG\" --set \"core.image.tag=$IMAGE_TAG\" --set \"platformConfig.platform.image_tag=$IMAGE_TAG\"" +echo "Images built with tag: $IMAGE_TAG" echo "----------------------------------------" -# Install/upgrade the helm chart with image tags -helm upgrade --install nemo-platform k8s/helm/ \ - -f e2e/k8s/values/local.yaml \ - --set "api.image.tag=$IMAGE_TAG" \ - --set "core.image.tag=$IMAGE_TAG" \ - --set "platformConfig.platform.image_tag=$IMAGE_TAG" +# Delegate helm install to install_helm_e2e.sh +export HELM_VALUES="${HELM_VALUES:-${REPO_ROOT}/e2e/k8s/values/minikube.yaml}" +export NMP_E2E_REGISTRY="${NMP_REGISTRY}" +export NMP_E2E_TAG="${IMAGE_TAG}" +export NMP_E2E_PULL_POLICY="Never" +export MINIKUBE_PROFILE +export REQUIRE_NMP_E2E_IMAGES=true + +exec "$SCRIPT_DIR/install_helm_e2e.sh" diff --git a/e2e/k8s/scripts/run_auth_e2e.sh b/e2e/k8s/scripts/run_auth_e2e.sh index 41f89f5381..2e5016b7b8 100755 --- a/e2e/k8s/scripts/run_auth_e2e.sh +++ b/e2e/k8s/scripts/run_auth_e2e.sh @@ -43,8 +43,8 @@ wait_for_url() { echo "Using minikube profile: ${MINIKUBE_PROFILE}" echo "Using base URL: ${BASE_URL}" -if ! wait_for_url "${BASE_URL}/health/ready"; then - echo "Platform did not become ready at ${BASE_URL}/health/ready" >&2 +if ! wait_for_url "${BASE_URL}/status"; then + echo "Platform did not become ready at ${BASE_URL}/status" >&2 exit 1 fi diff --git a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/quickstart/cli.py b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/quickstart/cli.py index 5fb3ebab04..03e1d59e0c 100644 --- a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/quickstart/cli.py +++ b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/quickstart/cli.py @@ -91,7 +91,7 @@ def _check_ready_endpoint(port: int, timeout: float = 2.0) -> bool: import httpx try: - response = httpx.get(f"http://localhost:{port}/health/ready", timeout=timeout) + response = httpx.get(f"http://localhost:{port}/status", timeout=timeout) return response.status_code == 200 except Exception: return False diff --git a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py index 0d00241d13..f14f462b0c 100644 --- a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py +++ b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py @@ -92,9 +92,9 @@ def _wait_for_healthy( timeout: int = _HEALTH_TIMEOUT_SECONDS, poll_interval: float = _HEALTH_POLL_INTERVAL, ) -> bool: - """Poll the platform health endpoint until it responds or timeout.""" + """Poll the platform status endpoint until it responds or timeout.""" effective_host = "localhost" if host in ("0.0.0.0", "::") else host # noqa: S104 - url = str(httpx.URL(scheme="http", host=effective_host, port=port, path="/health/ready")) + url = str(httpx.URL(scheme="http", host=effective_host, port=port, path="/status")) deadline = time.monotonic() + timeout while time.monotonic() < deadline: try: @@ -335,7 +335,7 @@ def start_services( ) -> None: """Start platform services in the background. - Detaches the process, polls /health/ready, then returns. + Detaches the process, polls /status, then returns. Examples: nemo services start diff --git a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/setup.py b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/setup.py index 4252279e8b..ec312b48f3 100644 --- a/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/setup.py +++ b/packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/setup.py @@ -283,7 +283,7 @@ def _bootstrap_config_if_missing(base_url: str, workspace: str) -> None: def _check_platform_reachable(base_url: str, timeout: float = 5.0) -> bool: """Return True if the platform health endpoint responds.""" try: - resp = httpx.get(f"{base_url.rstrip('/')}/health/ready", timeout=timeout) + resp = httpx.get(f"{base_url.rstrip('/')}/status", timeout=timeout) return resp.status_code == 200 except Exception: return False diff --git a/packages/nemo_platform_ext/tests/cli/commands/test_services_lifecycle.py b/packages/nemo_platform_ext/tests/cli/commands/test_services_lifecycle.py index da9953770b..fd45922a1c 100644 --- a/packages/nemo_platform_ext/tests/cli/commands/test_services_lifecycle.py +++ b/packages/nemo_platform_ext/tests/cli/commands/test_services_lifecycle.py @@ -413,10 +413,10 @@ def test_log_preserved_across_restart(self, tmp_path: Path) -> None: with open(desc_path, "w") as f: json.dump(desc, f, indent=2) -# HTTP server for /health/ready +# HTTP server for /status class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): - if self.path == "/health/ready": + if self.path == "/status": self.send_response(200) self.end_headers() self.wfile.write(b"ok") diff --git a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/quickstart/cli.py b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/quickstart/cli.py index 626c107aaf..456b074727 100644 --- a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/quickstart/cli.py +++ b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/quickstart/cli.py @@ -91,7 +91,7 @@ def _check_ready_endpoint(port: int, timeout: float = 2.0) -> bool: import httpx try: - response = httpx.get(f"http://localhost:{port}/health/ready", timeout=timeout) + response = httpx.get(f"http://localhost:{port}/status", timeout=timeout) return response.status_code == 200 except Exception: return False diff --git a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py index 35bc857140..e9ee1b915e 100644 --- a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py +++ b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py @@ -92,9 +92,9 @@ def _wait_for_healthy( timeout: int = _HEALTH_TIMEOUT_SECONDS, poll_interval: float = _HEALTH_POLL_INTERVAL, ) -> bool: - """Poll the platform health endpoint until it responds or timeout.""" + """Poll the platform status endpoint until it responds or timeout.""" effective_host = "localhost" if host in ("0.0.0.0", "::") else host # noqa: S104 - url = str(httpx.URL(scheme="http", host=effective_host, port=port, path="/health/ready")) + url = str(httpx.URL(scheme="http", host=effective_host, port=port, path="/status")) deadline = time.monotonic() + timeout while time.monotonic() < deadline: try: @@ -335,7 +335,7 @@ def start_services( ) -> None: """Start platform services in the background. - Detaches the process, polls /health/ready, then returns. + Detaches the process, polls /status, then returns. Examples: nemo services start diff --git a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/setup.py b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/setup.py index d9be1850e5..1d90fafa32 100644 --- a/sdk/python/nemo-platform/src/nemo_platform/cli/commands/setup.py +++ b/sdk/python/nemo-platform/src/nemo_platform/cli/commands/setup.py @@ -283,7 +283,7 @@ def _bootstrap_config_if_missing(base_url: str, workspace: str) -> None: def _check_platform_reachable(base_url: str, timeout: float = 5.0) -> bool: """Return True if the platform health endpoint responds.""" try: - resp = httpx.get(f"{base_url.rstrip('/')}/health/ready", timeout=timeout) + resp = httpx.get(f"{base_url.rstrip('/')}/status", timeout=timeout) return resp.status_code == 200 except Exception: return False diff --git a/sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_services_lifecycle.py b/sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_services_lifecycle.py index 24efecdd32..2d035e1355 100644 --- a/sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_services_lifecycle.py +++ b/sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_services_lifecycle.py @@ -413,10 +413,10 @@ def test_log_preserved_across_restart(self, tmp_path: Path) -> None: with open(desc_path, "w") as f: json.dump(desc, f, indent=2) -# HTTP server for /health/ready +# HTTP server for /status class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): - if self.path == "/health/ready": + if self.path == "/status": self.send_response(200) self.end_headers() self.wfile.write(b"ok")