Skip to content

test(global-router): add a multi-cluster Global Discovery deploy test - #18161

Draft
atchernych wants to merge 2 commits into
mainfrom
atchernych/global-discovery-e2e
Draft

atchernych wants to merge 2 commits into
mainfrom
atchernych/global-discovery-e2e

Conversation

@atchernych

Copy link
Copy Markdown
Contributor

Overview

Adds the most basic end-to-end test of multi-cluster Global Discovery, run after every merge. Two vClusters stand in for two clusters: the hub runs the Global Discovery Service, and the member runs a pool whose PoolRelay registers with it across the cluster boundary. The test checks that the hub lists the member's pool and drops it once the relay is gone.

hub vCluster                         member vCluster
Global Discovery Service  <--------  DynamoPoolRelay (operator-managed)
  --pool-discovery registrations       POST /pools on every heartbeat
  --registration-storage memory      DGD: Frontend + mocker worker

The job is non-blocking while the multi-cluster pieces land, and the test may fail until they do.

Key Changes

  • tests/deploy/global_discovery/test_global_discovery.py:
    • The relay reaches the service through the hub Service's ClusterIP. vCluster syncs pods and Services to one host cluster, so an address from one vCluster is routable from a pod of the other.
    • GET /pools on the hub must list the pool under the member's cluster ID, with the model, region and frontend URL the relay registered, and say the list is complete. Heartbeats must keep it listed for three leases, and it must leave about one lease after the relay is deleted.
    • On failure the test saves both clusters' state and logs.
  • tests/deploy/global_discovery/cluster.py: kubectl bound to one cluster's kubeconfig, retried with vcluster_utils.retry_vcluster_api, and HTTP through a port-forward that reopens. The dgd_utils helpers serve only the cluster that KUBECONFIG selects.
  • .github/workflows/post-merge-ci.yml:
    • global-discovery-service-build builds container/Dockerfile.global-discovery-service, which now takes DOCKER_PROXY as the operator's Dockerfile does.
    • deploy-test-global-discovery creates the hub and member vClusters, each with the operator, in a host namespace of their own, and runs the test. setup-dynamo-operator and connect-vcluster always tunnel to 127.0.0.1:8443 and reuse a tunnel they find there, so the job closes the hub's tunnel before it creates the member and connects to the hub again on 8444.
    • deploy-cleanup-global-discovery deletes both vClusters and the namespace.
    • The build and the test have continue-on-error, deploy-status-check does not wait for them, and the Slack notice leaves out jobs named Global Discovery.
  • Temporary: .github/workflows/pr.yaml carries copies of the three jobs, so the test runs on this PR. They come out before merge.

Product gaps

The test works around these. Each needs its own fix.

  • The operator does not pass the PoolRelay's Global Discovery settings. DynamoPoolRelay has no fields for --global-discovery-url, --routing-pool, --routing-pool-region or --routing-pool-frontends (feat(pool-relay): register the routing pool with Global Discovery #18077), and the controller sets none of them. The test sets the matching DYN_RELAY_* variables in the relay's pod template.
  • The relay does not log a registration it holds back. lib/llm/src/pool_relay/routing_pool.rs records why it registers nothing, for example while the DGD's serving topology has no model, only in routing_pool_unregistered_reason. That field is served by the relay's request-plane health endpoint, not the system port's /health, so a pool that never registers leaves no trace in the relay's log.
  • The PoolRelay controller refuses DGDs that use envFrom. Unless a frontend or worker container also sets DYN_DISCOVERY_BACKEND and DYN_KUBE_DISCOVERY_MODE, the relay is withdrawn as UnsupportedDiscovery. The examples load hf-token-secret with envFrom (for example examples/backends/vllm/deploy/agg.yaml), so a relay for one of them is refused. The test passes the token through a secretKeyRef.

Validation

  • Not yet run on a cluster: the pr.yaml jobs on this PR are its first run.
  • pre-commit passes. pytest collects the test, and the marker report finds no missing category for it. The DGD and DynamoPoolRelay manifests validate against the operator's CRD schemas, and the kubectl retry and port-forward paths pass against a stub kubectl.
  • An adversarial review found no blockers, and its findings are fixed.

Issues and Ongoing Work

Pull Request Checklist
  • I used Dynamo's /pr-checklist skill.

🤖 Generated with Claude Code

atchernych and others added 2 commits November 4, 2026 17:24
Run the most basic multi-cluster Global Discovery case on two vClusters
after every merge. The hub runs the Global Discovery Service in
registrations mode. The member runs a mocker DGD and its operator-managed
DynamoPoolRelay, which registers the pool with the hub across the cluster
boundary. The test checks that GET /pools lists the pool with its cluster
ID, model, region and frontend URL, that heartbeats keep it listed, and
that it leaves about one lease after the relay is deleted.

