Merge pull request #3249 from quay/fix-aci
Fix ACI conversion and add a registry test for it
This commit is contained in:
commit
6d5489b254
4 changed files with 74 additions and 7 deletions
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from flask import redirect, Blueprint, abort, send_file, make_response, request
|
from flask import redirect, Blueprint, abort, send_file, make_response, request
|
||||||
|
|
||||||
|
@ -350,7 +351,14 @@ def _repo_verb(namespace, repository, tag_name, verb, formatter, sign=False, che
|
||||||
|
|
||||||
def os_arch_checker(os, arch):
|
def os_arch_checker(os, arch):
|
||||||
def checker(tag, manifest):
|
def checker(tag, manifest):
|
||||||
image_json = manifest.leaf_layer.v1_metadata
|
try:
|
||||||
|
image_json = json.loads(manifest.get_parsed_manifest().leaf_layer.raw_v1_metadata)
|
||||||
|
except ValueError:
|
||||||
|
logger.exception('Could not parse leaf layer JSON for manifest %s', manifest)
|
||||||
|
return False
|
||||||
|
except TypeError:
|
||||||
|
logger.exception('Could not parse leaf layer JSON for manifest %s', manifest)
|
||||||
|
return False
|
||||||
|
|
||||||
# Verify the architecture and os.
|
# Verify the architecture and os.
|
||||||
operating_system = image_json.get('os', 'linux')
|
operating_system = image_json.get('os', 'linux')
|
||||||
|
|
|
@ -21,7 +21,8 @@ class AppCImageFormatter(TarImageFormatter):
|
||||||
def stream_generator(self, tag, manifest, synthetic_image_id, layer_iterator,
|
def stream_generator(self, tag, manifest, synthetic_image_id, layer_iterator,
|
||||||
tar_stream_getter_iterator, reporter=None):
|
tar_stream_getter_iterator, reporter=None):
|
||||||
image_mtime = 0
|
image_mtime = 0
|
||||||
created = manifest.get_parsed_manifest().created_datetime
|
parsed_manifest = manifest.get_parsed_manifest()
|
||||||
|
created = parsed_manifest.created_datetime
|
||||||
if created is not None:
|
if created is not None:
|
||||||
image_mtime = calendar.timegm(created.utctimetuple())
|
image_mtime = calendar.timegm(created.utctimetuple())
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class AppCImageFormatter(TarImageFormatter):
|
||||||
# Yield the manifest.
|
# Yield the manifest.
|
||||||
aci_manifest = json.dumps(DockerV1ToACIManifestTranslator.build_manifest(
|
aci_manifest = json.dumps(DockerV1ToACIManifestTranslator.build_manifest(
|
||||||
tag,
|
tag,
|
||||||
manifest,
|
parsed_manifest,
|
||||||
synthetic_image_id
|
synthetic_image_id
|
||||||
))
|
))
|
||||||
yield self.tar_file('manifest', aci_manifest, mtime=image_mtime)
|
yield self.tar_file('manifest', aci_manifest, mtime=image_mtime)
|
||||||
|
@ -172,7 +173,7 @@ class DockerV1ToACIManifestTranslator(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_manifest(tag, manifest, synthetic_image_id):
|
def build_manifest(tag, manifest, synthetic_image_id):
|
||||||
""" Builds an ACI manifest of an existing repository image. """
|
""" Builds an ACI manifest of an existing repository image. """
|
||||||
docker_layer_data = JSONPathDict(manifest.leaf_layer.v1_metadata)
|
docker_layer_data = JSONPathDict(json.loads(manifest.leaf_layer.raw_v1_metadata))
|
||||||
config = docker_layer_data['config'] or JSONPathDict({})
|
config = docker_layer_data['config'] or JSONPathDict({})
|
||||||
|
|
||||||
namespace = tag.repository.namespace_name
|
namespace = tag.repository.namespace_name
|
||||||
|
@ -209,7 +210,7 @@ class DockerV1ToACIManifestTranslator(object):
|
||||||
"labels": [
|
"labels": [
|
||||||
{
|
{
|
||||||
"name": "version",
|
"name": "version",
|
||||||
"value": tag,
|
"value": tag.name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "arch",
|
"name": "arch",
|
||||||
|
|
|
@ -33,7 +33,7 @@ def sized_images():
|
||||||
Image(id='parentid', bytes=parent_bytes, parent_id=None, size=len(parent_bytes),
|
Image(id='parentid', bytes=parent_bytes, parent_id=None, size=len(parent_bytes),
|
||||||
config={'foo': 'bar'}),
|
config={'foo': 'bar'}),
|
||||||
Image(id='someid', bytes=image_bytes, parent_id='parentid', size=len(image_bytes),
|
Image(id='someid', bytes=image_bytes, parent_id='parentid', size=len(image_bytes),
|
||||||
config={'foo': 'childbar'}),
|
config={'foo': 'childbar', 'Entrypoint': ['hello']}),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -950,6 +950,8 @@ def test_multilayer_squashed_images(use_estimates, pusher, multi_layer_images, l
|
||||||
|
|
||||||
# Pull the squashed version.
|
# Pull the squashed version.
|
||||||
response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials)
|
response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
tar = tarfile.open(fileobj=StringIO(response.content))
|
tar = tarfile.open(fileobj=StringIO(response.content))
|
||||||
|
|
||||||
# Verify the squashed image.
|
# Verify the squashed image.
|
||||||
|
@ -1002,6 +1004,8 @@ def test_squashed_images(use_estimates, pusher, sized_images, liveserver_session
|
||||||
|
|
||||||
# Pull the squashed version.
|
# Pull the squashed version.
|
||||||
response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials)
|
response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
tar = tarfile.open(fileobj=StringIO(response.content))
|
tar = tarfile.open(fileobj=StringIO(response.content))
|
||||||
|
|
||||||
# Verify the squashed image.
|
# Verify the squashed image.
|
||||||
|
@ -1030,6 +1034,60 @@ def test_squashed_images(use_estimates, pusher, sized_images, liveserver_session
|
||||||
assert tar.extractfile('contents').read() == 'some contents'
|
assert tar.extractfile('contents').read() == 'some contents'
|
||||||
|
|
||||||
|
|
||||||
|
EXPECTED_ACI_MANIFEST = {
|
||||||
|
"acKind": "ImageManifest",
|
||||||
|
"app": {
|
||||||
|
"environment": [],
|
||||||
|
"mountPoints": [],
|
||||||
|
"group": "root",
|
||||||
|
"user": "root",
|
||||||
|
"workingDirectory": "/",
|
||||||
|
"exec": [u'/bin/sh', u'-c', u'""hello""'],
|
||||||
|
"isolators": [],
|
||||||
|
"eventHandlers": [],
|
||||||
|
"ports": [],
|
||||||
|
"annotations": [
|
||||||
|
{"name": "created", "value": ""},
|
||||||
|
{"name": "homepage", "value": "http://localhost:5000/devtable/newrepo:latest"},
|
||||||
|
{"name": "quay.io/derived-image",
|
||||||
|
"value": "035333848582cdb72d2bac4a0809bc7eed9d88004cfb3463562013fce53c2499"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"labels": [
|
||||||
|
{"name": "version", "value": "latest"},
|
||||||
|
{"name": "arch", "value": "amd64"},
|
||||||
|
{"name": "os", "value": "linux"}
|
||||||
|
],
|
||||||
|
"acVersion": "0.6.1",
|
||||||
|
"name": "localhost/devtable/newrepo",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_aci_conversion(pusher, sized_images, liveserver_session,
|
||||||
|
liveserver, registry_server_executor, app_reloader):
|
||||||
|
""" Test: Pulling of ACI converted images. """
|
||||||
|
credentials = ('devtable', 'password')
|
||||||
|
|
||||||
|
# Push an image to download.
|
||||||
|
pusher.push(liveserver_session, 'devtable', 'newrepo', 'latest', sized_images,
|
||||||
|
credentials=credentials)
|
||||||
|
|
||||||
|
# Pull the ACI version.
|
||||||
|
response = liveserver_session.get('/c1/aci/server_name/devtable/newrepo/latest/aci/linux/amd64',
|
||||||
|
auth=credentials)
|
||||||
|
assert response.status_code == 200
|
||||||
|
tar = tarfile.open(fileobj=StringIO(response.content))
|
||||||
|
assert set(tar.getnames()) == {'manifest', 'rootfs', 'rootfs/contents'}
|
||||||
|
|
||||||
|
assert tar.extractfile('rootfs/contents').read() == 'some contents'
|
||||||
|
assert json.loads(tar.extractfile('manifest').read()) == EXPECTED_ACI_MANIFEST
|
||||||
|
|
||||||
|
# Pull the ACI signature.
|
||||||
|
response = liveserver_session.get('/c1/aci/server_name/devtable/newrepo/latest/aci.asc/linux/amd64',
|
||||||
|
auth=credentials)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('push_user, push_namespace, push_repo, mount_repo_name, expected_failure', [
|
@pytest.mark.parametrize('push_user, push_namespace, push_repo, mount_repo_name, expected_failure', [
|
||||||
# Successful mount, same namespace.
|
# Successful mount, same namespace.
|
||||||
('devtable', 'devtable', 'baserepo', 'devtable/baserepo', None),
|
('devtable', 'devtable', 'baserepo', 'devtable/baserepo', None),
|
||||||
|
|
Reference in a new issue