Skip to content
Closed
30 changes: 14 additions & 16 deletions .github/actions/start-services/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,22 @@ runs:
PERSISTENT_VOLUME_MOUNTS="test-volume-type:${test_volume_dir}"
export PERSISTENT_VOLUME_MOUNTS

# The backgrounded warm-up pull in start-databases discards its exit
# status; this synchronous call is the actual guarantee (near-instant
# when the warm-up succeeded) so a rate-limited pull retries instead
# of failing the collector's 30s health window. The tag comes from
# the Makefile that runs the image.
# The warm-up pull in start-databases discards its exit status; this
# synchronous call is the guarantee (near-instant when warm). Tag
# comes from the Makefile that runs the image, so bumps can't strand it.
OTEL_IMAGE=$(grep -oE 'otel/opentelemetry-collector-contrib:[0-9.]+' packages/otel-collector/Makefile | head -1)
./scripts/pull-retry.sh "$OTEL_IMAGE"

echo "Start otel-collector"
./scripts/start-service.sh "OtelCollector" packages/otel-collector run ~/logs/otel-collector.log http://localhost:13133/healthz
# Two concurrent boot phases: otel + orchestrator (OTLP exporters
# retry until the collector is up), then API + client-proxy once the
# orchestrator is healthy — API must not start before it.
./scripts/start-service.sh start "OtelCollector" packages/otel-collector run ~/logs/otel-collector.log
./scripts/start-service.sh start "Orchestrator" packages/orchestrator run-debug ~/logs/orchestrator.log
./scripts/start-service.sh wait "Orchestrator" ~/logs/orchestrator.log http://localhost:5008/health
./scripts/start-service.sh wait "OtelCollector" ~/logs/otel-collector.log http://localhost:13133/healthz


echo "Start orchestrator"
./scripts/start-service.sh "Orchestrator" packages/orchestrator run-debug ~/logs/orchestrator.log http://localhost:5008/health

echo "Start API"
STARTUP_TIMEOUT=60 ./scripts/start-service.sh "API" packages/api run ~/logs/api.log http://localhost:3000/health

# Start client-proxy (needed for auto-resume tests)
./scripts/start-service.sh "ClientProxy" packages/client-proxy run ~/logs/client-proxy.log http://localhost:3003
./scripts/start-service.sh start "API" packages/api run ~/logs/api.log
./scripts/start-service.sh start "ClientProxy" packages/client-proxy run ~/logs/client-proxy.log
STARTUP_TIMEOUT=60 ./scripts/start-service.sh wait "API" ~/logs/api.log http://localhost:3000/health
./scripts/start-service.sh wait "ClientProxy" ~/logs/client-proxy.log http://localhost:3003
shell: bash
82 changes: 59 additions & 23 deletions scripts/start-service.sh
Original file line number Diff line number Diff line change
@@ -1,30 +1,66 @@
#!/bin/bash

# Default timeout, override with STARTUP_TIMEOUT env
TIMEOUT=${STARTUP_TIMEOUT:-30}
# Start a service and/or wait for its health endpoint.
#
# Modes:
# start-service.sh start <name> <make_path> <make_command> <log_file>
# Kick the service off in the background and return immediately.
# start-service.sh wait <name> <log_file> <health_url>
# Poll the health URL until healthy (STARTUP_TIMEOUT, default 30s);
# on timeout print the log tail and fail.
# start-service.sh <name> <make_path> <make_command> <log_file> <health_url>
# Legacy form: start + wait in one call.
#
# Splitting start from wait lets independent services boot concurrently:
# `start A; start B; wait A; wait B` boots B while A is being waited on.

if [ "$#" -ne 5 ]; then
echo "Usage: $0 <name> <make_path> <make_command> <log_file> <health_url>"
exit 1
fi
set -uo pipefail

NAME="$1"
MAKE_PATH="$2"
MAKE_COMMAND="$3"
LOG_FILE="$4"
HEALTH_URL="$5"
# Default timeout, override with STARTUP_TIMEOUT env
TIMEOUT=${STARTUP_TIMEOUT:-30}

echo "Starting $NAME..."
make -C "$MAKE_PATH" "$MAKE_COMMAND" 2>&1 | tee "$LOG_FILE" &
start() {
local name="$1" make_path="$2" make_command="$3" log_file="$4"
echo "Starting $name..."
make -C "$make_path" "$make_command" 2>&1 | tee "$log_file" &
}

echo "Waiting for $NAME to become healthy at $HEALTH_URL (timeout: $TIMEOUT seconds)..."
for ((i = 0; i < TIMEOUT; i++)); do
if curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" | grep -q 200; then
echo "$NAME is healthy and running."
exit 0
fi
sleep 1
done
wait_healthy() {
local name="$1" log_file="$2" health_url="$3"
echo "Waiting for $name to become healthy at $health_url (timeout: $TIMEOUT seconds)..."
for ((i = 0; i < TIMEOUT; i++)); do
if curl -s -o /dev/null -w "%{http_code}" "$health_url" | grep -q 200; then
echo "$name is healthy and running."
return 0
fi
sleep 1
done
echo "$name failed to become healthy in time. Last log lines:"
tail -30 "$log_file" 2>/dev/null || true
return 1
}

