From 3bd1b21ca9f7986be1f47c047da2d9b75fabdf9f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 13 Sep 2018 14:12:23 -0400 Subject: [PATCH] Fix ACI conversion and add a registry test for it --- endpoints/verbs/__init__.py | 12 +++++-- image/appc/__init__.py | 9 ++--- test/registry/protocol_fixtures.py | 2 +- test/registry/registry_tests.py | 58 ++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/endpoints/verbs/__init__.py b/endpoints/verbs/__init__.py index eea5f8eff..499c9d998 100644 --- a/endpoints/verbs/__init__.py +++ b/endpoints/verbs/__init__.py @@ -1,5 +1,6 @@ -import logging import hashlib +import json +import logging 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 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. operating_system = image_json.get('os', 'linux') diff --git a/image/appc/__init__.py b/image/appc/__init__.py index 404813859..18986aef5 100644 --- a/image/appc/__init__.py +++ b/image/appc/__init__.py @@ -21,7 +21,8 @@ class AppCImageFormatter(TarImageFormatter): def stream_generator(self, tag, manifest, synthetic_image_id, layer_iterator, tar_stream_getter_iterator, reporter=None): 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: image_mtime = calendar.timegm(created.utctimetuple()) @@ -32,7 +33,7 @@ class AppCImageFormatter(TarImageFormatter): # Yield the manifest. aci_manifest = json.dumps(DockerV1ToACIManifestTranslator.build_manifest( tag, - manifest, + parsed_manifest, synthetic_image_id )) yield self.tar_file('manifest', aci_manifest, mtime=image_mtime) @@ -172,7 +173,7 @@ class DockerV1ToACIManifestTranslator(object): @staticmethod def build_manifest(tag, manifest, synthetic_image_id): """ 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({}) namespace = tag.repository.namespace_name @@ -209,7 +210,7 @@ class DockerV1ToACIManifestTranslator(object): "labels": [ { "name": "version", - "value": tag, + "value": tag.name, }, { "name": "arch", diff --git a/test/registry/protocol_fixtures.py b/test/registry/protocol_fixtures.py index 04a1e0f37..7455aa811 100644 --- a/test/registry/protocol_fixtures.py +++ b/test/registry/protocol_fixtures.py @@ -33,7 +33,7 @@ def sized_images(): Image(id='parentid', bytes=parent_bytes, parent_id=None, size=len(parent_bytes), config={'foo': 'bar'}), Image(id='someid', bytes=image_bytes, parent_id='parentid', size=len(image_bytes), - config={'foo': 'childbar'}), + config={'foo': 'childbar', 'Entrypoint': ['hello']}), ] diff --git a/test/registry/registry_tests.py b/test/registry/registry_tests.py index 877a02f0d..270f1b44e 100644 --- a/test/registry/registry_tests.py +++ b/test/registry/registry_tests.py @@ -950,6 +950,8 @@ def test_multilayer_squashed_images(use_estimates, pusher, multi_layer_images, l # Pull the squashed version. response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials) + assert response.status_code == 200 + tar = tarfile.open(fileobj=StringIO(response.content)) # Verify the squashed image. @@ -1002,6 +1004,8 @@ def test_squashed_images(use_estimates, pusher, sized_images, liveserver_session # Pull the squashed version. response = liveserver_session.get('/c1/squash/devtable/newrepo/latest', auth=credentials) + assert response.status_code == 200 + tar = tarfile.open(fileobj=StringIO(response.content)) # 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' +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', [ # Successful mount, same namespace. ('devtable', 'devtable', 'baserepo', 'devtable/baserepo', None),