Skip to content

Commit dc6f3c8

Browse files
authored
🎨Director-v0: compatible with both registries + upgraded registry to v3 (#8240)
1 parent d9aef02 commit dc6f3c8

File tree

11 files changed

+116
-201
lines changed

11 files changed

+116
-201
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ local-registry: .env ## creates a local docker registry and configure simcore to
707707
--publish 5000:5000 \
708708
--volume $(LOCAL_REGISTRY_VOLUME):/var/lib/registry \
709709
--name $(LOCAL_REGISTRY_HOSTNAME) \
710-
registry:2)
710+
registry:3)
711711

712712
# WARNING: environment file .env is now setup to use local registry on port 5000 without any security (take care!)...
713713
@echo REGISTRY_AUTH=False >> .env

packages/pytest-simcore/src/pytest_simcore/docker_registry.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,45 @@
2222

2323
from .helpers.host import get_localhost_ip
2424

25-
log = logging.getLogger(__name__)
25+
_logger = logging.getLogger(__name__)
2626

2727

2828
@pytest.fixture(scope="session")
2929
def docker_registry(keep_docker_up: bool) -> Iterator[str]:
30+
"""sets up and runs a docker registry container locally and returns its URL"""
31+
yield from _docker_registry_impl(keep_docker_up, registry_version="3")
32+
33+
34+
@pytest.fixture(scope="session")
35+
def docker_registry_v2() -> Iterator[str]:
36+
"""sets up and runs a docker registry v2 container locally and returns its URL"""
37+
yield from _docker_registry_impl(keep_docker_up=False, registry_version="2")
38+
39+
40+
def _docker_registry_impl(keep_docker_up: bool, registry_version: str) -> Iterator[str]:
3041
"""sets up and runs a docker registry container locally and returns its URL"""
3142
# run the registry outside of the stack
3243
docker_client = docker.from_env()
3344
# try to login to private registry
3445
host = "127.0.0.1"
35-
port = 5000
46+
port = 5000 if registry_version == "3" else 5001
3647
url = f"{host}:{port}"
48+
container_name = f"pytest_registry_v{registry_version}"
49+
volume_name = f"pytest_registry_v{registry_version}_data"
50+
3751
container = None
3852
try:
3953
docker_client.login(registry=url, username="simcore")
40-
container = docker_client.containers.list(filters={"name": "pytest_registry"})[
41-
0
42-
]
54+
container = docker_client.containers.list(filters={"name": container_name})[0]
4355
print("Warning: docker registry is already up!")
4456
except Exception: # pylint: disable=broad-except
4557
container = docker_client.containers.run(
46-
"registry:2",
47-
ports={"5000": "5000"},
48-
name="pytest_registry",
58+
f"registry:{registry_version}",
59+
ports={"5000": port},
60+
name=container_name,
4961
environment=["REGISTRY_STORAGE_DELETE_ENABLED=true"],
5062
restart_policy={"Name": "always"},
51-
volumes={
52-
"pytest_registry_data": {"bind": "/var/lib/registry", "mode": "rw"}
53-
},
63+
volumes={volume_name: {"bind": "/var/lib/registry", "mode": "rw"}},
5464
detach=True,
5565
)
5666

@@ -79,9 +89,9 @@ def docker_registry(keep_docker_up: bool) -> Iterator[str]:
7989
os.environ["REGISTRY_SSL"] = "False"
8090
os.environ["REGISTRY_AUTH"] = "False"
8191
# the registry URL is how to access from the container (e.g. for accessing the API)
82-
os.environ["REGISTRY_URL"] = f"{get_localhost_ip()}:5000"
92+
os.environ["REGISTRY_URL"] = f"{get_localhost_ip()}:{port}"
8393
# the registry PATH is how the docker engine shall access the images (usually same as REGISTRY_URL but for testing)
84-
os.environ["REGISTRY_PATH"] = "127.0.0.1:5000"
94+
os.environ["REGISTRY_PATH"] = f"127.0.0.1:{port}"
8595
os.environ["REGISTRY_USER"] = "simcore"
8696
os.environ["REGISTRY_PW"] = ""
8797

