def test___new__(): """Test that a formatted SHA256 can be instantiated.""" digest = "0123456789012345678901234567890123456789012345678901234567890123" formattedsha256 = FormattedSHA256(digest) assert formattedsha256 assert formattedsha256.sha256 == digest # pylint: disable=no-member assert str(formattedsha256) == f"sha256:{digest}" digest = "sha256:0123456789012345678901234567890123456789012345678901234567890123" formattedsha256 = FormattedSHA256(digest) assert formattedsha256 assert formattedsha256.sha256 == digest[7:] # pylint: disable=no-member assert str(formattedsha256) == digest with pytest.raises(ValueError) as exc_info: FormattedSHA256(None) assert "None" in str(exc_info.value) digest = "012345678901234567890123456789012345678901234567890123456789012" with pytest.raises(ValueError) as exc_info: FormattedSHA256(digest) assert digest in str(exc_info.value) digest = "sha1:0123456789012345678901234567890123456789012345678901234567890123" with pytest.raises(ValueError) as exc_info: FormattedSHA256(digest) assert digest in str(exc_info.value)
def get_test_data_local() -> Generator[TypingGetTestDataLocal, None, None]: """Dynamically initializes test data for a local mutable registry.""" images = [ { "image": "{0}/library/python", "tag": "3.7.2-slim-stretch", "digests": { DockerMediaTypes.DISTRIBUTION_MANIFEST_V2: FormattedSHA256( "0005ba40bf87e486d7061ca0112123270e4a6088b5071223c8d467db3dbba908" ), }, "original_endpoint": Indices.DOCKERHUB, "protocol": "http", }, { "image": "{0}/library/busybox", "tag": "1.30.1", "digests": { DockerMediaTypes.DISTRIBUTION_MANIFEST_V2: FormattedSHA256( "4fe8827f51a5e11bb83afa8227cbccb402df840d32c6b633b7ad079bc8144100" ), }, "original_endpoint": Indices.DOCKERHUB, "protocol": "http", }, ] for image in images: yield image
def test_set_tag(archive_repositories: ArchiveRepositories, image_name: ImageName, name: str): """Test repository tag assignment.""" tag = archive_repositories.get_tag(image_name) assert tag assert FormattedSHA256(tag) == image_name.digest digest = FormattedSHA256.calculate(name.encode("utf-8")) name = ImageName.parse(name) archive_repositories.set_tag(name, digest) assert FormattedSHA256(archive_repositories.get_tag(name)) == digest
async def chunk_file( file_in, file_out, *, file_in_is_async: bool = True, file_out_is_async: bool = True ) -> UtilChunkFile: """ Copies chunkcs from one file to another. Args: file_in: The file from which to retrieve the file chunks. file_out: The file to which to store the file chunks. file_in_is_async: If True, all file_in IO operations will be awaited. file_out_is_async: If True, all file_out IO operations will be awaited. Returns: dict: digest: The digest value of the chunked data. size: The byte size of the chunked data in bytes. """ coroutine_read = file_in.read if file_in_is_async else async_wrap(file_in.read) coroutine_write = ( file_out.write if file_out_is_async else async_wrap(file_out.write) ) hasher = hashlib.sha256() size = 0 while True: chunk = await coroutine_read(CHUNK_SIZE) if not chunk: break await coroutine_write(chunk) hasher.update(chunk) size += len(chunk) await be_kind_rewind(file_out, file_is_async=file_out_is_async) return {"digest": FormattedSHA256(hasher.hexdigest()), "size": size}
def test_get_config_digest(registry_v2_manifest: RegistryV2Manifest): """Test image configuration digest retrieval.""" formattedsha256 = FormattedSHA256( "8f1196ff19e7b5c5861de192ae77e8d7a692fcbca2dd3174d324980f72ab49bf") assert registry_v2_manifest.get_config_digest() == formattedsha256 assert (registry_v2_manifest.get_config_digest( ImageName.parse("ignored")) == formattedsha256)
def test_get_tag(archive_repositories: ArchiveRepositories, image_name: ImageName): """Test repository tag retrieval.""" tag = archive_repositories.get_tag(image_name) assert tag assert FormattedSHA256(tag) == image_name.digest assert not archive_repositories.get_tag(ImageName("does_not_exist")) assert not archive_repositories.get_tag(ImageName("does_not", tag="exist"))
def layer_to_digest(layer: str) -> FormattedSHA256: """ Coverts a archive layer identifier to a digest value. Args: layer: The archive layer identifier (relative tar path). Returns: The corresponding digest value in the form: <hash type>:<digest value>. """ return FormattedSHA256(layer[:-10])
async def test_layer_exists( registry_v2_image_source: RegistryV2ImageSource, known_good_image: TypingKnownGoodImage, **kwargs, ): """Test layer existence.""" LOGGER.debug("Retrieving manifest for: %s ...", known_good_image["image_name"]) manifest = await registry_v2_image_source.get_manifest( known_good_image["image_name"], **kwargs) layer = manifest.get_layers()[-1] assert await registry_v2_image_source.layer_exists( known_good_image["image_name"], layer, **kwargs) assert not await registry_v2_image_source.layer_exists( known_good_image["image_name"], FormattedSHA256("0" * 64), **kwargs)
async def put_image_layer_from_disk(self, image_name: ImageName, file, **kwargs) -> FormattedSHA256: # TODO: Convert to async if self.dry_run: LOGGER.debug("Dry Run: skipping put_image_layer_from_disk") return FormattedSHA256("0" * 64) # TODO: Do we really want to use random garbage here??? # Look into moby/.../save.go to find what to use instead. digest = FormattedSHA256.calculate("{0}{1}{2}".format( str(image_name), datetime.datetime.now(), random.randint(1, 101)).encode("utf-8")) layer = ArchiveManifest.digest_to_layer(digest) with open(self.archive, "rb+") as file_out: tar_mkdir(file_out, os.path.dirname(layer)) file_out.seek(0) tar(file_out, layer, file) return digest
async def test_layer_exists( registry_v2_image_source: RegistryV2ImageSource, known_good_image_local: Dict, **kwargs, ): """Test layer existence.""" if "protocol" in known_good_image_local: kwargs["protocol"] = known_good_image_local["protocol"] LOGGER.debug("Retrieving manifest for: %s ...", known_good_image_local["image_name"]) manifest = await registry_v2_image_source.get_manifest( known_good_image_local["image_name"], **kwargs) layer = manifest.get_layers()[-1] assert await registry_v2_image_source.layer_exists( known_good_image_local["image_name"], layer, **kwargs) assert not await registry_v2_image_source.layer_exists( known_good_image_local["image_name"], FormattedSHA256("0" * 64), ** kwargs)
def test_remove_tags(archive_changeset: ArchiveChangeset): """Test repository tag removal""" image_name = ImageName( "base", digest=FormattedSHA256( "adecf4209bb9dd67d96393774cbd7f8bd2bad3596da42cde33daa0c41b14ac62" ), tag="7.2", ) tag = ArchiveChangeset.get_repository_tag(image_name) manifest = archive_changeset.get_manifest(image_name) assert tag in manifest.get_tags() archive_changeset.remove_tags(["dummy"]) manifest = archive_changeset.get_manifest(image_name) assert tag in manifest.get_tags() archive_changeset.remove_tags([tag]) manifest = archive_changeset.get_manifest(image_name) assert not manifest.get_tags()
def append_manifest(self, manifest: "ArchiveManifest"): """ Appends an archive manifest to the archive changeset. Args: manifest: The archive manifest to be appended. """ # Remove the image if it already exists self.remove_manifest( FormattedSHA256(manifest.get_json()["Config"][:-5])) # Remove all tags being assigned to the new image ... tags = ArchiveChangeset.normalize_tags(manifest.get_tags()) if tags: self.remove_tags(tags) # Append the new image configuration ... _json = self.get_json() _json.append(manifest.get_json()) self._set_json(_json)
def formattedsha256() -> FormattedSHA256: """Provides a FormattedSHA256 instance with a distinct digest value.""" return FormattedSHA256( "ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789")
def get_config_digest(self, image_name: ImageName = None) -> FormattedSHA256: config = self.get_config(image_name) return FormattedSHA256(config["Config"][:-5])
def get_config_digest(self, image_name: ImageName = None) -> FormattedSHA256: return FormattedSHA256(self.get_json()["Config"][:-5])