Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #3
0
    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)
Beispiel #4
0
    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()