@@ -124,7 +134,7 @@ def registry_settings(
124134
@tenacity.retry(
125135
wait=tenacity.wait_fixed(2),
126136
stop=tenacity.stop_after_delay(20),
127-
before_sleep=tenacity.before_sleep_log(log, logging.INFO),
137+
before_sleep=tenacity.before_sleep_log(_logger, logging.INFO),
128138
reraise=True,
129139
)
130140
def wait_till_registry_is_responsive(url: str) -> bool:

scripts/metrics/Makefile

Lines changed: 0 additions & 11 deletions
This file was deleted.

scripts/metrics/compute_list_of_images_in_registry.py

Lines changed: 0 additions & 144 deletions
This file was deleted.

scripts/metrics/requirements.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

services/director/src/simcore_service_director/core/errors.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ class ServiceNotAvailableError(DirectorRuntimeError):
1717
msg_template: str = "Service {service_name}:{service_tag} is not available"
1818

1919

20+
class DockerRegistryUnsupportedManifestSchemaVersionError(DirectorRuntimeError):
21+
msg_template: str = (
22+
"Docker registry schema version {version} issue with {service_name}:{service_tag}"
23+
)
24+
25+
2026
class ServiceUUIDNotFoundError(DirectorRuntimeError):
2127
msg_template: str = "Service with uuid {service_uuid} was not found"
2228

services/director/src/simcore_service_director/registry_proxy.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .constants import DIRECTOR_SIMCORE_SERVICES_PREFIX
2626
from .core.errors import (
2727
DirectorRuntimeError,
28+
DockerRegistryUnsupportedManifestSchemaVersionError,
2829
RegistryConnectionError,
2930
ServiceNotAvailableError,
3031
)
@@ -375,22 +376,55 @@ async def get_image_labels(
375376
app: FastAPI, image: str, tag: str, *, update_cache=False
376377
) -> tuple[dict[str, str], str | None]:
377378
"""Returns image labels and the image manifest digest"""
379+
with log_context(_logger, logging.DEBUG, msg=f"get {image}:{tag} labels"):
380+
request_result, headers = await registry_request(
381+
app,
382+
path=f"{image}/manifests/{tag}",
383+
method="GET",
384+
use_cache=not update_cache,
385+
)
378386

379-
_logger.debug("getting image labels of %s:%s", image, tag)
380-
path = f"{image}/manifests/{tag}"
381-
request_result, headers = await registry_request(
382-
app, path=path, method="GET", use_cache=not update_cache
383-
)
384-
v1_compatibility_key = json_loads(request_result["history"][0]["v1Compatibility"])
385-
container_config: dict[str, Any] = v1_compatibility_key.get(
386-
"container_config", v1_compatibility_key["config"]
387-
)
388-
labels: dict[str, str] = container_config["Labels"]
389-
390-
headers = headers or {}
391-
manifest_digest: str | None = headers.get(_DOCKER_CONTENT_DIGEST_HEADER, None)
387+
schema_version = request_result["schemaVersion"]
388+
labels: dict[str, str] = {}
389+
match schema_version:
390+
case 2:
391+
# Image Manifest Version 2, Schema 2 -> defaults in registries v3 (https://distribution.github.io/distribution/spec/manifest-v2-2/)
392+
media_type = request_result["mediaType"]
393+
if (
394+
media_type
395+
== "application/vnd.docker.distribution.manifest.list.v2+json"
396+
):
397+
raise DockerRegistryUnsupportedManifestSchemaVersionError(
398+
version=schema_version,
399+
service_name=image,
400+
service_tag=tag,
401+
reason="Multiple architectures images are currently not supported and need to be implemented",
402+
)
403+
config_digest = request_result["config"]["digest"]
404+
# Fetch the config blob
405+
config_result, _ = await registry_request(
406+
app,
407+
path=f"{image}/blobs/{config_digest}",
408+
method="GET",
409+
use_cache=not update_cache,
410+
)
411+
labels = config_result.get("config", {}).get("Labels", {})
412+
case 1:
413+
# Image Manifest Version 2, Schema 1 deprecated in docker hub since 2024-11-04
414+
v1_compatibility_key = json_loads(
415+
request_result["history"][0]["v1Compatibility"]
416+
)
417+
container_config: dict[str, Any] = v1_compatibility_key.get(
418+
"container_config", v1_compatibility_key.get("config", {})
419+
)
420+
labels = container_config.get("Labels", {})
421+
case _:
422+
raise DockerRegistryUnsupportedManifestSchemaVersionError(
423+
version=schema_version, service_name=image, service_tag=tag
424+
)
392425

393-
_logger.debug("retrieved labels of image %s:%s", image, tag)
426+
headers = headers or {}
427+
manifest_digest: str | None = headers.get(_DOCKER_CONTENT_DIGEST_HEADER, None)
394428

395429
return (labels, manifest_digest)
396430

0 commit comments

Comments
 (0)