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:
commit
1d57e1eb9f
7 changed files with 53 additions and 16 deletions
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
Reference in a new issue