name: 'Dynamo Graph Deployment Test' description: 'Deploy a DynamoGraphDeployment to Kubernetes, validate it serves requests, and cleanup' inputs: # Kubernetes Configuration kubeconfig_base64: description: 'Base64-encoded kubeconfig for cluster access' required: true namespace: description: 'Kubernetes namespace for deployment' required: true registry: description: 'Container registry hostname' required: true operator_tag: description: 'Operator image tag (default: main-operator)' required: false default: 'main-operator' hf_token: description: 'HuggingFace token for model access' required: false default: '' dockerhub_username: description: 'Docker Hub username for image pull secrets. Required for rerun self-bootstrap if private images are used.' required: false default: '' dockerhub_password: description: 'Docker Hub password for image pull secrets. Required for rerun self-bootstrap if private images are used.' required: false default: '' framework: description: 'Framework name (vllm, sglang, trtllm)' required: false default: '' profile: description: 'Deployment profile (e.g., disagg_router, agg)' required: false default: '' image: description: 'Full container image reference for the framework runtime' required: false default: '' platform_arch: description: 'Platform architecture (amd64, arm64)' required: false default: 'amd64' test_name: description: 'Name for artifact naming. Defaults to {framework}_{profile}.' required: false default: '' extra_pytest_args: description: 'Additional pytest arguments (e.g., --frontend-image=...)' required: false default: '' runs: using: "composite" steps: - name: Setup Kubeconfig id: setup-kubeconfig shell: bash env: NAMESPACE: ${{ inputs.namespace }} run: | set +x echo "${{ inputs.kubeconfig_base64 }}" | base64 -d > ${{ github.workspace }}/.kubeconfig chmod 600 ${{ github.workspace }}/.kubeconfig echo "KUBECONFIG=${{ github.workspace }}/.kubeconfig" >> $GITHUB_ENV export KUBECONFIG=${{ github.workspace }}/.kubeconfig kubectl config set-context --current --namespace=${NAMESPACE} kubectl config get-contexts - name: Set up Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' cache: 'pip' cache-dependency-path: 'container/deps/requirements.test.txt' - name: Install test dependencies shell: bash run: | python -m pip install --upgrade pip pip install -r container/deps/requirements.test.txt - name: Deploy and Test id: deploy shell: bash env: KUBECONFIG: ${{ github.workspace }}/.kubeconfig NAMESPACE: ${{ inputs.namespace }} FRAMEWORK: ${{ inputs.framework }} PROFILE: ${{ inputs.profile }} IMAGE: ${{ inputs.image }} DYN_TEST_OUTPUT_PATH: ${{ github.workspace }}/test-output TEST_NAME: ${{ inputs.test_name }} EXTRA_PYTEST_ARGS: ${{ inputs.extra_pytest_args }} DYNAMO_TEST_FRAMEWORK: ${{ inputs.framework }} DYNAMO_TEST_PLATFORM: ${{ inputs.platform_arch }} DYNAMO_TEST_TYPE: deploy_${{ inputs.profile }} DYNAMO_TEST_WORKFLOW: ${{ github.workflow }} run: | mkdir -p test-results PYTEST_ARGS="" if [ -n "${FRAMEWORK}" ]; then PYTEST_ARGS+=" --framework=${FRAMEWORK}" fi if [ -n "${PROFILE}" ]; then PYTEST_ARGS+=" --profile=${PROFILE}" fi if [ -n "${IMAGE}" ]; then PYTEST_ARGS+=" --image=${IMAGE}" fi if [ -z "${TEST_NAME}" ]; then TEST_NAME="${FRAMEWORK:-unknown}_${PROFILE:-unknown}" fi pytest tests/deploy/test_deploy.py \ --namespace="${NAMESPACE}" \ ${PYTEST_ARGS} \ ${EXTRA_PYTEST_ARGS} \ -v -s \ --durations=10 \ --junitxml=test-results/pytest_deploy_${TEST_NAME}_${{ inputs.platform_arch }}_${{ github.run_id }}_${{ job.check_run_id }}.xml \ --alluredir=test-results/allure-results \ --log-cli-level=INFO - name: Cleanup Deployment if: always() shell: bash env: NAMESPACE: ${{ inputs.namespace }} GRAPH_NAME: ${{ steps.deploy.outputs.graph_name }} run: | echo "::group::Cleanup Deployment" set -x export KUBECONFIG=${{ github.workspace }}/.kubeconfig echo "=== PRE-CLEANUP STATUS ===" kubectl get dynamographdeployments -n $NAMESPACE || true kubectl get pods -n $NAMESPACE || true kubectl get dynamographdeployments -n $NAMESPACE --no-headers 2>/dev/null \ | awk '$2 == "False" {print $1}' \ | while read -r dep_name; do echo ">>> DETAILED DESCRIPTION FOR FAILED DEPLOYMENT: $dep_name" kubectl describe dynamographdeployments "$dep_name" -n $NAMESPACE done || true if kubectl get dynamographdeployments "${GRAPH_NAME}" -n $NAMESPACE &>/dev/null; then echo "DGD ${GRAPH_NAME} still exists after test, deleting..." kubectl delete dynamographdeployments ${GRAPH_NAME} -n $NAMESPACE --timeout=60s else echo "DGD ${GRAPH_NAME} already cleaned up by test" fi echo "::endgroup::" - name: Determine test name for artifacts id: test-name if: always() shell: bash env: TEST_NAME: ${{ inputs.test_name }} FRAMEWORK: ${{ inputs.framework }} PROFILE: ${{ inputs.profile }} run: | if [ -z "${TEST_NAME}" ]; then TEST_NAME="${FRAMEWORK:-unknown}_${PROFILE:-unknown}" fi echo "name=${TEST_NAME}" >> $GITHUB_OUTPUT - name: Upload Test Results uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f #v6 if: always() with: name: test-results-${{ steps.test-name.outputs.name }}-${{ inputs.platform_arch }}-${{ github.run_id }}-${{ job.check_run_id }} path: test-results/pytest_deploy_${{ steps.test-name.outputs.name }}_${{ inputs.platform_arch }}_${{ github.run_id }}_${{ job.check_run_id }}.xml retention-days: 7 - name: Upload Pod Logs uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f #v6 if: always() with: name: pod-logs-${{ steps.test-name.outputs.name }}-${{ inputs.platform_arch }}-${{ github.run_id }}-${{ job.check_run_id }} path: ${{ github.workspace }}/test-output/ if-no-files-found: warn retention-days: 7 - name: Upload Allure Results uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f #v6 if: always() with: name: allure-results-${{ steps.test-name.outputs.name }}-${{ inputs.platform_arch }}-${{ github.run_id }}-${{ job.check_run_id }} path: test-results/allure-results/ retention-days: 7 if-no-files-found: ignore