-
Notifications
You must be signed in to change notification settings - Fork 141
fix(ci): wait for code-server before UI selenium tests #2720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ | |
| import os | ||
| import subprocess | ||
| import time | ||
| import urllib.error | ||
| import urllib.request | ||
| from collections.abc import Generator | ||
| from datetime import datetime | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
@@ -49,6 +51,46 @@ def is_container_healthy() -> bool: | |
| return result.returncode == 0 | ||
|
|
||
|
|
||
| def _wait_for_code_server_http( | ||
| url: str = "http://127.0.0.1:8080/", | ||
| timeout_sec: int = 600, | ||
| ) -> None: | ||
| """Wait until code-server accepts HTTP (not covered by Selenium :4444 healthcheck). | ||
|
|
||
| The compose file healthcheck only verifies Selenium Grid; code-server can still | ||
| be starting, which otherwise leads to long WebDriver/page-load timeouts in CI. | ||
| """ | ||
| deadline = time.time() + timeout_sec | ||
| attempt = 0 | ||
| while time.time() < deadline: | ||
| attempt += 1 | ||
| try: | ||
| with urllib.request.urlopen(url, timeout=5) as resp: | ||
| if resp.status == 200: | ||
| log.info("code-server ready at %s (attempt %s)", url, attempt) | ||
| return | ||
| except urllib.error.HTTPError as exc: | ||
| # Upstream still starting — keep polling. Other responses mean HTTP is up. | ||
| if exc.code in (502, 503, 504): | ||
| pass | ||
| else: | ||
| log.info( | ||
| "code-server ready at %s (HTTP %s, attempt %s)", | ||
| url, | ||
| exc.code, | ||
| attempt, | ||
| ) | ||
| return | ||
| except (urllib.error.URLError, OSError, TimeoutError): | ||
| pass | ||
| if attempt <= 3 or attempt % 20 == 0: | ||
| log.info("Waiting for code-server at %s: %s", url, attempt) | ||
| time.sleep(2) | ||
| pytest.fail( | ||
| f"code-server did not become ready at {url} within {timeout_sec}s", | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def browser_setup( | ||
| request: pytest.FixtureRequest, | ||
|
|
@@ -62,7 +104,7 @@ def browser_setup( | |
| "capturemanager" | ||
| ) # type: ignore[name-defined] | ||
| try: | ||
| if not is_container_healthy() or True: | ||
| if not is_container_healthy(): | ||
| subprocess.run( | ||
| f"podman rm -f {CONTAINER_NAME} 2>/dev/null || true", | ||
| shell=True, | ||
|
|
@@ -80,16 +122,30 @@ def browser_setup( | |
| shell=True, | ||
| cwd=_PROJECT_ROOT, | ||
| ) | ||
| health_deadline = time.time() + int( | ||
| os.environ.get("UI_CONTAINER_HEALTH_TIMEOUT", "900"), | ||
| ) | ||
|
Comment on lines
+125
to
+127
|
||
| count = 0 | ||
| while True: | ||
| while time.time() < health_deadline: | ||
| if is_container_healthy(): | ||
| break | ||
| count += 1 | ||
| time.sleep(1) | ||
| log.info( | ||
| "Waiting for container %s to be healthy: %s", CONTAINER_NAME, count | ||
| if count <= 5 or count % 15 == 0: | ||
| log.info( | ||
| "Waiting for container %s to be healthy: %s", | ||
| CONTAINER_NAME, | ||
| count, | ||
| ) | ||
| else: | ||
| pytest.fail( | ||
| f"container {CONTAINER_NAME} did not become healthy in time", | ||
| ) | ||
|
Comment on lines
+125
to
143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '42,52p;125,147p' test/ui/fixtures/ui_fixtures.py
rg -n -C3 'healthcheck|4444|8080' --iglob '*compose*.y*ml' || trueRepository: ansible/vscode-ansible Length of output: 2414 Add an explicit timeout to the subprocess call in While the docker-compose.yml defines a healthcheck with a 5-second timeout, the Patch sketch-def is_container_healthy() -> bool:
+def is_container_healthy(timeout_sec: int = 10) -> bool:
"""Check if the selenium container is healthy."""
- result = subprocess.run(
- f"podman healthcheck run {CONTAINER_NAME}",
- shell=True,
- check=False,
- text=True,
- capture_output=True,
- )
+ try:
+ result = subprocess.run(
+ f"podman healthcheck run {CONTAINER_NAME}",
+ shell=True,
+ check=False,
+ text=True,
+ capture_output=True,
+ timeout=timeout_sec,
+ )
+ except subprocess.TimeoutExpired:
+ return False
return result.returncode == 0🤖 Prompt for AI Agents |
||
|
|
||
| _wait_for_code_server_http( | ||
| timeout_sec=int(os.environ.get("UI_CODE_SERVER_TIMEOUT", "600")), | ||
| ) | ||
|
Comment on lines
+145
to
+147
|
||
|
|
||
| browser = os.environ.get("BROWSER_TYPE") | ||
| options: FirefoxOptions | ChromeOptions | ||
| if browser == "chrome": | ||
|
|
@@ -103,6 +159,8 @@ def browser_setup( | |
| command_executor="http://localhost:4444/wd/hub", | ||
| options=options, | ||
| ) | ||
| driver.set_page_load_timeout(300) | ||
| driver.set_script_timeout(300) | ||
| driver.maximize_window() | ||
|
|
||
| yield driver, "https://stage.ai.ansible.redhat.com/login" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
urllib.request.urlopen()raisesHTTPErrorthat still holds an open response file (exc.fp). In the retry path (502/503/504) this exception is not closed, so repeated polling can leak sockets/file descriptors in long CI runs. Close theHTTPError(e.g.,exc.close()or ensure the response is closed viacontextlib.closing).