Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down
6 changes: 3 additions & 3 deletions e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand Down Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions e2e/k8s/scripts/install_helm_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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}"
Expand Down
64 changes: 34 additions & 30 deletions e2e/k8s/scripts/local_build_and_upgrade.sh
Original file line number Diff line number Diff line change
@@ -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-<epoch>)
# 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}"
Comment thread
matthewgrossman marked this conversation as resolved.

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"
4 changes: 2 additions & 2 deletions e2e/k8s/scripts/run_auth_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading