Exemple #1
0
def test_build_schema1():
    manifest = DockerSchema2Manifest(Bytes.for_string_or_unicode(MANIFEST_BYTES))
    assert not manifest.has_remote_layer

    retriever = ContentRetrieverForTesting(
        {
            "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7": CONFIG_BYTES,
        }
    )

    builder = DockerSchema1ManifestBuilder("somenamespace", "somename", "sometag")
    manifest._populate_schema1_builder(builder, retriever)
    schema1 = builder.build(docker_v2_signing_key)

    assert schema1.media_type == DOCKER_SCHEMA1_SIGNED_MANIFEST_CONTENT_TYPE
Exemple #2
0
def test_invalid_manifestlist():
    # Build a manifest list with a schema 1 manifest of the wrong architecture.
    builder = DockerSchema1ManifestBuilder("foo", "bar", "baz")
    builder.add_layer("sha:2356", '{"id": "foo"}')
    manifest = builder.build().unsigned()

    listbuilder = DockerSchema2ManifestListBuilder()
    listbuilder.add_manifest(manifest, "amd32", "linux")
    manifestlist = listbuilder.build()

    retriever = ContentRetrieverForTesting()
    retriever.add_digest(manifest.digest, manifest.bytes.as_encoded_str())

    with pytest.raises(MismatchManifestException):
        manifestlist.validate(retriever)
Exemple #3
0
def store_tag_manifest(namespace, repo_name, tag_name, image_id):
    builder = DockerSchema1ManifestBuilder(namespace, repo_name, tag_name)
    storage_id_map = {}
    try:
        image_storage = ImageStorage.select().where(
            ~(ImageStorage.content_checksum >> None)).get()
        builder.add_layer(image_storage.content_checksum, '{"id": "foo"}')
        storage_id_map[image_storage.content_checksum] = image_storage.id
    except ImageStorage.DoesNotExist:
        pass

    manifest = builder.build(docker_v2_signing_key)
    manifest_row, _ = model.tag.store_tag_manifest_for_testing(
        namespace, repo_name, tag_name, manifest, image_id, storage_id_map)
    return manifest_row
Exemple #4
0
def test_validate_manifest_with_emoji(with_key):
    builder = DockerSchema1ManifestBuilder('somenamespace', 'somerepo',
                                           'sometag')
    builder.add_layer(
        'sha256:abcde',
        json.dumps({
            'id': 'someid',
            'author': u'😱',
        }, ensure_ascii=False))

    built = builder.build(with_key, ensure_ascii=False)
    built._validate()

    # Ensure the manifest can be reloaded.
    built_bytes = built.bytes.as_encoded_str()
    DockerSchema1Manifest(Bytes.for_string_or_unicode(built_bytes))
Exemple #5
0
def test_validate_manifest_with_emoji(with_key):
    builder = DockerSchema1ManifestBuilder("somenamespace", "somerepo",
                                           "sometag")
    builder.add_layer(
        "sha256:abcde",
        json.dumps({
            "id": "someid",
            "author": u"😱",
        }, ensure_ascii=False))

    built = builder.build(with_key, ensure_ascii=False)
    built._validate()

    # Ensure the manifest can be reloaded.
    built_bytes = built.bytes.as_encoded_str()
    DockerSchema1Manifest(Bytes.for_string_or_unicode(built_bytes))
Exemple #6
0
def test_build_unencoded_unicode_manifest(with_key):
    builder = DockerSchema1ManifestBuilder("somenamespace", "somerepo",
                                           "sometag")
    builder.add_layer(
        "sha256:abcde",
        json.dumps(
            {
                "id": "someid",
                "author": "Sômé guy",
            },
            ensure_ascii=False,
        ),
    )

    built = builder.build(with_key, ensure_ascii=False)
    built._validate()
def test_get_or_create_manifest_invalid_image(initialized_db):
    repository = get_repository("devtable", "simple")

    latest_tag = get_tag(repository, "latest")
    parsed = DockerSchema1Manifest(Bytes.for_string_or_unicode(
        latest_tag.manifest.manifest_bytes),
                                   validate=False)

    builder = DockerSchema1ManifestBuilder("devtable", "simple", "anothertag")
    builder.add_layer(parsed.blob_digests[0],
                      '{"id": "foo", "parent": "someinvalidimageid"}')
    sample_manifest_instance = builder.build(docker_v2_signing_key)

    created_manifest = get_or_create_manifest(repository,
                                              sample_manifest_instance,
                                              storage)
    assert created_manifest is None
def test_create_manifest_and_retarget_tag(registry_model):
    repository_ref = registry_model.lookup_repository("devtable", "simple")
    latest_tag = registry_model.get_repo_tag(repository_ref, "latest")
    manifest = registry_model.get_manifest_for_tag(latest_tag).get_parsed_manifest()

    builder = DockerSchema1ManifestBuilder("devtable", "simple", "anothertag")
    builder.add_layer(manifest.blob_digests[0], '{"id": "%s"}' % "someid")
    sample_manifest = builder.build(docker_v2_signing_key)
    assert sample_manifest is not None

    another_manifest, tag = registry_model.create_manifest_and_retarget_tag(
        repository_ref, sample_manifest, "anothertag", storage
    )
    assert another_manifest is not None
    assert tag is not None

    assert tag.name == "anothertag"
    assert another_manifest.get_parsed_manifest().manifest_dict == sample_manifest.manifest_dict
Exemple #9
0
def move_tag(repository, tag, image_ids, expect_gc=True):
    namespace = repository.namespace_user.username
    name = repository.name

    repo_ref = RepositoryReference.for_repo_obj(repository)
    builder = DockerSchema1ManifestBuilder(namespace, name, tag)

    # NOTE: Building root to leaf.
    parent_id = None
    for image_id in image_ids:
        config = {
            "id": image_id,
            "config": {
                "Labels": {
                    "foo": "bar",
                    "meh": "grah",
                }
            },
        }

        if parent_id:
            config["parent"] = parent_id

        # Create a storage row for the layer blob.
        _, layer_blob_digest = _populate_blob(repository,
                                              image_id.encode("ascii"))

        builder.insert_layer(layer_blob_digest, json.dumps(config))
        parent_id = image_id

    # Store the manifest.
    manifest = builder.build(docker_v2_signing_key)
    registry_model.create_manifest_and_retarget_tag(repo_ref,
                                                    manifest,
                                                    tag,
                                                    storage,
                                                    raise_on_error=True)

    tag_ref = registry_model.get_repo_tag(repo_ref, tag)
    manifest_ref = registry_model.get_manifest_for_tag(tag_ref)
    registry_model.populate_legacy_images_for_testing(manifest_ref, storage)

    if expect_gc:
        assert gc_now(repository) == expect_gc
Exemple #10
0
    def build_schema1(self,
                      namespace,
                      repo_name,
                      tag_name,
                      images,
                      blobs,
                      options,
                      arch="amd64"):
        builder = DockerSchema1ManifestBuilder(namespace, repo_name, tag_name,
                                               arch)

        for image in reversed(images):
            assert image.urls is None

            checksum = "sha256:" + hashlib.sha256(image.bytes).hexdigest()
            blobs[checksum] = image.bytes

            # If invalid blob references were requested, just make it up.
            if options.manifest_invalid_blob_references:
                checksum = "sha256:" + hashlib.sha256(
                    "notarealthing").hexdigest()

            layer_dict = {"id": image.id, "parent": image.parent_id}
            if image.config is not None:
                layer_dict["config"] = image.config

            if image.size is not None:
                layer_dict["Size"] = image.size

            if image.created is not None:
                layer_dict["created"] = image.created

            builder.add_layer(
                checksum,
                json.dumps(layer_dict, ensure_ascii=options.ensure_ascii))

        # Build the manifest.
        built = builder.build(self.jwk, ensure_ascii=options.ensure_ascii)

        # Validate it before we send it.
        DockerSchema1Manifest(built.bytes)
        return built
Exemple #11
0
def test_build_unencoded_unicode_manifest(with_key):
    builder = DockerSchema1ManifestBuilder("somenamespace", "somerepo",
                                           "sometag")
    builder.add_layer(
        "sha256:abcde",
        json.dumps(
            {
                "id": "someid",
                "author": "Sômé guy",
            },
            ensure_ascii=False,
        ),
    )

    built = builder.build(with_key, ensure_ascii=False)
    # Assert kid was created correctly
    # https://docs.docker.com/registry/spec/auth/jwt/
    if with_key is not None:
        assert len(built.signatures) == 1
        assert re.match(KID_FORMAT_REGEX,
                        built.signatures[0]["header"]["jwk"]["kid"])
    built._validate()
Exemple #12
0
def test_create_manifest_and_retarget_tag(registry_model):
    repository_ref = registry_model.lookup_repository('devtable', 'simple')
    latest_tag = registry_model.get_repo_tag(repository_ref,
                                             'latest',
                                             include_legacy_image=True)
    manifest = registry_model.get_manifest_for_tag(
        latest_tag).get_parsed_manifest()

    builder = DockerSchema1ManifestBuilder('devtable', 'simple', 'anothertag')
    builder.add_layer(manifest.blob_digests[0],
                      '{"id": "%s"}' % latest_tag.legacy_image.docker_image_id)
    sample_manifest = builder.build(docker_v2_signing_key)
    assert sample_manifest is not None

    another_manifest, tag = registry_model.create_manifest_and_retarget_tag(
        repository_ref, sample_manifest, 'anothertag', storage)
    assert another_manifest is not None
    assert tag is not None

    assert tag.name == 'anothertag'
    assert another_manifest.get_parsed_manifest(
    ).manifest_dict == sample_manifest.manifest_dict
Exemple #13
0
def test_create_manifest_and_retarget_tag_with_labels(registry_model):
    repository_ref = registry_model.lookup_repository("devtable", "simple")
    latest_tag = registry_model.get_repo_tag(repository_ref, "latest")
    manifest = registry_model.get_manifest_for_tag(
        latest_tag).get_parsed_manifest()

    json_metadata = {
        "id": "someid",
        "config": {
            "Labels": {
                "quay.expires-after": "2w",
            },
        },
    }

    builder = DockerSchema1ManifestBuilder("devtable", "simple", "anothertag")
    builder.add_layer(manifest.blob_digests[0], json.dumps(json_metadata))
    sample_manifest = builder.build(docker_v2_signing_key)
    assert sample_manifest is not None

    another_manifest, tag = registry_model.create_manifest_and_retarget_tag(
        repository_ref, sample_manifest, "anothertag", storage)
    assert another_manifest is not None
    assert tag is not None

    assert tag.name == "anothertag"
    assert another_manifest.get_parsed_manifest(
    ).manifest_dict == sample_manifest.manifest_dict

    # Ensure the labels were applied.
    assert tag.lifetime_end_ms is not None

    # Create another tag and retarget it to an existing manifest; it should have an end date.
    # This is from a Quay's tag api, so it will not attempt to create a manifest first.
    yet_another_tag = registry_model.retarget_tag(repository_ref,
                                                  "yet_another_tag",
                                                  another_manifest, storage,
                                                  docker_v2_signing_key)
    assert yet_another_tag.lifetime_end_ms is not None
Exemple #14
0
def test_manifestbackfillworker_mislinked_manifest(clear_rows, initialized_db):
    """
    Tests that a manifest whose image is mislinked will have its storages relinked properly.
    """
    # Delete existing tag manifest so we can reuse the tag.
    TagManifestLabel.delete().execute()
    TagManifest.delete().execute()

    repo = model.repository.get_repository("devtable", "complex")
    tag_v30 = model.tag.get_active_tag("devtable", "gargantuan", "v3.0")
    tag_v50 = model.tag.get_active_tag("devtable", "gargantuan", "v5.0")

    # Add a mislinked manifest, by having its layer point to a blob in v3.0 but its image
    # be the v5.0 image.
    builder = DockerSchema1ManifestBuilder("devtable", "gargantuan", "sometag")
    builder.add_layer(tag_v30.image.storage.content_checksum, '{"id": "foo"}')
    manifest = builder.build(docker_v2_signing_key)

    mislinked_manifest = TagManifest.create(
        json_data=manifest.bytes.as_encoded_str(),
        digest=manifest.digest,
        tag=tag_v50)

    # Backfill the manifest and ensure its proper content checksum was linked.
    assert _backfill_manifest(mislinked_manifest)

    map_row = TagManifestToManifest.get(tag_manifest=mislinked_manifest)
    assert not map_row.broken

    manifest_row = map_row.manifest
    legacy_image = ManifestLegacyImage.get(manifest=manifest_row).image
    assert legacy_image == tag_v50.image

    manifest_blobs = list(
        ManifestBlob.select().where(ManifestBlob.manifest == manifest_row))
    assert len(manifest_blobs) == 1
    assert manifest_blobs[
        0].blob.content_checksum == tag_v30.image.storage.content_checksum
Exemple #15
0
def test_unicode_emoji(registry_model):
    builder = DockerSchema1ManifestBuilder("devtable", "simple", "latest")
    builder.add_layer(
        "sha256:abcde",
        json.dumps(
            {
                "id": "someid",
                "author": "😱",
            },
            ensure_ascii=False,
        ),
    )

    manifest = builder.build(ensure_ascii=False)
    manifest._validate()

    for blob_digest in manifest.local_blob_digests:
        _populate_blob(blob_digest)

    # Create the manifest in the database.
    repository_ref = registry_model.lookup_repository("devtable", "simple")
    created_manifest, _ = registry_model.create_manifest_and_retarget_tag(
        repository_ref, manifest, "latest", storage)
    assert created_manifest
    assert created_manifest.digest == manifest.digest
    assert (created_manifest.internal_manifest_bytes.as_encoded_str() ==
            manifest.bytes.as_encoded_str())

    # Look it up again and validate.
    found = registry_model.lookup_manifest_by_digest(repository_ref,
                                                     manifest.digest,
                                                     allow_dead=True)
    assert found
    assert found.digest == manifest.digest
    assert found.internal_manifest_bytes.as_encoded_str(
    ) == manifest.bytes.as_encoded_str()
    assert found.get_parsed_manifest().digest == manifest.digest
def test_create_manifest_with_temp_tag(initialized_db, registry_model):
    builder = DockerSchema1ManifestBuilder("devtable", "simple", "latest")
    builder.add_layer(
        "sha256:abcde", json.dumps({"id": "someid", "author": "some user",}, ensure_ascii=False)
    )

    manifest = builder.build(ensure_ascii=False)

    for blob_digest in manifest.local_blob_digests:
        _populate_blob(blob_digest)

    # Create the manifest in the database.
    repository_ref = registry_model.lookup_repository("devtable", "simple")
    created = registry_model.create_manifest_with_temp_tag(repository_ref, manifest, 300, storage)
    assert created.digest == manifest.digest

    # Ensure it cannot be found normally, since it is simply temp-tagged.
    assert registry_model.lookup_manifest_by_digest(repository_ref, manifest.digest) is None

    # Ensure it can be found, which means it is temp-tagged.
    found = registry_model.lookup_manifest_by_digest(
        repository_ref, manifest.digest, allow_dead=True
    )
    assert found is not None
