def decrypt_metadata(self, image_metadata): if (self.metadata_encryption_key is not None and 'location' in image_metadata.keys() and image_metadata['location'] is not None): location = crypt.urlsafe_decrypt(self.metadata_encryption_key, image_metadata['location']) image_metadata['location'] = location return image_metadata
def test_encryption(self): # Check that original plaintext and unencrypted ciphertext match # Check keys of the three allowed lengths key_list = ["1234567890abcdef", "12345678901234567890abcd", "1234567890abcdef1234567890ABCDEF"] plaintext_list = [""] blocksize = 64 for i in range(3 * blocksize): plaintext_list.append(os.urandom(i)) for key in key_list: for plaintext in plaintext_list: ciphertext = crypt.urlsafe_encrypt(key, plaintext, blocksize) self.assertTrue(ciphertext != plaintext) text = crypt.urlsafe_decrypt(key, ciphertext) self.assertTrue(plaintext == text)
def test_remote_image(self): """Verify an image added using a 'Location' header can be retrieved""" self.cleanup() self.start_servers(**self.__dict__.copy()) # 1. POST /images with public image named Image1 image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Is-Public': 'True'} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers, body=image_data) self.assertEqual(response.status, 201) data = json.loads(content) self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) self.assertEqual(data['image']['size'], FIVE_KB) image_id1 = data['image']['id'] # 2. GET first image # Verify all information on image we just added is correct path = "http://%s:%d/v1/images/%s" args = ("0.0.0.0", self.api_port, image_id1) http = httplib2.Http() response, content = http.request(path % args, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) # 3. GET first image from registry in order to find S3 location path = "http://%s:%d/images/%s" args = ("0.0.0.0", self.registry_port, image_id1) http = httplib2.Http() response, content = http.request(path % args, 'GET') if hasattr(self, 'metadata_encryption_key'): key = self.metadata_encryption_key else: key = self.api_server.metadata_encryption_key loc = json.loads(content)['image']['location'] s3_store_location = crypt.urlsafe_decrypt(key, loc) # 4. POST /images using location generated by Image1 image_id2 = utils.generate_uuid() image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Id': image_id2, 'X-Image-Meta-Name': 'Image2', 'X-Image-Meta-Is-Public': 'True', 'X-Image-Meta-Location': s3_store_location} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) # 5. GET second image and make sure it can stream the image path = "http://%s:%d/v1/images/%s" args = ("0.0.0.0", self.api_port, image_id2) http = httplib2.Http() response, content = http.request(path % args, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) # 6. DELETE first and second images path = "http://%s:%d/v1/images/%s" args = ("0.0.0.0", self.api_port, image_id1) http = httplib2.Http() http.request(path % args, 'DELETE') path = "http://%s:%d/v1/images/%s" args = ("0.0.0.0", self.api_port, image_id2) http = httplib2.Http() http.request(path % args, 'DELETE') self.stop_servers()
def test_remote_image(self): """ Ensure we can retrieve an image that was not stored by tank itself """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # POST /images with public image named Image1 image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Is-Public': 'True'} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers, body=image_data) self.assertEqual(response.status, 201, content) data = json.loads(content) self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) self.assertEqual(data['image']['size'], FIVE_KB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) image_id = data['image']['id'] # GET image and make sure data was uploaded path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_KB).hexdigest()) # Find the location that was just added and use it as # the remote image location for the next image path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port, image_id) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) self.assertTrue('location' in data['image'].keys()) loc = data['image']['location'] if hasattr(self, 'metadata_encryption_key'): key = self.metadata_encryption_key else: key = self.api_server.metadata_encryption_key chase_location = crypt.urlsafe_decrypt(key, loc) # POST /images with public image named Image1 without uploading data image_data = "*" * FIVE_KB headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Is-Public': 'True', 'X-Image-Meta-Location': chase_location} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201, content) data = json.loads(content) self.assertEqual(data['image']['checksum'], None) self.assertEqual(data['image']['size'], 0) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) image_id2 = data['image']['id'] # GET /images/2 ensuring the data already in chase is accessible path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id2) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) self.assertEqual(response['content-length'], str(FIVE_KB)) self.assertEqual(content, "*" * FIVE_KB) self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_KB).hexdigest()) # DELETE boty images # Verify image and all chunks are gone... path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id) http = httplib2.Http() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 200) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id2) http = httplib2.Http() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 200) self.stop_servers()
def test_large_objects(self): """ We test the large object manifest code path in the Chase driver. In the case where an image file is bigger than the config variable chase_store_large_object_size, then we chunk the image into Chase, and add a manifest put_object at the end. We test that the delete of the large object cleans up all the chunks in Chase, in addition to the manifest file (LP Bug# 833285) """ self.cleanup() self.chase_store_large_object_size = 2 # In MB self.chase_store_large_object_chunk_size = 1 # In MB self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # GET /images # Verify no public images path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) self.assertEqual(content, '{"images": []}') # POST /images with public image named Image1 # attribute and no custom properties. Verify a 200 OK is returned image_data = "*" * FIVE_MB headers = {'Content-Type': 'application/octet-stream', 'X-Image-Meta-Name': 'Image1', 'X-Image-Meta-Is-Public': 'True'} path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers, body=image_data) self.assertEqual(response.status, 201, content) data = json.loads(content) self.assertEqual(data['image']['checksum'], hashlib.md5(image_data).hexdigest()) self.assertEqual(data['image']['size'], FIVE_MB) self.assertEqual(data['image']['name'], "Image1") self.assertEqual(data['image']['is_public'], True) image_id = data['image']['id'] # HEAD image # Verify image found now path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id) http = httplib2.Http() response, content = http.request(path, 'HEAD') self.assertEqual(response.status, 200) self.assertEqual(response['x-image-meta-name'], "Image1") # GET image # Verify all information on image we just added is correct path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) expected_image_headers = { 'x-image-meta-id': image_id, 'x-image-meta-name': 'Image1', 'x-image-meta-is_public': 'True', 'x-image-meta-status': 'active', 'x-image-meta-disk_format': '', 'x-image-meta-container_format': '', 'x-image-meta-size': str(FIVE_MB) } expected_std_headers = { 'content-length': str(FIVE_MB), 'content-type': 'application/octet-stream'} for expected_key, expected_value in expected_image_headers.items(): self.assertEqual(response[expected_key], expected_value, "For key '%s' expected header value '%s'. Got '%s'" % (expected_key, expected_value, response[expected_key])) for expected_key, expected_value in expected_std_headers.items(): self.assertEqual(response[expected_key], expected_value, "For key '%s' expected header value '%s'. Got '%s'" % (expected_key, expected_value, response[expected_key])) self.assertEqual(content, "*" * FIVE_MB) self.assertEqual(hashlib.md5(content).hexdigest(), hashlib.md5("*" * FIVE_MB).hexdigest()) # We test that the delete of the large object cleans up all the # chunks in Chase, in addition to the manifest file (LP Bug# 833285) # Grab the actual Chase location and query the object manifest for # the chunks/segments. We will check that the segments don't exist # after we delete the object through Tank... path = "http://%s:%d/images/%s" % ("0.0.0.0", self.registry_port, image_id) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) data = json.loads(content) image_loc = data['image']['location'] if hasattr(self, 'metadata_encryption_key'): key = self.metadata_encryption_key else: key = self.api_server.metadata_encryption_key image_loc = crypt.urlsafe_decrypt(key, image_loc) image_loc = get_location_from_uri(image_loc) chase_loc = image_loc.store_location from chase.common import client as chase_client chase_conn = chase_client.Connection( authurl=chase_loc.chase_auth_url, user=chase_loc.user, key=chase_loc.key) # Verify the object manifest exists headers = chase_conn.head_object(chase_loc.container, chase_loc.obj) manifest = headers.get('x-object-manifest') self.assertTrue(manifest is not None, "Manifest could not be found!") # Grab the segment identifiers obj_container, obj_prefix = manifest.split('/', 1) segments = [segment['name'] for segment in chase_conn.get_container(obj_container, prefix=obj_prefix)[1]] # Verify the segments exist for segment in segments: headers = chase_conn.head_object(obj_container, segment) self.assertTrue(headers.get('content-length') is not None, headers) # DELETE image # Verify image and all chunks are gone... path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, image_id) http = httplib2.Http() response, content = http.request(path, 'DELETE') self.assertEqual(response.status, 200) # Verify the segments no longer exist for segment in segments: self.assertRaises(chase_client.ClientException, chase_conn.head_object, obj_container, segment) self.stop_servers()