Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions .github/actions/setup-devservices/bootstrap-snuba.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
cause a timeout.

Requires: XDIST_WORKERS env var
Reads: /tmp/ds-exit (written by setup-devservices/wait.sh)
Reads: /tmp/ds-exit (written by setup-devservices/wait.py)
Writes: /tmp/snuba-bootstrap-exit
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
Expand Down Expand Up @@ -91,11 +92,12 @@ def docker_inspect(container: str, fmt: str) -> str:


def inspect_snuba_container() -> tuple[str, str]:
image = docker_inspect("snuba-snuba-1", "{{.Config.Image}}")
network = docker_inspect(
"snuba-snuba-1",
"{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}",
)
r = docker("inspect", "snuba-snuba-1", "--format", "{{json .}}")
if r.returncode != 0:
fail("Could not inspect snuba-snuba-1 container")
info = json.loads(r.stdout)
image = info["Config"]["Image"]
network = next(iter(info["NetworkSettings"]["Networks"]), "")
if not image or not network:
fail("Could not inspect snuba-snuba-1 container")
return image, network
Expand All @@ -120,16 +122,29 @@ def run_parallel(fn: Callable[[int], Any], workers: range, *, fail_fast: bool =
def wait_for_prerequisites(timeout: int = 300) -> None:
log("Waiting for ClickHouse and Snuba container...")
start = time.monotonic()
next_status_at = 0.0
while True:
if time.monotonic() - start > timeout:
elapsed = time.monotonic() - start
if elapsed > timeout:
fail("Timed out waiting for Snuba bootstrap prerequisites")
if http_ok("http://localhost:8123/") and docker_inspect("snuba-snuba-1", "{{.Id}}"):
ch_ok = http_ok("http://localhost:8123/")
snuba_ok = http_ok("http://localhost:1218/health")
container_ok = bool(docker_inspect("snuba-snuba-1", "{{.Id}}"))
if ch_ok and snuba_ok and container_ok:
break
if elapsed >= next_status_at:
log(
f" still waiting ({elapsed:.0f}s): "
f"clickhouse={'ok' if ch_ok else 'not ready'} "
f"snuba-snuba-1={'ok' if snuba_ok else 'not ready'} "
f"container={'ok' if container_ok else 'not ready'}"
)
next_status_at = elapsed + 30
time.sleep(2)
log(f"Prerequisites ready ({time.monotonic() - start:.0f}s)")


def wait_for_devservices(timeout: int = 300) -> None:
def wait_for_devservices(timeout: int = 600) -> None:
start = time.monotonic()
while not DS_EXIT.exists():
if time.monotonic() - start > timeout:
Expand Down
120 changes: 120 additions & 0 deletions .github/actions/setup-devservices/wait.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Wait for the background devservices process started by the setup-devservices action.

Usage: wait.py [timeout_seconds]

Reads: /tmp/ds-exit, /tmp/ds.log (written by the setup-devservices action)
Writes: $GITHUB_ENV (DJANGO_LIVE_TEST_SERVER_ADDRESS)
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
import time
from pathlib import Path

DS_EXIT = Path("/tmp/ds-exit")
DS_LOG = Path("/tmp/ds.log")
TIMEOUT = 300
Comment thread
joshuarli marked this conversation as resolved.


def log(msg: str) -> None:
print(msg, flush=True)


def docker(*args: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(["docker", *args], capture_output=True, text=True)


def stream_log(pos: int) -> int:
"""Print any new content written to DS_LOG since pos. Returns the new position."""
if not DS_LOG.exists():
return pos
with DS_LOG.open() as f:
f.seek(pos)
chunk = f.read()
if chunk:
sys.stdout.write(chunk)
sys.stdout.flush()
return f.tell()


def container_inspect_dump() -> None:
ids = docker("ps", "-aq").stdout.split()
if not ids:
return

r = docker("inspect", *ids)
if r.returncode != 0:
return

containers = json.loads(r.stdout)
for c in containers:
name = c["Name"].lstrip("/")
status = c["State"]["Status"]
health = c["State"].get("Health")
health_status = health["Status"] if health else "n/a"
log(f"{name} status={status} health={health_status}")

log("")
for c in containers:
health = c["State"].get("Health")
if not health or health["Status"] == "healthy":
continue
name = c["Name"].lstrip("/")
log(f"--- {name} last health check ---")
for entry in health.get("Log", []):
log(f" exit={entry['ExitCode']} {entry['Output'].strip()}")


def wait(timeout: int = TIMEOUT) -> None:
start = time.monotonic()
log_pos = 0

while not DS_EXIT.exists():
elapsed = time.monotonic() - start
if elapsed > timeout:
log_pos = stream_log(log_pos)
log(f"::error::Timed out waiting for devservices after {timeout}s")
log("--- container health on timeout ---")
container_inspect_dump()
sys.exit(1)
log_pos = stream_log(log_pos)
time.sleep(2)

# Drain any remaining log output.
stream_log(log_pos)

rc = int(DS_EXIT.read_text().strip())
if rc != 0:
log(f"::error::devservices up failed (exit {rc})")
log("--- container health on failure ---")
container_inspect_dump()
sys.exit(1)

r = docker(
Comment thread
joshuarli marked this conversation as resolved.
"network",
"inspect",
"bridge",
"--format",
"{{(index .IPAM.Config 0).Gateway}}",
)
if r.returncode != 0:
log(f"::error::docker network inspect bridge failed: {r.stderr.strip()}")
sys.exit(1)
gateway = r.stdout.strip()
github_env = os.environ.get("GITHUB_ENV")
if github_env:
with open(github_env, "a") as f:
f.write(f"DJANGO_LIVE_TEST_SERVER_ADDRESS={gateway}\n")

r2 = docker("ps", "-a")
if r2.stdout.strip():
log(r2.stdout.strip())


if __name__ == "__main__":
wait(int(sys.argv[1]) if len(sys.argv) > 1 else TIMEOUT)
26 changes: 0 additions & 26 deletions .github/actions/setup-devservices/wait.sh

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/acceptance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Wait for Snuba bootstrap
if: env.XDIST_PER_WORKER_SNUBA == '1'
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Run API docs tests
run: |
Expand Down Expand Up @@ -267,7 +267,7 @@ jobs:
if [ "${XDIST_PER_WORKER_SNUBA}" = "1" ]; then
python3 ./.github/actions/setup-devservices/bootstrap-snuba.py &
fi
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Download odiff binary
run: |
Expand Down Expand Up @@ -388,7 +388,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: run tests
run: |
Expand Down Expand Up @@ -437,7 +437,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Run test
env:
Expand Down Expand Up @@ -523,7 +523,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Sync API Urls to TypeScript
run: |
Expand Down Expand Up @@ -561,7 +561,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Migration & lockfile checks
env:
Expand Down Expand Up @@ -606,7 +606,7 @@ jobs:
- name: Wait for devservices
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Run test
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/openapi-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
if: steps.changes.outputs.api_docs == 'true'
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Checkout getsentry/sentry-api-schema
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
if: steps.changes.outputs.api_docs == 'true'
run: |
sentry init
./.github/actions/setup-devservices/wait.sh
./.github/actions/setup-devservices/wait.py

- name: Checkout getsentry/sentry-api-schema
if: steps.changes.outputs.api_docs == 'true'
Expand Down
Loading