Enhancements for Docker schema implementations in preparing for supporting schema 2 in the OCI model
This adds additional required properties and methods to the Docker schema interface to allow us to treat both schema1 and schema2 manifests and lists logically equivalent from the OCI mode perspective
This commit is contained in:
parent
6b86b87a16
commit
e344d4a5cf
12 changed files with 447 additions and 22 deletions
|
@ -127,3 +127,5 @@ def test_valid_config():
|
|||
else:
|
||||
assert 'Hostname' not in v1_compat['container_config']
|
||||
assert v1_compat['container_config']['Cmd'] == history_entry.command
|
||||
|
||||
assert config.labels == {}
|
||||
|
|
|
@ -3,7 +3,8 @@ import pytest
|
|||
|
||||
from image.docker.schema1 import DockerSchema1Manifest
|
||||
from image.docker.schema2.manifest import DockerSchema2Manifest
|
||||
from image.docker.schema2.list import MalformedSchema2ManifestList, DockerSchema2ManifestList
|
||||
from image.docker.schema2.list import (MalformedSchema2ManifestList, DockerSchema2ManifestList,
|
||||
DockerSchema2ManifestListBuilder)
|
||||
from image.docker.schema2.test.test_manifest import MANIFEST_BYTES as v22_bytes
|
||||
from image.docker.test.test_schema1 import MANIFEST_BYTES as v21_bytes
|
||||
|
||||
|
@ -58,6 +59,13 @@ def test_valid_manifestlist():
|
|||
|
||||
manifestlist = DockerSchema2ManifestList(MANIFESTLIST_BYTES)
|
||||
assert len(manifestlist.manifests(_get_manifest)) == 2
|
||||
assert (manifestlist.digest ==
|
||||
'sha256:7e22fdbe49736329786c9b4fdc154cc9251b190ca6b4cf33aed00efc0fc3df25')
|
||||
|
||||
assert manifestlist.media_type == 'application/vnd.docker.distribution.manifest.list.v2+json'
|
||||
assert manifestlist.bytes == MANIFESTLIST_BYTES
|
||||
assert manifestlist.manifest_dict == json.loads(MANIFESTLIST_BYTES)
|
||||
assert set(manifestlist.blob_digests) == {'sha256:e6', 'sha256:5b'}
|
||||
|
||||
for index, manifest in enumerate(manifestlist.manifests(_get_manifest)):
|
||||
if index == 0:
|
||||
|
@ -68,3 +76,25 @@ def test_valid_manifestlist():
|
|||
assert manifest.manifest_obj.schema_version == 1
|
||||
|
||||
assert manifestlist.get_v1_compatible_manifest(_get_manifest).manifest_obj.schema_version == 1
|
||||
|
||||
assert manifestlist.layers is None
|
||||
assert manifestlist.leaf_layer_v1_image_id is None
|
||||
assert manifestlist.legacy_image_ids is None
|
||||
|
||||
|
||||
def test_builder():
|
||||
def _get_manifest(digest):
|
||||
if digest == 'sha256:e6':
|
||||
return v22_bytes
|
||||
else:
|
||||
return v21_bytes
|
||||
|
||||
existing = DockerSchema2ManifestList(MANIFESTLIST_BYTES)
|
||||
|
||||
builder = DockerSchema2ManifestListBuilder()
|
||||
for index, manifest in enumerate(existing.manifests(_get_manifest)):
|
||||
builder.add_manifest(manifest.manifest_obj, "amd64", "os")
|
||||
|
||||
built = builder.build()
|
||||
assert len(built.manifests(_get_manifest)) == 2
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import pytest
|
|||
from app import docker_v2_signing_key
|
||||
from image.docker.schema1 import (DockerSchema1ManifestBuilder,
|
||||
DOCKER_SCHEMA1_SIGNED_MANIFEST_CONTENT_TYPE)
|
||||
from image.docker.schema2.manifest import MalformedSchema2Manifest, DockerSchema2Manifest
|
||||
from image.docker.schema2.manifest import (MalformedSchema2Manifest, DockerSchema2Manifest,
|
||||
DockerSchema2ManifestBuilder)
|
||||
from image.docker.schema2.test.test_config import CONFIG_BYTES
|
||||
|
||||
@pytest.mark.parametrize('json_data', [
|
||||
|
@ -71,8 +72,41 @@ def test_valid_manifest():
|
|||
assert manifest.leaf_layer.compressed_size == 73109
|
||||
|
||||
blob_digests = list(manifest.blob_digests)
|
||||
assert len(blob_digests) == len(manifest.layers)
|
||||
assert blob_digests == [str(layer.digest) for layer in manifest.layers]
|
||||
assert len(blob_digests) == len(manifest.layers) + 1
|
||||
|
||||
expected = [str(layer.digest) for layer in manifest.layers] + [manifest.config.digest]
|
||||
assert blob_digests == expected
|
||||
|
||||
|
||||
def test_schema2_builder():
|
||||
manifest = DockerSchema2Manifest(MANIFEST_BYTES)
|
||||
|
||||
builder = DockerSchema2ManifestBuilder()
|
||||
builder.set_config_digest(manifest.config.digest, manifest.config.size)
|
||||
|
||||
for layer in manifest.layers:
|
||||
builder.add_layer(layer.digest, layer.compressed_size, urls=layer.urls)
|
||||
|
||||
built = builder.build()
|
||||
assert built.layers == manifest.layers
|
||||
assert built.config == manifest.config
|
||||
|
||||
|
||||
def test_get_manifest_labels():
|
||||
labels = dict(foo='bar', baz='meh')
|
||||
|
||||
def _lookup_config(digest):
|
||||
config_str = json.dumps({
|
||||
"config": {
|
||||
"Labels": labels,
|
||||
},
|
||||
"rootfs": {"type": "layers", "diff_ids": []},
|
||||
"history": [],
|
||||
})
|
||||
return config_str + ' ' * (1885 - len(config_str))
|
||||
|
||||
manifest = DockerSchema2Manifest(MANIFEST_BYTES)
|
||||
assert manifest.get_manifest_labels(_lookup_config) == labels
|
||||
|
||||
|
||||
def test_build_schema1():
|
||||
|
@ -85,7 +119,8 @@ def test_build_schema1():
|
|||
assert schema1.media_type == DOCKER_SCHEMA1_SIGNED_MANIFEST_CONTENT_TYPE
|
||||
assert len(schema1.layers) == len(manifest.layers)
|
||||
assert set(schema1.image_ids) == set([l.v1_id for l in manifest.layers_with_v1_ids])
|
||||
assert set(schema1.parent_image_ids) == set([l.v1_parent_id for l in manifest.layers_with_v1_ids if l.v1_parent_id])
|
||||
assert set(schema1.parent_image_ids) == set([l.v1_parent_id for l in
|
||||
manifest.layers_with_v1_ids if l.v1_parent_id])
|
||||
|
||||
manifest_layers = list(manifest.layers_with_v1_ids)
|
||||
for index, layer in enumerate(schema1.layers):
|
||||
|
@ -95,3 +130,45 @@ def test_build_schema1():
|
|||
|
||||
for index, digest in enumerate(schema1.blob_digests):
|
||||
assert digest == str(list(manifest.blob_digests)[index])
|
||||
|
||||
|
||||
def test_generate_legacy_layers():
|
||||
builder = DockerSchema2ManifestBuilder()
|
||||
builder.add_layer('sha256:abc123', 123)
|
||||
builder.add_layer('sha256:def456', 789)
|
||||
builder.set_config_digest('sha256:def456', 2000)
|
||||
manifest = builder.build()
|
||||
|
||||
def _lookup_config(digest):
|
||||
config_str = json.dumps({
|
||||
"config": {
|
||||
},
|
||||
"rootfs": {"type": "layers", "diff_ids": []},
|
||||
"history": [
|
||||
{
|
||||
"created": "2018-04-03T18:37:09.284840891Z",
|
||||
"created_by": "foo"
|
||||
},
|
||||
{
|
||||
"created": "2018-04-12T18:37:09.284840891Z",
|
||||
"created_by": "bar"
|
||||
},
|
||||
],
|
||||
})
|
||||
return config_str + ' ' * (2000 - len(config_str))
|
||||
|
||||
legacy_layers = list(manifest.generate_legacy_layers({}, _lookup_config))
|
||||
assert len(legacy_layers) == 2
|
||||
assert legacy_layers[0].content_checksum == 'sha256:abc123'
|
||||
assert legacy_layers[1].content_checksum == 'sha256:def456'
|
||||
|
||||
assert legacy_layers[0].created == "2018-04-03T18:37:09.284840891Z"
|
||||
assert legacy_layers[1].created == "2018-04-12T18:37:09.284840891Z"
|
||||
|
||||
assert legacy_layers[0].command == '"foo"'
|
||||
assert legacy_layers[1].command == '"bar"'
|
||||
|
||||
assert legacy_layers[1].parent_image_id == legacy_layers[0].image_id
|
||||
assert legacy_layers[0].parent_image_id is None
|
||||
|
||||
assert legacy_layers[0].image_id != legacy_layers[1]
|
||||
|
|
Reference in a new issue