diff --git a/_testlib/http_client.py b/_testlib/http_client.py index 34e86f9c..7ad4185a 100644 --- a/_testlib/http_client.py +++ b/_testlib/http_client.py @@ -43,7 +43,7 @@ def _request_with_retries( *, timeout: float = 10.0, retries: int = 10, - backoff: float = 2.0, + backoff: float = 1.0, expected_status: int | None = 200, verify: bool | str | None = None, **kwargs, @@ -95,7 +95,7 @@ def http_get( *, timeout: float = 10.0, retries: int = 10, - backoff: float = 2.0, + backoff: float = 1.0, expected_status: int | None = 200, verify: bool | str | None = None, **kwargs, @@ -113,7 +113,7 @@ def http_post( *, timeout: float = 10.0, retries: int = 10, - backoff: float = 2.0, + backoff: float = 1.0, expected_status: int | None = 200, verify: bool | str | None = None, **kwargs, diff --git a/_testlib/unikraft.py b/_testlib/unikraft.py index 420a1173..5603e606 100644 --- a/_testlib/unikraft.py +++ b/_testlib/unikraft.py @@ -150,6 +150,29 @@ def get_instance(self, target: str) -> dict[str, Any]: return _parse_json(proc.stdout) + def wait_instance( + self, target: str, state: str, *, timeout: float | None = 150 + ) -> dict[str, Any]: + """Wait until ``target`` reaches the given ``state``. + + Wraps ``unikraft instances wait --until state==``. + Returns the parsed JSON description of the instance once it reaches + the desired state. + """ + proc = self.run( + [ + "instances", + "wait", + target, + "--until", + f"state=={state}", + "--output", + "json", + ], + timeout=timeout, + ) + return _parse_json(proc.stdout) + def delete_instance(self, target: str) -> None: try: proc =self.run(["instances", "delete", target], check=False) diff --git a/build-environments/test_build-environments.py b/build-environments/test_build-environments.py index c06f07e9..0f7923cc 100644 --- a/build-environments/test_build-environments.py +++ b/build-environments/test_build-environments.py @@ -61,7 +61,6 @@ def _cleanup_base(): # 1. Build the base image. unikraft.build(context, base_tag) - time.sleep(3) # 2. Run the base image; it auto-converts into a template. unikraft.run_instance( @@ -85,13 +84,12 @@ def _cleanup_base(): # 4. Build ROM images. unikraft.build(context / "rom1", rom1_tag) unikraft.build(context / "rom2", rom2_tag) - time.sleep(3) return template_name, rom1_tag, rom2_tag def test_build_environments_rom1( - _template_and_roms, request, unikraft, test_run_id, http + _template_and_roms, request, unikraft, test_run_id, http, wait_instance ): """Run an instance from the template with ROM1 and verify response.""" template_name, rom1_tag, _ = _template_and_roms @@ -112,13 +110,14 @@ def test_build_environments_rom1( url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(instance_name, "standby") resp = http(url) assert resp.status_code == 200 assert "Bye, World!" in resp.text def test_build_environments_rom2( - _template_and_roms, request, unikraft, test_run_id, http + _template_and_roms, request, unikraft, test_run_id, http, wait_instance ): """Run an instance from the template with ROM2 and verify response.""" template_name, _, rom2_tag = _template_and_roms @@ -139,6 +138,7 @@ def test_build_environments_rom2( url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(instance_name, "standby") resp = http(url) assert resp.status_code == 200 assert "Auf Wiedersehen!" in resp.text diff --git a/caddy2.7-go1.21/test_caddy2.7-go1.21.py b/caddy2.7-go1.21/test_caddy2.7-go1.21.py index 07d8296a..97de1907 100644 --- a/caddy2.7-go1.21/test_caddy2.7-go1.21.py +++ b/caddy2.7-go1.21/test_caddy2.7-go1.21.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_caddy_serves_welcome_page(build_image, run_instance, http): +def test_caddy_serves_welcome_page(build_image, run_instance, http, wait_instance): image = build_image("caddy2.7-go1.21", "caddy2.7-go1.21") instance = run_instance( @@ -24,6 +24,8 @@ def test_caddy_serves_welcome_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, world!" in resp.text diff --git a/chromium-cdp/test_chromium_cdp.py b/chromium-cdp/test_chromium_cdp.py index cb120397..24ed4657 100644 --- a/chromium-cdp/test_chromium_cdp.py +++ b/chromium-cdp/test_chromium_cdp.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_chromium_version(build_image, run_instance, http): +def test_chromium_version(build_image, run_instance, http, wait_instance): image = build_image("chromium-cdp", "chromium-cdp") instance = run_instance( @@ -24,6 +24,8 @@ def test_chromium_version(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/json/version") assert resp.status_code == 200 assert "Browser" in resp.text diff --git a/conftest.py b/conftest.py index 56e70df6..5324b7ea 100644 --- a/conftest.py +++ b/conftest.py @@ -32,6 +32,7 @@ from _testlib.socat import SocatTunnel from _testlib.unikraft import ( UnikraftCLI, + extract_instance_name, extract_instance_url, ) @@ -138,10 +139,6 @@ def _build(example_dir: str, image_name: str) -> str: unikraft.build(context, tag) - # TODO: drop this once the platform exposes a way to wait until a - # freshly-built image is fully available for `unikraft run`. - time.sleep(3) - return tag return _build @@ -228,6 +225,31 @@ def _cleanup() -> None: return _run +# --------------------------------------------------------------------------- +# Instance wait fixture +# --------------------------------------------------------------------------- + + +WaitInstance = Callable[[str, str], dict[str, Any]] + + +@pytest.fixture +def wait_instance(unikraft: UnikraftCLI) -> WaitInstance: + """Wait until a named instance reaches the given state. + + Usage:: + + def test_foo(run_instance, wait_instance): + instance = run_instance(image, ...) + wait_instance(extract_instance_name(instance), "running") + """ + + def _wait(target: str, state: str) -> dict[str, Any]: + return unikraft.wait_instance(target, state) + + return _wait + + # --------------------------------------------------------------------------- # HTTP helper # --------------------------------------------------------------------------- @@ -284,4 +306,5 @@ def _create(host: str, port: int, local_port: int) -> SocatTunnel: "run_instance", "socat_tunnel", "unikraft", + "wait_instance", ] diff --git a/debian-ssh/test_debian-ssh.py b/debian-ssh/test_debian-ssh.py index 4b9d9dfd..e8ac4626 100644 --- a/debian-ssh/test_debian-ssh.py +++ b/debian-ssh/test_debian-ssh.py @@ -12,12 +12,11 @@ from __future__ import annotations import subprocess -import time from pathlib import Path import pytest -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name SSH_PORT = 2222 @@ -36,7 +35,7 @@ def _generate_test_keypair(tmp_path: Path) -> tuple[Path, str]: return key_path, pub_key -def test_debian_ssh(build_image, run_instance, socat_tunnel, tmp_path): +def test_debian_ssh(build_image, run_instance, socat_tunnel, wait_instance, tmp_path): """Build, deploy, and SSH into a Debian instance.""" private_key, public_key = _generate_test_keypair(tmp_path) @@ -58,37 +57,24 @@ def test_debian_ssh(build_image, run_instance, socat_tunnel, tmp_path): # Set up a socat TLS tunnel to the SSH port. tunnel = socat_tunnel(host, SSH_PORT, SSH_PORT) - # Retry SSH connection with back-off while the instance boots. - last_err = None - for _ in range(10): - try: - result = subprocess.run( - [ - "ssh", - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - "-o", "ConnectTimeout=10", - "-i", str(private_key), - "-p", str(tunnel.local_port), - "-l", "root", - "127.0.0.1", - "echo hello-from-pytest", - ], - capture_output=True, - text=True, - timeout=30, - ) - if result.returncode == 0: - assert "hello-from-pytest" in result.stdout - return - last_err = RuntimeError( - f"ssh exited {result.returncode}: {result.stderr}" - ) - except (subprocess.TimeoutExpired, OSError) as exc: - last_err = exc - - time.sleep(5) - - raise AssertionError( - f"could not connect via SSH after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "running") + result = subprocess.run( + [ + "ssh", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "ConnectTimeout=10", + "-i", str(private_key), + "-p", str(tunnel.local_port), + "-l", "root", + "127.0.0.1", + "echo hello-from-pytest", + ], + capture_output=True, + text=True, + timeout=30, + ) + assert result.returncode == 0, ( + f"ssh failed (exit={result.returncode})\nstdout: {result.stdout}\nstderr: {result.stderr}" + ) + assert "hello-from-pytest" in result.stdout diff --git a/dragonflydb/test_dragonflydb.py b/dragonflydb/test_dragonflydb.py index b88fdbdb..18633264 100644 --- a/dragonflydb/test_dragonflydb.py +++ b/dragonflydb/test_dragonflydb.py @@ -15,10 +15,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_dragonflydb(build_image, run_instance, http): +def test_dragonflydb(build_image, run_instance, http, wait_instance): """Build, deploy, and exercise a DragonflyDB instance.""" image = build_image("dragonflydb", "dragonflydb") @@ -31,6 +31,8 @@ def test_dragonflydb(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # http() already retries with back-off, so no manual sleep needed. # ------------------------------------------------------------------ # 1. HTTP status page — matches existing CI workflow (curl check). diff --git a/duckdb-go1.21/test_duckdb-go1.21.py b/duckdb-go1.21/test_duckdb-go1.21.py index 6254c333..cc04adfd 100644 --- a/duckdb-go1.21/test_duckdb-go1.21.py +++ b/duckdb-go1.21/test_duckdb-go1.21.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_duckdb_go_serves_query_result(build_image, run_instance, http): +def test_duckdb_go_serves_query_result(build_image, run_instance, http, wait_instance): image = build_image("duckdb-go1.21", "duckdb-go1.21") instance = run_instance( @@ -24,6 +24,8 @@ def test_duckdb_go_serves_query_result(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "42" in resp.text diff --git a/feature-change-instance-cmd/test_feature-change-instance-cmd.py b/feature-change-instance-cmd/test_feature-change-instance-cmd.py index 22f61b7c..7aeeee93 100644 --- a/feature-change-instance-cmd/test_feature-change-instance-cmd.py +++ b/feature-change-instance-cmd/test_feature-change-instance-cmd.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_serves_hello(build_image, run_instance, http): +def test_httpserver_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("feature-change-instance-cmd", "feature-change-instance-cmd") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/github-webhook-node/test_github-webhook-node.py b/github-webhook-node/test_github-webhook-node.py index 58cda4fb..1741ecc3 100644 --- a/github-webhook-node/test_github-webhook-node.py +++ b/github-webhook-node/test_github-webhook-node.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_github_webhook_health(build_image, run_instance, http): +def test_github_webhook_health(build_image, run_instance, http, wait_instance): image = build_image("github-webhook-node", "github-webhook-node") instance = run_instance( @@ -27,6 +27,8 @@ def test_github_webhook_health(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/health") assert resp.status_code == 200 diff --git a/grafana/test_grafana.py b/grafana/test_grafana.py index fa810d7d..4e27bf48 100644 --- a/grafana/test_grafana.py +++ b/grafana/test_grafana.py @@ -10,10 +10,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_grafana(build_image, run_instance, http): +def test_grafana(build_image, run_instance, http, wait_instance): """Build, deploy, and verify Grafana serves its login page.""" image = build_image("grafana", "grafana") @@ -26,6 +26,8 @@ def test_grafana(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 body = resp.text.lower() diff --git a/haproxy/test_haproxy.py b/haproxy/test_haproxy.py index 113dbd98..fd64bb9b 100644 --- a/haproxy/test_haproxy.py +++ b/haproxy/test_haproxy.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_haproxy_stats_page(build_image, run_instance, http): +def test_haproxy_stats_page(build_image, run_instance, http, wait_instance): image = build_image("haproxy", "haproxy") instance = run_instance( @@ -24,6 +24,8 @@ def test_haproxy_stats_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/stats") assert resp.status_code == 200 assert "HAProxy" in resp.text diff --git a/httpserver-boost1.74-gpp13.2/test_httpserver-boost1.74-gpp13.2.py b/httpserver-boost1.74-gpp13.2/test_httpserver-boost1.74-gpp13.2.py index 8f36c7b0..c8cdd733 100644 --- a/httpserver-boost1.74-gpp13.2/test_httpserver-boost1.74-gpp13.2.py +++ b/httpserver-boost1.74-gpp13.2/test_httpserver-boost1.74-gpp13.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_boost_gpp_serves_hello(build_image, run_instance, http): +def test_httpserver_boost_gpp_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-boost1.74-gpp13.2", "httpserver-boost1.74-gpp13.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_boost_gpp_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-bun/test_httpserver-bun.py b/httpserver-bun/test_httpserver-bun.py index 7c694c1f..dcb3c8a7 100644 --- a/httpserver-bun/test_httpserver-bun.py +++ b/httpserver-bun/test_httpserver-bun.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_bun_serves_hello(build_image, run_instance, http): +def test_httpserver_bun_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-bun", "httpserver-bun") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_bun_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-c-debug/test_httpserver-c-debug.py b/httpserver-c-debug/test_httpserver-c-debug.py index e9d4aaaa..a8d71577 100644 --- a/httpserver-c-debug/test_httpserver-c-debug.py +++ b/httpserver-c-debug/test_httpserver-c-debug.py @@ -12,10 +12,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_c_debug_serves_hello(build_image, run_instance, http): +def test_httpserver_c_debug_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-c-debug", "httpserver-c-debug") instance = run_instance( @@ -27,6 +27,8 @@ def test_httpserver_c_debug_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-dotnet10.0/test_httpserver-dotnet10.0.py b/httpserver-dotnet10.0/test_httpserver-dotnet10.0.py index 6b37de22..9438bbcf 100644 --- a/httpserver-dotnet10.0/test_httpserver-dotnet10.0.py +++ b/httpserver-dotnet10.0/test_httpserver-dotnet10.0.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_dotnet_serves_hello(build_image, run_instance, http): +def test_httpserver_dotnet_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-dotnet10.0", "httpserver-dotnet10.0") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_dotnet_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-elixir1.16/test_httpserver-elixir1.16.py b/httpserver-elixir1.16/test_httpserver-elixir1.16.py index bd80d50c..40ffbf28 100644 --- a/httpserver-elixir1.16/test_httpserver-elixir1.16.py +++ b/httpserver-elixir1.16/test_httpserver-elixir1.16.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_elixir_serves_hello(build_image, run_instance, http): +def test_httpserver_elixir_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-elixir1.16", "httpserver-elixir1.16") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_elixir_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-erlang26.2/test_httpserver-erlang26.2.py b/httpserver-erlang26.2/test_httpserver-erlang26.2.py index 40832564..d8de303a 100644 --- a/httpserver-erlang26.2/test_httpserver-erlang26.2.py +++ b/httpserver-erlang26.2/test_httpserver-erlang26.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_erlang_serves_hello(build_image, run_instance, http): +def test_httpserver_erlang_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-erlang26.2", "httpserver-erlang26.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_erlang_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-expressjs4.18-node21/test_httpserver-expressjs4.18-node21.py b/httpserver-expressjs4.18-node21/test_httpserver-expressjs4.18-node21.py index 2895a48e..c7ff8d96 100644 --- a/httpserver-expressjs4.18-node21/test_httpserver-expressjs4.18-node21.py +++ b/httpserver-expressjs4.18-node21/test_httpserver-expressjs4.18-node21.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_expressjs_serves_hello(build_image, run_instance, http): +def test_httpserver_expressjs_serves_hello(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-expressjs4.18-node21", "httpserver-expressjs4.18-node21", @@ -27,6 +27,8 @@ def test_httpserver_expressjs_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-flask-redis/test_httpserver-flask-redis.py b/httpserver-flask-redis/test_httpserver-flask-redis.py index 1fa345c6..eb3f135e 100644 --- a/httpserver-flask-redis/test_httpserver-flask-redis.py +++ b/httpserver-flask-redis/test_httpserver-flask-redis.py @@ -14,7 +14,7 @@ import re -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url def _extract_count(text: str) -> int: @@ -24,7 +24,7 @@ def _extract_count(text: str) -> int: return int(match.group(1)) -def test_flask_redis_counter(build_image, run_instance, http, test_run_id): +def test_flask_redis_counter(build_image, run_instance, http, test_run_id, wait_instance): redis_domain = f"redis-{test_run_id}.internal" # 1. Build and deploy internal Redis instance. @@ -55,6 +55,8 @@ def test_flask_redis_counter(build_image, run_instance, http, test_run_id): url = extract_instance_url(flask_instance) assert url, f"could not determine instance URL from: {flask_instance!r}" + wait_instance(extract_instance_name(flask_instance), "running") + # 3. First request — counter should be at least 1. resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-gcc13.2/test_httpserver-gcc13.2.py b/httpserver-gcc13.2/test_httpserver-gcc13.2.py index 1518fc14..88b74bd1 100644 --- a/httpserver-gcc13.2/test_httpserver-gcc13.2.py +++ b/httpserver-gcc13.2/test_httpserver-gcc13.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_gcc_serves_hello(build_image, run_instance, http): +def test_httpserver_gcc_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-gcc13.2", "httpserver-gcc13.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_gcc_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-go1.21/test_httpserver-go1.21.py b/httpserver-go1.21/test_httpserver-go1.21.py index 90bf7181..51cf0112 100644 --- a/httpserver-go1.21/test_httpserver-go1.21.py +++ b/httpserver-go1.21/test_httpserver-go1.21.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_go_serves_hello(build_image, run_instance, http): +def test_httpserver_go_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-go1.21", "httpserver-go1.21") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_go_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-go1.22-redis/test_httpserver-go1.22-redis.py b/httpserver-go1.22-redis/test_httpserver-go1.22-redis.py index 93b4248c..05ac7380 100644 --- a/httpserver-go1.22-redis/test_httpserver-go1.22-redis.py +++ b/httpserver-go1.22-redis/test_httpserver-go1.22-redis.py @@ -10,19 +10,19 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url REDIS_PASSWORD = "unikraft" -def test_go_redis_set_get(build_image, run_instance, http, http_post, test_run_id): +def test_go_redis_set_get(build_image, run_instance, http, http_post, wait_instance, test_run_id): redis_domain = f"go122-redis-{test_run_id}.internal" # 1. Build and deploy internal Redis instance. redis_image = build_image("httpserver-go1.22-redis/redis", "go122-redis-db") - run_instance( + redis_instance = run_instance( redis_image, memory="256M", extra_args=[ @@ -31,6 +31,7 @@ def test_go_redis_set_get(build_image, run_instance, http, http_post, test_run_i "-e", f"REDIS_PASSWORD={REDIS_PASSWORD}", ], ) + wait_instance(extract_instance_name(redis_instance), "standby") # 2. Build and deploy the Go HTTP server. app_image = build_image("httpserver-go1.22-redis/httpserver-go", "go122-redis-app") @@ -48,6 +49,8 @@ def test_go_redis_set_get(build_image, run_instance, http, http_post, test_run_i url = extract_instance_url(app_instance) assert url, f"could not determine instance URL from: {app_instance!r}" + wait_instance(extract_instance_name(app_instance), "running") + # 3. POST a key-value pair. resp = http_post(url, data={"key": "pytest-key", "value": "pytest-value"}) assert resp.status_code == 200 diff --git a/httpserver-gpp13.2/test_httpserver-gpp13.2.py b/httpserver-gpp13.2/test_httpserver-gpp13.2.py index 624502ac..679d5d79 100644 --- a/httpserver-gpp13.2/test_httpserver-gpp13.2.py +++ b/httpserver-gpp13.2/test_httpserver-gpp13.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_gpp_serves_hello(build_image, run_instance, http): +def test_httpserver_gpp_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-gpp13.2", "httpserver-gpp13.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_gpp_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-java17-spring-petclinic/test_httpserver-java17-spring-petclinic.py b/httpserver-java17-spring-petclinic/test_httpserver-java17-spring-petclinic.py index e06d26fa..db6a0fee 100644 --- a/httpserver-java17-spring-petclinic/test_httpserver-java17-spring-petclinic.py +++ b/httpserver-java17-spring-petclinic/test_httpserver-java17-spring-petclinic.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_spring_petclinic_serves_page(build_image, run_instance, http): +def test_spring_petclinic_serves_page(build_image, run_instance, http, wait_instance): image = build_image("httpserver-java17-spring-petclinic", "httpserver-java17-spring-petclinic") instance = run_instance( @@ -24,6 +24,8 @@ def test_spring_petclinic_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "PetClinic" in resp.text diff --git a/httpserver-java17-springboot3.5.x/test_httpserver-java17-springboot3.5.x.py b/httpserver-java17-springboot3.5.x/test_httpserver-java17-springboot3.5.x.py index 57394860..9062e792 100644 --- a/httpserver-java17-springboot3.5.x/test_httpserver-java17-springboot3.5.x.py +++ b/httpserver-java17-springboot3.5.x/test_httpserver-java17-springboot3.5.x.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_springboot_serves_hello(build_image, run_instance, http): +def test_springboot_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-java17-springboot3.5.x", "httpserver-java17-springboot3.5.x") instance = run_instance( @@ -24,6 +24,8 @@ def test_springboot_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/hello") assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-java21/test_httpserver-java21.py b/httpserver-java21/test_httpserver-java21.py index f24d88dc..f89950ea 100644 --- a/httpserver-java21/test_httpserver-java21.py +++ b/httpserver-java21/test_httpserver-java21.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_java21_serves_hello(build_image, run_instance, http): +def test_httpserver_java21_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-java21", "httpserver-java21") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_java21_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-lua5.1/test_httpserver-lua5.1.py b/httpserver-lua5.1/test_httpserver-lua5.1.py index 903ac493..dd8dd2a7 100644 --- a/httpserver-lua5.1/test_httpserver-lua5.1.py +++ b/httpserver-lua5.1/test_httpserver-lua5.1.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_lua_serves_hello(build_image, run_instance, http): +def test_httpserver_lua_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-lua5.1", "httpserver-lua5.1") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_lua_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-nginx-vite-vanilla/test_httpserver-nginx-vite-vanilla.py b/httpserver-nginx-vite-vanilla/test_httpserver-nginx-vite-vanilla.py index a2c65302..09e709b9 100644 --- a/httpserver-nginx-vite-vanilla/test_httpserver-nginx-vite-vanilla.py +++ b/httpserver-nginx-vite-vanilla/test_httpserver-nginx-vite-vanilla.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_nginx_vite_serves_page(build_image, run_instance, http): +def test_httpserver_nginx_vite_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-nginx-vite-vanilla", "httpserver-nginx-vite-vanilla", @@ -27,5 +27,7 @@ def test_httpserver_nginx_vite_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node-express-puppeteer/test_httpserver-node-express-puppeteer.py b/httpserver-node-express-puppeteer/test_httpserver-node-express-puppeteer.py index 6aacf072..3300cf50 100644 --- a/httpserver-node-express-puppeteer/test_httpserver-node-express-puppeteer.py +++ b/httpserver-node-express-puppeteer/test_httpserver-node-express-puppeteer.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_puppeteer_serves_page(build_image, run_instance, http): +def test_httpserver_puppeteer_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-node-express-puppeteer", "httpserver-node-express-puppeteer", @@ -27,5 +27,7 @@ def test_httpserver_puppeteer_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node-vite-ssr-vanilla/test_httpserver-node-vite-ssr-vanilla.py b/httpserver-node-vite-ssr-vanilla/test_httpserver-node-vite-ssr-vanilla.py index 137a4d2a..fcd1a605 100644 --- a/httpserver-node-vite-ssr-vanilla/test_httpserver-node-vite-ssr-vanilla.py +++ b/httpserver-node-vite-ssr-vanilla/test_httpserver-node-vite-ssr-vanilla.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_node_vite_ssr_serves_page(build_image, run_instance, http): +def test_node_vite_ssr_serves_page(build_image, run_instance, http, wait_instance): image = build_image("httpserver-node-vite-ssr-vanilla", "httpserver-node-vite-ssr-vanilla") instance = run_instance( @@ -28,6 +28,8 @@ def test_node_vite_ssr_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello Vite!" in resp.text diff --git a/httpserver-node-vite-vanilla/test_httpserver-node-vite-vanilla.py b/httpserver-node-vite-vanilla/test_httpserver-node-vite-vanilla.py index 98d7be6f..24dbac18 100644 --- a/httpserver-node-vite-vanilla/test_httpserver-node-vite-vanilla.py +++ b/httpserver-node-vite-vanilla/test_httpserver-node-vite-vanilla.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_node_vite_vanilla_serves_page(build_image, run_instance, http): +def test_node_vite_vanilla_serves_page(build_image, run_instance, http, wait_instance): image = build_image("httpserver-node-vite-vanilla", "httpserver-node-vite-vanilla") instance = run_instance( @@ -27,6 +27,8 @@ def test_node_vite_vanilla_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Vite App" in resp.text diff --git a/httpserver-node21-nextjs/test_httpserver-node21-nextjs.py b/httpserver-node21-nextjs/test_httpserver-node21-nextjs.py index e41f44f3..dc9c98b7 100644 --- a/httpserver-node21-nextjs/test_httpserver-node21-nextjs.py +++ b/httpserver-node21-nextjs/test_httpserver-node21-nextjs.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_nextjs_serves_page(build_image, run_instance, http): +def test_httpserver_nextjs_serves_page(build_image, run_instance, http, wait_instance): image = build_image("httpserver-node21-nextjs", "httpserver-node21-nextjs") instance = run_instance( @@ -24,5 +24,7 @@ def test_httpserver_nextjs_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node21-solid-start/test_httpserver-node21-solid-start.py b/httpserver-node21-solid-start/test_httpserver-node21-solid-start.py index 9f641e3a..92f7fb55 100644 --- a/httpserver-node21-solid-start/test_httpserver-node21-solid-start.py +++ b/httpserver-node21-solid-start/test_httpserver-node21-solid-start.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_solid_start_serves_page(build_image, run_instance, http): +def test_httpserver_solid_start_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-node21-solid-start", "httpserver-node21-solid-start", @@ -27,5 +27,7 @@ def test_httpserver_solid_start_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node22-react-router/test_httpserver-node22-react-router.py b/httpserver-node22-react-router/test_httpserver-node22-react-router.py index 8644ec02..d9654957 100644 --- a/httpserver-node22-react-router/test_httpserver-node22-react-router.py +++ b/httpserver-node22-react-router/test_httpserver-node22-react-router.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_react_router_serves_page(build_image, run_instance, http): +def test_httpserver_react_router_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-node22-react-router", "httpserver-node22-react-router", @@ -27,5 +27,7 @@ def test_httpserver_react_router_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node22-sveltekit/test_httpserver-node22-sveltekit.py b/httpserver-node22-sveltekit/test_httpserver-node22-sveltekit.py index 237c2f7e..b4a8635a 100644 --- a/httpserver-node22-sveltekit/test_httpserver-node22-sveltekit.py +++ b/httpserver-node22-sveltekit/test_httpserver-node22-sveltekit.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_sveltekit_serves_page(build_image, run_instance, http): +def test_httpserver_sveltekit_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-node22-sveltekit", "httpserver-node22-sveltekit", @@ -27,5 +27,7 @@ def test_httpserver_sveltekit_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-node26/test_httpserver-node26.py b/httpserver-node26/test_httpserver-node26.py index 193166c3..68023ad6 100644 --- a/httpserver-node26/test_httpserver-node26.py +++ b/httpserver-node26/test_httpserver-node26.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_node_serves_hello(build_image, run_instance, http): +def test_httpserver_node_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-node26", "httpserver-node26") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_node_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-perl5.42/test_httpserver-perl5.42.py b/httpserver-perl5.42/test_httpserver-perl5.42.py index ec7636f3..e8250438 100644 --- a/httpserver-perl5.42/test_httpserver-perl5.42.py +++ b/httpserver-perl5.42/test_httpserver-perl5.42.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_perl_serves_hello(build_image, run_instance, http): +def test_httpserver_perl_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-perl5.42", "httpserver-perl5.42") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_perl_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-php8.2/test_httpserver-php8.2.py b/httpserver-php8.2/test_httpserver-php8.2.py index 9b7cc48b..72f455b8 100644 --- a/httpserver-php8.2/test_httpserver-php8.2.py +++ b/httpserver-php8.2/test_httpserver-php8.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_php_serves_hello(build_image, run_instance, http): +def test_httpserver_php_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-php8.2", "httpserver-php8.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_php_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-prisma-expressjs4.19-node18/test_httpserver-prisma-expressjs4.19-node18.py b/httpserver-prisma-expressjs4.19-node18/test_httpserver-prisma-expressjs4.19-node18.py index a6256b1c..2674b939 100644 --- a/httpserver-prisma-expressjs4.19-node18/test_httpserver-prisma-expressjs4.19-node18.py +++ b/httpserver-prisma-expressjs4.19-node18/test_httpserver-prisma-expressjs4.19-node18.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_prisma_serves_users(build_image, run_instance, http): +def test_httpserver_prisma_serves_users(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-prisma-expressjs4.19-node18", "httpserver-prisma-expressjs4.19-node18", @@ -27,6 +27,8 @@ def test_httpserver_prisma_serves_users(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/users") assert resp.status_code == 200 users = resp.json() diff --git a/httpserver-python3.12-django5.0/test_httpserver-python3.12-django5.0.py b/httpserver-python3.12-django5.0/test_httpserver-python3.12-django5.0.py index 7484d3a5..db51a0cc 100644 --- a/httpserver-python3.12-django5.0/test_httpserver-python3.12-django5.0.py +++ b/httpserver-python3.12-django5.0/test_httpserver-python3.12-django5.0.py @@ -12,10 +12,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_django_serves_admin(build_image, run_instance, http): +def test_django_serves_admin(build_image, run_instance, http, wait_instance): image = build_image("httpserver-python3.12-django5.0", "httpserver-python3.12-django5.0") instance = run_instance( @@ -27,6 +27,8 @@ def test_django_serves_admin(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/admin/", expected_status=200) assert resp.status_code == 200 assert "Django" in resp.text diff --git a/httpserver-python3.12-fastapi-0.121.3/test_httpserver-python3.12-fastapi-0.121.3.py b/httpserver-python3.12-fastapi-0.121.3/test_httpserver-python3.12-fastapi-0.121.3.py index 59e063ee..98b9049e 100644 --- a/httpserver-python3.12-fastapi-0.121.3/test_httpserver-python3.12-fastapi-0.121.3.py +++ b/httpserver-python3.12-fastapi-0.121.3/test_httpserver-python3.12-fastapi-0.121.3.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_fastapi_serves_json(build_image, run_instance, http): +def test_httpserver_fastapi_serves_json(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-python3.12-fastapi-0.121.3", "httpserver-python3.12-fastapi-0.121.3", @@ -27,6 +27,8 @@ def test_httpserver_fastapi_serves_json(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 body = resp.json() diff --git a/httpserver-python3.12-flask3.0-sqlite/test_httpserver-python3.12-flask3.0-sqlite.py b/httpserver-python3.12-flask3.0-sqlite/test_httpserver-python3.12-flask3.0-sqlite.py index ab58f4d0..f2927238 100644 --- a/httpserver-python3.12-flask3.0-sqlite/test_httpserver-python3.12-flask3.0-sqlite.py +++ b/httpserver-python3.12-flask3.0-sqlite/test_httpserver-python3.12-flask3.0-sqlite.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_flask_sqlite_serves_page(build_image, run_instance, http): +def test_httpserver_flask_sqlite_serves_page(build_image, run_instance, http, wait_instance): image = build_image( "httpserver-python3.12-flask3.0-sqlite", "httpserver-python3.12-flask3.0-sqlite", @@ -27,6 +27,8 @@ def test_httpserver_flask_sqlite_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "FlaskBlog" in resp.text diff --git a/httpserver-python3.12-flask3.0/test_httpserver-python3.12-flask3.0.py b/httpserver-python3.12-flask3.0/test_httpserver-python3.12-flask3.0.py index 723c2ce6..280e1b9f 100644 --- a/httpserver-python3.12-flask3.0/test_httpserver-python3.12-flask3.0.py +++ b/httpserver-python3.12-flask3.0/test_httpserver-python3.12-flask3.0.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_flask_serves_hello(build_image, run_instance, http): +def test_httpserver_flask_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-python3.12-flask3.0", "httpserver-python3.12-flask3.0") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_flask_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-python3.12/test_httpserver-python3.12.py b/httpserver-python3.12/test_httpserver-python3.12.py index 7ff94ed3..5033db6c 100644 --- a/httpserver-python3.12/test_httpserver-python3.12.py +++ b/httpserver-python3.12/test_httpserver-python3.12.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_python_serves_welcome_message(build_image, run_instance, http): +def test_python_serves_welcome_message(build_image, run_instance, http, wait_instance): image = build_image("httpserver-python3.12", "httpserver-python3.12") instance = run_instance( @@ -24,6 +24,8 @@ def test_python_serves_welcome_message(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-ruby3.2/test_httpserver-ruby3.2.py b/httpserver-ruby3.2/test_httpserver-ruby3.2.py index 3a51f954..453aacff 100644 --- a/httpserver-ruby3.2/test_httpserver-ruby3.2.py +++ b/httpserver-ruby3.2/test_httpserver-ruby3.2.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_ruby32_serves_hello(build_image, run_instance, http): +def test_httpserver_ruby32_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-ruby3.2", "httpserver-ruby3.2") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_ruby32_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-rust-trunkrs-leptos/test_httpserver-rust-trunkrs-leptos.py b/httpserver-rust-trunkrs-leptos/test_httpserver-rust-trunkrs-leptos.py index 70d76ef1..b646f047 100644 --- a/httpserver-rust-trunkrs-leptos/test_httpserver-rust-trunkrs-leptos.py +++ b/httpserver-rust-trunkrs-leptos/test_httpserver-rust-trunkrs-leptos.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_rust_leptos_serves_page(build_image, run_instance, http): +def test_httpserver_rust_leptos_serves_page(build_image, run_instance, http, wait_instance): image = build_image("httpserver-rust-trunkrs-leptos", "httpserver-rust-trunkrs-leptos") instance = run_instance( @@ -24,5 +24,7 @@ def test_httpserver_rust_leptos_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/httpserver-rust1.75-tokio/test_httpserver-rust1.75-tokio.py b/httpserver-rust1.75-tokio/test_httpserver-rust1.75-tokio.py index 857965c3..c0dffec2 100644 --- a/httpserver-rust1.75-tokio/test_httpserver-rust1.75-tokio.py +++ b/httpserver-rust1.75-tokio/test_httpserver-rust1.75-tokio.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_rust_tokio_serves_hello(build_image, run_instance, http): +def test_httpserver_rust_tokio_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-rust1.75-tokio", "httpserver-rust1.75-tokio") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_rust_tokio_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-rust1.88-actix-web4/test_httpserver-rust1.88-actix-web4.py b/httpserver-rust1.88-actix-web4/test_httpserver-rust1.88-actix-web4.py index f0ceb049..3c714461 100644 --- a/httpserver-rust1.88-actix-web4/test_httpserver-rust1.88-actix-web4.py +++ b/httpserver-rust1.88-actix-web4/test_httpserver-rust1.88-actix-web4.py @@ -10,10 +10,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_rust_actix_serves_hello(build_image, run_instance, http): +def test_httpserver_rust_actix_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-rust1.88-actix-web4", "httpserver-rust1.88-actix-web4") instance = run_instance( @@ -25,6 +25,8 @@ def test_httpserver_rust_actix_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/httpserver-rust1.88-rocket0.5/test_httpserver-rust1.88-rocket0.5.py b/httpserver-rust1.88-rocket0.5/test_httpserver-rust1.88-rocket0.5.py index 998cb38b..05bd06dd 100644 --- a/httpserver-rust1.88-rocket0.5/test_httpserver-rust1.88-rocket0.5.py +++ b/httpserver-rust1.88-rocket0.5/test_httpserver-rust1.88-rocket0.5.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_rust_rocket_serves_greeting(build_image, run_instance, http): +def test_httpserver_rust_rocket_serves_greeting(build_image, run_instance, http, wait_instance): image = build_image("httpserver-rust1.88-rocket0.5", "httpserver-rust1.88-rocket0.5") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_rust_rocket_serves_greeting(build_image, run_instance, http) url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/wave/Rocketeer/100") assert resp.status_code == 200 assert "Hello" in resp.text diff --git a/httpserver-rust1.91/test_httpserver-rust1.91.py b/httpserver-rust1.91/test_httpserver-rust1.91.py index 21ffd178..308d6164 100644 --- a/httpserver-rust1.91/test_httpserver-rust1.91.py +++ b/httpserver-rust1.91/test_httpserver-rust1.91.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_rust_serves_hello(build_image, run_instance, http): +def test_httpserver_rust_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("httpserver-rust1.91", "httpserver-rust1.91") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_rust_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, World!" in resp.text diff --git a/hugo0.122/test_hugo0.122.py b/hugo0.122/test_hugo0.122.py index 278d1557..6b32b467 100644 --- a/hugo0.122/test_hugo0.122.py +++ b/hugo0.122/test_hugo0.122.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_hugo_serves_site(build_image, run_instance, http): +def test_hugo_serves_site(build_image, run_instance, http, wait_instance): image = build_image("hugo0.122", "hugo0.122") instance = run_instance( @@ -24,6 +24,8 @@ def test_hugo_serves_site(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "My New Hugo Site" in resp.text diff --git a/imaginary/test_imaginary.py b/imaginary/test_imaginary.py index 90488f07..ca56fea8 100644 --- a/imaginary/test_imaginary.py +++ b/imaginary/test_imaginary.py @@ -10,10 +10,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_imaginary(build_image, run_instance, http): +def test_imaginary(build_image, run_instance, http, wait_instance): """Build, deploy, and verify Imaginary's health endpoint.""" image = build_image("imaginary", "imaginary") @@ -26,6 +26,8 @@ def test_imaginary(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # The old workflow checks /health; the README shows it returns JSON # with fields like "uptime", "allocatedMemory", "goroutines", etc. resp = http(f"{url}/health") diff --git a/mariadb/test_mariadb.py b/mariadb/test_mariadb.py index e5b81518..bca2d033 100644 --- a/mariadb/test_mariadb.py +++ b/mariadb/test_mariadb.py @@ -15,11 +15,9 @@ from __future__ import annotations -import time - import pymysql -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name MARIA_USER = "root" MARIA_PASSWORD = "unikraft" @@ -39,7 +37,7 @@ def _connect(port: int): ) -def test_mariadb(build_image, run_instance, socat_tunnel): +def test_mariadb(build_image, run_instance, socat_tunnel, wait_instance): """Build, deploy, and exercise a MariaDB instance.""" image = build_image("mariadb", "mariadb") @@ -58,23 +56,8 @@ def test_mariadb(build_image, run_instance, socat_tunnel): # while MariaDB inside speaks plain MySQL protocol. tunnel = socat_tunnel(host, MARIA_PORT, MARIA_PORT) - # Retry the connection with back-off while MariaDB finishes - # first-boot initialisation (system tables, timezone import, etc.). - # pymysql has no built-in retry; connect() is eager and cleans up - # its socket on failure, so no leak on the except path. - conn = None - last_err = None - for _ in range(6): - try: - conn = _connect(tunnel.local_port) - break - except pymysql.err.OperationalError as exc: - last_err = exc - time.sleep(10) - if conn is None: - raise AssertionError( - f"could not connect to MariaDB after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "running") + conn = _connect(tunnel.local_port) try: cur = conn.cursor() diff --git a/mcp-server-arxiv/test_mcp-server-arxiv.py b/mcp-server-arxiv/test_mcp-server-arxiv.py index 6525a7ff..4bc82afd 100644 --- a/mcp-server-arxiv/test_mcp-server-arxiv.py +++ b/mcp-server-arxiv/test_mcp-server-arxiv.py @@ -12,10 +12,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_mcp_server_arxiv_responds(build_image, run_instance, http_post): +def test_mcp_server_arxiv_responds(build_image, run_instance, http_post, wait_instance): image = build_image("mcp-server-arxiv", "mcp-server-arxiv") instance = run_instance( @@ -27,6 +27,8 @@ def test_mcp_server_arxiv_responds(build_image, run_instance, http_post): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # Send an MCP initialize request to the server's HTTP transport endpoint. resp = http_post( f"{url}/mcp", diff --git a/mcp-server-simple/test_mcp-server-simple.py b/mcp-server-simple/test_mcp-server-simple.py index eaa1c51a..3c983e45 100644 --- a/mcp-server-simple/test_mcp-server-simple.py +++ b/mcp-server-simple/test_mcp-server-simple.py @@ -12,10 +12,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_mcp_server_simple_responds(build_image, run_instance, http_post): +def test_mcp_server_simple_responds(build_image, run_instance, http_post, wait_instance): image = build_image("mcp-server-simple", "mcp-server-simple") instance = run_instance( @@ -27,6 +27,8 @@ def test_mcp_server_simple_responds(build_image, run_instance, http_post): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # Send an MCP initialize request to the server's HTTP transport endpoint. resp = http_post( f"{url}/mcp", diff --git a/memcached1.6/test_memcached1.6.py b/memcached1.6/test_memcached1.6.py index e89334ff..02652e40 100644 --- a/memcached1.6/test_memcached1.6.py +++ b/memcached1.6/test_memcached1.6.py @@ -19,12 +19,11 @@ from __future__ import annotations import ssl -import time from pymemcache.client.base import Client as MemcacheClient from pymemcache.exceptions import MemcacheUnexpectedCloseError -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name MEMCACHED_PORT = 11211 @@ -41,7 +40,7 @@ def _connect(host: str) -> MemcacheClient: ) -def test_memcached(build_image, run_instance): +def test_memcached(build_image, run_instance, wait_instance): """Build, deploy, and exercise a Memcached instance.""" image = build_image("memcached1.6", "memcached16") @@ -54,30 +53,8 @@ def test_memcached(build_image, run_instance): host = extract_instance_fqdn(instance) assert host, f"could not determine instance FQDN from: {instance!r}" - # Retry the connection with back-off while the instance starts up. - # pymemcache's base Client has no built-in retry, so we loop manually. - # The constructor is lazy — connections are made on first command. - client = None - last_err = None - for _ in range(10): - try: - client = _connect(host) - client.version() # probe connectivity - break - except ( - ConnectionRefusedError, - ConnectionResetError, - TimeoutError, - ssl.SSLError, - MemcacheUnexpectedCloseError, - ) as exc: - last_err = exc - client = None - time.sleep(3) - if client is None: - raise AssertionError( - f"could not connect to Memcached after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "running") + client = _connect(host) try: # ------------------------------------------------------------------ # 1. set / get — basic key-value round-trip. diff --git a/minecraft/test_minecraft.py b/minecraft/test_minecraft.py index c69347ce..7eed1800 100644 --- a/minecraft/test_minecraft.py +++ b/minecraft/test_minecraft.py @@ -136,7 +136,6 @@ def _cleanup_base(): # 1. Build the base image. unikraft.build(context, base_tag) - time.sleep(3) # 2. Run the base image with the base ROM; it auto-converts into a template. unikraft.run_instance( @@ -165,7 +164,7 @@ def _cleanup_base(): def test_minecraft_server_responds( - _minecraft_template, request, unikraft, test_run_id, socat_tunnel + _minecraft_template, request, unikraft, test_run_id, socat_tunnel, wait_instance ): """Run a Minecraft instance from template and verify SLP response.""" template_name = _minecraft_template @@ -188,21 +187,8 @@ def test_minecraft_server_responds( # Set up a socat TLS tunnel to the Minecraft port. tunnel = socat_tunnel(host, MINECRAFT_PORT, MINECRAFT_PORT) - # Retry the SLP query with back-off while the server finishes starting. - status = None - last_err = None - for _ in range(20): - try: - status = _minecraft_slp("127.0.0.1", tunnel.local_port) - break - except (ConnectionError, OSError, AssertionError, ValueError) as exc: - last_err = exc - time.sleep(10) - - if status is None: - raise AssertionError( - f"could not get Minecraft SLP status after retries: {last_err}" - ) from last_err + wait_instance(instance_name, "standby") + status = _minecraft_slp("127.0.0.1", tunnel.local_port) # Verify the response contains expected fields. assert "version" in status, f"missing 'version' in SLP response: {status}" diff --git a/minio/test_minio.py b/minio/test_minio.py index 6808b1c8..76786e6d 100644 --- a/minio/test_minio.py +++ b/minio/test_minio.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_hugo_serves_site(build_image, run_instance, http): +def test_hugo_serves_site(build_image, run_instance, http, wait_instance): image = build_image("minio", "minio") instance = run_instance( @@ -24,6 +24,8 @@ def test_hugo_serves_site(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/health/live") assert resp.status_code == 200 assert "html" in resp.text diff --git a/mysql/test_mysql.py b/mysql/test_mysql.py index a0bc5ecb..fa3a21b4 100644 --- a/mysql/test_mysql.py +++ b/mysql/test_mysql.py @@ -15,11 +15,9 @@ from __future__ import annotations -import time - import pymysql -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name MYSQL_USER = "root" MYSQL_PASSWORD = "unikraft" @@ -39,7 +37,7 @@ def _connect(port: int): ) -def test_mysql(build_image, run_instance, socat_tunnel): +def test_mysql(build_image, run_instance, socat_tunnel, wait_instance): """Build, deploy, and exercise a MySQL instance.""" image = build_image("mysql", "mysql") @@ -58,23 +56,8 @@ def test_mysql(build_image, run_instance, socat_tunnel): # while MySQL inside speaks plain MySQL protocol. tunnel = socat_tunnel(host, MYSQL_PORT, MYSQL_PORT) - # Retry the connection with back-off while MySQL finishes - # first-boot initialisation (system tables, timezone import, etc.). - # pymysql has no built-in retry; connect() is eager and cleans up - # its socket on failure, so no leak on the except path. - conn = None - last_err = None - for _ in range(6): - try: - conn = _connect(tunnel.local_port) - break - except pymysql.err.OperationalError as exc: - last_err = exc - time.sleep(10) - if conn is None: - raise AssertionError( - f"could not connect to MySQL after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "running") + conn = _connect(tunnel.local_port) try: cur = conn.cursor() diff --git a/neo4j/test_neo4j.py b/neo4j/test_neo4j.py index 91ba08ca..a0307e23 100644 --- a/neo4j/test_neo4j.py +++ b/neo4j/test_neo4j.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_httpserver_flask_serves_hello(build_image, run_instance, http): +def test_httpserver_flask_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("neo4j", "neo4j") instance = run_instance( @@ -24,6 +24,8 @@ def test_httpserver_flask_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/nginx-flask-mongo/test_nginx-flask-mongo.py b/nginx-flask-mongo/test_nginx-flask-mongo.py index a8476838..851a3a84 100644 --- a/nginx-flask-mongo/test_nginx-flask-mongo.py +++ b/nginx-flask-mongo/test_nginx-flask-mongo.py @@ -13,12 +13,12 @@ import logging -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url log = logging.getLogger(__name__) -def test_nginx_flask_mongo(build_image, run_instance, http, unikraft, request, test_run_id): +def test_nginx_flask_mongo(build_image, run_instance, http, unikraft, request, test_run_id, wait_instance): volume_name = f"nginx-flask-mongo-data-{test_run_id}" def _cleanup_volume(): @@ -79,6 +79,8 @@ def _cleanup_volume(): url = extract_instance_url(nginx_instance) assert url, f"could not determine instance URL from: {nginx_instance!r}" + wait_instance(extract_instance_name(nginx_instance), "running") + # 5. Verify the response from the full stack. resp = http(url) assert resp.status_code == 200 diff --git a/nginx/test_nginx.py b/nginx/test_nginx.py index 26bc93e2..482b7a5e 100644 --- a/nginx/test_nginx.py +++ b/nginx/test_nginx.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_nginx_serves_welcome_page(build_image, run_instance, http): +def test_nginx_serves_welcome_page(build_image, run_instance, http, wait_instance): image = build_image("nginx", "nginx") instance = run_instance( @@ -24,6 +24,8 @@ def test_nginx_serves_welcome_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Welcome to nginx" in resp.text diff --git a/node-code-execution/test_node-code-execution.py b/node-code-execution/test_node-code-execution.py index 53fa80e4..5d9d8d98 100644 --- a/node-code-execution/test_node-code-execution.py +++ b/node-code-execution/test_node-code-execution.py @@ -61,7 +61,6 @@ def _cleanup_base(): # 1. Build the base image. unikraft.build(context, base_tag) - time.sleep(3) # 2. Run the base image; it auto-converts into a template. unikraft.run_instance( @@ -85,13 +84,12 @@ def _cleanup_base(): # 4. Build ROM images. unikraft.build(context / "rom1", rom1_tag) unikraft.build(context / "rom2", rom2_tag) - time.sleep(3) return template_name, rom1_tag, rom2_tag def test_node_code_execution_rom1( - _template_and_roms, request, unikraft, test_run_id, http + _template_and_roms, request, unikraft, test_run_id, http, wait_instance ): """Run an instance from the template with ROM1 and verify response.""" template_name, rom1_tag, _ = _template_and_roms @@ -112,13 +110,14 @@ def test_node_code_execution_rom1( url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(instance_name, "standby") resp = http(url) assert resp.status_code == 200 assert "Bye, World!" in resp.text def test_node_code_execution_rom2( - _template_and_roms, request, unikraft, test_run_id, http + _template_and_roms, request, unikraft, test_run_id, http, wait_instance ): """Run an instance from the template with ROM2 and verify response.""" template_name, _, rom2_tag = _template_and_roms @@ -139,6 +138,7 @@ def test_node_code_execution_rom2( url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(instance_name, "standby") resp = http(url) assert resp.status_code == 200 assert "Auf Wiedersehen!" in resp.text diff --git a/node-playwright-chromium/test_node-playwright-chromium.py b/node-playwright-chromium/test_node-playwright-chromium.py index 359a65e6..ad644156 100644 --- a/node-playwright-chromium/test_node-playwright-chromium.py +++ b/node-playwright-chromium/test_node-playwright-chromium.py @@ -10,12 +10,12 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url _PNG_MAGIC = b"\x89PNG\r\n\x1a\n" -def test_node_playwright_chromium_screenshot(build_image, run_instance, http): +def test_node_playwright_chromium_screenshot(build_image, run_instance, http, wait_instance): image = build_image( "node-playwright-chromium", "node-playwright-chromium", @@ -30,6 +30,8 @@ def test_node_playwright_chromium_screenshot(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/?page=https://example.com") assert resp.status_code == 200 assert resp.content[:8] == _PNG_MAGIC, "response is not a valid PNG image" diff --git a/node-playwright-firefox/test_node-playwright-firefox.py b/node-playwright-firefox/test_node-playwright-firefox.py index bdc6eb7d..d6a67a8e 100644 --- a/node-playwright-firefox/test_node-playwright-firefox.py +++ b/node-playwright-firefox/test_node-playwright-firefox.py @@ -10,12 +10,12 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url _PNG_MAGIC = b"\x89PNG\r\n\x1a\n" -def test_node_playwright_firefox_screenshot(build_image, run_instance, http): +def test_node_playwright_firefox_screenshot(build_image, run_instance, http, wait_instance): image = build_image( "node-playwright-firefox", "node-playwright-firefox", @@ -30,6 +30,8 @@ def test_node_playwright_firefox_screenshot(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/?page=https://example.com") assert resp.status_code == 200 assert resp.content[:8] == _PNG_MAGIC, "response is not a valid PNG image" diff --git a/node-playwright-webkit/test_node-playwright-webkit.py b/node-playwright-webkit/test_node-playwright-webkit.py index a5358acd..a75c5b45 100644 --- a/node-playwright-webkit/test_node-playwright-webkit.py +++ b/node-playwright-webkit/test_node-playwright-webkit.py @@ -10,12 +10,12 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url _PNG_MAGIC = b"\x89PNG\r\n\x1a\n" -def test_node_playwright_webkit_screenshot(build_image, run_instance, http): +def test_node_playwright_webkit_screenshot(build_image, run_instance, http, wait_instance): image = build_image( "node-playwright-webkit", "node-playwright-webkit", @@ -30,6 +30,8 @@ def test_node_playwright_webkit_screenshot(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/?page=https://example.com") assert resp.status_code == 200 assert resp.content[:8] == _PNG_MAGIC, "response is not a valid PNG image" diff --git a/node18-agario/test_node18-agario.py b/node18-agario/test_node18-agario.py index a5464a14..81117f99 100644 --- a/node18-agario/test_node18-agario.py +++ b/node18-agario/test_node18-agario.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_agario_serves_page(build_image, run_instance, http): +def test_agario_serves_page(build_image, run_instance, http, wait_instance): image = build_image("node18-agario", "node18-agario") instance = run_instance( @@ -24,6 +24,8 @@ def test_agario_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert " None: """Connect to the WebSocket server and exercise its echo behaviour.""" uri = f"wss://{host}" - # Retry connection with back-off while the instance starts up. - conn = None - last_err = None - for attempt in range(10): - try: - conn = await asyncio.wait_for( - websockets.connect(uri), - timeout=10, - ) - break - except Exception as exc: - last_err = exc - if attempt < 9: - await asyncio.sleep(3) - if conn is None: - raise AssertionError( - f"could not connect to WebSocket server after retries: {last_err}" - ) from last_err - + conn = await asyncio.wait_for( + websockets.connect(uri), + timeout=10, + ) async with conn: # ------------------------------------------------------------------ # 1. Greeting — server sends a welcome message on connect. @@ -67,7 +52,7 @@ async def _websocket_test(host: str) -> None: assert reply.decode() == msg, f"expected {msg!r}, got {reply!r}" -def test_node21_websocket(build_image, run_instance): +def test_node21_websocket(build_image, run_instance, wait_instance): """Build, deploy, and exercise a Node.js WebSocket echo server.""" image = build_image("node21-websocket", "node21-websocket") @@ -80,4 +65,5 @@ def test_node21_websocket(build_image, run_instance): host = extract_instance_fqdn(instance) assert host, f"could not determine instance FQDN from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") asyncio.run(_websocket_test(host)) diff --git a/node24-karaoke/test_node24-karaoke.py b/node24-karaoke/test_node24-karaoke.py index c4177eb0..54ca48ce 100644 --- a/node24-karaoke/test_node24-karaoke.py +++ b/node24-karaoke/test_node24-karaoke.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_karaoke_serves_page(build_image, run_instance, http): +def test_karaoke_serves_page(build_image, run_instance, http, wait_instance): image = build_image("node24-karaoke", "node24-karaoke") instance = run_instance( @@ -24,5 +24,7 @@ def test_karaoke_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/novnc-browser/test_novnc-browser.py b/novnc-browser/test_novnc-browser.py index 20e1d59e..8f8aa864 100644 --- a/novnc-browser/test_novnc-browser.py +++ b/novnc-browser/test_novnc-browser.py @@ -11,11 +11,11 @@ import pytest -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url @pytest.mark.timeout(900) -def test_novnc_serves_page(build_image, run_instance, http): +def test_novnc_serves_page(build_image, run_instance, http, wait_instance): image = build_image("novnc-browser", "novnc-browser") instance = run_instance( @@ -27,6 +27,8 @@ def test_novnc_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # noVNC has a heavy startup (Xvfb, mutter, tint2, x11vnc, then noVNC # proxy) so allow plenty of time for the HTTP endpoint to become ready. resp = http(url, retries=30, backoff=5.0, timeout=30.0) diff --git a/openclaw/test_openclaw.py b/openclaw/test_openclaw.py index dc8a3ae0..38ed6012 100644 --- a/openclaw/test_openclaw.py +++ b/openclaw/test_openclaw.py @@ -12,10 +12,9 @@ from __future__ import annotations import subprocess -import time from pathlib import Path -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name SSH_PORT = 2222 @@ -34,7 +33,7 @@ def _generate_test_keypair(tmp_path: Path) -> tuple[Path, str]: return key_path, pub_key -def test_openclaw_ssh_and_binary(build_image, run_instance, socat_tunnel, tmp_path): +def test_openclaw_ssh_and_binary(build_image, run_instance, socat_tunnel, wait_instance, tmp_path): """Build, deploy, SSH in, and verify the openclaw binary is present.""" private_key, public_key = _generate_test_keypair(tmp_path) @@ -56,37 +55,24 @@ def test_openclaw_ssh_and_binary(build_image, run_instance, socat_tunnel, tmp_pa # Set up a socat TLS tunnel to the SSH port. tunnel = socat_tunnel(host, SSH_PORT, SSH_PORT) - # Retry SSH connection with back-off while the instance boots. - last_err = None - for _ in range(10): - try: - result = subprocess.run( - [ - "ssh", - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - "-o", "ConnectTimeout=10", - "-i", str(private_key), - "-p", str(tunnel.local_port), - "-l", "root", - "127.0.0.1", - "which openclaw", - ], - capture_output=True, - text=True, - timeout=30, - ) - if result.returncode == 0: - assert "openclaw" in result.stdout - return - last_err = RuntimeError( - f"ssh exited {result.returncode}: {result.stderr}" - ) - except (subprocess.TimeoutExpired, OSError) as exc: - last_err = exc - - time.sleep(5) - - raise AssertionError( - f"could not connect via SSH after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "standby") + result = subprocess.run( + [ + "ssh", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "ConnectTimeout=10", + "-i", str(private_key), + "-p", str(tunnel.local_port), + "-l", "root", + "127.0.0.1", + "which openclaw", + ], + capture_output=True, + text=True, + timeout=30, + ) + assert result.returncode == 0, ( + f"ssh failed (exit={result.returncode})\nstdout: {result.stdout}\nstderr: {result.stderr}" + ) + assert "openclaw" in result.stdout diff --git a/opentelemetry-collector/test_opentelemetry-collector.py b/opentelemetry-collector/test_opentelemetry-collector.py index 17300e14..abb73288 100644 --- a/opentelemetry-collector/test_opentelemetry-collector.py +++ b/opentelemetry-collector/test_opentelemetry-collector.py @@ -11,10 +11,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_opentelemetry_collector(build_image, run_instance, http): +def test_opentelemetry_collector(build_image, run_instance, http, wait_instance): """Build, deploy, and verify the OTel Collector health check responds.""" image = build_image("opentelemetry-collector", "opentelemetry-collector") @@ -27,5 +27,7 @@ def test_opentelemetry_collector(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/phoenix-postgres/test_phoenix-postgres.py b/phoenix-postgres/test_phoenix-postgres.py index e6eb5a6d..f4b97632 100644 --- a/phoenix-postgres/test_phoenix-postgres.py +++ b/phoenix-postgres/test_phoenix-postgres.py @@ -13,7 +13,7 @@ import logging import uuid -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url log = logging.getLogger(__name__) @@ -22,7 +22,7 @@ PG_DB = "myapp_prod" -def test_phoenix_postgres(build_image, run_instance, http, unikraft, request, test_run_id): +def test_phoenix_postgres(build_image, run_instance, http, unikraft, request, test_run_id, wait_instance): volume_name = f"phoenix-pytest-db-data-{test_run_id}" def _cleanup_volume(): @@ -81,6 +81,8 @@ def _cleanup_volume(): url = extract_instance_url(phoenix_instance) assert url, f"could not determine instance URL from: {phoenix_instance!r}" + wait_instance(extract_instance_name(phoenix_instance), "running") + # 4. Verify Phoenix is serving. resp = http(url) assert resp.status_code == 200 diff --git a/postgres/test_postgres.py b/postgres/test_postgres.py index d2e67e1d..b3d8d183 100644 --- a/postgres/test_postgres.py +++ b/postgres/test_postgres.py @@ -12,11 +12,9 @@ from __future__ import annotations -import time - import psycopg2 -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name # The README deploys with POSTGRES_PASSWORD=unikraft. PG_USER = "postgres" @@ -38,7 +36,7 @@ def _connect(host: str): ) -def test_postgres(build_image, run_instance): +def test_postgres(build_image, run_instance, wait_instance): """Build, deploy, and exercise a PostgreSQL instance.""" image = build_image("postgres", "postgres") @@ -52,23 +50,9 @@ def test_postgres(build_image, run_instance): host = extract_instance_fqdn(instance) assert host, f"could not determine instance FQDN from: {instance!r}" - # Retry the connection with back-off while PostgreSQL finishes - # first-boot initialisation. - # psycopg2 has no built-in retry; connect() is eager and the C - # extension cleans up the libpq connection on failure, so no leak. - conn = None - last_err = None - for _ in range(10): - try: - conn = _connect(host) - break - except psycopg2.OperationalError as exc: - last_err = exc - time.sleep(5) - if conn is None: - raise AssertionError( - f"could not connect to PostgreSQL after retries: {last_err}" - ) from last_err + wait_instance(extract_instance_name(instance), "running") + + conn = _connect(host) try: conn.autocommit = True cur = conn.cursor() diff --git a/python-playwright-chromium/test_python-playwright-chromium.py b/python-playwright-chromium/test_python-playwright-chromium.py index c73ef983..c7a22149 100644 --- a/python-playwright-chromium/test_python-playwright-chromium.py +++ b/python-playwright-chromium/test_python-playwright-chromium.py @@ -10,12 +10,12 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url _PNG_MAGIC = b"\x89PNG\r\n\x1a\n" -def test_python_playwright_chromium_screenshot(build_image, run_instance, http): +def test_python_playwright_chromium_screenshot(build_image, run_instance, http, wait_instance): image = build_image( "python-playwright-chromium", "python-playwright-chromium", @@ -30,6 +30,8 @@ def test_python_playwright_chromium_screenshot(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/?page=https://example.com") assert resp.status_code == 200 assert resp.content[:8] == _PNG_MAGIC, "response is not a valid PNG image" diff --git a/ruby3.2-rails/test_ruby3.2-rails.py b/ruby3.2-rails/test_ruby3.2-rails.py index 67d82d51..d66a75e4 100644 --- a/ruby3.2-rails/test_ruby3.2-rails.py +++ b/ruby3.2-rails/test_ruby3.2-rails.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_ruby_rails_serves_hello(build_image, run_instance, http): +def test_ruby_rails_serves_hello(build_image, run_instance, http, wait_instance): image = build_image("ruby3.2-rails", "ruby3.2-rails") instance = run_instance( @@ -28,6 +28,8 @@ def test_ruby_rails_serves_hello(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(f"{url}/hello") assert resp.status_code == 200 assert "Hello World" in resp.text diff --git a/skipper0.18/test_skipper0.18.py b/skipper0.18/test_skipper0.18.py index 760a5ffb..130c9d27 100644 --- a/skipper0.18/test_skipper0.18.py +++ b/skipper0.18/test_skipper0.18.py @@ -10,10 +10,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_skipper(build_image, run_instance, http): +def test_skipper(build_image, run_instance, http, wait_instance): """Build, deploy, and verify Skipper serves its configured response.""" image = build_image("skipper0.18", "skipper018") @@ -26,6 +26,8 @@ def test_skipper(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "Hello, world from Skipper on Unikraft!" in resp.text diff --git a/spin-wagi-http/test_spin-wagi-http.py b/spin-wagi-http/test_spin-wagi-http.py index 2e39547e..0bcfe26e 100644 --- a/spin-wagi-http/test_spin-wagi-http.py +++ b/spin-wagi-http/test_spin-wagi-http.py @@ -10,10 +10,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_spin_wagi_serves_hello_and_goodbye(build_image, run_instance, http): +def test_spin_wagi_serves_hello_and_goodbye(build_image, run_instance, http, wait_instance): image = build_image("spin-wagi-http", "spin-wagi-http") instance = run_instance( @@ -25,6 +25,8 @@ def test_spin_wagi_serves_hello_and_goodbye(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp_hello = http(f"{url}/hello") assert resp_hello.status_code == 200 assert "Hello, Fermyon!" in resp_hello.text diff --git a/traefik/test_traefik.py b/traefik/test_traefik.py index 29bcba4b..49d69ccf 100644 --- a/traefik/test_traefik.py +++ b/traefik/test_traefik.py @@ -13,10 +13,10 @@ from urllib.parse import urlparse -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_traefik_dashboard(build_image, run_instance, http): +def test_traefik_dashboard(build_image, run_instance, http, wait_instance): """Build, deploy, and verify the Traefik dashboard is accessible.""" image = build_image("traefik", "traefik") @@ -29,6 +29,8 @@ def test_traefik_dashboard(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + # The old workflow and README test the dashboard on port 8080. parsed = urlparse(url) dashboard_url = f"https://{parsed.hostname}:8080/dashboard/" diff --git a/tyk/test_tyk.py b/tyk/test_tyk.py index eb5811b6..1dc589ea 100644 --- a/tyk/test_tyk.py +++ b/tyk/test_tyk.py @@ -11,13 +11,13 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url REDIS_PASSWORD = "unikraft" -def test_tyk_hello(build_image, run_instance, http): +def test_tyk_hello(build_image, run_instance, http, wait_instance): # 1. Build and deploy internal Redis instance. # NOTE: domain must be "tyk-redis.internal" — hardcoded in tyk/rootfs/etc/tyk.conf. redis_image = build_image("tyk/redis", "tyk-redis") @@ -47,6 +47,8 @@ def test_tyk_hello(build_image, run_instance, http): url = extract_instance_url(tyk_instance) assert url, f"could not determine instance URL from: {tyk_instance!r}" + wait_instance(extract_instance_name(tyk_instance), "running") + # 3. Query the /hello endpoint — Tyk returns a health-check JSON # with status "pass". resp = http(f"{url}/hello") diff --git a/visual-studio-code-server/test_visual-studio-code-server.py b/visual-studio-code-server/test_visual-studio-code-server.py index be5edbfe..9b08b9c5 100644 --- a/visual-studio-code-server/test_visual-studio-code-server.py +++ b/visual-studio-code-server/test_visual-studio-code-server.py @@ -15,14 +15,14 @@ import logging import uuid -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url log = logging.getLogger(__name__) def test_visual_studio_code_server( request, build_image, run_instance, unikraft, http -): +, wait_instance): """Build, deploy, and verify the VS Code Server login page loads.""" # Create a volume for workspace persistence, matching the old workflow. vol_name = f"code-workspace-pytest-{uuid.uuid4().hex[:8]}" @@ -64,5 +64,7 @@ def _delete_volume(): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 diff --git a/vsftpd/test_vsftpd.py b/vsftpd/test_vsftpd.py index b8726379..0ffc6b07 100644 --- a/vsftpd/test_vsftpd.py +++ b/vsftpd/test_vsftpd.py @@ -14,11 +14,10 @@ import shutil import subprocess import tempfile -import time import pytest -from _testlib.unikraft import extract_instance_fqdn +from _testlib.unikraft import extract_instance_fqdn, extract_instance_name FTP_USER = "root" FTP_PASSWORD = "rootpass" @@ -45,7 +44,7 @@ def _lftp(host: str, commands: str, timeout: float = 30) -> subprocess.Completed ) -def test_vsftpd(build_image, run_instance): +def test_vsftpd(build_image, run_instance, wait_instance): """Build, deploy, and exercise a vsftpd instance.""" image = build_image("vsftpd", "vsftpd") @@ -67,17 +66,13 @@ def test_vsftpd(build_image, run_instance): host = extract_instance_fqdn(instance) assert host, f"could not determine instance FQDN from: {instance!r}" - # Retry with back-off while the instance starts. - last_err = None - for _ in range(10): - result = _lftp(host, "ls") - if result.returncode == 0: - break - last_err = result.stderr - time.sleep(5) - else: + name = extract_instance_name(instance) + wait_instance(name, "standby") + + result = _lftp(host, "ls") + if result.returncode != 0: raise AssertionError( - f"could not connect to vsftpd after retries: {last_err}" + f"could not connect to vsftpd (exit={result.returncode})\nstdout: {result.stdout}\nstderr: {result.stderr}" ) # ls succeeded — matches the README demonstration. diff --git a/wazero-import-go/test_wazero-import-go.py b/wazero-import-go/test_wazero-import-go.py index 14854d2d..fe229190 100644 --- a/wazero-import-go/test_wazero-import-go.py +++ b/wazero-import-go/test_wazero-import-go.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_wazero_serves_age_output(build_image, run_instance, http): +def test_wazero_serves_age_output(build_image, run_instance, http, wait_instance): image = build_image("wazero-import-go", "wazero-import-go") instance = run_instance( @@ -24,6 +24,8 @@ def test_wazero_serves_age_output(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "log_i32 >>" in resp.text diff --git a/wordpress-all-in-one/test_wordpress-all-in-one.py b/wordpress-all-in-one/test_wordpress-all-in-one.py index 449a1ffc..34422755 100644 --- a/wordpress-all-in-one/test_wordpress-all-in-one.py +++ b/wordpress-all-in-one/test_wordpress-all-in-one.py @@ -9,10 +9,10 @@ from __future__ import annotations -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url -def test_wordpress_serves_page(build_image, run_instance, http): +def test_wordpress_serves_page(build_image, run_instance, http, wait_instance): image = build_image("wordpress-all-in-one", "wordpress-all-in-one") instance = run_instance( @@ -24,6 +24,8 @@ def test_wordpress_serves_page(build_image, run_instance, http): url = extract_instance_url(instance) assert url, f"could not determine instance URL from: {instance!r}" + wait_instance(extract_instance_name(instance), "running") + resp = http(url) assert resp.status_code == 200 assert "WordPress" in resp.text diff --git a/wordpress/test_wordpress.py b/wordpress/test_wordpress.py index 032188f7..24b07447 100644 --- a/wordpress/test_wordpress.py +++ b/wordpress/test_wordpress.py @@ -14,14 +14,14 @@ import logging -from _testlib.unikraft import extract_instance_url +from _testlib.unikraft import extract_instance_name, extract_instance_url log = logging.getLogger(__name__) MARIADB_ROOT_PASSWORD = "unikraft" -def test_wordpress_serves_page(build_image, run_instance, http, unikraft, request, test_run_id): +def test_wordpress_serves_page(build_image, run_instance, http, unikraft, request, test_run_id, wait_instance): wp_volume = f"wordpress-wp-data-{test_run_id}" db_volume = f"wordpress-db-data-{test_run_id}" @@ -73,6 +73,8 @@ def _cleanup_volumes(): url = extract_instance_url(wp_instance) assert url, f"could not determine instance URL from: {wp_instance!r}" + wait_instance(extract_instance_name(wp_instance), "running") + # 4. Verify WordPress is served (install page or homepage). resp = http(url) assert resp.status_code == 200