mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
0a3da88912 closed the EOL contract at the declaration level: the four retired
roles carry `enabled: false`, an end-of-life README banner and three lints.
iter_role_images still enumerated their images, so the mirror job kept trying
to copy tags nobody can consume. MinIO's quay tags answer 401 to anonymous
pulls and exist nowhere else, which failed that job on every run while no
deploy could have reached the result: an eol role sits outside the lifecycle
envelope default.env tests.
role_lifecycle() reads the stage off the one entry that carries a `lifecycle`
field, which is the role's primary service, and iter_role_images skips the
retired ones. Measured against the tree: 202 images, with web-app-confluence,
web-app-jira, web-app-minio and web-app-phpldapadmin all absent.
test_version_pins_are_watched.py gains the same skip. utils/update/docker.py:271
builds its watched set from iter_role_images, so dropping the retired roles
there made their `version:` pins look unwatched and the lint went red on three
of them. The coherent reading is that a role no updater can reach cannot
declare a watcher either, and the lint docstring now says so rather than
claiming every pin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from utils.docker.image.discovery import iter_role_images, role_lifecycle
|
|
from utils.roles.mapping import ROLE_FILE_META_SERVICES
|
|
|
|
SUPPORTED = """---
|
|
engine:
|
|
image: libretranslate/libretranslate
|
|
version: v1.9.6
|
|
lifecycle: stable
|
|
helper:
|
|
image: alpine
|
|
version: "3.22"
|
|
"""
|
|
|
|
RETIRED = """---
|
|
store:
|
|
image: quay.io/minio/minio
|
|
version: RELEASE.2025-09-07T16-13-09Z
|
|
lifecycle: eol
|
|
client:
|
|
image: quay.io/minio/mc
|
|
version: RELEASE.2025-08-13T08-35-41Z
|
|
"""
|
|
|
|
UNSTAGED = """---
|
|
engine:
|
|
image: libretranslate/libretranslate
|
|
version: v1.9.6
|
|
"""
|
|
|
|
|
|
class TestImageDiscoveryLifecycle(unittest.TestCase):
|
|
def _images(self, services_yaml: str) -> set[str]:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
services = Path(tmp) / "roles" / "web-app-demo" / ROLE_FILE_META_SERVICES
|
|
services.parent.mkdir(parents=True)
|
|
services.write_text(services_yaml, encoding="utf-8")
|
|
return {ref.service for ref in iter_role_images(Path(tmp))}
|
|
|
|
def test_a_supported_role_yields_every_service(self) -> None:
|
|
self.assertEqual({"engine", "helper"}, self._images(SUPPORTED))
|
|
|
|
def test_a_retired_role_yields_nothing(self) -> None:
|
|
"""Its upstream may withdraw the images; no deploy can reach them."""
|
|
self.assertEqual(set(), self._images(RETIRED))
|
|
|
|
def test_the_sibling_of_a_retired_service_goes_with_it(self) -> None:
|
|
"""Only the primary entry carries the stage, and it speaks for the role."""
|
|
self.assertNotIn("client", self._images(RETIRED))
|
|
|
|
def test_a_role_that_names_no_stage_is_kept(self) -> None:
|
|
self.assertEqual({"engine"}, self._images(UNSTAGED))
|
|
|
|
|
|
class TestRoleLifecycle(unittest.TestCase):
|
|
def test_it_reads_the_entry_that_carries_the_field(self) -> None:
|
|
self.assertEqual(
|
|
"eol", role_lifecycle({"a": {"image": "x"}, "b": {"lifecycle": "eol"}})
|
|
)
|
|
|
|
def test_it_is_empty_when_no_entry_carries_one(self) -> None:
|
|
self.assertEqual("", role_lifecycle({"a": {"image": "x"}}))
|
|
|
|
def test_a_non_mapping_entry_does_not_break_it(self) -> None:
|
|
self.assertEqual("", role_lifecycle({"a": "scalar"}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|