Fix bug around pushing manifest lists that refer to the same manifest twice as children
This commit is contained in:
parent
45db1d27e7
commit
54904cfd6e
3 changed files with 115 additions and 3 deletions
|
@ -59,7 +59,7 @@ def _create_manifest(repository_id, manifest_interface_instance, storage):
|
||||||
# Load, parse and get/create the child manifests, if any.
|
# Load, parse and get/create the child manifests, if any.
|
||||||
retriever = RepositoryContentRetriever.for_repository(repository_id, storage)
|
retriever = RepositoryContentRetriever.for_repository(repository_id, storage)
|
||||||
child_manifest_refs = manifest_interface_instance.child_manifests(retriever)
|
child_manifest_refs = manifest_interface_instance.child_manifests(retriever)
|
||||||
child_manifest_rows = []
|
child_manifest_rows = {}
|
||||||
child_manifest_label_dicts = []
|
child_manifest_label_dicts = []
|
||||||
|
|
||||||
if child_manifest_refs is not None:
|
if child_manifest_refs is not None:
|
||||||
|
@ -90,7 +90,7 @@ def _create_manifest(repository_id, manifest_interface_instance, storage):
|
||||||
logger.error('Could not get/create child manifest')
|
logger.error('Could not get/create child manifest')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
child_manifest_rows.append(child_manifest_info.manifest)
|
child_manifest_rows[child_manifest_info.manifest.digest] = child_manifest_info.manifest
|
||||||
child_manifest_label_dicts.append(labels)
|
child_manifest_label_dicts.append(labels)
|
||||||
|
|
||||||
# Ensure all the blobs in the manifest exist.
|
# Ensure all the blobs in the manifest exist.
|
||||||
|
@ -147,7 +147,7 @@ def _create_manifest(repository_id, manifest_interface_instance, storage):
|
||||||
if child_manifest_rows:
|
if child_manifest_rows:
|
||||||
children_to_insert = [dict(manifest=manifest, child_manifest=child_manifest,
|
children_to_insert = [dict(manifest=manifest, child_manifest=child_manifest,
|
||||||
repository=repository_id)
|
repository=repository_id)
|
||||||
for child_manifest in child_manifest_rows]
|
for child_manifest in child_manifest_rows.values()]
|
||||||
ManifestChild.insert_many(children_to_insert).execute()
|
ManifestChild.insert_many(children_to_insert).execute()
|
||||||
|
|
||||||
# Define the labels for the manifest (if any).
|
# Define the labels for the manifest (if any).
|
||||||
|
|
|
@ -241,6 +241,80 @@ def test_get_or_create_manifest_list(initialized_db):
|
||||||
assert child_manifests[v2_manifest.digest].media_type.name == v2_manifest.media_type
|
assert child_manifests[v2_manifest.digest].media_type.name == v2_manifest.media_type
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_or_create_manifest_list_duplicate_child_manifest(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.
|
||||||
|
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 manifest.
|
||||||
|
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, with the child manifest repeated.
|
||||||
|
list_builder = DockerSchema2ManifestListBuilder()
|
||||||
|
list_builder.add_manifest(v2_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) == 1
|
||||||
|
assert v2_manifest.digest in child_manifests
|
||||||
|
assert child_manifests[v2_manifest.digest].media_type.name == v2_manifest.media_type
|
||||||
|
|
||||||
|
# Try to create again and ensure we get back the same manifest list.
|
||||||
|
created2_tuple = get_or_create_manifest(repository, manifest_list, storage)
|
||||||
|
assert created2_tuple is not None
|
||||||
|
assert created2_tuple.manifest == created_list
|
||||||
|
|
||||||
|
|
||||||
def test_get_or_create_manifest_with_remote_layers(initialized_db):
|
def test_get_or_create_manifest_with_remote_layers(initialized_db):
|
||||||
repository = create_repository('devtable', 'newrepo', None)
|
repository = create_repository('devtable', 'newrepo', None)
|
||||||
|
|
||||||
|
|
|
@ -1493,3 +1493,41 @@ def test_push_pull_manifest_list_missing_manifest(v22_protocol, basic_images, li
|
||||||
[], blobs,
|
[], blobs,
|
||||||
credentials=credentials, options=options,
|
credentials=credentials, options=options,
|
||||||
expected_failure=Failures.INVALID_MANIFEST)
|
expected_failure=Failures.INVALID_MANIFEST)
|
||||||
|
|
||||||
|
|
||||||
|
def test_push_pull_manifest_list_again(v22_protocol, basic_images, different_images,
|
||||||
|
liveserver_session, app_reloader, data_model):
|
||||||
|
""" Test: Push a new tag with a manifest list containing two manifests, push it again, and pull
|
||||||
|
it.
|
||||||
|
"""
|
||||||
|
if data_model != 'oci_model':
|
||||||
|
return
|
||||||
|
|
||||||
|
credentials = ('devtable', 'password')
|
||||||
|
options = ProtocolOptions()
|
||||||
|
|
||||||
|
# Build the manifests that will go in the list.
|
||||||
|
blobs = {}
|
||||||
|
|
||||||
|
first_manifest = v22_protocol.build_schema2(basic_images, blobs, options)
|
||||||
|
second_manifest = v22_protocol.build_schema2(different_images, blobs, options)
|
||||||
|
|
||||||
|
# Create and push the manifest list.
|
||||||
|
builder = DockerSchema2ManifestListBuilder()
|
||||||
|
builder.add_manifest(first_manifest, 'amd64', 'linux')
|
||||||
|
builder.add_manifest(second_manifest, 'arm', 'linux')
|
||||||
|
manifestlist = builder.build()
|
||||||
|
|
||||||
|
v22_protocol.push_list(liveserver_session, 'devtable', 'newrepo', 'latest', manifestlist,
|
||||||
|
[first_manifest, second_manifest], blobs,
|
||||||
|
credentials=credentials, options=options)
|
||||||
|
|
||||||
|
# Push the manifest list again. This should more or less no-op.
|
||||||
|
options.skip_head_checks = True
|
||||||
|
v22_protocol.push_list(liveserver_session, 'devtable', 'newrepo', 'latest', manifestlist,
|
||||||
|
[first_manifest, second_manifest], blobs,
|
||||||
|
credentials=credentials, options=options)
|
||||||
|
|
||||||
|
# Pull and verify the manifest list.
|
||||||
|
v22_protocol.pull_list(liveserver_session, 'devtable', 'newrepo', 'latest', manifestlist,
|
||||||
|
credentials=credentials, options=options)
|
||||||
|
|
Reference in a new issue