Add schema2 list support
This commit is contained in:
parent
52b12131f7
commit
a73cd9170a
7 changed files with 320 additions and 48 deletions
194
image/docker/schema2/list.py
Normal file
194
image/docker/schema2/list.py
Normal file
|
@ -0,0 +1,194 @@
|
|||
import json
|
||||
|
||||
from cachetools import lru_cache
|
||||
from jsonschema import validate as validate_schema, ValidationError
|
||||
|
||||
from image.docker.schema1 import DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE
|
||||
from image.docker.schema1 import DockerSchema1Manifest
|
||||
from image.docker.schema2 import (DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE,
|
||||
DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
|
||||
from image.docker.schema2.manifest import DockerSchema2Manifest
|
||||
|
||||
# Keys.
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_VERSION_KEY = 'schemaVersion'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY = 'mediaType'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_SIZE_KEY = 'size'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_DIGEST_KEY = 'digest'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MANIFESTS_KEY = 'manifests'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_PLATFORM_KEY = 'platform'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_ARCHITECTURE_KEY = 'architecture'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_KEY = 'os'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_VERSION_KEY = 'os.version'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_FEATURES_KEY = 'os.features'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_FEATURES_KEY = 'features'
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_VARIANT_KEY = 'variant'
|
||||
|
||||
|
||||
class MalformedSchema2ManifestList(Exception):
|
||||
"""
|
||||
Raised when a manifest list fails an assertion that should be true according to the
|
||||
Docker Manifest v2.2 Specification.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LazyManifestLoader(object):
|
||||
def __init__(self, manifest_data, lookup_manifest_fn):
|
||||
self._manifest_data = manifest_data
|
||||
self._lookup_manifest_fn = lookup_manifest_fn
|
||||
self._loaded_manifest = None
|
||||
|
||||
@property
|
||||
def manifest_obj(self):
|
||||
if self._loaded_manifest is not None:
|
||||
return self._loaded_manifest
|
||||
|
||||
self._loaded_manifest = self._load_manifest()
|
||||
return self._loaded_manifest
|
||||
|
||||
def _load_manifest(self):
|
||||
digest = self._manifest_data[DOCKER_SCHEMA2_MANIFESTLIST_DIGEST_KEY]
|
||||
size = self._manifest_data[DOCKER_SCHEMA2_MANIFESTLIST_SIZE_KEY]
|
||||
manifest_bytes = self._lookup_manifest_fn(digest)
|
||||
if len(manifest_bytes) != size:
|
||||
raise MalformedSchema2ManifestList('Size of manifest does not match that retrieved: %s vs %s',
|
||||
len(manifest_bytes), size)
|
||||
|
||||
content_type = self._manifest_data[DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY]
|
||||
if content_type == DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE:
|
||||
return DockerSchema2Manifest(manifest_bytes)
|
||||
|
||||
if content_type == DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE:
|
||||
return DockerSchema1Manifest(manifest_bytes, validate=False)
|
||||
|
||||
raise MalformedSchema2ManifestList('Unknown manifest content type')
|
||||
|
||||
|
||||
class DockerSchema2ManifestList(object):
|
||||
METASCHEMA = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_VERSION_KEY: {
|
||||
'type': 'number',
|
||||
'description': 'The version of the manifest list. Must always be `2`.',
|
||||
'minimum': 2,
|
||||
'maximum': 2,
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'The media type of the manifest list.',
|
||||
'enum': [DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE],
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MANIFESTS_KEY: {
|
||||
'type': 'array',
|
||||
'description': 'The manifests field contains a list of manifests for specific platforms',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'The MIME type of the referenced object. This will generally be ' +
|
||||
'application/vnd.docker.distribution.manifest.v2+json, but it ' +
|
||||
'could also be application/vnd.docker.distribution.manifest.v1+json ' +
|
||||
'if the manifest list references a legacy schema-1 manifest.',
|
||||
'enum': [DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE, DOCKER_SCHEMA1_MANIFEST_CONTENT_TYPE],
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_SIZE_KEY: {
|
||||
'type': 'number',
|
||||
'description': 'The size in bytes of the object. This field exists so that a ' +
|
||||
'client will have an expected size for the content before ' +
|
||||
'validating. If the length of the retrieved content does not ' +
|
||||
'match the specified length, the content should not be trusted.',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_DIGEST_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'The content addressable digest of the manifest in the blob store',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_PLATFORM_KEY: {
|
||||
'type': 'object',
|
||||
'description': 'The platform object describes the platform which the image in ' +
|
||||
'the manifest runs on',
|
||||
'properties': {
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_ARCHITECTURE_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'Specifies the CPU architecture, for example amd64 or ppc64le.',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'Specifies the operating system, for example linux or windows',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_VERSION_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'Specifies the operating system version, for example 10.0.10586',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_FEATURES_KEY: {
|
||||
'type': 'array',
|
||||
'description': 'specifies an array of strings, each listing a required OS ' +
|
||||
'feature (for example on Windows win32k)',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_VARIANT_KEY: {
|
||||
'type': 'string',
|
||||
'description': 'Specifies a variant of the CPU, for example armv6l to specify ' +
|
||||
'a particular CPU variant of the ARM CPU',
|
||||
},
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_FEATURES_KEY: {
|
||||
'type': 'array',
|
||||
'description': 'specifies an array of strings, each listing a required CPU ' +
|
||||
'feature (for example sse4 or aes).',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': [DOCKER_SCHEMA2_MANIFESTLIST_ARCHITECTURE_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_OS_KEY],
|
||||
},
|
||||
},
|
||||
'required': [DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_SIZE_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_DIGEST_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_PLATFORM_KEY],
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': [DOCKER_SCHEMA2_MANIFESTLIST_VERSION_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MEDIATYPE_KEY,
|
||||
DOCKER_SCHEMA2_MANIFESTLIST_MANIFESTS_KEY],
|
||||
}
|
||||
|
||||
def __init__(self, manifest_bytes):
|
||||
self._layers = None
|
||||
|
||||
try:
|
||||
self._parsed = json.loads(manifest_bytes)
|
||||
except ValueError as ve:
|
||||
raise MalformedSchema2ManifestList('malformed manifest data: %s' % ve)
|
||||
|
||||
try:
|
||||
validate_schema(self._parsed, DockerSchema2ManifestList.METASCHEMA)
|
||||
except ValidationError as ve:
|
||||
raise MalformedSchema2ManifestList('manifest data does not match schema: %s' % ve)
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def manifests(self, lookup_manifest_fn):
|
||||
""" Returns the manifests in the list. The `lookup_manifest_fn` is a function
|
||||
that returns the manifest bytes for the specified digest.
|
||||
"""
|
||||
manifests = self._parsed[DOCKER_SCHEMA2_MANIFESTLIST_MANIFESTS_KEY]
|
||||
return [LazyManifestLoader(m, lookup_manifest_fn) for m in manifests]
|
||||
|
||||
def get_v1_compatible_manifest(self, lookup_manifest_fn):
|
||||
""" Returns the manifest that is compatible with V1, by virtue of being `amd64` and `linux`.
|
||||
If none, returns None.
|
||||
"""
|
||||
for manifest in self.manifests(lookup_manifest_fn):
|
||||
platform = manifest._manifest_data[DOCKER_SCHEMA2_MANIFESTLIST_PLATFORM_KEY]
|
||||
architecture = platform[DOCKER_SCHEMA2_MANIFESTLIST_ARCHITECTURE_KEY]
|
||||
os = platform[DOCKER_SCHEMA2_MANIFESTLIST_OS_KEY]
|
||||
if architecture == 'amd64' and os == 'linux':
|
||||
return manifest
|
||||
|
||||
return None
|
Reference in a new issue