def test_missing_link(initialized_db):
    """ Tests for a corner case that could result in missing a link to a blob referenced by a
      manifest. The test exercises the case as follows:

      1) Push a manifest of a single layer with a Docker ID `FIRST_ID`, pointing
          to blob `FIRST_BLOB`. The database should contain the tag referencing the layer, with
          no changed ID and the blob not being GCed.

      2) Push a manifest of two layers:

          Layer 1: `FIRST_ID` with blob `SECOND_BLOB`: Will result in a new synthesized ID
          Layer 2: `SECOND_ID` with blob `THIRD_BLOB`: Will result in `SECOND_ID` pointing to the
                  `THIRD_BLOB`, with a parent pointing to the new synthesized ID's layer.

      3) Push a manifest of two layers:

          Layer 1: `THIRD_ID` with blob `FOURTH_BLOB`: Will result in a new `THIRD_ID` layer
          Layer 2: `FIRST_ID` with blob  `THIRD_BLOB`: Since `FIRST_ID` already points to `SECOND_BLOB`,
                  this will synthesize a new ID. With the current bug, the synthesized ID will match
                  that of `SECOND_ID`, leaving `THIRD_ID` unlinked and therefore, after a GC, missing
                  `FOURTH_BLOB`.
  """
    with set_tag_expiration_policy('devtable', 0):
        location_name = storage.preferred_locations[0]
        location = database.ImageStorageLocation.get(name=location_name)

        # Create first blob.
        first_blob_sha = 'sha256:' + hashlib.sha256("FIRST").hexdigest()
        model.blob.store_blob_record_and_temp_link(ADMIN_ACCESS_USER, REPO,
                                                   first_blob_sha, location, 0,
                                                   0, 0)

        # Push the first manifest.
        first_manifest = (DockerSchema1ManifestBuilder(
            ADMIN_ACCESS_USER, REPO, FIRST_TAG).add_layer(
                first_blob_sha,
                '{"id": "first"}').build(docker_v2_signing_key))

        _write_manifest(ADMIN_ACCESS_USER, REPO, FIRST_TAG, first_manifest)

        # Delete all temp tags and perform GC.
        _perform_cleanup()

        # Ensure that the first blob still exists, along with the first tag.
        assert model.blob.get_repo_blob_by_digest(ADMIN_ACCESS_USER, REPO,
                                                  first_blob_sha) is not None

        repository_ref = registry_model.lookup_repository(
            ADMIN_ACCESS_USER, REPO)
        found_tag = registry_model.get_repo_tag(repository_ref,
                                                FIRST_TAG,
                                                include_legacy_image=True)
        assert found_tag is not None
        assert found_tag.legacy_image.docker_image_id == 'first'

        # Create the second and third blobs.
        second_blob_sha = 'sha256:' + hashlib.sha256("SECOND").hexdigest()
        third_blob_sha = 'sha256:' + hashlib.sha256("THIRD").hexdigest()

        model.blob.store_blob_record_and_temp_link(ADMIN_ACCESS_USER, REPO,
                                                   second_blob_sha, location,
                                                   0, 0, 0)
        model.blob.store_blob_record_and_temp_link(ADMIN_ACCESS_USER, REPO,
                                                   third_blob_sha, location, 0,
                                                   0, 0)

        # Push the second manifest.
        second_manifest = (DockerSchema1ManifestBuilder(
            ADMIN_ACCESS_USER, REPO, SECOND_TAG).add_layer(
                third_blob_sha,
                '{"id": "second", "parent": "first"}').add_layer(
                    second_blob_sha,
                    '{"id": "first"}').build(docker_v2_signing_key))

        _write_manifest(ADMIN_ACCESS_USER, REPO, SECOND_TAG, second_manifest)

        # Delete all temp tags and perform GC.
        _perform_cleanup()

        # Ensure that the first and second blobs still exists, along with the second tag.
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, first_blob_sha) is not None
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, second_blob_sha) is not None
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, third_blob_sha) is not None

        found_tag = registry_model.get_repo_tag(repository_ref,
                                                FIRST_TAG,
                                                include_legacy_image=True)
        assert found_tag is not None
        assert found_tag.legacy_image.docker_image_id == 'first'

        # Ensure the IDs have changed.
        found_tag = registry_model.get_repo_tag(repository_ref,
                                                SECOND_TAG,
                                                include_legacy_image=True)
        assert found_tag is not None
        assert found_tag.legacy_image.docker_image_id != 'second'

        # Create the fourth blob.
        fourth_blob_sha = 'sha256:' + hashlib.sha256("FOURTH").hexdigest()
        model.blob.store_blob_record_and_temp_link(ADMIN_ACCESS_USER, REPO,
                                                   fourth_blob_sha, location,
                                                   0, 0, 0)

        # Push the third manifest.
        third_manifest = (
            DockerSchema1ManifestBuilder(
                ADMIN_ACCESS_USER, REPO, THIRD_TAG).add_layer(
                    third_blob_sha,
                    '{"id": "second", "parent": "first"}').add_layer(
                        fourth_blob_sha, '{"id": "first"}'
                    )  # Note the change in BLOB from the second manifest.
            .build(docker_v2_signing_key))

        _write_manifest(ADMIN_ACCESS_USER, REPO, THIRD_TAG, third_manifest)

        # Delete all temp tags and perform GC.
        _perform_cleanup()

        # Ensure all blobs are present.
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, first_blob_sha) is not None
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, second_blob_sha) is not None
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, third_blob_sha) is not None
        assert registry_model.get_repo_blob_by_digest(
            repository_ref, fourth_blob_sha) is not None

        # Ensure new synthesized IDs were created.
        second_tag = registry_model.get_repo_tag(repository_ref,
                                                 SECOND_TAG,
                                                 include_legacy_image=True)
        third_tag = registry_model.get_repo_tag(repository_ref,
                                                THIRD_TAG,
                                                include_legacy_image=True)
        assert second_tag.legacy_image.docker_image_id != third_tag.legacy_image.docker_image_id
Exemple #18
0
def __create_manifest_and_tags(repo,
                               structure,
                               creator_username,
                               tag_map,
                               current_level=0,
                               builder=None,
                               last_leaf_id=None):
    num_layers, subtrees, tag_names = structure

    num_layers = num_layers or 1

    tag_names = tag_names or []
    tag_names = [tag_names] if not isinstance(tag_names, list) else tag_names

    repo_ref = RepositoryReference.for_repo_obj(repo)
    builder = (builder if builder else DockerSchema1ManifestBuilder(
        repo.namespace_user.username, repo.name, ""))

    # TODO: Change this to a mixture of Schema1 and Schema2 manifest once we no longer need to
    # read from storage for Schema2.

    # Populate layers. Note, we do this in reverse order using insert_layer, as it is easier to
    # add the leaf last (even though Schema1 has it listed first).
    parent_id = last_leaf_id
    leaf_id = None
    for layer_index in range(0, num_layers):
        content = "layer-%s-%s-%s" % (layer_index, current_level,
                                      get_epoch_timestamp_ms())
        _, digest = _populate_blob(repo, content.encode("ascii"))
        current_id = "abcdef%s%s%s" % (layer_index, current_level,
                                       get_epoch_timestamp_ms())

        if layer_index == num_layers - 1:
            leaf_id = current_id

        config = {
            "id": current_id,
            "Size": len(content),
        }
        if parent_id:
            config["parent"] = parent_id

        builder.insert_layer(digest, json.dumps(config))
        parent_id = current_id

    for tag_name in tag_names:
        adjusted_tag_name = tag_name
        now = datetime.utcnow()
        if tag_name[0] == "#":
            adjusted_tag_name = tag_name[1:]
            now = now - timedelta(seconds=1)

        manifest = builder.clone(adjusted_tag_name).build()

        with freeze_time(now):
            created_tag, _ = registry_model.create_manifest_and_retarget_tag(
                repo_ref,
                manifest,
                adjusted_tag_name,
                store,
                raise_on_error=True)
            assert created_tag
            tag_map[adjusted_tag_name] = created_tag

    for subtree in subtrees:
        __create_manifest_and_tags(
            repo,
            subtree,
            creator_username,
            tag_map,
            current_level=current_level + 1,
            builder=builder,
            last_leaf_id=leaf_id,
        )
