Merge pull request #147 from mhdawson/rag-evals #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: E2E Tests | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - 'frontend/**' | |
| - 'deploy/helm/**' | |
| - 'tests/**' | |
| - '.github/workflows/e2e-tests.yaml' | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| # MaaS configuration - can be overridden with repository secrets for different environments | |
| env: | |
| MAAS_ENDPOINT: ${{ secrets.MAAS_ENDPOINT }} | |
| MAAS_MODEL_ID: ${{ secrets.MAAS_MODEL_ID }} | |
| # MAAS_API_KEY is passed as a secret in the helm install step | |
| jobs: | |
| unit-tests: | |
| name: Unit Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install unit test dependencies | |
| run: | | |
| pip install -r tests/unit/requirements.txt | |
| - name: Run unit tests | |
| run: | | |
| pytest tests/unit/ -v --tb=short --cov=frontend --cov-report=term-missing | |
| - name: Upload unit test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: unit-test-results | |
| path: | | |
| .coverage | |
| htmlcov/ | |
| integration-tests: | |
| name: Integration Tests (Streamlit App) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: unit-tests | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install integration test dependencies | |
| run: | | |
| pip install -r tests/integration/requirements.txt | |
| - name: Run integration tests | |
| run: | | |
| pytest tests/integration/test_*.py -v --tb=short | |
| - name: Upload integration test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: integration-test-results | |
| path: pytest-results/ | |
| llamastack-integration-tests: | |
| name: LlamaStack Integration Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| needs: unit-tests | |
| # Skip this job for fork PRs since secrets are not available | |
| if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install test dependencies | |
| run: | | |
| pip install -r tests/integration/llamastack/requirements.txt | |
| - name: Create Kind cluster config file | |
| run: | | |
| cat <<EOF > kind-config.yaml | |
| kind: Cluster | |
| apiVersion: kind.x-k8s.io/v1alpha4 | |
| nodes: | |
| - role: control-plane | |
| extraPortMappings: | |
| - containerPort: 30080 | |
| hostPort: 8501 | |
| protocol: TCP | |
| - containerPort: 30081 | |
| hostPort: 8321 | |
| protocol: TCP | |
| EOF | |
| - name: Create Kind cluster | |
| uses: helm/kind-action@v1 | |
| with: | |
| cluster_name: rag-e2e | |
| config: kind-config.yaml | |
| - name: Install Required CRDs | |
| run: | | |
| echo "Installing CRDs required by helm chart subcomponents..." | |
| # OpenShift Route CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: routes.route.openshift.io | |
| spec: | |
| group: route.openshift.io | |
| names: | |
| kind: Route | |
| listKind: RouteList | |
| plural: routes | |
| singular: route | |
| scope: Namespaced | |
| versions: | |
| - name: v1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| # KServe InferenceService CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: inferenceservices.serving.kserve.io | |
| spec: | |
| group: serving.kserve.io | |
| names: | |
| kind: InferenceService | |
| listKind: InferenceServiceList | |
| plural: inferenceservices | |
| singular: inferenceservice | |
| scope: Namespaced | |
| versions: | |
| - name: v1beta1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| # KServe ServingRuntime CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: servingruntimes.serving.kserve.io | |
| spec: | |
| group: serving.kserve.io | |
| names: | |
| kind: ServingRuntime | |
| listKind: ServingRuntimeList | |
| plural: servingruntimes | |
| singular: servingruntime | |
| scope: Namespaced | |
| versions: | |
| - name: v1alpha1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| # OpenDataHub DataSciencePipelinesApplication CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: datasciencepipelinesapplications.datasciencepipelinesapplications.opendatahub.io | |
| spec: | |
| group: datasciencepipelinesapplications.opendatahub.io | |
| names: | |
| kind: DataSciencePipelinesApplication | |
| listKind: DataSciencePipelinesApplicationList | |
| plural: datasciencepipelinesapplications | |
| singular: datasciencepipelinesapplication | |
| scope: Namespaced | |
| versions: | |
| - name: v1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| # Kubeflow Notebook CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: notebooks.kubeflow.org | |
| spec: | |
| group: kubeflow.org | |
| names: | |
| kind: Notebook | |
| listKind: NotebookList | |
| plural: notebooks | |
| singular: notebook | |
| scope: Namespaced | |
| versions: | |
| - name: v1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| echo "Waiting for all CRDs to be established..." | |
| kubectl wait --for condition=established --timeout=60s crd/routes.route.openshift.io | |
| kubectl wait --for condition=established --timeout=60s crd/inferenceservices.serving.kserve.io | |
| kubectl wait --for condition=established --timeout=60s crd/servingruntimes.serving.kserve.io | |
| kubectl wait --for condition=established --timeout=60s crd/datasciencepipelinesapplications.datasciencepipelinesapplications.opendatahub.io | |
| kubectl wait --for condition=established --timeout=60s crd/notebooks.kubeflow.org | |
| echo "✅ All required CRDs installed successfully" | |
| - name: Verify cluster | |
| run: | | |
| kubectl cluster-info | |
| kubectl get nodes | |
| kubectl get pods -A | |
| kubectl get crds | grep route || echo "Route CRD check" | |
| - name: Add Helm repository | |
| run: | | |
| helm repo add rag-charts https://rh-ai-quickstart.github.io/ai-architecture-charts | |
| helm repo update | |
| - name: Build Helm dependencies | |
| run: | | |
| cd deploy/helm/rag | |
| helm dependency build | |
| - name: Display MaaS configuration | |
| run: | | |
| echo "=========================================" | |
| echo "MaaS Configuration (Public)" | |
| echo "=========================================" | |
| echo "Model ID: ${MAAS_MODEL_ID}" | |
| echo "Endpoint: ${MAAS_ENDPOINT}" | |
| echo "=========================================" | |
| - name: Validate MaaS API key | |
| env: | |
| MAAS_API_KEY: ${{ secrets.MAAS_API_KEY }} | |
| run: | | |
| # Check if MAAS_API_KEY secret is set | |
| if [ -z "${MAAS_API_KEY}" ]; then | |
| echo "" | |
| echo "❌ ERROR: MAAS_API_KEY secret is not configured!" | |
| echo "" | |
| # Check if this is a fork PR | |
| if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then | |
| echo "⚠️ This appears to be a pull request from a fork." | |
| echo "GitHub Actions does not provide access to secrets for security reasons" | |
| echo "when running workflows triggered by pull requests from forks." | |
| fi | |
| echo "" | |
| echo "To fix this, add the MAAS_API_KEY secret to your repository:" | |
| echo "1. Go to: Settings > Secrets and variables > Actions" | |
| echo "2. Click 'New repository secret'" | |
| echo "3. Name: MAAS_API_KEY" | |
| echo "4. Value: Your Red Hat MaaS API key" | |
| echo "" | |
| echo "Note: For fork PRs, these tests will be skipped automatically." | |
| echo "For more information, see:" | |
| echo "https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions" | |
| echo "" | |
| exit 1 | |
| fi | |
| echo "✅ MaaS API key is configured" | |
| - name: Install RAG application with MaaS | |
| env: | |
| MAAS_API_KEY: ${{ secrets.MAAS_API_KEY }} | |
| run: | | |
| # Create namespace | |
| kubectl create namespace rag-e2e || true | |
| echo "=========================================" | |
| echo "Installing RAG application with MaaS" | |
| echo "=========================================" | |
| echo "Using model: ${MAAS_MODEL_ID}" | |
| echo "Helm chart: deploy/helm/rag" | |
| echo "Namespace: rag-e2e" | |
| echo "Values: tests/e2e/values-e2e.yaml" | |
| echo "=========================================" | |
| # Install the chart with e2e values | |
| # Override MaaS configuration from environment variables | |
| # Note: All MaaS settings (endpoint, model ID, API key) passed via --set | |
| # Some values may appear as *** in logs due to GitHub Actions secret masking | |
| helm install rag deploy/helm/rag \ | |
| --namespace rag-e2e \ | |
| --values tests/e2e/values-e2e.yaml \ | |
| --set global.models.${MAAS_MODEL_ID}.url="${MAAS_ENDPOINT}" \ | |
| --set global.models.${MAAS_MODEL_ID}.id="${MAAS_MODEL_ID}" \ | |
| --set global.models.${MAAS_MODEL_ID}.enabled=true \ | |
| --set global.models.${MAAS_MODEL_ID}.apiToken="${MAAS_API_KEY}" \ | |
| --set-json llama-stack.initContainers='[]' \ | |
| --skip-crds \ | |
| --timeout 20m \ | |
| --debug | |
| - name: Wait for core services to be ready | |
| run: | | |
| echo "=========================================" | |
| echo "Listing all resources in namespace..." | |
| echo "=========================================" | |
| kubectl get all -n rag-e2e | |
| echo "" | |
| echo "=========================================" | |
| echo "Checking deployments..." | |
| echo "=========================================" | |
| kubectl get deployments -n rag-e2e -o wide | |
| echo "" | |
| echo "=========================================" | |
| echo "Checking pods..." | |
| echo "=========================================" | |
| kubectl get pods -n rag-e2e -o wide | |
| echo "" | |
| echo "=========================================" | |
| echo "Waiting for Llama Stack deployment (10min timeout)..." | |
| echo "=========================================" | |
| kubectl wait --for=condition=available --timeout=600s \ | |
| deployment/llamastack -n rag-e2e || { | |
| echo "❌ Llama Stack deployment failed to become available" | |
| echo "Pod status:" | |
| kubectl get pods -l app.kubernetes.io/name=llamastack -n rag-e2e | |
| echo "Pod logs:" | |
| kubectl logs -l app.kubernetes.io/name=llamastack -n rag-e2e --tail=100 | |
| exit 1 | |
| } | |
| echo "" | |
| echo "=========================================" | |
| echo "Waiting for RAG UI deployment (5min timeout)..." | |
| echo "=========================================" | |
| kubectl wait --for=condition=available --timeout=300s \ | |
| deployment/rag -n rag-e2e || { | |
| echo "❌ RAG UI deployment failed to become available" | |
| echo "Pod status:" | |
| kubectl get pods -l app.kubernetes.io/name=rag -n rag-e2e | |
| echo "Pod logs:" | |
| kubectl logs -l app.kubernetes.io/name=rag -n rag-e2e --tail=100 | |
| exit 1 | |
| } | |
| echo "" | |
| echo "=========================================" | |
| echo "Waiting for pods to be ready..." | |
| echo "=========================================" | |
| kubectl wait --for=condition=ready --timeout=600s \ | |
| pod -l app.kubernetes.io/name=llamastack -n rag-e2e | |
| kubectl wait --for=condition=ready --timeout=300s \ | |
| pod -l app.kubernetes.io/name=rag -n rag-e2e | |
| echo "" | |
| echo "=========================================" | |
| echo "✅ ALL CORE SERVICES ARE READY!" | |
| echo "=========================================" | |
| kubectl get pods -n rag-e2e -o wide | |
| - name: Expose services via NodePort | |
| run: | | |
| # Expose RAG UI | |
| kubectl patch service rag -n rag-e2e -p '{"spec":{"type":"NodePort","ports":[{"port":8501,"nodePort":30080}]}}' | |
| # Expose Llama Stack | |
| kubectl patch service llamastack -n rag-e2e -p '{"spec":{"type":"NodePort","ports":[{"port":8321,"nodePort":30081}]}}' | |
| # Verify services | |
| kubectl get services -n rag-e2e | |
| - name: Port forward services | |
| run: | | |
| # Start port forwarding in background | |
| kubectl port-forward -n rag-e2e svc/rag 8501:8501 & | |
| kubectl port-forward -n rag-e2e svc/llamastack 8321:8321 & | |
| # Wait for port forwarding to establish | |
| sleep 10 | |
| - name: Run LlamaStack integration tests with MaaS inference | |
| env: | |
| LLAMA_STACK_ENDPOINT: http://localhost:8321 | |
| RAG_UI_ENDPOINT: http://localhost:8501 | |
| INFERENCE_MODEL: ${{ env.MAAS_MODEL_ID }} | |
| SKIP_MODEL_TESTS: "false" # Enable inference tests with MaaS | |
| run: | | |
| echo "Starting LlamaStack integration tests with MaaS-enabled inference..." | |
| echo "Model: ${INFERENCE_MODEL}" | |
| echo "MaaS Endpoint: ${MAAS_ENDPOINT}" | |
| pytest tests/integration/llamastack/ -v --tb=short | |
| - name: Debug - Get pod logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== Deployment status ===" | |
| kubectl get deployments -n rag-e2e | |
| echo "=== Pod status ===" | |
| kubectl get pods -n rag-e2e -o wide | |
| echo "=== Service status ===" | |
| kubectl get services -n rag-e2e | |
| echo "=== Events ===" | |
| kubectl get events -n rag-e2e --sort-by='.lastTimestamp' | |
| echo "=== RAG UI logs ===" | |
| kubectl logs -l app.kubernetes.io/name=rag -n rag-e2e --tail=200 || echo "No RAG UI logs available" | |
| echo "=== Llama Stack logs (CRITICAL - Check model registration) ===" | |
| kubectl logs -l app.kubernetes.io/name=llamastack -n rag-e2e --tail=300 || echo "No Llama Stack logs available" | |
| echo "=== Llama Stack pod details ===" | |
| kubectl describe pod -l app.kubernetes.io/name=llamastack -n rag-e2e || echo "No pod details" | |
| echo "=== PGVector logs ===" | |
| kubectl logs -l app.kubernetes.io/name=pgvector -n rag-e2e --tail=100 || echo "No PGVector logs available" | |
| echo "=== MinIO logs ===" | |
| kubectl logs -l app.kubernetes.io/name=minio -n rag-e2e --tail=100 || echo "No MinIO logs available" | |
| echo "=== Ingestion Pipeline logs (if enabled) ===" | |
| kubectl logs -l app.kubernetes.io/name=ingestion-pipeline -n rag-e2e --tail=100 || echo "No ingestion pipeline logs" | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: llamastack-integration-test-results | |
| path: pytest-results/ | |
| - name: Cleanup | |
| if: always() | |
| run: | | |
| # Kill port-forward processes | |
| pkill -f "kubectl port-forward" || true | |
| # Optional: Uncomment to delete cluster | |
| # kind delete cluster --name rag-e2e | |
| fork-pr-notice: | |
| name: Fork PR Notice | |
| runs-on: ubuntu-latest | |
| # Only run this job for fork PRs | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository | |
| steps: | |
| - name: Notice about skipped tests | |
| run: | | |
| echo "## ⚠️ Fork Pull Request Detected" | |
| echo "" | |
| echo "This pull request is from a fork. For security reasons, GitHub Actions does not" | |
| echo "provide access to repository secrets (like \`MAAS_API_KEY\`) for workflows triggered" | |
| echo "by pull requests from forks." | |
| echo "" | |
| echo "**The following tests have been skipped:**" | |
| echo "- LlamaStack Integration Tests (requires MAAS_API_KEY)" | |
| echo "- UI E2E Tests (requires MAAS_API_KEY)" | |
| echo "" | |
| echo "**Tests that still run:**" | |
| echo "- ✅ Unit Tests" | |
| echo "- ✅ Integration Tests (Streamlit App)" | |
| echo "" | |
| echo "The MaaS-dependent tests will run automatically when this PR is merged to \`main\`." | |
| echo "" | |
| echo "For more information, see:" | |
| echo "https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions" | |
| ui-e2e-tests: | |
| name: UI E2E Tests (Playwright) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| needs: [unit-tests, integration-tests] | |
| # Skip this job for fork PRs since secrets are not available | |
| if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install UI test dependencies | |
| run: | | |
| pip install -r tests/e2e_ui/requirements.txt | |
| - name: Install Playwright browsers | |
| run: | | |
| playwright install chromium | |
| - name: Create Kind cluster config file | |
| run: | | |
| cat <<EOF > kind-config.yaml | |
| kind: Cluster | |
| apiVersion: kind.x-k8s.io/v1alpha4 | |
| nodes: | |
| - role: control-plane | |
| extraPortMappings: | |
| - containerPort: 30080 | |
| hostPort: 8501 | |
| protocol: TCP | |
| - containerPort: 30081 | |
| hostPort: 8321 | |
| protocol: TCP | |
| EOF | |
| - name: Create Kind cluster | |
| uses: helm/kind-action@v1 | |
| with: | |
| cluster_name: rag-e2e-ui | |
| config: kind-config.yaml | |
| - name: Install Required CRDs | |
| run: | | |
| echo "Installing CRDs required by helm chart subcomponents..." | |
| # OpenShift Route CRD | |
| kubectl apply -f - <<EOF | |
| apiVersion: apiextensions.k8s.io/v1 | |
| kind: CustomResourceDefinition | |
| metadata: | |
| name: routes.route.openshift.io | |
| spec: | |
| group: route.openshift.io | |
| names: | |
| kind: Route | |
| listKind: RouteList | |
| plural: routes | |
| singular: route | |
| scope: Namespaced | |
| versions: | |
| - name: v1 | |
| served: true | |
| storage: true | |
| schema: | |
| openAPIV3Schema: | |
| type: object | |
| x-kubernetes-preserve-unknown-fields: true | |
| EOF | |
| kubectl wait --for condition=established --timeout=60s crd/routes.route.openshift.io | |
| echo "✅ All required CRDs installed successfully" | |
| - name: Add Helm repository | |
| run: | | |
| helm repo add rag-charts https://rh-ai-quickstart.github.io/ai-architecture-charts | |
| helm repo update | |
| - name: Build Helm dependencies | |
| run: | | |
| cd deploy/helm/rag | |
| helm dependency build | |
| - name: Install RAG application | |
| env: | |
| MAAS_API_KEY: ${{ secrets.MAAS_API_KEY }} | |
| run: | | |
| kubectl create namespace rag-e2e-ui || true | |
| helm install rag deploy/helm/rag \ | |
| --namespace rag-e2e-ui \ | |
| --values tests/e2e/values-e2e.yaml \ | |
| --set global.models.${MAAS_MODEL_ID}.url="${MAAS_ENDPOINT}" \ | |
| --set global.models.${MAAS_MODEL_ID}.id="${MAAS_MODEL_ID}" \ | |
| --set global.models.${MAAS_MODEL_ID}.enabled=true \ | |
| --set global.models.${MAAS_MODEL_ID}.apiToken="${MAAS_API_KEY}" \ | |
| --set-json llama-stack.initContainers='[]' \ | |
| --skip-crds \ | |
| --timeout 20m \ | |
| --debug | |
| - name: Wait for services to be ready | |
| run: | | |
| kubectl wait --for=condition=available --timeout=600s \ | |
| deployment/llamastack -n rag-e2e-ui | |
| kubectl wait --for=condition=available --timeout=300s \ | |
| deployment/rag -n rag-e2e-ui | |
| echo "Waiting for pods to be ready..." | |
| kubectl wait --for=condition=ready --timeout=600s \ | |
| pod -l app.kubernetes.io/name=llamastack -n rag-e2e-ui | |
| kubectl wait --for=condition=ready --timeout=300s \ | |
| pod -l app.kubernetes.io/name=rag -n rag-e2e-ui | |
| echo "✅ All pods are ready" | |
| kubectl get pods -n rag-e2e-ui | |
| - name: Expose services via NodePort | |
| run: | | |
| kubectl patch service rag -n rag-e2e-ui -p '{"spec":{"type":"NodePort","ports":[{"port":8501,"nodePort":30080}]}}' | |
| kubectl patch service llamastack -n rag-e2e-ui -p '{"spec":{"type":"NodePort","ports":[{"port":8321,"nodePort":30081}]}}' | |
| # Verify services | |
| kubectl get services -n rag-e2e-ui | |
| - name: Run UI E2E tests with Playwright | |
| env: | |
| RAG_UI_ENDPOINT: http://localhost:8501 | |
| LLAMA_STACK_ENDPOINT: http://localhost:8321 | |
| MAAS_ENDPOINT: ${{ env.MAAS_ENDPOINT }} | |
| MAAS_MODEL_ID: ${{ env.MAAS_MODEL_ID }} | |
| SKIP_MODEL_TESTS: "false" # Enable MaaS inference tests in UI | |
| run: | | |
| echo "Starting port forwarding and running tests..." | |
| # Start port forwarding in background and keep them running | |
| echo "Starting port forwarding for RAG UI..." | |
| kubectl port-forward -n rag-e2e-ui svc/rag 8501:8501 > /tmp/rag-portforward.log 2>&1 & | |
| RAG_PF_PID=$! | |
| echo "RAG port-forward PID: $RAG_PF_PID" | |
| echo "Starting port forwarding for LlamaStack..." | |
| kubectl port-forward -n rag-e2e-ui svc/llamastack 8321:8321 > /tmp/llamastack-portforward.log 2>&1 & | |
| LLAMASTACK_PF_PID=$! | |
| echo "LlamaStack port-forward PID: $LLAMASTACK_PF_PID" | |
| # Function to check if port forwarding is working | |
| check_port_forwarding() { | |
| (timeout 2 bash -c 'cat < /dev/null > /dev/tcp/localhost/8501' 2>/dev/null) && \ | |
| (timeout 2 bash -c 'cat < /dev/null > /dev/tcp/localhost/8321' 2>/dev/null) | |
| } | |
| # Function to verify processes are alive | |
| check_processes() { | |
| kill -0 $RAG_PF_PID 2>/dev/null && kill -0 $LLAMASTACK_PF_PID 2>/dev/null | |
| } | |
| # Wait for port forwarding to establish | |
| echo "Waiting for port forwarding to be ready..." | |
| for i in {1..30}; do | |
| if check_port_forwarding && check_processes; then | |
| echo "✅ Port forwarding is working! (attempt $i)" | |
| break | |
| fi | |
| if [ $i -eq 30 ]; then | |
| echo "❌ Port forwarding failed to establish after 30 attempts" | |
| echo "RAG port-forward log:" | |
| cat /tmp/rag-portforward.log || true | |
| echo "LlamaStack port-forward log:" | |
| cat /tmp/llamastack-portforward.log || true | |
| echo "Checking port-forward processes:" | |
| ps aux | grep "kubectl port-forward" || true | |
| echo "Checking if ports are listening:" | |
| ss -tlnp | grep -E ':(8501|8321)' || netstat -tlnp 2>/dev/null | grep -E ':(8501|8321)' || true | |
| exit 1 | |
| fi | |
| echo "Waiting for port forwarding... (attempt $i/30)" | |
| sleep 2 | |
| done | |
| # Set up cleanup trap to kill port forwarding on exit | |
| trap "echo 'Cleaning up port forwarding...'; kill $RAG_PF_PID $LLAMASTACK_PF_PID 2>/dev/null || true" EXIT | |
| echo "Running UI E2E tests with MaaS integration..." | |
| echo "MaaS Endpoint: ${MAAS_ENDPOINT}" | |
| echo "MaaS Model ID: ${MAAS_MODEL_ID}" | |
| # Run tests | |
| pytest tests/e2e_ui/ -v --tb=short --browser chromium || TEST_EXIT_CODE=$? | |
| # Verify port forwarding was still working after tests | |
| if ! check_port_forwarding; then | |
| echo "⚠️ Warning: Port forwarding stopped working during tests" | |
| echo "RAG port-forward log:" | |
| cat /tmp/rag-portforward.log || true | |
| echo "LlamaStack port-forward log:" | |
| cat /tmp/llamastack-portforward.log || true | |
| fi | |
| # Exit with test result | |
| exit ${TEST_EXIT_CODE:-0} | |
| - name: Upload Playwright test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-test-results | |
| path: | | |
| test-results/ | |
| playwright-report/ | |
| - name: Upload Playwright screenshots | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-screenshots | |
| path: test-results/ | |
| - name: Debug - Get pod logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== RAG UI logs ===" | |
| kubectl logs -l app.kubernetes.io/name=rag -n rag-e2e-ui --tail=200 || echo "No RAG UI logs" | |
| echo "=== Llama Stack logs ===" | |
| kubectl logs -l app.kubernetes.io/name=llamastack -n rag-e2e-ui --tail=300 || echo "No Llama Stack logs" | |
| - name: Cleanup UI tests | |
| if: always() | |
| run: | | |
| pkill -f "kubectl port-forward" || true | |
| # kind delete cluster --name rag-e2e-ui |