From 5c23fb7d784a8b4777e1792b8c3664d1d66fdc56 Mon Sep 17 00:00:00 2001 From: autonomic-bot <64+autonomic-bot@noreply.git.autonomic.zone> Date: Mon, 26 Oct 2026 17:33:19 +0000 Subject: [PATCH 1/4] fix(lasuite): restore MinIO for published upgrade bases --- tests/lasuite-docs/previous/VERSION | 1 + tests/lasuite-docs/previous/compose.previous.yml | 14 ++++++++++++++ tests/lasuite-drive/previous/VERSION | 1 + tests/lasuite-drive/previous/compose.previous.yml | 14 ++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 tests/lasuite-docs/previous/VERSION create mode 100644 tests/lasuite-docs/previous/compose.previous.yml create mode 100644 tests/lasuite-drive/previous/VERSION create mode 100644 tests/lasuite-drive/previous/compose.previous.yml diff --git a/tests/lasuite-docs/previous/VERSION b/tests/lasuite-docs/previous/VERSION new file mode 100644 index 0000000..938df7f --- /dev/null +++ b/tests/lasuite-docs/previous/VERSION @@ -0,0 +1 @@ +0.4.0+v5.4.1 diff --git a/tests/lasuite-docs/previous/compose.previous.yml b/tests/lasuite-docs/previous/compose.previous.yml new file mode 100644 index 0000000..da3c874 --- /dev/null +++ b/tests/lasuite-docs/previous/compose.previous.yml @@ -0,0 +1,14 @@ +# The published base points at the removed docker.io/minio/minio image. +# Keep its /data layout, credentials and bucket initialization; run the same +# available MinIO build used by current recipes without Bitnami's wrapper. +services: + minio: + image: docker.io/bitnamilegacy/minio:2025.7.23-debian-12-r5@sha256:6dabb4a2088c9a79908de3bc05f4586c23ad2182c8908e7e3acbf61c1467fb20 + user: "0:0" + environment: + MC_CONFIG_DIR: /tmp/mc-base-init + entrypoint: + - /bin/sh + - -c + - /minio-initialize.sh & exec "$$@" + - -- diff --git a/tests/lasuite-drive/previous/VERSION b/tests/lasuite-drive/previous/VERSION new file mode 100644 index 0000000..855e347 --- /dev/null +++ b/tests/lasuite-drive/previous/VERSION @@ -0,0 +1 @@ +0.12.0+v0.21.0 diff --git a/tests/lasuite-drive/previous/compose.previous.yml b/tests/lasuite-drive/previous/compose.previous.yml new file mode 100644 index 0000000..da3c874 --- /dev/null +++ b/tests/lasuite-drive/previous/compose.previous.yml @@ -0,0 +1,14 @@ +# The published base points at the removed docker.io/minio/minio image. +# Keep its /data layout, credentials and bucket initialization; run the same +# available MinIO build used by current recipes without Bitnami's wrapper. +services: + minio: + image: docker.io/bitnamilegacy/minio:2025.7.23-debian-12-r5@sha256:6dabb4a2088c9a79908de3bc05f4586c23ad2182c8908e7e3acbf61c1467fb20 + user: "0:0" + environment: + MC_CONFIG_DIR: /tmp/mc-base-init + entrypoint: + - /bin/sh + - -c + - /minio-initialize.sh & exec "$$@" + - -- -- 2.54.0 From b944eca41d0fbfea20a1b61ce0df268c5f45c6e8 Mon Sep 17 00:00:00 2001 From: autonomic-bot <64+autonomic-bot@noreply.git.autonomic.zone> Date: Mon, 26 Oct 2026 17:34:56 +0000 Subject: [PATCH 2/4] fix(runner): allow version-guarded base repair overlays --- runner/harness/lifecycle.py | 4 ++++ tests/unit/test_previous.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/runner/harness/lifecycle.py b/runner/harness/lifecycle.py index e6e332b..1240b8d 100644 --- a/runner/harness/lifecycle.py +++ b/runner/harness/lifecycle.py @@ -487,6 +487,10 @@ def deploy_app( # may already include the environmental compose.ccci.yml). Stripped before the head redeploy # (generic.perform_upgrade) so the PR head runs unmodified. if apply_previous: + # This untracked overlay also trips abra's pinned-deploy clean-tree gate. + # The exact version was checked out above; chaos deploys that base plus + # its repair, just as for compose.ccci.yml, without fetching latest. + chaos = True cf = compose_file_add(extra.get("COMPOSE_FILE", "compose.yml"), PREVIOUS_COMPOSE) abra.env_set(domain, "COMPOSE_FILE", cf) provide_previous_overlay(recipe) diff --git a/tests/unit/test_previous.py b/tests/unit/test_previous.py index 1203aeb..859db76 100644 --- a/tests/unit/test_previous.py +++ b/tests/unit/test_previous.py @@ -116,3 +116,37 @@ def test_previous_status_stale_without_marker(monkeypatch): monkeypatch.setattr(lifecycle, "previous_target_version", lambda r: None) st = lifecycle.previous_status("rx", "version", "0.7.0+3.3.1") assert st["apply"] is False and st["stale"] is True and "marker" in st["reason"] + + +def test_previous_overlay_deploys_checked_out_base_without_clean_tree_gate(monkeypatch): + events = [] + for name in ( + "acquire_app_lock", + "provide_ccci_overlay", + "provide_previous_overlay", + "prepull_images", + ): + monkeypatch.setattr(lifecycle, name, lambda *a, **kw: None) + for name in ("app_config_remove", "app_new", "env_set", "secret_generate"): + monkeypatch.setattr(lifecycle.abra, name, lambda *a, **kw: None) + monkeypatch.setattr(lifecycle.meta_mod, "extra_env", lambda *a: {}) + monkeypatch.setattr(lifecycle.meta_mod, "hook_ctx", lambda *a: None) + monkeypatch.setattr(lifecycle.abra, "has_lightweight_version_tags", lambda r: False) + monkeypatch.setattr(lifecycle, "has_ccci_overlay", lambda r: False) + monkeypatch.setattr( + lifecycle.abra, "recipe_checkout", lambda r, v: events.append(("checkout", v)) + ) + monkeypatch.setattr( + lifecycle.abra, "deploy", lambda d, **kw: events.append(("deploy", kw["chaos"])) + ) + for apply_previous in (False, True): + events.clear() + lifecycle.deploy_app( + "example", + "example.invalid", + version="1.0.0", + apply_previous=apply_previous, + meta=object(), + _count_deploy=False, + ) + assert events == [("checkout", "1.0.0"), ("deploy", apply_previous)] -- 2.54.0 From ccc29d5acc475e026aa3005615c8642e36e30326 Mon Sep 17 00:00:00 2001 From: autonomic-bot <64+autonomic-bot@noreply.git.autonomic.zone> Date: Mon, 26 Oct 2026 17:41:57 +0000 Subject: [PATCH 3/4] fix(runner): retry convergence confirmation after restore --- runner/harness/generic.py | 10 ++++++++-- tests/unit/test_serving_retry.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_serving_retry.py diff --git a/runner/harness/generic.py b/runner/harness/generic.py index 7db0af3..0c1b94f 100644 --- a/runner/harness/generic.py +++ b/runner/harness/generic.py @@ -93,9 +93,15 @@ def assert_serving(domain: str, meta) -> None: Steps 1–2 are BOUNDED POLLS (no bare sleep), so a state-mutating op (upgrade/restore) that leaves the app briefly reconverging settles, while a persistent failure still fails within the timeout.""" deadline = time.time() + meta.DEPLOY_TIMEOUT - while time.time() < deadline and not lifecycle.services_converged(domain): + converged = False + while time.time() < deadline: + # Keep both observations inside the retry budget: a service may restart + # between the initial convergence sample and its confirmation. + if lifecycle.services_converged(domain) and lifecycle.services_converged(domain): + converged = True + break time.sleep(5) - assert lifecycle.services_converged(domain), f"{domain}: services did not converge" + assert converged, f"{domain}: services did not converge" path = meta.HEALTH_PATH ok = tuple(meta.HEALTH_OK) diff --git a/tests/unit/test_serving_retry.py b/tests/unit/test_serving_retry.py new file mode 100644 index 0000000..26b9c92 --- /dev/null +++ b/tests/unit/test_serving_retry.py @@ -0,0 +1,30 @@ +"""Convergence confirmation must retry a transient second-sample failure.""" + +from types import SimpleNamespace + +import pytest + +from harness import generic + + +def _setup(monkeypatch, samples): + clock = [0] + monkeypatch.setattr(generic.time, "time", lambda: clock[0]) + monkeypatch.setattr(generic.time, "sleep", lambda n: clock.__setitem__(0, clock[0] + n)) + monkeypatch.setattr(generic.lifecycle, "services_converged", lambda d: next(samples)) + monkeypatch.setattr(generic.lifecycle, "http_fetch", lambda *a: (200, "application")) + monkeypatch.setattr(generic, "served_cert", lambda d: (True, "*.ci.autonomic.zone")) + return SimpleNamespace(DEPLOY_TIMEOUT=20, HTTP_TIMEOUT=20, HEALTH_PATH="/", HEALTH_OK=(200,)) + + +def test_confirmation_failure_retries_until_services_settle(monkeypatch): + meta = _setup(monkeypatch, iter([True, False, True, True])) + generic.assert_serving("example.ci.autonomic.zone", meta) + + +def test_persistent_confirmation_failure_still_fails(monkeypatch): + import itertools + + meta = _setup(monkeypatch, itertools.cycle([True, False])) + with pytest.raises(AssertionError, match="services did not converge"): + generic.assert_serving("example.ci.autonomic.zone", meta) -- 2.54.0 From efe0b3bf12e8ad9834b4835694fc9851ee3f4ce1 Mon Sep 17 00:00:00 2001 From: autonomic-bot <64+autonomic-bot@noreply.git.autonomic.zone> Date: Mon, 26 Oct 2026 17:42:05 +0000 Subject: [PATCH 4/4] style: sort serving retry test imports --- tests/unit/test_serving_retry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test_serving_retry.py b/tests/unit/test_serving_retry.py index 26b9c92..3837e22 100644 --- a/tests/unit/test_serving_retry.py +++ b/tests/unit/test_serving_retry.py @@ -3,7 +3,6 @@ from types import SimpleNamespace import pytest - from harness import generic -- 2.54.0