Repository: libcloud Updated Branches: refs/heads/trunk 29d1480ba -> a83130139
Adding support for GCE node labels. Closes #1115 Signed-off-by: Quentin Pradet <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/a8313013 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/a8313013 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/a8313013 Branch: refs/heads/trunk Commit: a831301394443399bdee613176d0e84d0154d14b Parents: 29d1480 Author: maxlip <[email protected]> Authored: Wed Sep 20 14:09:18 2017 -0700 Committer: Quentin Pradet <[email protected]> Committed: Mon Sep 25 07:52:49 2017 +0400 ---------------------------------------------------------------------- CHANGES.rst | 2 + libcloud/compute/drivers/gce.py | 60 +++++++++++++++++--- ..._us_central1_a_node_name_setLabels_post.json | 15 +++++ ...l1_a_instances_node_name_setLabels_post.json | 15 +++++ libcloud/test/compute/test_gce.py | 36 ++++++++++++ 5 files changed, 119 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/a8313013/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index bc42ce5..1644367 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,6 +16,8 @@ Compute - Update ProfitBricks driver and add support for the new API v4. (GITHUB-1103) [Nurfet Becirevic] +- [GCE] Support GCE node labels. (GITHUB-1115) [@maxlip] + Changes in Apache Libcloud 2.2.1 -------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/a8313013/libcloud/compute/drivers/gce.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py index c9c3c17..7b052a2 100644 --- a/libcloud/compute/drivers/gce.py +++ b/libcloud/compute/drivers/gce.py @@ -1919,6 +1919,29 @@ class GCENodeDriver(NodeDriver): self.connection.async_request(request, method='POST', data=body) return True + def ex_set_node_labels(self, node, labels): + """ + Set labels for the specified node. + + :keyword node: The existing target Node (instance) for the request. + :type node: ``Node`` + + :keyword labels: Set (or clear with None) labels for this node. + :type labels: ``dict`` or ``None`` + + :return: True if successful + :rtype: ``bool`` + """ + if not isinstance(node, Node): + raise ValueError("Must specify a valid libcloud node object.") + node_name = node.name + zone_name = node.extra['zone'].name + current_fp = node.extra['labelFingerprint'] + body = {'labels': labels, 'labelFingerprint': current_fp} + request = '/zones/%s/instances/%s/setLabels' % (zone_name, node_name) + self.connection.async_request(request, method='POST', data=body) + return True + def ex_get_serial_output(self, node): """ Fetch the console/serial port output from the node. @@ -3699,7 +3722,7 @@ class GCENodeDriver(NodeDriver): ex_service_accounts=None, description=None, ex_can_ip_forward=None, ex_disks_gce_struct=None, ex_nic_gce_struct=None, ex_on_host_maintenance=None, ex_automatic_restart=None, - ex_preemptible=None, ex_image_family=None): + ex_preemptible=None, ex_image_family=None, ex_labels=None): """ Create a new node and return a node object for the node. @@ -3820,6 +3843,9 @@ class GCENodeDriver(NodeDriver): to use this keyword. :type ex_image_family: ``str`` or ``None`` + :keyword ex_labels: Labels dictionary for instance. + :type ex_labels: ``dict`` or ``None`` + :return: A Node object for the new node. :rtype: :class:`Node` """ @@ -3879,7 +3905,7 @@ class GCENodeDriver(NodeDriver): ex_boot_disk, external_ip, ex_disk_type, ex_disk_auto_delete, ex_service_accounts, description, ex_can_ip_forward, ex_disks_gce_struct, ex_nic_gce_struct, ex_on_host_maintenance, - ex_automatic_restart, ex_preemptible, ex_subnetwork) + ex_automatic_restart, ex_preemptible, ex_subnetwork, ex_labels) self.connection.async_request(request, method='POST', data=node_data) return self.ex_get_node(name, location.name) @@ -4033,7 +4059,7 @@ class GCENodeDriver(NodeDriver): service_accounts=None, on_host_maintenance=None, automatic_restart=None, preemptible=None, tags=None, metadata=None, description=None, disks_gce_struct=None, nic_gce_struct=None, - use_selflinks=True): + use_selflinks=True, labels=None): """ Create the GCE instance properties needed for instance templates. @@ -4143,6 +4169,9 @@ class GCENodeDriver(NodeDriver): details. :type nic_gce_struct: ``list`` or ``None`` + :type labels: Labels dict for instance + :type labels: ``dict`` or ``None`` + :return: A dictionary formatted for use with the GCE API. :rtype: ``dict`` """ @@ -4218,6 +4247,8 @@ class GCENodeDriver(NodeDriver): if metadata: instance_properties['metadata'] = self._format_metadata( fingerprint='na', metadata=metadata) + if labels: + instance_properties['labels'] = labels if can_ip_forward: instance_properties['canIpForward'] = True @@ -4566,7 +4597,7 @@ class GCENodeDriver(NodeDriver): description=None, ex_can_ip_forward=None, ex_disks_gce_struct=None, ex_nic_gce_struct=None, ex_on_host_maintenance=None, ex_automatic_restart=None, ex_image_family=None, - ex_preemptible=None): + ex_preemptible=None, ex_labels=None): """ Create multiple nodes and return a list of Node objects. @@ -4702,6 +4733,9 @@ class GCENodeDriver(NodeDriver): to use this keyword. :type ex_image_family: ``str`` or ``None`` + :param ex_labels: Label dict for node. + :type ex_labels: ``dict`` + :return: A list of Node objects for the new nodes. :rtype: ``list`` of :class:`Node` @@ -4752,7 +4786,8 @@ class GCENodeDriver(NodeDriver): 'ex_nic_gce_struct': ex_nic_gce_struct, 'ex_on_host_maintenance': ex_on_host_maintenance, 'ex_automatic_restart': ex_automatic_restart, - 'ex_preemptible': ex_preemptible} + 'ex_preemptible': ex_preemptible, + 'ex_labels': ex_labels} # List for holding the status information for disk/node creation. status_list = [] @@ -7644,7 +7679,7 @@ class GCENodeDriver(NodeDriver): ex_service_accounts=None, description=None, ex_can_ip_forward=None, ex_disks_gce_struct=None, ex_nic_gce_struct=None, ex_on_host_maintenance=None, ex_automatic_restart=None, - ex_preemptible=None, ex_subnetwork=None): + ex_preemptible=None, ex_subnetwork=None, ex_labels=None): """ Returns a request and body to create a new node. @@ -7759,6 +7794,9 @@ class GCENodeDriver(NodeDriver): :param ex_subnetwork: The network to associate with the node. :type ex_subnetwork: :class:`GCESubnetwork` + :param ex_labels: Label dict for node. + :type ex_labels: ``dict`` or ``None`` + :return: A tuple containing a request string and a node_data dict. :rtype: ``tuple`` of ``str`` and ``dict`` """ @@ -7786,8 +7824,8 @@ class GCENodeDriver(NodeDriver): service_accounts=ex_service_accounts, on_host_maintenance=ex_on_host_maintenance, automatic_restart=ex_automatic_restart, preemptible=ex_preemptible, - tags=tags, metadata=metadata, description=description, - disks_gce_struct=ex_disks_gce_struct, + tags=tags, metadata=metadata, labels=ex_labels, + description=description, disks_gce_struct=ex_disks_gce_struct, nic_gce_struct=ex_nic_gce_struct, use_selflinks=use_selflinks) node_data['name'] = name @@ -7891,7 +7929,9 @@ class GCENodeDriver(NodeDriver): ex_on_host_maintenance=node_attrs['ex_on_host_maintenance'], ex_automatic_restart=node_attrs['ex_automatic_restart'], ex_subnetwork=node_attrs['subnetwork'], - ex_preemptible=node_attrs['ex_preemptible']) + ex_preemptible=node_attrs['ex_preemptible'], + ex_labels=node_attrs['ex_labels'] + ) try: node_res = self.connection.request(request, method='POST', @@ -8362,6 +8402,8 @@ class GCENodeDriver(NodeDriver): extra['serviceAccounts'] = node.get('serviceAccounts', []) extra['scheduling'] = node.get('scheduling', {}) extra['boot_disk'] = None + extra['labels'] = node.get('labels') + extra['labelFingerprint'] = node.get('labelFingerprint') for disk in extra['disks']: if disk.get('boot') and disk.get('type') == 'PERSISTENT': http://git-wip-us.apache.org/repos/asf/libcloud/blob/a8313013/libcloud/test/compute/fixtures/gce/operations_operation_zones_us_central1_a_node_name_setLabels_post.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_zones_us_central1_a_node_name_setLabels_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_zones_us_central1_a_node_name_setLabels_post.json new file mode 100644 index 0000000..f72e383 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_zones_us_central1_a_node_name_setLabels_post.json @@ -0,0 +1,15 @@ +{ + "endTime": "2013-06-26T10:05:07.630-07:00", + "id": "3681664092089171723", + "insertTime": "2013-06-26T10:05:03.271-07:00", + "kind": "compute#operation", + "name": "operation-setLabels_post", + "operationType": "insert", + "progress": 100, + "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/zones/us-central1-a/operations/operation-setLabels_post", + "startTime": "2013-06-26T10:05:03.315-07:00", + "status": "DONE", + "targetId": "16211908079305042870", + "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/zones/us-central1-a/instances/node-name/setLabels", + "user": "[email protected]" +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/a8313013/libcloud/test/compute/fixtures/gce/zones_us_central1_a_instances_node_name_setLabels_post.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/gce/zones_us_central1_a_instances_node_name_setLabels_post.json b/libcloud/test/compute/fixtures/gce/zones_us_central1_a_instances_node_name_setLabels_post.json new file mode 100644 index 0000000..e698a3c --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/zones_us_central1_a_instances_node_name_setLabels_post.json @@ -0,0 +1,15 @@ +{ + "endTime": "2013-06-26T10:05:07.630-07:00", + "id": "3681664092089171723", + "insertTime": "2013-06-26T10:05:03.271-07:00", + "kind": "compute#operation", + "name": "operation-setLabelspost", + "operationType": "insert", + "progress": 0, + "selfLink": "https://www.googleapis.com/compute/v1/projects/project_name/zones/us-central1-a/operations/operation-setLabels_post", + "startTime": "2013-06-26T10:05:03.315-07:00", + "status": "PENDING", + "targetId": "16211908079305042870", + "targetLink": "https://www.googleapis.com/compute/v1/projects/project_name/zones/us-central1-a/instances/node-name/setLabels", + "user": "[email protected]" +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/a8313013/libcloud/test/compute/test_gce.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_gce.py b/libcloud/test/compute/test_gce.py index 8332f08..31a1bce 100644 --- a/libcloud/test/compute/test_gce.py +++ b/libcloud/test/compute/test_gce.py @@ -1216,6 +1216,21 @@ class GCENodeDriverTest(GoogleTestCase, TestCaseMixin): self.assertEqual(data['metadata']['items'][0]['key'], 'k0') self.assertEqual(data['metadata']['items'][0]['value'], 'v0') + def test_create_node_with_labels(self): + node_name = 'node-name' + image = self.driver.ex_get_image('debian-7') + size = self.driver.ex_get_size('n1-standard-1') + zone = self.driver.ex_get_zone('us-central1-a') + + # labels is a dict + labels = {'label1': 'v1', 'label2': 'v2'} + request, data = self.driver._create_node_req(node_name, size, image, + zone, ex_labels=labels) + self.assertTrue(data['labels'] is not None) + self.assertEqual(len(data['labels']), 2) + self.assertEqual(data['labels']['label1'], 'v1') + self.assertEqual(data['labels']['label2'], 'v2') + def test_create_node_existing(self): node_name = 'libcloud-demo-europe-np-node' image = self.driver.ex_get_image('debian-7') @@ -1862,6 +1877,15 @@ class GCENodeDriverTest(GoogleTestCase, TestCaseMixin): 'value': 'v2'}]} self.driver.ex_set_node_metadata(node, gcedict) + def test_ex_set_node_labels(self): + node = self.driver.ex_get_node('node-name', 'us-central1-a') + # Test basic values + simplelabel = {'key': 'value'} + self.driver.ex_set_node_labels(node, simplelabel) + # Test multiple values + multilabels = {'item1': 'val1', 'item2': 'val2'} + self.driver.ex_set_node_labels(node, multilabels) + def test_ex_get_region(self): region_name = 'us-central1' region = self.driver.ex_get_region(region_name) @@ -2079,6 +2103,12 @@ class GCEMockHttp(MockHttp): 'zones_us_central1_a_instances_node_name_setMetadata_post.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones_us_central1_a_instances_node_name_setLabels(self, method, url, + body, headers): + body = self.fixtures.load( + 'zones_us_central1_a_instances_node_name_setLabels_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _setCommonInstanceMetadata(self, method, url, body, headers): if method == 'POST': body = self.fixtures.load('setCommonInstanceMetadata_post.json') @@ -2554,6 +2584,12 @@ class GCEMockHttp(MockHttp): 'operations_operation_zones_us_central1_a_node_name_setMetadata_post.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones_us_central1_a_operations_operation_setLabels_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_zones_us_central1_a_node_name_setLabels_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones_us_central1_a_operations_operation_zones_us_central1_a_targetInstances_post( self, method, url, body, headers): body = self.fixtures.load(