post-merge-ci.yml builds the service image, runs the test in a host
namespace of its own, and deletes both vClusters. The jobs are
non-blocking while the multi-cluster pieces land. The service's Dockerfile
takes DOCKER_PROXY, as the operator's does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
TEMPORARY: pr.yaml gets copies of the three post-merge jobs, so the test
runs on its own pull request. Remove them before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Anna Tchernych <atchernych@nvidia.com>
@github-actions github-actions Bot added test documentation Improvements or additions to documentation container actions labels Nov 5, 2026
Comment thread .github/workflows/pr.yaml
Comment on lines +1449 to +1493
name: Global Discovery Service
needs: [changed-files]
if: |
needs.changed-files.outputs.run_deploy_tests == 'true' &&
needs.changed-files.outputs.deploy == 'true'
runs-on: dynamo-cpu-4gi-amd64
continue-on-error: true
outputs:
image_tag: ${{ steps.build.outputs.image_tag }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialize Dynamo Builder
uses: $/.github/actions/init-dynamo-builder
with:
builder_name: ${{ needs.changed-files.outputs.builder_name }}
flavor: general
arch: linux/amd64
namespace: ${{ vars.BUILDKIT_NAMESPACE }}
- name: Docker Login
uses: $/.github/actions/docker-login
with:
aws_default_region: ${{ vars.AWS_DEFAULT_REGION }}
ecr_registry: ${{ vars.ECR_REGISTRY }}
azure_acr_hostname: ${{ secrets.AZURE_ACR_HOSTNAME }}
azure_acr_user: ${{ secrets.AZURE_ACR_USER }}
azure_acr_password: ${{ secrets.AZURE_ACR_PASSWORD }}
- name: Build and push the Global Discovery Service image
id: build
shell: bash
env:
IMAGE: ${{ secrets.AZURE_ACR_HOSTNAME }}/${{ vars.ACR_REPOSITORY }}
IMAGE_TAG: ${{ github.sha }}-global-discovery-service
DOCKER_PROXY: ${{ vars.ECR_REGISTRY }}/dockerhub/
run: |
set -euo pipefail
docker buildx build --push \
--platform linux/amd64 \
--build-arg "DOCKER_PROXY=${DOCKER_PROXY}" \
-t "${IMAGE}:${IMAGE_TAG}" \
-f container/Dockerfile.global-discovery-service .
# Only the tag crosses jobs: an output holding a secret is dropped.
echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"

deploy-test-global-discovery:
@devin-ai-integration

Copy link
Copy Markdown
Contributor

❌ Dynamo PR CI failed — run 41948641293 (attempt 1) on 9cdec8f76b

Gate checks: ✅ backend-status-check · ✅ deploy-status-check · ✅ dynamo-status-check

Other Jobs
Global Discovery Deploy Test ❌ 1

Failure details

1 job failed: the Global Discovery Deploy Test could not create its second (member) vCluster because vCluster refuses to create more than one virtual cluster in the same host namespace.

❌ Global Discovery Deploy Test: vcluster create fatal: already a virtual cluster in namespace

Job: Global Discovery Deploy Test · Failed step: Create the member vCluster · Logs: gh run view --job 126994131613 -R ai-dynamo/dynamo --log-failed

Checking for existing vCluster ci-41948641293-gd-member in gh-id-41948641293-global-discovery-dt
No existing vCluster named ci-41948641293-gd-member found.
Using host Kubernetes v1.36.4; pinning vCluster guest to v1.36.4
+ vcluster create ci-41948641293-gd-member --namespace gh-id-41948641293-global-discovery-dt --connect=false --upgrade --set controlPlane.distro.k8s.enabled=true --set controlPlane.distro.k8s.version=v1.36.4
01:50:03 fatal there is already a virtual cluster in namespace gh-id-41948641293-global-discovery-dt; creating multiple virtual clusters inside the same namespace is not supported
+ exit 1
##[error]Process completed with exit code 1.

The hub vCluster ci-41948641293-gd-hub was created and the Dynamo platform installed in it earlier in the job; creating the member vCluster ci-41948641293-gd-member in the same host namespace gh-id-41948641293-global-discovery-dt then fails with vCluster's one-cluster-per-namespace restriction. The later diagnostics (127.0.0.1:8443 connection refused, missing Grove webhook Service) are follow-on noise from this failure.

For agents
{"pr": 18161, "run_id": 41948641293, "run_attempt": 1, "head_sha": "9cdec8f76b434b695b641c1ea6368789e3a68ed8",
 "failures": [{"job": "Global Discovery Deploy Test", "job_id": 126994131613, "failed_step": "Create the member vCluster", "signature": "vcluster create: there is already a virtual cluster in namespace gh-id-41948641293-global-discovery-dt; creating multiple virtual clusters inside the same namespace is not supported", "tests": [], "log_cmd": "gh run view --job 126994131613 -R ai-dynamo/dynamo --log-failed"}]}

Posted automatically by Devin for run 41948641293. Updated on every full-CI run of this PR.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

actions container documentation Improvements or additions to documentation size/XXL test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants