Add a uniqueness hash to derived image storage to break caching over tags
This allows converted ACIs and squashed images to be unique based on the specified tag. Fixes #92
This commit is contained in:
parent
a33a70a419
commit
a43b741f1b
7 changed files with 119 additions and 40 deletions
|
@ -12,7 +12,7 @@ import gpgme
|
|||
|
||||
import Crypto.Random
|
||||
from cachetools import lru_cache
|
||||
from flask import request, jsonify, abort
|
||||
from flask import request, jsonify
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.ext.testing import LiveServerTestCase
|
||||
|
||||
|
@ -342,7 +342,7 @@ class V1RegistryPushMixin(V1RegistryMixin):
|
|||
push_version = 'v1'
|
||||
|
||||
def do_push(self, namespace, repository, username, password, images=None, expect_failure=None,
|
||||
munge_shas=False):
|
||||
munge_shas=False, tag_names=None):
|
||||
images = images or self._get_default_images()
|
||||
auth = (username, password)
|
||||
repo_name = _get_repo_name(namespace, repository)
|
||||
|
@ -361,7 +361,6 @@ class V1RegistryPushMixin(V1RegistryMixin):
|
|||
|
||||
for image_data in images:
|
||||
image_id = image_data['id']
|
||||
last_image_id = image_id
|
||||
|
||||
# PUT /v1/images/{imageID}/json
|
||||
image_json_data = {'id': image_id}
|
||||
|
@ -385,9 +384,10 @@ class V1RegistryPushMixin(V1RegistryMixin):
|
|||
headers={'X-Docker-Checksum-Payload': checksum},
|
||||
auth='sig')
|
||||
|
||||
|
||||
# PUT /v1/repositories/{namespace}/{repository}/tags/latest
|
||||
self.do_tag(namespace, repository, 'latest', images[-1]['id'])
|
||||
tag_names = tag_names or ['latest']
|
||||
for tag_name in tag_names:
|
||||
self.do_tag(namespace, repository, tag_name, images[-1]['id'])
|
||||
|
||||
# PUT /v1/repositories/{namespace}/{repository}/images
|
||||
self.conduct('PUT', '/v1/repositories/%s/images' % repo_name,
|
||||
|
@ -1500,19 +1500,21 @@ class TorrentV2PushTests(RegistryTestCaseMixin, TorrentTestMixin, V2RegistryPush
|
|||
class ACIConversionTests(RegistryTestCaseMixin, V1RegistryPushMixin, LiveServerTestCase):
|
||||
""" Tests for registry ACI conversion. """
|
||||
|
||||
def get_converted_image(self):
|
||||
response = self.conduct('GET', '/c1/aci/localhost:5000/devtable/newrepo/latest/aci/linux/amd64/', auth='sig')
|
||||
def get_converted_image(self, tag_name='latest'):
|
||||
url = '/c1/aci/localhost:5000/devtable/newrepo/' + tag_name + '/aci/linux/amd64/'
|
||||
response = self.conduct('GET', url, auth='sig')
|
||||
tar = tarfile.open(fileobj=StringIO(response.content))
|
||||
return tar, response.content
|
||||
|
||||
def get_converted_signature(self):
|
||||
def get_converted_signature(self, tag_name='latest'):
|
||||
counter = 0
|
||||
|
||||
# Give time for the signature to be written before continuing. As we don't exactly know when
|
||||
# this is (based on CPU conditions when the test is being run), we try a backoff and sleep
|
||||
# approach.
|
||||
while counter < 10:
|
||||
response = self.conduct('GET', '/c1/aci/localhost:5000/devtable/newrepo/latest/aci.asc/linux/amd64/', auth='sig', expected_code=None)
|
||||
url = '/c1/aci/localhost:5000/devtable/newrepo/' + tag_name + '/aci.asc/linux/amd64/'
|
||||
response = self.conduct('GET', url, auth='sig', expected_code=None)
|
||||
if response.status_code == 202 or response.status_code == 404:
|
||||
counter += 1
|
||||
time.sleep(counter * 2)
|
||||
|
@ -1574,7 +1576,8 @@ class ACIConversionTests(RegistryTestCaseMixin, V1RegistryPushMixin, LiveServerT
|
|||
"annotations": [
|
||||
{"name": "created", "value": ""},
|
||||
{"name": "homepage", "value": "http://localhost:5000/devtable/newrepo:latest"},
|
||||
{"name": "quay.io/derived-image", "value": "fa916d5ca4da5348628dfffcfc943288a0cca521cd21a6d2981a85ec1d7f7a3a"}
|
||||
{"name": "quay.io/derived-image",
|
||||
"value": "fa916d5ca4da5348628dfffcfc943288a0cca521cd21a6d2981a85ec1d7f7a3a"}
|
||||
]
|
||||
},
|
||||
"labels": [
|
||||
|
@ -1609,6 +1612,36 @@ class ACIConversionTests(RegistryTestCaseMixin, V1RegistryPushMixin, LiveServerT
|
|||
self._verify_signature(signature_again, converted)
|
||||
self._verify_signature(signature_again, converted_again)
|
||||
|
||||
def assertHasDerivedImage(self, manifest, expected):
|
||||
for annotation in manifest['app']['annotations']:
|
||||
if annotation['name'] == 'homepage':
|
||||
self.assertEqual(expected, annotation['value'])
|
||||
return
|
||||
|
||||
self.fail('Derived image annotation not found in metadata')
|
||||
|
||||
def test_conversion_different_tags(self):
|
||||
initial_images = [
|
||||
{
|
||||
'id': 'initialid',
|
||||
'contents': 'the initial image',
|
||||
},
|
||||
]
|
||||
|
||||
# Create the repo.
|
||||
self.do_push('devtable', 'newrepo', 'devtable', 'password', images=initial_images,
|
||||
tag_names=['latest', 'sometag'])
|
||||
|
||||
# Pull the squashed version of tag latest.
|
||||
latest_tar, _ = self.get_converted_image(tag_name='latest')
|
||||
latest_manifest = json.loads(latest_tar.extractfile(latest_tar.getmember('manifest')).read())
|
||||
self.assertHasDerivedImage(latest_manifest, 'http://localhost:5000/devtable/newrepo:latest')
|
||||
|
||||
# Pull the squashed version of tag sometag.
|
||||
sometag_tar, _ = self.get_converted_image(tag_name='sometag')
|
||||
sometag_manifest = json.loads(sometag_tar.extractfile(sometag_tar.getmember('manifest')).read())
|
||||
self.assertHasDerivedImage(sometag_manifest, 'http://localhost:5000/devtable/newrepo:sometag')
|
||||
|
||||
|
||||
def test_multilayer_conversion(self):
|
||||
images = [
|
||||
|
|
Reference in a new issue