first commit
This commit is contained in:
301
.github/workflows/scripts/test-docker-image.sh
vendored
Executable file
301
.github/workflows/scripts/test-docker-image.sh
vendored
Executable file
@@ -0,0 +1,301 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Test Docker image by building, starting with docker-compose, and running E2E API tests
|
||||
# Usage: ./test-docker-image.sh <platform>
|
||||
# Example: ./test-docker-image.sh linux/amd64
|
||||
|
||||
# Get the absolute path of the script directory
|
||||
if command -v readlink >/dev/null 2>&1 && readlink -f "$0" >/dev/null 2>&1; then
|
||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
else
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||
fi
|
||||
|
||||
# Repository root (3 levels up from .github/workflows/scripts)
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd -P)"
|
||||
|
||||
|
||||
# Setup Go workspace for CI (go.work is gitignored, must be regenerated)
|
||||
source "$SCRIPT_DIR/setup-go-workspace.sh"
|
||||
|
||||
PLATFORM=${1:-linux/amd64}
|
||||
ARCH=$(echo "$PLATFORM" | cut -d'/' -f2)
|
||||
IMAGE_TAG="bifrost-test:ci-${GITHUB_SHA:-local}-${ARCH}"
|
||||
CONTAINER_NAME="bifrost-test-${ARCH}"
|
||||
TEST_PORT=8080
|
||||
DOCKER_COMPOSE_FILE="$REPO_ROOT/tests/docker-compose.yml"
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
CONFIG_FILE="$TEMP_DIR/config.json"
|
||||
|
||||
echo "=== Testing Docker image for ${PLATFORM} ==="
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
echo ""
|
||||
echo "=== Cleaning up ==="
|
||||
|
||||
# Stop and remove Bifrost container
|
||||
echo "Stopping Bifrost container..."
|
||||
docker stop "${CONTAINER_NAME}" > /dev/null 2>&1 || true
|
||||
docker rm "${CONTAINER_NAME}" > /dev/null 2>&1 || true
|
||||
|
||||
# Stop docker-compose services
|
||||
echo "Stopping docker-compose services..."
|
||||
docker compose -f "$DOCKER_COMPOSE_FILE" down -v > /dev/null 2>&1 || true
|
||||
|
||||
# Remove test image
|
||||
echo "Removing test image..."
|
||||
docker rmi "${IMAGE_TAG}" > /dev/null 2>&1 || true
|
||||
|
||||
# Remove temp directory
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Build the image using local module sources (pre-release CI builds)
|
||||
echo "Building Docker image (local modules)..."
|
||||
docker build \
|
||||
--platform "${PLATFORM}" \
|
||||
-f transports/Dockerfile.local \
|
||||
-t "${IMAGE_TAG}" \
|
||||
.
|
||||
|
||||
echo "Build complete: ${IMAGE_TAG}"
|
||||
|
||||
# Start docker-compose services (Postgres, Weaviate, Redis, Qdrant)
|
||||
echo ""
|
||||
echo "=== Starting docker-compose services ==="
|
||||
docker compose -f "$DOCKER_COMPOSE_FILE" up -d
|
||||
|
||||
# Wait for Postgres to be ready
|
||||
echo "Waiting for Postgres to be ready..."
|
||||
MAX_WAIT=60
|
||||
ELAPSED=0
|
||||
while [ $ELAPSED -lt $MAX_WAIT ]; do
|
||||
if docker compose -f "$DOCKER_COMPOSE_FILE" exec -T postgres pg_isready -U bifrost -d bifrost > /dev/null 2>&1; then
|
||||
echo "Postgres is ready"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
ELAPSED=$((ELAPSED + 2))
|
||||
done
|
||||
|
||||
if [ $ELAPSED -ge $MAX_WAIT ]; then
|
||||
echo "ERROR: Postgres did not become ready within ${MAX_WAIT}s"
|
||||
docker compose -f "$DOCKER_COMPOSE_FILE" logs postgres
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the docker network name
|
||||
NETWORK_NAME=$(docker compose -f "$DOCKER_COMPOSE_FILE" ps --format json | head -1 | jq -r '.Networks' 2>/dev/null || echo "tests_bifrost_network")
|
||||
if [ -z "$NETWORK_NAME" ] || [ "$NETWORK_NAME" = "null" ]; then
|
||||
NETWORK_NAME="tests_bifrost_network"
|
||||
fi
|
||||
|
||||
# Generate config.json with all providers and Postgres stores
|
||||
echo ""
|
||||
echo "=== Generating config.json ==="
|
||||
cat > "$CONFIG_FILE" << 'CONFIGEOF'
|
||||
{
|
||||
"$schema": "https://www.getbifrost.ai/schema",
|
||||
"providers": {
|
||||
"openai": {
|
||||
"keys": [{ "name": "OpenAI API Key", "value": "env.OPENAI_API_KEY", "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"elevenlabs": {
|
||||
"keys": [{ "name": "ElevenLabs API Key", "value": "env.ELEVENLABS_API_KEY", "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"xai": {
|
||||
"keys": [{ "name": "Xai API Key", "value": "env.XAI_API_KEY", "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"huggingface": {
|
||||
"keys": [{ "name": "Hugging Face API Key", "value": "env.HUGGING_FACE_API_KEY", "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"anthropic": {
|
||||
"keys": [{ "name": "Anthropic API Key", "value": "env.ANTHROPIC_API_KEY", "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"gemini": {
|
||||
"keys": [{ "value": "env.GEMINI_API_KEY", "weight": 1, "use_for_batch_api": true, "name": "Gemini API Key" }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"vertex": {
|
||||
"keys": [{ "name": "Vertex API Key", "vertex_key_config": { "project_id": "env.VERTEX_PROJECT_ID", "region": "env.GOOGLE_LOCATION", "auth_credentials": "env.VERTEX_CREDENTIALS" }, "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"mistral": {
|
||||
"keys": [{ "name": "Mistral API Key", "value": "env.MISTRAL_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"cohere": {
|
||||
"keys": [{ "name": "Cohere API Key", "value": "env.COHERE_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"groq": {
|
||||
"keys": [{ "name": "Groq API Key", "value": "env.GROQ_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"perplexity": {
|
||||
"keys": [{ "name": "Perplexity API Key", "value": "env.PERPLEXITY_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"cerebras": {
|
||||
"keys": [{ "name": "Cerebras API Key", "value": "env.CEREBRAS_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"openrouter": {
|
||||
"keys": [{ "name": "OpenRouter API Key", "value": "env.OPENROUTER_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"parasail": {
|
||||
"keys": [{ "name": "Parasail API Key", "value": "env.PARASAIL_API_KEY", "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"azure": {
|
||||
"keys": [{ "name": "Azure API Key", "value": "env.AZURE_API_KEY", "azure_key_config": { "endpoint": "env.AZURE_ENDPOINT", "api_version": "env.AZURE_API_VERSION" }, "weight": 1 }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"bedrock": {
|
||||
"keys": [{ "name": "Bedrock API Key", "bedrock_key_config": { "access_key": "env.AWS_ACCESS_KEY_ID", "secret_key": "env.AWS_SECRET_ACCESS_KEY", "region": "env.AWS_REGION", "arn": "env.AWS_ARN" }, "weight": 1, "use_for_batch_api": true }],
|
||||
"network_config": { "default_request_timeout_in_seconds": 300 }
|
||||
},
|
||||
"replicate": {
|
||||
"keys": [{ "name": "Replicate API KEY", "value": "env.REPLICATE_API_KEY", "weight": 1.0, "use_for_batch_api": true }]
|
||||
}
|
||||
},
|
||||
"config_store": {
|
||||
"enabled": true,
|
||||
"type": "postgres",
|
||||
"config": {
|
||||
"host": "postgres",
|
||||
"port": "5432",
|
||||
"user": "bifrost",
|
||||
"password": "bifrost_password",
|
||||
"db_name": "bifrost",
|
||||
"ssl_mode": "disable"
|
||||
}
|
||||
},
|
||||
"logs_store": {
|
||||
"enabled": true,
|
||||
"type": "postgres",
|
||||
"config": {
|
||||
"host": "postgres",
|
||||
"port": "5432",
|
||||
"user": "bifrost",
|
||||
"password": "bifrost_password",
|
||||
"db_name": "bifrost",
|
||||
"ssl_mode": "disable"
|
||||
}
|
||||
},
|
||||
"governance": {
|
||||
"virtual_keys": [
|
||||
{
|
||||
"id": "vk-test",
|
||||
"value": "sk-bf-test-key",
|
||||
"is_active": true,
|
||||
"name": "vk-test"
|
||||
}
|
||||
]
|
||||
},
|
||||
"client": {
|
||||
"drop_excess_requests": false,
|
||||
"initial_pool_size": 300,
|
||||
"allowed_origins": ["http://localhost:3000", "https://localhost:3000"],
|
||||
"enable_logging": true,
|
||||
"enforce_governance_header": false,
|
||||
"allow_direct_keys": false,
|
||||
"max_request_body_size_mb": 100
|
||||
},
|
||||
"encryption_key": ""
|
||||
}
|
||||
CONFIGEOF
|
||||
|
||||
echo "Config file created at: $CONFIG_FILE"
|
||||
|
||||
# Run the Bifrost container connected to the docker-compose network
|
||||
echo ""
|
||||
echo "=== Starting Bifrost container ==="
|
||||
docker run -d \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
--platform "${PLATFORM}" \
|
||||
--network "${NETWORK_NAME}" \
|
||||
-p ${TEST_PORT}:8080 \
|
||||
-e APP_PORT=8080 \
|
||||
-e APP_HOST=0.0.0.0 \
|
||||
-e OPENAI_API_KEY="${OPENAI_API_KEY:-}" \
|
||||
-e ELEVENLABS_API_KEY="${ELEVENLABS_API_KEY:-}" \
|
||||
-e XAI_API_KEY="${XAI_API_KEY:-}" \
|
||||
-e HUGGING_FACE_API_KEY="${HUGGING_FACE_API_KEY:-}" \
|
||||
-e ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}" \
|
||||
-e GEMINI_API_KEY="${GEMINI_API_KEY:-}" \
|
||||
-e VERTEX_PROJECT_ID="${VERTEX_PROJECT_ID:-}" \
|
||||
-e VERTEX_CREDENTIALS="${VERTEX_CREDENTIALS:-}" \
|
||||
-e GOOGLE_LOCATION="${GOOGLE_LOCATION:-us-central1}" \
|
||||
-e MISTRAL_API_KEY="${MISTRAL_API_KEY:-}" \
|
||||
-e COHERE_API_KEY="${COHERE_API_KEY:-}" \
|
||||
-e GROQ_API_KEY="${GROQ_API_KEY:-}" \
|
||||
-e PERPLEXITY_API_KEY="${PERPLEXITY_API_KEY:-}" \
|
||||
-e CEREBRAS_API_KEY="${CEREBRAS_API_KEY:-}" \
|
||||
-e OPENROUTER_API_KEY="${OPENROUTER_API_KEY:-}" \
|
||||
-e PARASAIL_API_KEY="${PARASAIL_API_KEY:-}" \
|
||||
-e AZURE_API_KEY="${AZURE_API_KEY:-}" \
|
||||
-e AZURE_ENDPOINT="${AZURE_ENDPOINT:-}" \
|
||||
-e AZURE_API_VERSION="${AZURE_API_VERSION:-}" \
|
||||
-e AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-}" \
|
||||
-e AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-}" \
|
||||
-e AWS_REGION="${AWS_REGION:-us-east-1}" \
|
||||
-e AWS_ARN="${AWS_ARN:-}" \
|
||||
-e REPLICATE_API_KEY="${REPLICATE_API_KEY:-}" \
|
||||
-v "$CONFIG_FILE:/app/data/config.json:ro" \
|
||||
"${IMAGE_TAG}"
|
||||
|
||||
# Wait for Bifrost to be ready
|
||||
echo "Waiting for Bifrost to start..."
|
||||
MAX_WAIT=60
|
||||
ELAPSED=0
|
||||
HEALTH_OK=0
|
||||
while [ $ELAPSED -lt $MAX_WAIT ]; do
|
||||
if curl -sf "http://localhost:${TEST_PORT}/health" > /dev/null 2>&1; then
|
||||
echo "Bifrost health check passed (attempt $((ELAPSED/2 + 1)))"
|
||||
HEALTH_OK=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
ELAPSED=$((ELAPSED + 2))
|
||||
done
|
||||
|
||||
if [ $HEALTH_OK -eq 0 ]; then
|
||||
echo "ERROR: Bifrost health check failed!"
|
||||
echo "Container logs:"
|
||||
docker logs "${CONTAINER_NAME}" 2>&1 | tail -100 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# # Run E2E API tests
|
||||
# echo ""
|
||||
# echo "=== Running E2E API tests ==="
|
||||
# export BIFROST_BASE_URL="http://localhost:${TEST_PORT}"
|
||||
# export CI=1
|
||||
|
||||
# echo pwd: $(pwd)
|
||||
# # Run the E2E API test scripts (marked as flaky - failures are logged but don't block)
|
||||
# if ! ./tests/e2e/api/runners/run-newman-inference-tests.sh; then
|
||||
# echo "WARNING: runners/run-newman-inference-tests.sh failed (flaky test - continuing)"
|
||||
# fi
|
||||
# if ! ./tests/e2e/api/run-all-integrations.sh; then
|
||||
# echo "WARNING: run-all-integrations.sh failed (flaky test - continuing)"
|
||||
# fi
|
||||
# if ! ./tests/e2e/api/runners/run-newman-api-tests.sh; then
|
||||
# echo "WARNING: run-newman-api-tests.sh failed (flaky test - continuing)"
|
||||
# fi
|
||||
|
||||
# echo ""
|
||||
# echo "=== Docker image E2E API test passed for ${PLATFORM} ==="
|
||||
Reference in New Issue
Block a user