mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 12:47:04 +00:00
Add registry.gitlab.com to the mirrorable registries so discovery yields the GitLab CNG images to the version updater and mirror sync. Seed the generic v2 tag scan with the OCI last cursor for v-prefixed pins and raise the page size to 1000: CNG repos carry tens of thousands of commit-sha tags that sort before the release tags, so an unseeded scan exhausted the page cap without reaching any semver tag. Pin every gitlab service version in meta/services.yml and drop the GITLAB_VERSION template override so services.yml is the single source for per-service versions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
142 lines
5.0 KiB
Python
142 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import unittest
|
|
import urllib.error
|
|
from email.message import Message
|
|
from unittest import mock
|
|
|
|
from utils.docker import registry
|
|
|
|
|
|
def _headers(values: dict[str, str] | None = None) -> Message:
|
|
msg = Message()
|
|
for key, value in (values or {}).items():
|
|
msg[key] = value
|
|
return msg
|
|
|
|
|
|
class _Resp:
|
|
def __init__(self, status: int = 200, headers=None, body: bytes = b""):
|
|
self.status = status
|
|
self.headers = _headers(headers)
|
|
self._body = body
|
|
|
|
def read(self) -> bytes:
|
|
return self._body
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *_exc) -> bool:
|
|
return False
|
|
|
|
|
|
def _http_error(code: int, headers=None) -> urllib.error.HTTPError:
|
|
return urllib.error.HTTPError(
|
|
"https://reg/v2", code, "err", _headers(headers), io.BytesIO(b"")
|
|
)
|
|
|
|
|
|
class TestManifestExists(unittest.TestCase):
|
|
def test_present_returns_true(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", return_value=_Resp(status=200)
|
|
) as opened:
|
|
self.assertIs(registry.manifest_exists("postgres", "16"), True)
|
|
url = opened.call_args.args[0].full_url
|
|
self.assertEqual(
|
|
url, "https://registry-1.docker.io/v2/library/postgres/manifests/16"
|
|
)
|
|
|
|
def test_absent_returns_false(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", side_effect=_http_error(404)
|
|
):
|
|
self.assertIs(registry.manifest_exists("postgres", "999"), False)
|
|
|
|
def test_network_error_is_indeterminate(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", side_effect=urllib.error.URLError("x")
|
|
):
|
|
self.assertIsNone(registry.manifest_exists("postgres", "16"))
|
|
|
|
def test_auth_wall_without_realm_is_indeterminate(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", side_effect=_http_error(401)
|
|
):
|
|
self.assertIsNone(registry.manifest_exists("quay.io/foo/bar", "1"))
|
|
|
|
def test_non_dockerhub_registry_host(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", return_value=_Resp(status=200)
|
|
) as opened:
|
|
registry.manifest_exists("quay.io/keycloak/keycloak", "26.6.3")
|
|
url = opened.call_args.args[0].full_url
|
|
self.assertEqual(url, "https://quay.io/v2/keycloak/keycloak/manifests/26.6.3")
|
|
|
|
|
|
class TestFetchRegistryTags(unittest.TestCase):
|
|
def test_anonymous_tags(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request,
|
|
"urlopen",
|
|
return_value=_Resp(body=b'{"tags": ["1.0.0", "1.1.0"]}'),
|
|
):
|
|
self.assertEqual(
|
|
registry.fetch_registry_tags("mcr.microsoft.com/foo/bar"),
|
|
["1.0.0", "1.1.0"],
|
|
)
|
|
|
|
def test_bearer_challenge_then_retry(self) -> None:
|
|
challenge = 'Bearer realm="https://auth.example/token",service="reg",scope="repository:foo/bar:pull"'
|
|
|
|
def _fake(req, *_a, **_kw):
|
|
url = req.full_url
|
|
if url.startswith("https://auth.example/token"):
|
|
return _Resp(body=b'{"token": "abc"}')
|
|
if "Authorization" not in req.headers and "Authorization" not in {
|
|
k.title(): v for k, v in req.header_items()
|
|
}:
|
|
raise _http_error(401, {"WWW-Authenticate": challenge})
|
|
return _Resp(body=b'{"tags": ["2.0.0"]}')
|
|
|
|
with mock.patch.object(registry.urllib.request, "urlopen", side_effect=_fake):
|
|
self.assertEqual(registry.fetch_registry_tags("ghcr.io/foo/bar"), ["2.0.0"])
|
|
|
|
def test_pagination_follows_link(self) -> None:
|
|
page1 = _Resp(
|
|
headers={"Link": '</v2/library/foo/tags/list?n=100&last=b>; rel="next"'},
|
|
body=b'{"tags": ["a"]}',
|
|
)
|
|
page2 = _Resp(body=b'{"tags": ["b"]}')
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", side_effect=[page1, page2]
|
|
):
|
|
self.assertEqual(registry.fetch_registry_tags("foo"), ["a", "b"])
|
|
|
|
def test_network_failure_returns_empty(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request, "urlopen", side_effect=urllib.error.URLError("x")
|
|
):
|
|
self.assertEqual(registry.fetch_registry_tags("foo"), [])
|
|
|
|
def test_last_cursor_seeds_query(self) -> None:
|
|
with mock.patch.object(
|
|
registry.urllib.request,
|
|
"urlopen",
|
|
return_value=_Resp(body=b'{"tags": ["v1.0.0"]}'),
|
|
) as opened:
|
|
self.assertEqual(
|
|
registry.fetch_registry_tags("registry.gitlab.com/foo/bar", last="v"),
|
|
["v1.0.0"],
|
|
)
|
|
url = opened.call_args.args[0].full_url
|
|
self.assertEqual(
|
|
url, "https://registry.gitlab.com/v2/foo/bar/tags/list?n=1000&last=v"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|