Skip to content

Commit

Permalink
fix: 提高 Blobs.stat 的兼容性
Browse files Browse the repository at this point in the history
  • Loading branch information
shabbywu committed Dec 28, 2021
1 parent 649c0be commit e32e507
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 25 deletions.
2 changes: 1 addition & 1 deletion moby_distribution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from moby_distribution.spec.image_json import ImageJSON
from moby_distribution.spec.manifest import ManifestSchema1, ManifestSchema2, OCIManifestSchema1

__version__ = "0.3.0"
__version__ = "0.3.1"
__ALL__ = [
"DockerRegistryV2Client",
"Blob",
Expand Down
19 changes: 14 additions & 5 deletions moby_distribution/registry/resources/blobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(
repo: str,
digest: Optional[str] = None,
local_path: Optional[Union[Path, str]] = None,
fileobj: Optional[Union[BinaryIO, 'HashSigner']] = None,
fileobj: Optional[Union[BinaryIO, 'HashSignWrapper']] = None,
client: DockerRegistryV2Client = default_client,
):
super().__init__(repo, client)
Expand Down Expand Up @@ -48,8 +48,8 @@ def stat(self, digest: Optional[str] = None) -> Descriptor:
# Content-Type: application/octet-stream
mediaType=headers["Content-Type"],
size=headers["Content-Length"],
digest=headers["Docker-Content-Digest"],
urls=[url],
digest=headers.get("Docker-Content-Digest", digest),
urls=[headers.get("Location", url)],
)

def download(self, digest: Optional[str] = None):
Expand All @@ -69,7 +69,7 @@ def upload(self) -> Descriptor:
uuid, location = self._initiate_blob_upload()
blob = BlobWriter(uuid, location, client=self.client)
with self.accessor.open(mode="rb") as fh:
signer = HashSigner(fh=blob)
signer = HashSignWrapper(fh=blob)
shutil.copyfileobj(fsrc=fh, fdst=signer, length=1024 * 1024 * 4)

digest = signer.digest()
Expand Down Expand Up @@ -234,7 +234,16 @@ def tell(self) -> int:
return self.size


class HashSigner:
class HashSignWrapper:
"""A Wrapper can sign the content of fh when copying it.
Usage:
>>> import shutil
>>> src = open("somewhere", mode="rb")
>>> dest = HashSignWrapper(open("somewhere", mode="wb"))
>>> shutil.copyfileobj(src, dest)
"""

def __init__(self, fh: Union[BinaryIO, CounterIO, BlobWriter] = CounterIO(), constructor=hashlib.sha256):
self._raw_fh = fh
self.signer = constructor()
Expand Down
38 changes: 20 additions & 18 deletions moby_distribution/registry/resources/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import hashlib
import io
import json
import logging
import shutil
import tarfile
from pathlib import Path
Expand All @@ -11,7 +12,7 @@

from moby_distribution.registry.client import DockerRegistryV2Client, default_client
from moby_distribution.registry.resources import RepositoryResource
from moby_distribution.registry.resources.blobs import Blob, HashSigner
from moby_distribution.registry.resources.blobs import Blob, HashSignWrapper
from moby_distribution.registry.resources.manifests import ManifestRef
from moby_distribution.registry.utils import generate_temp_dir
from moby_distribution.spec.image_json import ImageJSON
Expand All @@ -21,6 +22,8 @@
ManifestSchema2,
)

logger = logging.getLogger(__name__)


class ImageManifest(BaseModel):
config: str = Field(default="", alias="Config")
Expand All @@ -32,7 +35,7 @@ class LayerRef(BaseModel):
repo: str = ""
digest: str = ""
size: int = -1
exists: bool = True
exists: bool = False
local_path: Optional[Path]


Expand Down Expand Up @@ -147,27 +150,27 @@ def add_layer(self, layer: LayerRef) -> DockerManifestLayerDescriptor:
if not layer.exists and not layer.local_path:
raise ValueError("Unknown layer")

