Merge pull request #3311 from quay/loosen-schema-requirements

Make command optional in schema 2 manifests (as per OCI spec) and pull out additional information
This commit is contained in:
Joseph Schorr 2018-12-12 10:16:30 -05:00 committed by GitHub
commit 1d57e1eb9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 16 deletions

View file

@ -37,10 +37,12 @@ def _label_dict(label):
def _layer_dict(manifest_layer, index):
# NOTE: The `command` in the layer is either a JSON string of an array (schema 1) or
# a single string (schema 2). The block below normalizes it to have the same format.
try:
command = json.loads(manifest_layer.command)
except (TypeError, ValueError):
command = [manifest_layer.command]
command = None
if manifest_layer.command:
try:
command = json.loads(manifest_layer.command)
except (TypeError, ValueError):
command = [manifest_layer.command]
return {
'index': index,
@ -48,6 +50,8 @@ def _layer_dict(manifest_layer, index):
'is_remote': manifest_layer.is_remote,
'urls': manifest_layer.urls,
'command': command,
'comment': manifest_layer.comment,
'author': manifest_layer.author,
'blob_digest': str(manifest_layer.blob_digest),
'created_datetime': format_date(manifest_layer.created_datetime),
}

View file

@ -82,7 +82,8 @@ class Schema1Layer(namedtuple('Schema1Layer', ['digest', 'v1_metadata', 'raw_v1_
class Schema1V1Metadata(namedtuple('Schema1V1Metadata', ['image_id', 'parent_image_id', 'created',
'comment', 'command', 'labels'])):
'comment', 'command', 'author',
'labels'])):
"""
Represents the necessary data extracted from the v1 compatibility string in a given layer of a
Manifest.
@ -315,6 +316,8 @@ class DockerSchema1Manifest(ManifestInterface):
is_remote=False,
urls=None,
command=layer.v1_metadata.command,
comment=layer.v1_metadata.comment,
author=layer.v1_metadata.author,
blob_digest=layer.digest,
created_datetime=created_datetime,
internal_layer=layer)
@ -372,9 +375,13 @@ class DockerSchema1Manifest(ManifestInterface):
raise MalformedSchema1Manifest('id field missing from v1Compatibility JSON')
labels = v1_metadata.get('config', {}).get('Labels', {}) or {}
extracted = Schema1V1Metadata(v1_metadata['id'], v1_metadata.get('parent'),
v1_metadata.get('created'), v1_metadata.get('comment'),
command, labels)
extracted = Schema1V1Metadata(image_id=v1_metadata['id'],
parent_image_id=v1_metadata.get('parent'),
created=v1_metadata.get('created'),
comment=v1_metadata.get('comment'),
author=v1_metadata.get('author'),
command=command,
labels=labels)
compressed_size = v1_metadata.get('Size')
yield Schema1Layer(image_digest, extracted, metadata_string, compressed_size, False, None)
@ -483,6 +490,7 @@ class DockerSchema1Manifest(ManifestInterface):
image_id=working_image_id,
created=extracted_v1_metadata.created,
comment=extracted_v1_metadata.comment,
author=extracted_v1_metadata.author,
command=extracted_v1_metadata.command,
compat_json=v1_metadata_json,
parent_image_id=parent_image_id,

View file

@ -108,12 +108,14 @@ DOCKER_SCHEMA2_CONFIG_HISTORY_KEY = "history"
DOCKER_SCHEMA2_CONFIG_ROOTFS_KEY = "rootfs"
DOCKER_SCHEMA2_CONFIG_CREATED_KEY = "created"
DOCKER_SCHEMA2_CONFIG_CREATED_BY_KEY = "created_by"
DOCKER_SCHEMA2_CONFIG_COMMENT_KEY = "comment"
DOCKER_SCHEMA2_CONFIG_AUTHOR_KEY = "author"
DOCKER_SCHEMA2_CONFIG_EMPTY_LAYER_KEY = "empty_layer"
DOCKER_SCHEMA2_CONFIG_TYPE_KEY = "type"
LayerHistory = namedtuple('LayerHistory', ['created', 'created_datetime', 'command', 'is_empty',
'raw_entry'])
'author', 'comment', 'raw_entry'])
class MalformedSchema2Config(ManifestException):
@ -151,8 +153,15 @@ class DockerSchema2Config(object):
'description': 'The command used to create the layer',
'x-example': '\/bin\/sh -c #(nop) ADD file:somesha in /',
},
DOCKER_SCHEMA2_CONFIG_COMMENT_KEY: {
'type': 'string',
'description': 'Comment describing the layer',
},
DOCKER_SCHEMA2_CONFIG_AUTHOR_KEY: {
'type': 'string',
'description': 'The author of the layer',
},
},
'required': [DOCKER_SCHEMA2_CONFIG_CREATED_KEY, DOCKER_SCHEMA2_CONFIG_CREATED_BY_KEY],
'additionalProperties': True,
},
},
@ -211,7 +220,7 @@ class DockerSchema2Config(object):
for history_entry in self._parsed[DOCKER_SCHEMA2_CONFIG_HISTORY_KEY]:
if history_entry.get(DOCKER_SCHEMA2_CONFIG_EMPTY_LAYER_KEY, False):
return True
return False
@property
@ -220,8 +229,10 @@ class DockerSchema2Config(object):
for history_entry in self._parsed[DOCKER_SCHEMA2_CONFIG_HISTORY_KEY]:
created_datetime = parse_date(history_entry[DOCKER_SCHEMA2_CONFIG_CREATED_KEY])
yield LayerHistory(created_datetime=created_datetime,
created=history_entry[DOCKER_SCHEMA2_CONFIG_CREATED_KEY],
command=history_entry[DOCKER_SCHEMA2_CONFIG_CREATED_BY_KEY],
created=history_entry.get(DOCKER_SCHEMA2_CONFIG_CREATED_KEY),
command=history_entry.get(DOCKER_SCHEMA2_CONFIG_CREATED_BY_KEY),
author=history_entry.get(DOCKER_SCHEMA2_CONFIG_AUTHOR_KEY),
comment=history_entry.get(DOCKER_SCHEMA2_CONFIG_COMMENT_KEY),
is_empty=history_entry.get(DOCKER_SCHEMA2_CONFIG_EMPTY_LAYER_KEY, False),
raw_entry=history_entry)
@ -235,9 +246,18 @@ class DockerSchema2Config(object):
if v1_parent_id is not None:
v1_compatibility['parent'] = v1_parent_id
if 'created' not in v1_compatibility:
if 'created' not in v1_compatibility and history.created:
v1_compatibility['created'] = history.created
if 'author' not in v1_compatibility and history.author:
v1_compatibility['author'] = history.author
if 'comment' not in v1_compatibility and history.comment:
v1_compatibility['comment'] = history.comment
if 'throwaway' not in v1_compatibility and history.is_empty:
v1_compatibility['throwaway'] = True
if 'container_config' not in v1_compatibility:
v1_compatibility['container_config'] = {
'Cmd': [history.command],

View file

@ -221,6 +221,8 @@ class DockerSchema2Manifest(ManifestInterface):
command=image_layer.history.command,
blob_digest=image_layer.blob_digest,
created_datetime=image_layer.history.created_datetime,
author=image_layer.history.author,
comment=image_layer.history.comment,
internal_layer=image_layer)
@property

View file

@ -3,4 +3,5 @@ from collections import namedtuple
ManifestImageLayer = namedtuple('ManifestImageLayer', ['layer_id', 'compressed_size',
'is_remote', 'urls', 'command',
'blob_digest', 'created_datetime',
'author', 'comment',
'internal_layer'])

View file

@ -9,7 +9,7 @@ from collections import namedtuple
class DockerV1Metadata(namedtuple('DockerV1Metadata',
['namespace_name', 'repo_name', 'image_id', 'checksum',
'content_checksum', 'created', 'comment', 'command',
'parent_image_id', 'compat_json'])):
'author', 'parent_image_id', 'compat_json'])):
"""
DockerV1Metadata represents all of the metadata for a given Docker v1 Image.
The original form of the metadata is stored in the compat_json field.

View file

@ -1,6 +1,8 @@
<div class="manifest-view-layer-element" ng-class="getClass()">
<div class="image-command">
<image-command command="layer.command"></image-command>
<image-command command="layer.command" ng-if="layer.command"></image-command>
<i ng-if="!layer.command && layer.comment">{{ layer.comment }}</i>
<code ng-if="!layer.command && !layer.comment">{{ layer.blob_digest }}</code>
</div>
<div class="image-layer-dot"></div>
<div class="image-layer-line"></div>