e344d4a5cf
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
174 lines
5.9 KiB
Python
174 lines
5.9 KiB
Python
import json
|
|
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,
|
|
DockerSchema2ManifestBuilder)
|
|
from image.docker.schema2.test.test_config import CONFIG_BYTES
|
|
|
|
@pytest.mark.parametrize('json_data', [
|
|
'',
|
|
'{}',
|
|
"""
|
|
{
|
|
"unknown": "key"
|
|
}
|
|
""",
|
|
])
|
|
def test_malformed_manifests(json_data):
|
|
with pytest.raises(MalformedSchema2Manifest):
|
|
DockerSchema2Manifest(json_data)
|
|
|
|
|
|
MANIFEST_BYTES = json.dumps({
|
|
"schemaVersion": 2,
|
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
|
"config": {
|
|
"mediaType": "application/vnd.docker.container.image.v1+json",
|
|
"size": 1885,
|
|
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
|
},
|
|
"layers": [
|
|
{
|
|
"mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
|
|
"size": 1234,
|
|
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
|
|
"urls": ['http://some/url'],
|
|
},
|
|
{
|
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
"size": 32654,
|
|
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
|
},
|
|
{
|
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
"size": 16724,
|
|
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
|
},
|
|
{
|
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
"size": 73109,
|
|
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
|
},
|
|
],
|
|
})
|
|
|
|
def test_valid_manifest():
|
|
manifest = DockerSchema2Manifest(MANIFEST_BYTES)
|
|
assert manifest.config.size == 1885
|
|
assert str(manifest.config.digest) == 'sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7'
|
|
assert manifest.media_type == "application/vnd.docker.distribution.manifest.v2+json"
|
|
|
|
assert len(manifest.layers) == 4
|
|
assert manifest.layers[0].is_remote
|
|
assert manifest.layers[0].compressed_size == 1234
|
|
assert str(manifest.layers[0].digest) == 'sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736'
|
|
assert manifest.layers[0].urls
|
|
|
|
assert manifest.leaf_layer == manifest.layers[3]
|
|
assert not manifest.leaf_layer.is_remote
|
|
assert manifest.leaf_layer.compressed_size == 73109
|
|
|
|
blob_digests = list(manifest.blob_digests)
|
|
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():
|
|
manifest = DockerSchema2Manifest(MANIFEST_BYTES)
|
|
|
|
builder = DockerSchema1ManifestBuilder('somenamespace', 'somename', 'sometag')
|
|
manifest.populate_schema1_builder(builder, lambda digest: CONFIG_BYTES)
|
|
schema1 = builder.build(docker_v2_signing_key)
|
|
|
|
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])
|
|
|
|
manifest_layers = list(manifest.layers_with_v1_ids)
|
|
for index, layer in enumerate(schema1.layers):
|
|
assert layer.digest == manifest_layers[index].layer.digest
|
|
assert layer.v1_metadata.image_id == manifest_layers[index].v1_id
|
|
assert layer.v1_metadata.parent_image_id == manifest_layers[index].v1_parent_id
|
|
|
|
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]
|