From ffc30a5791a7d1a1362085bc760657f10d1d8d33 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Mon, 29 Jun 2026 13:19:00 +0000 Subject: [PATCH] test(integration): de-flake otel pre-job config test The test dispatched a quick workflow, waited for it to complete, then SSHed into the runner to read the otel config the pre-job script wrote. Ephemeral runner VMs are torn down on job completion, so the inspection raced the runner-manager cleanup: when cleanup won, get_single_runner found zero VMs and the test failed with an empty runner list. Dispatch the long-running wait workflow and inspect the runner while its job is in progress, so the VM is guaranteed alive. Poll on the config file to absorb the pre-job hook write timing. --- tests/integration/test_charm_runner.py | 37 ++++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/tests/integration/test_charm_runner.py b/tests/integration/test_charm_runner.py index d2ed7b5459..4d605fb85b 100644 --- a/tests/integration/test_charm_runner.py +++ b/tests/integration/test_charm_runner.py @@ -216,21 +216,36 @@ def test_otel_collector_endpoint_pre_job_installs_config( ) wait_for_runner_ready(juju, app) - dispatch_workflow( + # Dispatch the long-running wait workflow (instead of a quick one) so the runner stays + # busy while we inspect it. An ephemeral runner's VM is torn down on job completion, which + # races the inspection below; keeping the job in progress guarantees the VM is still alive. + workflow = dispatch_workflow( app_name=app, branch=test_github_branch, github_repository=github_repository, conclusion="success", - workflow_id_or_name=DISPATCH_TEST_WORKFLOW_FILENAME, - dispatch_input={"runner": app}, + workflow_id_or_name=DISPATCH_WAIT_TEST_WORKFLOW_FILENAME, + dispatch_input={"runner": app, "minutes": "5"}, + wait=False, ) + wait_for(lambda: workflow.update() or workflow.status == "in_progress") - exit_code, stdout, stderr = instance_helper.run_in_instance( - unit_name=f"{app}/0", - command="sudo cat /etc/otelcol/config.d/github.yaml", - ) + def _read_otel_config() -> str: + """Read the otel config written by the pre-job script, retrying until it exists. + + The pre-job hook may not have finished writing the file the moment the job is reported + in progress, so callers poll on a truthy (non-empty) return value. + + Returns: + The otel config file content, or an empty string if not yet written. + """ + exit_code, stdout, _ = instance_helper.run_in_instance( + unit_name=f"{app}/0", + command="sudo cat /etc/otelcol/config.d/github.yaml", + ) + return stdout if exit_code == 0 and stdout else "" + + config = wait_for(_read_otel_config, timeout=120, check_interval=10) - assert exit_code == 0, stderr - assert stdout is not None - assert "exporters:" in stdout - assert f"endpoint: {endpoint}" in stdout + assert "exporters:" in config + assert f"endpoint: {endpoint}" in config