diff --git a/moby_distribution/__init__.py b/moby_distribution/__init__.py index 4910ffb..ba7fb4e 100644 --- a/moby_distribution/__init__.py +++ b/moby_distribution/__init__.py @@ -15,7 +15,7 @@ OCIManifestSchema1, ) -__version__ = "0.7.3" +__version__ = "0.7.4" __all__ = [ "DockerRegistryV2Client", "Blob", diff --git a/moby_distribution/registry/resources/manifests.py b/moby_distribution/registry/resources/manifests.py index 7446def..19e6e25 100644 --- a/moby_distribution/registry/resources/manifests.py +++ b/moby_distribution/registry/resources/manifests.py @@ -2,11 +2,24 @@ import libtrust -from moby_distribution.registry.client import DockerRegistryV2Client, URLBuilder, default_client +from moby_distribution.registry.client import ( + DockerRegistryV2Client, + URLBuilder, + default_client, +) from moby_distribution.registry.exceptions import ResourceNotFound, UnSupportMediaType from moby_distribution.registry.resources import RepositoryResource -from moby_distribution.registry.utils import TypeTimeout, client_default_timeout, get_private_key -from moby_distribution.spec.manifest import ManifestDescriptor, ManifestSchema1, ManifestSchema2, OCIManifestSchema1 +from moby_distribution.registry.utils import ( + TypeTimeout, + client_default_timeout, + get_private_key, +) +from moby_distribution.spec.manifest import ( + ManifestDescriptor, + ManifestSchema1, + ManifestSchema2, + OCIManifestSchema1, +) class ManifestRef(RepositoryResource): @@ -33,18 +46,24 @@ def get(self, media_type: str = ManifestSchema2.content_type()): raise UnSupportMediaType(media_type) type_ = self.TYPES[media_type] - url = URLBuilder.build_manifests_url(self.client.api_base_url, self.repo, self.reference) + url = URLBuilder.build_manifests_url( + self.client.api_base_url, self.repo, self.reference + ) headers = {"Accept": media_type} data = self.client.get(url=url, headers=headers, timeout=self.timeout).json() return type_(**data) - def get_metadata(self, media_type: str = ManifestSchema2.content_type()) -> Optional[ManifestDescriptor]: + def get_metadata( + self, media_type: str = ManifestSchema2.content_type() + ) -> Optional[ManifestDescriptor]: """return ManifestDescriptor if the manifest exists.""" if media_type not in self.TYPES: raise UnSupportMediaType(media_type) headers = {"Accept": media_type} - url = URLBuilder.build_manifests_url(self.client.api_base_url, self.repo, self.reference) + url = URLBuilder.build_manifests_url( + self.client.api_base_url, self.repo, self.reference + ) try: resp = self.client.head(url=url, headers=headers, timeout=self.timeout) except ResourceNotFound: @@ -69,7 +88,9 @@ def delete(self, raise_not_found: bool = True) -> bool: raise ResourceNotFound return False - url = URLBuilder.build_manifests_url(self.client.api_base_url, self.repo, descriptor.digest) + url = URLBuilder.build_manifests_url( + self.client.api_base_url, self.repo, descriptor.digest + ) try: resp = self.client.delete(url=url, timeout=self.timeout) except ResourceNotFound: @@ -79,7 +100,9 @@ def delete(self, raise_not_found: bool = True) -> bool: return resp.ok - def put(self, manifest: Union[ManifestSchema1, ManifestSchema2, OCIManifestSchema1]) -> bool: + def put( + self, manifest: Union[ManifestSchema1, ManifestSchema2, OCIManifestSchema1] + ) -> bool: """creates or updates the given manifest.""" if isinstance(manifest, ManifestSchema1): resp = self._put_legacy_manifest(manifest) @@ -90,7 +113,9 @@ def put(self, manifest: Union[ManifestSchema1, ManifestSchema2, OCIManifestSchem def _put_legacy_manifest(self, manifest: ManifestSchema1): """put the docker schema 1 manifest with signed signature to the repository""" - url = URLBuilder.build_manifests_url(self.client.api_base_url, self.repo, self.reference) + url = URLBuilder.build_manifests_url( + self.client.api_base_url, self.repo, self.reference + ) private_key = get_private_key() data = manifest.json( include={ @@ -100,18 +125,29 @@ def _put_legacy_manifest(self, manifest: ManifestSchema1): "fsLayers", "history", "schemaVersion", - } + }, ) js = libtrust.JSONSignature.new(data) js.sign(private_key) headers = {"Content-Type": manifest.content_type()} data = js.to_pretty_signature("signatures") - return self.client.put(url=url, data=data, headers=headers, timeout=self.timeout) + return self.client.put( + url=url, data=data, headers=headers, timeout=self.timeout + ) def _put_new_manifest(self, manifest: Union[ManifestSchema2, OCIManifestSchema1]): """put the docker schema 2 manifest or OCI manifest to the repository""" - url = URLBuilder.build_manifests_url(self.client.api_base_url, self.repo, self.reference) + url = URLBuilder.build_manifests_url( + self.client.api_base_url, self.repo, self.reference + ) headers = {"Content-Type": manifest.content_type()} - data = manifest.json() - return self.client.put(url=url, data=data, headers=headers, timeout=self.timeout) + data = manifest.json( + exclude={ + "config": {"urls"}, + "layers": {"__all__": {"urls"}}, + } + ) + return self.client.put( + url=url, data=data, headers=headers, timeout=self.timeout + ) diff --git a/pyproject.toml b/pyproject.toml index 65b2181..b040673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "moby-distribution" -version = "0.7.3" +version = "0.7.4" description = "Yet another moby(docker) distribution implement by python." authors = ["shabbywu "] license = "Apache-2.0" diff --git a/tests/integration/resources/test_manifests.py b/tests/integration/resources/test_manifests.py index 64e27c4..7351334 100644 --- a/tests/integration/resources/test_manifests.py +++ b/tests/integration/resources/test_manifests.py @@ -1,7 +1,14 @@ import hashlib import json +import docker import pytest +from moby_distribution.registry.resources.image import ( + ImageRef, + LayerRef, + ManifestSchema2, +) +from moby_distribution.registry.client import URLBuilder try: from pydantic import __version__ as pydantic_version @@ -74,3 +81,39 @@ def test_get_metadata( descriptor.digest == f"sha256:{hashlib.sha256(dumped.encode()).hexdigest()}" ) + + +class TestIntegration: + @pytest.fixture() + def docker_cli(self): + return docker.from_env() + + @pytest.fixture(autouse=True) + def _init_image( + self, + registry_client, + tmp_path, + alpine_tar, + alpine_append_layer, + docker_cli, + registry_netloc, + ): + ref = ImageRef.from_tarball( + workplace=tmp_path, + src=alpine_tar, + to_repo="alpine", + to_reference="manifest", + client=registry_client, + ) + ref.add_layer(LayerRef(local_path=alpine_append_layer)) + ref.push() + + def test_inspect(self, registry_client): + url = URLBuilder.build_manifests_url( + registry_client.api_base_url, repo="alpine", reference="manifest" + ) + headers = {"Accept": ManifestSchema2.content_type()} + data = registry_client.get(url=url, headers=headers).json() + assert data["config"].get("urls", []) == [] + for layer in data["layers"]: + assert layer.get("urls", []) == []