Exemple #19
0
def test_get_or_create_manifest(schema_version, initialized_db):
    repository = create_repository('devtable', 'newrepo', None)

    expected_labels = {
        'Foo': 'Bar',
        'Baz': 'Meh',
    }

    layer_json = json.dumps({
        'id':
        'somelegacyid',
        'config': {
            'Labels': expected_labels,
        },
        "rootfs": {
            "type": "layers",
            "diff_ids": []
        },
        "history": [
            {
                "created": "2018-04-03T18:37:09.284840891Z",
                "created_by": "do something",
            },
        ],
    })

    # Create a legacy image.
    find_create_or_link_image('somelegacyid', repository, 'devtable', {},
                              'local_us')

    # Add a blob containing the config.
    _, config_digest = _populate_blob(layer_json)

    # Add a blob of random data.
    random_data = 'hello world'
    _, random_digest = _populate_blob(random_data)

    # Build the manifest.
    if schema_version == 1:
        builder = DockerSchema1ManifestBuilder('devtable', 'simple',
                                               'anothertag')
        builder.add_layer(random_digest, layer_json)
        sample_manifest_instance = builder.build(docker_v2_signing_key)
    elif schema_version == 2:
        builder = DockerSchema2ManifestBuilder()
        builder.set_config_digest(config_digest, len(layer_json))
        builder.add_layer(random_digest, len(random_data))
        sample_manifest_instance = builder.build()

    # Create a new manifest.
    created_manifest = get_or_create_manifest(repository,
                                              sample_manifest_instance,
                                              storage)
    created = created_manifest.manifest
    newly_created = created_manifest.newly_created

    assert newly_created
    assert created is not None
    assert created.media_type.name == sample_manifest_instance.media_type
    assert created.digest == sample_manifest_instance.digest
    assert created.manifest_bytes == sample_manifest_instance.bytes.as_encoded_str(
    )
    assert created_manifest.labels_to_apply == expected_labels

    # Verify it has a temporary tag pointing to it.
    assert Tag.get(manifest=created, hidden=True).lifetime_end_ms

    # Verify the legacy image.
    legacy_image = get_legacy_image_for_manifest(created)
    assert legacy_image is not None
    assert legacy_image.storage.content_checksum == random_digest

    # Verify the linked blobs.
    blob_digests = [
        mb.blob.content_checksum
        for mb in ManifestBlob.select().where(ManifestBlob.manifest == created)
    ]

    assert random_digest in blob_digests
    if schema_version == 2:
        assert config_digest in blob_digests

    # Retrieve it again and ensure it is the same manifest.
    created_manifest2 = get_or_create_manifest(repository,
                                               sample_manifest_instance,
                                               storage)
    created2 = created_manifest2.manifest
    newly_created2 = created_manifest2.newly_created

    assert not newly_created2
    assert created2 == created

    # Ensure it again has a temporary tag.
    assert Tag.get(manifest=created2, hidden=True).lifetime_end_ms

    # Ensure the labels were added.
    labels = list(list_manifest_labels(created))
    assert len(labels) == 2

    labels_dict = {label.key: label.value for label in labels}
    assert labels_dict == expected_labels
Exemple #20
0
def test_images_shared_cas(default_tag_policy, initialized_db):
    """
    A repository, each two tags, pointing to the same image, which has image storage with the same
    *CAS path*, but *distinct records*.

    Deleting the first tag should delete the first image, and its storage, but not the file in
    storage, as it shares its CAS path.
    """
    with assert_gc_integrity(expect_storage_removed=True):
        repository = create_repository()

        # Create two image storage records with the same content checksum.
        content = b"hello world"
        digest = "sha256:" + hashlib.sha256(content).hexdigest()
        preferred = storage.preferred_locations[0]
        storage.put_content({preferred}, storage.blob_path(digest), content)

        is1 = database.ImageStorage.create(content_checksum=digest)
        is2 = database.ImageStorage.create(content_checksum=digest)

        location = database.ImageStorageLocation.get(name=preferred)

        database.ImageStoragePlacement.create(location=location, storage=is1)
        database.ImageStoragePlacement.create(location=location, storage=is2)

        # Temp link so its available.
        model.blob.store_blob_record_and_temp_link_in_repo(
            repository, digest, location, len(content), 120)

        # Ensure the CAS path exists.
        assert storage.exists({preferred}, storage.blob_path(digest))

        repo_ref = RepositoryReference.for_repo_obj(repository)

        # Store a manifest pointing to that path as `first`.
        builder = DockerSchema1ManifestBuilder(
            repository.namespace_user.username, repository.name, "first")
        builder.insert_layer(
            digest,
            json.dumps({
                "id": "i1",
            }),
        )
        manifest = builder.build(docker_v2_signing_key)
        registry_model.create_manifest_and_retarget_tag(repo_ref,
                                                        manifest,
                                                        "first",
                                                        storage,
                                                        raise_on_error=True)

        tag_ref = registry_model.get_repo_tag(repo_ref, "first")
        manifest_ref = registry_model.get_manifest_for_tag(tag_ref)
        registry_model.populate_legacy_images_for_testing(
            manifest_ref, storage)

        # Store another as `second`.
        builder = DockerSchema1ManifestBuilder(
            repository.namespace_user.username, repository.name, "second")
        builder.insert_layer(
            digest,
            json.dumps({
                "id": "i2",
            }),
        )
        manifest = builder.build(docker_v2_signing_key)
        created, _ = registry_model.create_manifest_and_retarget_tag(
            repo_ref, manifest, "second", storage, raise_on_error=True)

        tag_ref = registry_model.get_repo_tag(repo_ref, "second")
        manifest_ref = registry_model.get_manifest_for_tag(tag_ref)
        registry_model.populate_legacy_images_for_testing(
            manifest_ref, storage)

        # Manually retarget the second manifest's blob to the second row.
        try:
            second_blob = ManifestBlob.get(manifest=created._db_id, blob=is1)
            second_blob.blob = is2
            second_blob.save()
        except ManifestBlob.DoesNotExist:
            second_blob = ManifestBlob.get(manifest=created._db_id, blob=is2)
            second_blob.blob = is1
            second_blob.save()

        # Delete the temp reference.
        _delete_temp_links(repository)

        # Ensure the legacy images exist.
        assert_not_deleted(repository, "i1", "i2")

        # Delete the first tag.
        delete_tag(repository, "first")
        assert_deleted(repository, "i1")
        assert_not_deleted(repository, "i2")

        # Ensure the CAS path still exists.
        assert storage.exists({preferred}, storage.blob_path(digest))
