diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py index ee88a9f179..bb8bbb3feb 100644 --- a/libcloud/compute/drivers/gce.py +++ b/libcloud/compute/drivers/gce.py @@ -2346,6 +2346,42 @@ def ex_get_zone(self, name): return None return self._to_zone(response) + def ex_copy_image(self, name, url, description=None): + """ + Copy an image to your image collection. + + :param name: The name of the image + :type name: ``str`` + + :param url: The URL to the image. The URL can start with `gs://` + :param url: ``str`` + + :param description: The description of the image + :type description: ``str`` + + :return: NodeImage object based on provided information or None if an + image with that name is not found. + :rtype: :class:`NodeImage` or ``None`` + """ + + # the URL for an image can start with gs:// + if url.startswith('gs://'): + url = url.replace('gs://', 'https://storage.googleapis.com/', 1) + + image_data = { + 'name': name, + 'description': description, + 'sourceType': 'RAW', + 'rawDisk': { + 'source': url, + }, + } + + request = '/global/images' + self.connection.async_request(request, method='POST', + data=image_data) + return self.ex_get_image(name) + def _ex_connection_class_kwargs(self): return {'auth_type': self.auth_type, 'project': self.project} diff --git a/libcloud/test/compute/fixtures/gce/global_images.json b/libcloud/test/compute/fixtures/gce/global_images.json index 5c255968f7..d583b78c4e 100644 --- a/libcloud/test/compute/fixtures/gce/global_images.json +++ b/libcloud/test/compute/fixtures/gce/global_images.json @@ -29,6 +29,21 @@ "selfLink": "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/centos-6-v20131118", "sourceType": "RAW", "status": "READY" + }, + { + "creationTimestamp": "2014-03-09T21:04:31.291-07:00", + "description": "CoreOS test image", + "id": "15196339658718959621", + "kind": "compute#image", + "name": "coreos", + "preferredKernel": "https://www.googleapis.com/compute/v1/projects/google/global/kernels/gce-v20130603", + "rawDisk": { + "containerType": "TAR", + "source": "" + }, + "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/images/coreos", + "sourceType": "RAW", + "status": "READY" } ], "kind": "compute#imageList", diff --git a/libcloud/test/compute/fixtures/gce/global_images_post.json b/libcloud/test/compute/fixtures/gce/global_images_post.json new file mode 100644 index 0000000000..91ca4d3853 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_images_post.json @@ -0,0 +1,13 @@ +{ + "id": "15196339658718959621", + "insertTime": "2014-03-09T21:04:31.228-07:00", + "kind": "compute#operation", + "name": "coreos", + "operationType": "insert", + "progress": 0, + "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation-global_image_post", + "startTime": "2014-03-09T21:04:31.291-07:00", + "status": "PENDING", + "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/images/coreos", + "user": "897001307951@developer.gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_image_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_image_post.json new file mode 100644 index 0000000000..bcd653ace0 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_image_post.json @@ -0,0 +1,15 @@ +{ + "endTime": "2014-03-09T21:04:33.291-07:00", + "id": "15196339658718959621", + "insertTime": "2014-03-09T21:04:31.228-07:00", + "kind": "compute#operation", + "name": "coreos", + "operationType": "insert", + "progress": 100, + "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/operations/operation-global_image_post", + "startTime": "2014-03-09T21:04:31.291-07:00", + "status": "DONE", + "targetId": "12551176716147327315", + "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/global/images/coreos", + "user": "897001307951@developer.gserviceaccount.com" +} diff --git a/libcloud/test/compute/test_gce.py b/libcloud/test/compute/test_gce.py index 31f35bf44e..b4abc62060 100644 --- a/libcloud/test/compute/test_gce.py +++ b/libcloud/test/compute/test_gce.py @@ -136,7 +136,7 @@ def test_ex_list_forwarding_rules(self): def test_list_images(self): local_images = self.driver.list_images() debian_images = self.driver.list_images(ex_project='debian-cloud') - self.assertEqual(len(local_images), 2) + self.assertEqual(len(local_images), 3) self.assertEqual(len(debian_images), 19) self.assertEqual(local_images[0].name, 'debian-7-wheezy-v20130617') self.assertEqual(local_images[1].name, 'centos-6-v20131118') @@ -501,6 +501,14 @@ def test_ex_get_image(self): self.assertEqual(image.name, 'debian-6-squeeze-v20130926') self.assertTrue(image.extra['description'].startswith('Debian')) + def test_ex_copy_image(self): + name = 'coreos' + url = 'gs://storage.core-os.net/coreos/amd64-generic/247.0.0/coreos_production_gce.tar.gz' + description = 'CoreOS test image' + image = self.driver.ex_copy_image(name, url, description) + self.assertEqual(image.name, name) + self.assertEqual(image.extra['description'], description) + def test_ex_get_network(self): network_name = 'lcnetwork' network = self.driver.ex_get_network(network_name) @@ -669,7 +677,10 @@ def _global_firewalls_lcfirewall(self, method, url, body, headers): return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) def _global_images(self, method, url, body, headers): - body = self.fixtures.load('global_images.json') + if method == 'POST': + body = self.fixtures.load('global_images_post.json') + else: + body = self.fixtures.load('global_images.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) def _global_networks(self, method, url, body, headers): @@ -767,6 +778,12 @@ def _global_operations_operation_global_snapshots_lcsnapshot_delete( 'operations_operation_global_snapshots_lcsnapshot_delete.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _global_operations_operation_global_image_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_global_image_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _regions_us_central1_operations_operation_regions_us_central1_addresses_lcaddress_delete( self, method, url, body, headers): body = self.fixtures.load(