uncompressed_tarball_signer = HashSigner(fh=io.BytesIO())
uncompressed_tarball_signer = HashSignWrapper()
# Add local layer
if layer.local_path:
# Step 1: calculate the sha256 sum for the gzipped_tarball
gzipped_tarball_signer = HashSigner()
gzipped_signer = HashSignWrapper()
with layer.local_path.open(mode="rb") as gzipped:
shutil.copyfileobj(gzipped, gzipped_tarball_signer)
size = gzipped_tarball_signer.tell()
shutil.copyfileobj(gzipped, gzipped_signer)
size = gzipped_signer.tell()

# Step 2: calculate the sha256 sum for the uncompressed_tarball
with gzip.open(filename=layer.local_path) as uncompressed:
shutil.copyfileobj(uncompressed, uncompressed_tarball_signer)

if layer.digest and layer.digest != gzipped_tarball_signer.digest():
if layer.digest and layer.digest != gzipped_signer.digest():
raise ValueError(
"Wrong digest, layer.digest<'%s'> != signer.digest<'%s'>",
layer.digest,
gzipped_tarball_signer.digest(),
gzipped_signer.digest(),
)

layer.digest = gzipped_tarball_signer.digest()
layer.digest = gzipped_signer.digest()
layer.repo = self.repo
layer.size = size

Expand All @@ -176,29 +179,28 @@ def add_layer(self, layer: LayerRef) -> DockerManifestLayerDescriptor:
with generate_temp_dir() as temp_dir:
# Step 1: calculate the sha256 sum for the gzipped_tarball
with (temp_dir / "blob").open(mode="wb") as fh:
gzipped_tarball_signer = HashSigner(fh=fh)
Blob(
repo=layer.repo, digest=layer.digest, client=self.client, fileobj=gzipped_tarball_signer
).download()
size = gzipped_tarball_signer.tell()
gzipped_signer = HashSignWrapper(fh=fh)
Blob(repo=layer.repo, digest=layer.digest, fileobj=gzipped_signer, client=self.client).download()
size = gzipped_signer.tell()

# Step 2: calculate the sha256 sum for the uncompressed_tarball
with gzip.open(filename=(temp_dir / "blob")) as uncompressed:
shutil.copyfileobj(uncompressed, uncompressed_tarball_signer)

if layer.size != size:
raise ValueError("Wrong Size, layer.size<'%d'> != signer.size<'%d'>", layer.size, size)
if layer.digest != gzipped_tarball_signer.digest():
if layer.digest != gzipped_signer.digest():
raise ValueError(
"Wrong digest, layer.digest<'%s'> != signer.digest<'%s'>",
layer.digest,
gzipped_tarball_signer.digest(),
gzipped_signer.digest(),
)

self._dirty = True
self._append_diff_ids.append(uncompressed_tarball_signer.digest())
self.layers.append(layer)
return DockerManifestLayerDescriptor(
digest=gzipped_tarball_signer.digest(),
digest=gzipped_signer.digest(),
size=size,
)

Expand Down Expand Up @@ -227,7 +229,7 @@ def _save_layer(self, workplace: Path, layer: LayerRef) -> str:
Blob(repo=layer.repo, digest=layer.digest, local_path=temp_gzip_path, client=self.client).download()

with gzip.open(filename=temp_gzip_path) as gzipped, temp_tarball_path.open(mode="wb") as fh:
signer = HashSigner(fh)
signer = HashSignWrapper(fh)
shutil.copyfileobj(gzipped, signer)

tarball_path = f"{signer.digest()}/layer.tar"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "moby-distribution"
version = "0.3.0"
version = "0.3.1"
description = "Yet another moby(docker) distribution implement by python."
authors = ["shabbywu <[email protected]>"]
license = "Apache-2.0"
Expand Down

0 comments on commit e32e507

Please sign in to comment.