parent
6cf13c72ed
commit
47eec8fa06
1 changed files with 75 additions and 60 deletions
|
@ -78,16 +78,28 @@ class TestFeature(object):
|
|||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
|
||||
class V1RegistryMixin(object):
|
||||
def conduct(self, method, url, headers=None, data=None, auth=None, expected_code=200):
|
||||
headers = headers or {}
|
||||
headers['X-Docker-Token'] = self.docker_token
|
||||
class BaseRegistryMixin(object):
|
||||
def conduct(self, method, url, headers=None, data=None, auth=None, params=None, expected_code=200):
|
||||
params = params or {}
|
||||
params['_csrf_token'] = self.csrf_token
|
||||
|
||||
if self.signature and not auth:
|
||||
headers['Authorization'] = 'token ' + self.signature
|
||||
headers = headers or {}
|
||||
auth_tuple = None
|
||||
|
||||
if self.docker_token:
|
||||
headers['X-Docker-Token'] = self.docker_token
|
||||
|
||||
if auth == 'sig':
|
||||
if self.signature:
|
||||
headers['Authorization'] = 'token ' + self.signature
|
||||
elif auth == 'jwt':
|
||||
if self.jwt:
|
||||
headers['Authorization'] = 'Bearer ' + self.jwt
|
||||
elif auth:
|
||||
auth_tuple = auth
|
||||
|
||||
response = self.session.request(method, self.get_server_url() + url, headers=headers, data=data,
|
||||
auth=auth, params=dict(_csrf_token=self.csrf_token))
|
||||
auth=auth_tuple, params=params)
|
||||
if response.status_code != expected_code:
|
||||
print response.text
|
||||
|
||||
|
@ -100,14 +112,24 @@ class V1RegistryMixin(object):
|
|||
self.assertEquals(response.status_code, expected_code)
|
||||
return response
|
||||
|
||||
def ping(self):
|
||||
|
||||
def clearSession(self):
|
||||
self.signature = None
|
||||
self.docker_token = 'true'
|
||||
self.jwt = None
|
||||
|
||||
|
||||
class V1RegistryMixin(BaseRegistryMixin):
|
||||
def v1_ping(self):
|
||||
self.conduct('GET', '/v1/_ping')
|
||||
|
||||
|
||||
class V1RegistryPushMixin(V1RegistryMixin):
|
||||
def do_push(self, namespace, repository, username, password, images):
|
||||
auth = (username, password)
|
||||
|
||||
# Ping!
|
||||
self.ping()
|
||||
self.v1_ping()
|
||||
|
||||
# PUT /v1/repositories/{namespace}/{repository}/
|
||||
data = [{"id": image['id']} for image in images]
|
||||
|
@ -117,7 +139,8 @@ class V1RegistryMixin(object):
|
|||
|
||||
for image in images:
|
||||
# PUT /v1/images/{imageID}/json
|
||||
self.conduct('PUT', '/v1/images/%s/json' % image['id'], data=json.dumps(image))
|
||||
self.conduct('PUT', '/v1/images/%s/json' % image['id'],
|
||||
data=json.dumps(image), auth='sig')
|
||||
|
||||
# PUT /v1/images/{imageID}/layer
|
||||
tar_file_info = tarfile.TarInfo(name='image_name')
|
||||
|
@ -133,30 +156,35 @@ class V1RegistryMixin(object):
|
|||
layer_bytes = layer_data.getvalue()
|
||||
layer_data.close()
|
||||
|
||||
self.conduct('PUT', '/v1/images/%s/layer' % image['id'], data=StringIO(layer_bytes))
|
||||
self.conduct('PUT', '/v1/images/%s/layer' % image['id'],
|
||||
data=StringIO(layer_bytes), auth='sig')
|
||||
|
||||
# PUT /v1/images/{imageID}/checksum
|
||||
checksum = compute_simple(StringIO(layer_bytes), json.dumps(image))
|
||||
self.conduct('PUT', '/v1/images/%s/checksum' % image['id'],
|
||||
headers={'X-Docker-Checksum-Payload': checksum})
|
||||
headers={'X-Docker-Checksum-Payload': checksum},
|
||||
auth='sig')
|
||||
|
||||
|
||||
# PUT /v1/repositories/{namespace}/{repository}/tags/latest
|
||||
self.conduct('PUT', '/v1/repositories/%s/%s/tags/latest' % (namespace, repository),
|
||||
data='"' + images[0]['id'] + '"')
|
||||
data='"' + images[0]['id'] + '"',
|
||||
auth='sig')
|
||||
|
||||
# PUT /v1/repositories/{namespace}/{repository}/images
|
||||
self.conduct('PUT', '/v1/repositories/%s/%s/images' % (namespace, repository),
|
||||
expected_code=204)
|
||||
expected_code=204,
|
||||
auth='sig')
|
||||
|
||||
|
||||
class V1RegistryPullMixin(V1RegistryMixin):
|
||||
def do_pull(self, namespace, repository, username=None, password='password', expected_code=200):
|
||||
auth = None
|
||||
if username:
|
||||
auth = (username, password)
|
||||
|
||||
# Ping!
|
||||
self.ping()
|
||||
self.v1_ping()
|
||||
|
||||
prefix = '/v1/repositories/%s/%s/' % (namespace, repository)
|
||||
|
||||
|
@ -166,39 +194,20 @@ class V1RegistryMixin(object):
|
|||
return
|
||||
|
||||
# GET /v1/repositories/{namespace}/{repository}/
|
||||
result = json.loads(self.conduct('GET', prefix + 'tags').text)
|
||||
result = json.loads(self.conduct('GET', prefix + 'tags', auth='sig').text)
|
||||
|
||||
for image_id in result.values():
|
||||
# /v1/images/{imageID}/{ancestry, json, layer}
|
||||
image_prefix = '/v1/images/%s/' % image_id
|
||||
self.conduct('GET', image_prefix + 'ancestry')
|
||||
self.conduct('GET', image_prefix + 'json')
|
||||
self.conduct('GET', image_prefix + 'layer')
|
||||
|
||||
def clearSession(self):
|
||||
self.signature = None
|
||||
self.docker_token = 'true'
|
||||
self.conduct('GET', image_prefix + 'ancestry', auth='sig')
|
||||
self.conduct('GET', image_prefix + 'json', auth='sig')
|
||||
self.conduct('GET', image_prefix + 'layer', auth='sig')
|
||||
|
||||
|
||||
class V2RegistryMixin(object):
|
||||
def conduct(self, method, url, headers=None, params=None, data=None, auth=None, expected_code=200):
|
||||
headers = headers or {}
|
||||
params = params or {}
|
||||
params['_csrf_token'] = self.csrf_token
|
||||
|
||||
if self.docker_token and not auth:
|
||||
headers['Authorization'] = 'Bearer ' + self.docker_token
|
||||
|
||||
response = self.session.request(method, self.get_server_url() + url, headers=headers, data=data,
|
||||
auth=auth, params=params)
|
||||
if response.status_code != expected_code:
|
||||
print response.text
|
||||
|
||||
self.assertEquals(response.status_code, expected_code)
|
||||
return response
|
||||
|
||||
def ping(self):
|
||||
self.conduct('GET', '/v2/', expected_code=200 if self.docker_token else 401)
|
||||
class V2RegistryMixin(BaseRegistryMixin):
|
||||
def v2_ping(self):
|
||||
self.conduct('GET', '/v2/', expected_code=200 if self.jwt else 401, auth='jwt')
|
||||
|
||||
|
||||
def do_auth(self, username, password, namespace, repository, expected_code=200, scopes=[]):
|
||||
|
@ -215,12 +224,13 @@ class V2RegistryMixin(object):
|
|||
if expected_code == 200:
|
||||
response_json = json.loads(response.text)
|
||||
self.assertIsNotNone(response_json.get('token'))
|
||||
self.docker_token = response_json['token']
|
||||
self.jwt = response_json['token']
|
||||
|
||||
|
||||
class V2RegistryPushMixin(V2RegistryMixin):
|
||||
def do_push(self, namespace, repository, username, password, images):
|
||||
# Ping!
|
||||
self.ping()
|
||||
self.v2_ping()
|
||||
|
||||
# Auth.
|
||||
self.do_auth(username, password, namespace, repository, scopes=['push', 'pull'])
|
||||
|
@ -239,19 +249,19 @@ class V2RegistryMixin(object):
|
|||
# Layer data should not yet exist.
|
||||
checksum = 'sha256:' + hashlib.sha256(contents).hexdigest()
|
||||
self.conduct('HEAD', '/v2/%s/%s/blobs/%s' % (namespace, repository, checksum),
|
||||
expected_code=404)
|
||||
expected_code=404, auth='jwt')
|
||||
|
||||
# Start a new upload of the layer data.
|
||||
response = self.conduct('POST', '/v2/%s/%s/blobs/uploads/' % (namespace, repository),
|
||||
expected_code=202)
|
||||
expected_code=202, auth='jwt')
|
||||
|
||||
location = response.headers['Location'][len(self.get_server_url()):]
|
||||
|
||||
# PATCH the image data into the layer.
|
||||
self.conduct('PATCH', location, data=contents, expected_code=204)
|
||||
self.conduct('PATCH', location, data=contents, expected_code=204, auth='jwt')
|
||||
|
||||
# Finish the layer upload with a PUT.
|
||||
self.conduct('PUT', location, params=dict(digest=checksum), expected_code=201)
|
||||
self.conduct('PUT', location, params=dict(digest=checksum), expected_code=201, auth='jwt')
|
||||
|
||||
# Write the manifest.
|
||||
new_key = RSA.generate(2048)
|
||||
|
@ -260,16 +270,13 @@ class V2RegistryMixin(object):
|
|||
|
||||
self.conduct('PUT', '/v2/%s/%s/manifests/%s' % (namespace, repository, tag_name),
|
||||
data=manifest.bytes, expected_code=202,
|
||||
headers={'Content-Type': 'application/json'})
|
||||
headers={'Content-Type': 'application/json'}, auth='jwt')
|
||||
|
||||
|
||||
class V2RegistryPullMixin(V2RegistryMixin):
|
||||
def do_pull(self, namespace, repository, username=None, password='password', expected_code=200):
|
||||
auth = None
|
||||
if username:
|
||||
auth = (username, password)
|
||||
|
||||
# Ping!
|
||||
self.ping()
|
||||
self.v2_ping()
|
||||
|
||||
# Auth.
|
||||
self.do_auth(username, password, namespace, repository, scopes=['pull'],
|
||||
|
@ -279,16 +286,15 @@ class V2RegistryMixin(object):
|
|||
|
||||
# Retrieve the manifest for the tag.
|
||||
tag_name = 'latest'
|
||||
response = self.conduct('GET', '/v2/%s/%s/manifests/%s' % (namespace, repository, tag_name))
|
||||
response = self.conduct('GET', '/v2/%s/%s/manifests/%s' % (namespace, repository, tag_name),
|
||||
auth='jwt')
|
||||
manifest_data = json.loads(response.text)
|
||||
for layer in manifest_data['fsLayers']:
|
||||
blob_id = layer['blobSum']
|
||||
self.conduct('GET', '/v2/%s/%s/blobs/%s' % (namespace, repository, blob_id), expected_code=200)
|
||||
self.conduct('GET', '/v2/%s/%s/blobs/%s' % (namespace, repository, blob_id),
|
||||
expected_code=200, auth='jwt')
|
||||
|
||||
|
||||
def clearSession(self):
|
||||
self.docker_token = None
|
||||
|
||||
|
||||
class RegistryTestCaseMixin(object):
|
||||
maxDiff = None
|
||||
|
@ -500,12 +506,21 @@ class RegistryTestsMixin(object):
|
|||
self.do_pull('buynlarge', 'newrepo', 'devtable', 'password')
|
||||
|
||||
|
||||
class V1RegistryTests(V1RegistryMixin, RegistryTestsMixin, RegistryTestCaseMixin, LiveServerTestCase):
|
||||
class V1RegistryTests(V1RegistryPullMixin, V1RegistryPushMixin, RegistryTestsMixin,
|
||||
RegistryTestCaseMixin, LiveServerTestCase):
|
||||
""" Tests for V1 registry. """
|
||||
|
||||
class V2RegistryTests(V2RegistryMixin, RegistryTestsMixin, RegistryTestCaseMixin, LiveServerTestCase):
|
||||
class V2RegistryTests(V2RegistryPullMixin, V2RegistryPushMixin, RegistryTestsMixin,
|
||||
RegistryTestCaseMixin, LiveServerTestCase):
|
||||
""" Tests for V2 registry. """
|
||||
|
||||
class V1PushV2PullRegistryTests(V2RegistryPullMixin, V1RegistryPushMixin, RegistryTestsMixin,
|
||||
RegistryTestCaseMixin, LiveServerTestCase):
|
||||
""" Tests for V1 push, V2 pull registry. """
|
||||
|
||||
class V1PullV2PushRegistryTests(V1RegistryPullMixin, V2RegistryPushMixin, RegistryTestsMixin,
|
||||
RegistryTestCaseMixin, LiveServerTestCase):
|
||||
""" Tests for V1 pull, V2 push registry. """
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Reference in a new issue