echo "$NAME failed to become healthy in time."
exit 1
case "${1:-}" in
start)
if [ "$#" -ne 5 ]; then
echo "Usage: $0 start <name> <make_path> <make_command> <log_file>"
exit 1
fi
start "$2" "$3" "$4" "$5"
;;
wait)
if [ "$#" -ne 4 ]; then
echo "Usage: $0 wait <name> <log_file> <health_url>"
exit 1
fi
wait_healthy "$2" "$3" "$4"
;;
*)
if [ "$#" -ne 5 ]; then
echo "Usage: $0 <name> <make_path> <make_command> <log_file> <health_url>"
exit 1
fi
start "$1" "$2" "$3" "$4"
wait_healthy "$1" "$4" "$5"
;;
esac
1 change: 1 addition & 0 deletions tests/integration/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ check-tests-allowlist:
fi; \
done; \
echo "allow-list resolves; stale named and wildcard entries are rejected"
@./scripts/check-allowlist-completeness.sh

# CI entrypoint: run one package shard (or the whole suite with the default
# TEST_SHARD=all). TESTS_ONLY narrows the run to the allow-list within the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestSandboxDetailRunning(t *testing.T) {
assert.Equal(t, "base", *returnedSbx.Alias)
}

// compression-tests:excluded subject is the detail API surface; autopause config is fixture
func TestSandboxDetailReturnsLifecycleAndNetworkConfig(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down Expand Up @@ -87,6 +88,7 @@ func TestSandboxDetailReturnsLifecycleAndNetworkConfig(t *testing.T) {
assertDetail(t, api.Paused)
}

// compression-tests:excluded subject is the detail API surface; pause is fixture
func TestSandboxDetailPaused(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand All @@ -106,6 +108,7 @@ func TestSandboxDetailPaused(t *testing.T) {
assert.Equal(t, "base", *returnedSbx.Alias)
}

// compression-tests:excluded subject is the detail API surface; pause is fixture
func TestSandboxDetailPausingSandbox(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func TestInternetAccess(t *testing.T) {
}
}

// compression-tests:excluded subject is internet egress behaviour, excluded per the allow-list criterion
func TestInternetAccessResumedSbx(t *testing.T) {
t.Parallel()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/e2b-dev/infra/tests/integration/internal/utils"
)

// compression-tests:excluded subject is kill semantics; pause/resume set up the states to kill
func TestSandboxKill(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func TestSandboxListRunning_NoMetadata(t *testing.T) {
assert.Contains(t, sandboxIds, sandboxID)
}

// compression-tests:excluded subject is the list API surface; pause is fixture
func TestSandboxListPaused(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down Expand Up @@ -162,6 +163,7 @@ func TestSandboxListPaused(t *testing.T) {
assert.True(t, found)
}

// compression-tests:excluded subject is the list API surface; pause is fixture
func TestSandboxListPausing(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down Expand Up @@ -218,6 +220,7 @@ func TestSandboxListPausing(t *testing.T) {
require.NoError(t, err)
}

// compression-tests:excluded subject is the list API surface; pause is fixture
func TestSandboxListPaused_NoMetadata(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down Expand Up @@ -393,6 +396,7 @@ func TestSandboxListPaginationRunningLargerLimit(t *testing.T) { //nolint:tparal
})
}

// compression-tests:excluded subject is list pagination; pause is fixture
func TestSandboxListPaginationPaused(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down Expand Up @@ -443,6 +447,7 @@ func TestSandboxListPaginationPaused(t *testing.T) {
assert.Empty(t, nextToken)
}

// compression-tests:excluded subject is list pagination; pause is fixture
func TestSandboxListPaginationRunningAndPaused(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ func TestEgressFirewallAllowAndBlockCombination(t *testing.T) {
}

// TestEgressFirewallPersistsAfterResume tests that network config persists after pause/resume
// compression-tests:excluded subject is egress firewall behaviour, excluded per the allow-list criterion
func TestEgressFirewallPersistsAfterResume(t *testing.T) {
t.Parallel()
templateID := ensureNetworkTestTemplate(t)
Expand Down Expand Up @@ -708,6 +709,7 @@ func TestEgressFirewallHTTPSByIPNoHostname(t *testing.T) {
}

// TestEgressFirewallDomainPersistsAfterResume tests that domain-based network config persists after pause/resume
// compression-tests:excluded subject is egress firewall behaviour, excluded per the allow-list criterion
func TestEgressFirewallDomainPersistsAfterResume(t *testing.T) {
t.Parallel()
templateID := ensureNetworkTestTemplate(t)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func verifyConnectivity(

// TestUpdateNetworkConfig exercises all update scenarios using a single sandbox.
// Subtests run sequentially — each PUT fully replaces the previous config.
// compression-tests:excluded subject is network config surface; pause/resume cycles the states
func TestUpdateNetworkConfig(t *testing.T) { //nolint:tparallel // subtests are sequential
t.Parallel()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func TestSandboxTimeout_NotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, timeoutResp.StatusCode())
}

// compression-tests:excluded subject is the timeout API surface; pause is fixture
func TestSandboxSetTimeoutPausingSandbox(t *testing.T) {
t.Parallel()
c := setup.GetAPIClient()
Expand Down
1 change: 1 addition & 0 deletions tests/integration/internal/tests/team_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ UPDATE teams SET is_banned = $1 WHERE id = $2
// Mutate / Delete subtests use a synthetic sandbox ID because the
// blocked-team check runs before the resource is resolved — we only
// care that the request was (not) rejected by the blocked-team policy.
// compression-tests:excluded subject is team-blocking authz; the pause call targets a nonexistent sandbox
func TestBlockedTeam(t *testing.T) {
t.Parallel()
ctx := t.Context()
Expand Down
Loading
Loading