Make command optional in schema 2 manifests (as per OCI spec) and pull out additional information
Also updates the manifest view page to show the comment or digest if there is no command defined
This commit is contained in:
parent
fc691cefb4
commit
71b7a2b3a2
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,
|
||||
},
|
||||
},
|
||||
|
@ -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