Exemple #21
0
    def _build_manifest_for_legacy_image(self, tag_name, legacy_image_row):
        import features

        from app import app, docker_v2_signing_key

        repo = legacy_image_row.repository
        namespace_name = repo.namespace_user.username
        repo_name = repo.name

        # Find the v1 metadata for this image and its parents.
        try:
            parents = model.image.get_parent_images(namespace_name, repo_name,
                                                    legacy_image_row)
        except model.DataModelException:
            logger.exception(
                "Could not load parent images for legacy image %s",
                legacy_image_row.id)
            return None

        # If the manifest is being generated under the library namespace, then we make its namespace
        # empty.
        manifest_namespace = namespace_name
        if features.LIBRARY_SUPPORT and namespace_name == app.config[
                "LIBRARY_NAMESPACE"]:
            manifest_namespace = ""

        # Create and populate the manifest builder
        builder = DockerSchema1ManifestBuilder(manifest_namespace, repo_name,
                                               tag_name)

        # Add the leaf layer
        builder.add_layer(legacy_image_row.storage.content_checksum,
                          legacy_image_row.v1_json_metadata)
        if legacy_image_row.storage.uploading:
            logger.error("Cannot add an uploading storage row: %s",
                         legacy_image_row.storage.id)
            return None

        for parent_image in parents:
            if parent_image.storage.uploading:
                logger.error("Cannot add an uploading storage row: %s",
                             legacy_image_row.storage.id)
                return None

            builder.add_layer(parent_image.storage.content_checksum,
                              parent_image.v1_json_metadata)

        try:
            built_manifest = builder.build(docker_v2_signing_key)

            # If the generated manifest is greater than the maximum size, regenerate it with
            # intermediate metadata layers stripped down to their bare essentials.
            if len(built_manifest.bytes.as_encoded_str()
                   ) > MAXIMUM_GENERATED_MANIFEST_SIZE:
                built_manifest = builder.with_metadata_removed().build(
                    docker_v2_signing_key)

            if len(built_manifest.bytes.as_encoded_str()
                   ) > MAXIMUM_GENERATED_MANIFEST_SIZE:
                logger.error("Legacy image is too large to generate manifest")
                return None

            return built_manifest
        except ManifestException as me:
            logger.exception(
                "Got exception when trying to build manifest for legacy image %s",
                legacy_image_row)
            return None
Exemple #22
0
def test_get_or_create_manifest_list(initialized_db):
    repository = create_repository("devtable", "newrepo", None)

    expected_labels = {
        "Foo": "Bar",
        "Baz": "Meh",
    }

    layer_json = json.dumps({
        "id":
        "somelegacyid",
        "config": {
            "Labels": expected_labels,
        },
        "rootfs": {
            "type": "layers",
            "diff_ids": []
        },
        "history": [
            {
                "created": "2018-04-03T18:37:09.284840891Z",
                "created_by": "do something",
            },
        ],
    })

    # Create a legacy image.
    find_create_or_link_image("somelegacyid", repository, "devtable", {},
                              "local_us")

    # Add a blob containing the config.
    _, config_digest = _populate_blob(layer_json)

    # Add a blob of random data.
    random_data = "hello world"
    _, random_digest = _populate_blob(random_data)

    # Build the manifests.
    v1_builder = DockerSchema1ManifestBuilder("devtable", "simple",
                                              "anothertag")
    v1_builder.add_layer(random_digest, layer_json)
    v1_manifest = v1_builder.build(docker_v2_signing_key).unsigned()

    v2_builder = DockerSchema2ManifestBuilder()
    v2_builder.set_config_digest(config_digest,
                                 len(layer_json.encode("utf-8")))
    v2_builder.add_layer(random_digest, len(random_data.encode("utf-8")))
    v2_manifest = v2_builder.build()

    # Write the manifests.
    v1_created = get_or_create_manifest(repository, v1_manifest, storage)
    assert v1_created
    assert v1_created.manifest.digest == v1_manifest.digest

    v2_created = get_or_create_manifest(repository, v2_manifest, storage)
    assert v2_created
    assert v2_created.manifest.digest == v2_manifest.digest

    # Build the manifest list.
    list_builder = DockerSchema2ManifestListBuilder()
    list_builder.add_manifest(v1_manifest, "amd64", "linux")
    list_builder.add_manifest(v2_manifest, "amd32", "linux")
    manifest_list = list_builder.build()

    # Write the manifest list, which should also write the manifests themselves.
    created_tuple = get_or_create_manifest(repository, manifest_list, storage)
    assert created_tuple is not None

    created_list = created_tuple.manifest
    assert created_list
    assert created_list.media_type.name == manifest_list.media_type
    assert created_list.digest == manifest_list.digest
    assert created_list.config_media_type == manifest_list.config_media_type
    assert created_list.layers_compressed_size == manifest_list.layers_compressed_size

    # Ensure the child manifest links exist.
    child_manifests = {
        cm.child_manifest.digest: cm.child_manifest
        for cm in ManifestChild.select().where(
            ManifestChild.manifest == created_list)
    }
    assert len(child_manifests) == 2
    assert v1_manifest.digest in child_manifests
    assert v2_manifest.digest in child_manifests

    assert child_manifests[
        v1_manifest.digest].media_type.name == v1_manifest.media_type
    assert child_manifests[
        v2_manifest.digest].media_type.name == v2_manifest.media_type
