Merge pull request #3249 from quay/fix-aci

Fix ACI conversion and add a registry test for it
This commit is contained in:
Joseph Schorr 2018-09-13 15:30:34 -04:00 committed by GitHub
commit 6d5489b254
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 7 deletions

View file

@ -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')

View file

@ -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",

View file

@ -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']}),
] ]

View file

@ -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),