Exemple #23
0
def test_get_or_create_manifest(schema_version, initialized_db):
    repository = create_repository("devtable", "newrepo", None)

    expected_labels = {
        "Foo": "Bar",
        "Baz": "Meh",
    }

    layer_json = json.dumps({
        "id":
        "somelegacyid",
        "config": {
            "Labels": expected_labels,
        },
        "rootfs": {
            "type": "layers",
            "diff_ids": []
        },
        "history": [
            {
                "created": "2018-04-03T18:37:09.284840891Z",
                "created_by": "do something",
            },
        ],
    })

    # Create a legacy image.
    find_create_or_link_image("somelegacyid", repository, "devtable", {},
                              "local_us")

    # Add a blob containing the config.
    _, config_digest = _populate_blob(layer_json)

    # Add a blob of random data.
    random_data = "hello world"
    _, random_digest = _populate_blob(random_data)

    # Build the manifest.
    if schema_version == 1:
        builder = DockerSchema1ManifestBuilder("devtable", "simple",
                                               "anothertag")
        builder.add_layer(random_digest, layer_json)
        sample_manifest_instance = builder.build(docker_v2_signing_key)
    elif schema_version == 2:
        builder = DockerSchema2ManifestBuilder()
        builder.set_config_digest(config_digest,
                                  len(layer_json.encode("utf-8")))
        builder.add_layer(random_digest, len(random_data.encode("utf-8")))
        sample_manifest_instance = builder.build()

    assert sample_manifest_instance.layers_compressed_size is not None

    # Create a new manifest.
    created_manifest = get_or_create_manifest(repository,
                                              sample_manifest_instance,
                                              storage)
    created = created_manifest.manifest
    newly_created = created_manifest.newly_created

    assert newly_created
    assert created is not None
    assert created.media_type.name == sample_manifest_instance.media_type
    assert created.digest == sample_manifest_instance.digest
    assert created.manifest_bytes == sample_manifest_instance.bytes.as_encoded_str(
    )
    assert created_manifest.labels_to_apply == expected_labels
    assert created.config_media_type == sample_manifest_instance.config_media_type
    assert created.layers_compressed_size == sample_manifest_instance.layers_compressed_size

    # Lookup the manifest and verify.
    found = lookup_manifest(repository, created.digest, allow_dead=True)
    assert found.digest == created.digest
    assert found.config_media_type == created.config_media_type
    assert found.layers_compressed_size == created.layers_compressed_size

    # Verify it has a temporary tag pointing to it.
    assert Tag.get(manifest=created, hidden=True).lifetime_end_ms

    # Verify the linked blobs.
    blob_digests = [
        mb.blob.content_checksum
        for mb in ManifestBlob.select().where(ManifestBlob.manifest == created)
    ]

    assert random_digest in blob_digests
    if schema_version == 2:
        assert config_digest in blob_digests

    # Retrieve it again and ensure it is the same manifest.
    created_manifest2 = get_or_create_manifest(repository,
                                               sample_manifest_instance,
                                               storage)
    created2 = created_manifest2.manifest
    newly_created2 = created_manifest2.newly_created

    assert not newly_created2
    assert created2 == created

    # Ensure it again has a temporary tag.
    assert Tag.get(manifest=created2, hidden=True).lifetime_end_ms

    # Ensure the labels were added.
    labels = list(list_manifest_labels(created))
    assert len(labels) == 2

    labels_dict = {label.key: label.value for label in labels}
    assert labels_dict == expected_labels
Exemple #24
0
def test_get_or_create_manifest_list(initialized_db):
    repository = create_repository('devtable', 'newrepo', None)

    expected_labels = {
        'Foo': 'Bar',
        'Baz': 'Meh',
    }

    layer_json = json.dumps({
        'id':
        'somelegacyid',
        'config': {
            'Labels': expected_labels,
        },
        "rootfs": {
            "type": "layers",
            "diff_ids": []
        },
        "history": [
            {
                "created": "2018-04-03T18:37:09.284840891Z",
                "created_by": "do something",
            },
        ],
    })

    # Create a legacy image.
    find_create_or_link_image('somelegacyid', repository, 'devtable', {},
                              'local_us')

    # Add a blob containing the config.
    _, config_digest = _populate_blob(layer_json)

    # Add a blob of random data.
    random_data = 'hello world'
    _, random_digest = _populate_blob(random_data)

    # Build the manifests.
    v1_builder = DockerSchema1ManifestBuilder('devtable', 'simple',
                                              'anothertag')
    v1_builder.add_layer(random_digest, layer_json)
    v1_manifest = v1_builder.build(docker_v2_signing_key).unsigned()

    v2_builder = DockerSchema2ManifestBuilder()
    v2_builder.set_config_digest(config_digest, len(layer_json))
    v2_builder.add_layer(random_digest, len(random_data))
    v2_manifest = v2_builder.build()

    # Write the manifests.
    v1_created = get_or_create_manifest(repository, v1_manifest, storage)
    assert v1_created
    assert v1_created.manifest.digest == v1_manifest.digest

    v2_created = get_or_create_manifest(repository, v2_manifest, storage)
    assert v2_created
    assert v2_created.manifest.digest == v2_manifest.digest

    # Build the manifest list.
    list_builder = DockerSchema2ManifestListBuilder()
    list_builder.add_manifest(v1_manifest, 'amd64', 'linux')
    list_builder.add_manifest(v2_manifest, 'amd32', 'linux')
    manifest_list = list_builder.build()

    # Write the manifest list, which should also write the manifests themselves.
    created_tuple = get_or_create_manifest(repository, manifest_list, storage)
    assert created_tuple is not None

    created_list = created_tuple.manifest
    assert created_list
    assert created_list.media_type.name == manifest_list.media_type
    assert created_list.digest == manifest_list.digest

    # Ensure the child manifest links exist.
    child_manifests = {
        cm.child_manifest.digest: cm.child_manifest
        for cm in ManifestChild.select().where(
            ManifestChild.manifest == created_list)
    }
    assert len(child_manifests) == 2
    assert v1_manifest.digest in child_manifests
    assert v2_manifest.digest in child_manifests

    assert child_manifests[
        v1_manifest.digest].media_type.name == v1_manifest.media_type
    assert child_manifests[
        v2_manifest.digest].media_type.name == v2_manifest.media_type
Exemple #25
0
def test_build_with_metadata_removed():
    builder = DockerSchema1ManifestBuilder("somenamespace", "somerepo",
                                           "sometag")
    builder.add_layer(
        "sha256:abcde",
        json.dumps({
            "id": "someid",
            "parent": "someid",
            "author": u"😱",
            "comment": "hello world!",
            "created": "1975-01-02 12:34",
            "Size": 5678,
            "container_config": {
                "Cmd": "foobar",
                "more": "stuff",
                "goes": "here",
            },
        }),
    )
    builder.add_layer(
        "sha256:abcde",
        json.dumps({
            "id": "anotherid",
            "author": u"😱",
            "created": "1985-02-03 12:34",
            "Size": 1234,
            "container_config": {
                "Cmd": "barbaz",
                "more": "stuff",
                "goes": "here",
            },
        }),
    )

    built = builder.build(None)
    built._validate()

    assert built.leaf_layer_v1_image_id == "someid"

    with_metadata_removed = builder.with_metadata_removed().build()
    with_metadata_removed._validate()

    built_layers = list(built.get_layers(None))
    with_metadata_removed_layers = list(with_metadata_removed.get_layers(None))

    assert len(built_layers) == len(with_metadata_removed_layers)
    for index, built_layer in enumerate(built_layers):
        with_metadata_removed_layer = with_metadata_removed_layers[index]

        assert built_layer.layer_id == with_metadata_removed_layer.layer_id
        assert built_layer.compressed_size == with_metadata_removed_layer.compressed_size
        assert built_layer.command == with_metadata_removed_layer.command
        assert built_layer.comment == with_metadata_removed_layer.comment
        assert built_layer.author == with_metadata_removed_layer.author
        assert built_layer.blob_digest == with_metadata_removed_layer.blob_digest
        assert built_layer.created_datetime == with_metadata_removed_layer.created_datetime

    assert built.leaf_layer_v1_image_id == with_metadata_removed.leaf_layer_v1_image_id
    assert built_layers[-1].layer_id == built.leaf_layer_v1_image_id

    assert json.loads(
        built_layers[-1].internal_layer.raw_v1_metadata) == json.loads(
            with_metadata_removed_layers[-1].internal_layer.raw_v1_metadata)
Exemple #26
0
def test_build_with_metadata_removed():
    builder = DockerSchema1ManifestBuilder('somenamespace', 'somerepo',
                                           'sometag')
    builder.add_layer(
        'sha256:abcde',
        json.dumps({
            'id': 'someid',
            'parent': 'someid',
            'author': u'😱',
            'comment': 'hello world!',
            'created': '1975-01-02 12:34',
            'Size': 5678,
            'container_config': {
                'Cmd': 'foobar',
                'more': 'stuff',
                'goes': 'here',
            },
        }))
    builder.add_layer(
        'sha256:abcde',
        json.dumps({
            'id': 'anotherid',
            'author': u'😱',
            'created': '1985-02-03 12:34',
            'Size': 1234,
            'container_config': {
                'Cmd': 'barbaz',
                'more': 'stuff',
                'goes': 'here',
            },
        }))

    built = builder.build(None)
    built._validate()

    assert built.leaf_layer_v1_image_id == 'someid'

    with_metadata_removed = builder.with_metadata_removed().build()
    with_metadata_removed._validate()

    built_layers = list(built.get_layers(None))
    with_metadata_removed_layers = list(with_metadata_removed.get_layers(None))

    assert len(built_layers) == len(with_metadata_removed_layers)
    for index, built_layer in enumerate(built_layers):
        with_metadata_removed_layer = with_metadata_removed_layers[index]

        assert built_layer.layer_id == with_metadata_removed_layer.layer_id
        assert built_layer.compressed_size == with_metadata_removed_layer.compressed_size
        assert built_layer.command == with_metadata_removed_layer.command
        assert built_layer.comment == with_metadata_removed_layer.comment
        assert built_layer.author == with_metadata_removed_layer.author
        assert built_layer.blob_digest == with_metadata_removed_layer.blob_digest
        assert built_layer.created_datetime == with_metadata_removed_layer.created_datetime

    assert built.leaf_layer_v1_image_id == with_metadata_removed.leaf_layer_v1_image_id
    assert built_layers[-1].layer_id == built.leaf_layer_v1_image_id

    assert (json.loads(
        built_layers[-1].internal_layer.raw_v1_metadata) == json.loads(
            with_metadata_removed_layers[-1].internal_layer.raw_v1_metadata))