From 7791f28e8b30ea5eb56fad83c27e77ff95f8c3a6 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 21 Mar 2016 17:06:58 -0400 Subject: [PATCH 1/4] Add 'Subscription.set_iam_policy' API wrapper. Toward #1073. --- docs/pubsub-usage.rst | 12 +++++ gcloud/pubsub/subscription.py | 25 +++++++++ gcloud/pubsub/test_subscription.py | 84 ++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index 28a21c27ad6d..6c41f3eb7048 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -329,3 +329,15 @@ Fetch the IAM policy for a subscription ['systemAccount:abc-1234@systemaccounts.example.com'] >>> policy.readers ['domain:example.com'] + +Update the IAM policy for a subscription: + +.. doctest:: + + >>> from gcloud import pubsub + >>> client = pubsub.Client() + >>> topic = client.topic('topic_name') + >>> subscription = topic.subscription('subscription_name') + >>> policy = subscription.get_iam_policy() # API request + >>> policy.writers.add(policy.group('editors-list@example.com')) + >>> subscriptoin.set_iam_policy(policy) # API request diff --git a/gcloud/pubsub/subscription.py b/gcloud/pubsub/subscription.py index 5802266488b7..baa351699ee4 100644 --- a/gcloud/pubsub/subscription.py +++ b/gcloud/pubsub/subscription.py @@ -283,3 +283,28 @@ def get_iam_policy(self, client=None): path = '%s:getIamPolicy' % (self.path,) resp = client.connection.api_request(method='GET', path=path) return Policy.from_api_repr(resp) + + def set_iam_policy(self, policy, client=None): + """Update the IAM policy for the subscription. + + See: + https://cloud.google.com/pubsub/reference/rest/v1/projects.subscriptions/setIamPolicy + + :type policy: :class:`gcloud.pubsub.iam.Policy` + :param policy: the new policy, typically fetched via + :meth:`getIamPolicy` and updated in place. + + :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType`` + :param client: the client to use. If not passed, falls back to the + ``client`` stored on the current subscription's topic. + + :rtype: :class:`gcloud.pubsub.iam.Policy` + :returns: updated policy created from the resource returned by the + ``setIamPolicy`` API request. + """ + client = self._require_client(client) + path = '%s:setIamPolicy' % (self.path,) + resource = policy.to_api_repr() + resp = client.connection.api_request( + method='POST', path=path, data=resource) + return Policy.from_api_repr(resp) diff --git a/gcloud/pubsub/test_subscription.py b/gcloud/pubsub/test_subscription.py index 72b3d80753ce..8350556dd165 100644 --- a/gcloud/pubsub/test_subscription.py +++ b/gcloud/pubsub/test_subscription.py @@ -555,6 +555,90 @@ def test_get_iam_policy_w_alternate_client(self): self.assertEqual(req['method'], 'GET') self.assertEqual(req['path'], '/%s' % PATH) + def test_set_iam_policy_w_bound_client(self): + from gcloud.pubsub.iam import Policy + OWNER1 = 'group:cloud-logs@google.com' + OWNER2 = 'user:phred@example.com' + WRITER1 = 'domain:google.com' + WRITER2 = 'user:phred@example.com' + READER1 = 'serviceAccount:1234-abcdef@service.example.com' + READER2 = 'user:phred@example.com' + POLICY = { + 'etag': 'DEADBEEF', + 'version': 17, + 'bindings': [ + {'role': 'roles/owner', 'members': [OWNER1, OWNER2]}, + {'role': 'roles/writer', 'members': [WRITER1, WRITER2]}, + {'role': 'roles/reader', 'members': [READER1, READER2]}, + ], + } + RESPONSE = POLICY.copy() + RESPONSE['etag'] = 'ABACABAF' + RESPONSE['version'] = 18 + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + SUB_NAME = 'sub_name' + PATH = 'projects/%s/subscriptions/%s:setIamPolicy' % ( + PROJECT, SUB_NAME) + + conn = _Connection(RESPONSE) + CLIENT = _Client(project=PROJECT, connection=conn) + topic = _Topic(TOPIC_NAME, client=CLIENT) + subscription = self._makeOne(SUB_NAME, topic) + policy = Policy('DEADBEEF', 17) + policy.owners.add(OWNER1) + policy.owners.add(OWNER2) + policy.writers.add(WRITER1) + policy.writers.add(WRITER2) + policy.readers.add(READER1) + policy.readers.add(READER2) + + new_policy = subscription.set_iam_policy(policy) + + self.assertEqual(new_policy.etag, 'ABACABAF') + self.assertEqual(new_policy.version, 18) + self.assertEqual(sorted(new_policy.owners), [OWNER1, OWNER2]) + self.assertEqual(sorted(new_policy.writers), [WRITER1, WRITER2]) + self.assertEqual(sorted(new_policy.readers), [READER1, READER2]) + + self.assertEqual(len(conn._requested), 1) + req = conn._requested[0] + self.assertEqual(req['method'], 'POST') + self.assertEqual(req['path'], '/%s' % PATH) + self.assertEqual(req['data'], POLICY) + + def test_set_iam_policy_w_alternate_client(self): + from gcloud.pubsub.iam import Policy + RESPONSE = {'etag': 'ACAB'} + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + SUB_NAME = 'sub_name' + PATH = 'projects/%s/subscriptions/%s:setIamPolicy' % ( + PROJECT, SUB_NAME) + + conn1 = _Connection() + conn2 = _Connection(RESPONSE) + CLIENT1 = _Client(project=PROJECT, connection=conn1) + CLIENT2 = _Client(project=PROJECT, connection=conn2) + topic = _Topic(TOPIC_NAME, client=CLIENT1) + subscription = self._makeOne(SUB_NAME, topic) + + policy = Policy() + new_policy = subscription.set_iam_policy(policy, client=CLIENT2) + + self.assertEqual(new_policy.etag, 'ACAB') + self.assertEqual(new_policy.version, None) + self.assertEqual(sorted(new_policy.owners), []) + self.assertEqual(sorted(new_policy.writers), []) + self.assertEqual(sorted(new_policy.readers), []) + + self.assertEqual(len(conn1._requested), 0) + self.assertEqual(len(conn2._requested), 1) + req = conn2._requested[0] + self.assertEqual(req['method'], 'POST') + self.assertEqual(req['path'], '/%s' % PATH) + self.assertEqual(req['data'], {}) + class _Connection(object): From 054e934bd52822f4e4144a4afba2670acf53c99d Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 22 Mar 2016 15:50:11 -0400 Subject: [PATCH 2/4] Typo fix. Addresses: https://github.com/GoogleCloudPlatform/gcloud-python/pull/1645#discussion_r57055800. --- docs/pubsub-usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index 6c41f3eb7048..d3dbba1a94ff 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -340,4 +340,4 @@ Update the IAM policy for a subscription: >>> subscription = topic.subscription('subscription_name') >>> policy = subscription.get_iam_policy() # API request >>> policy.writers.add(policy.group('editors-list@example.com')) - >>> subscriptoin.set_iam_policy(policy) # API request + >>> subscription.set_iam_policy(policy) # API request From 45fcfdf3aa43a32a852add819377f6368573d6fa Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 22 Mar 2016 15:51:52 -0400 Subject: [PATCH 3/4] Fix spelling of related method in docstring. Addresses: https://github.com/GoogleCloudPlatform/gcloud-python/pull/1645#discussion_r57055877 --- gcloud/pubsub/subscription.py | 2 +- gcloud/pubsub/topic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gcloud/pubsub/subscription.py b/gcloud/pubsub/subscription.py index baa351699ee4..56fb0d337f49 100644 --- a/gcloud/pubsub/subscription.py +++ b/gcloud/pubsub/subscription.py @@ -292,7 +292,7 @@ def set_iam_policy(self, policy, client=None): :type policy: :class:`gcloud.pubsub.iam.Policy` :param policy: the new policy, typically fetched via - :meth:`getIamPolicy` and updated in place. + :meth:`get_iam_policy` and updated in place. :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the diff --git a/gcloud/pubsub/topic.py b/gcloud/pubsub/topic.py index e4795b20a96e..9f1834bbd43f 100644 --- a/gcloud/pubsub/topic.py +++ b/gcloud/pubsub/topic.py @@ -286,7 +286,7 @@ def set_iam_policy(self, policy, client=None): :type policy: :class:`gcloud.pubsub.iam.Policy` :param policy: the new policy, typically fetched via - :meth:`getIamPolicy` and updated in place. + :meth:`get_iam_policy` and updated in place. :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType`` :param client: the client to use. If not passed, falls back to the From 8cc3057d2bc8a77dbfef8c15889dc97b2f5e20a8 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 22 Mar 2016 15:55:53 -0400 Subject: [PATCH 4/4] Use role constants introduced in ebf5051e432af9e06bd8a41ce42a64b449755b34. --- gcloud/pubsub/test_subscription.py | 14 ++++++++------ gcloud/pubsub/test_topic.py | 6 ++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/gcloud/pubsub/test_subscription.py b/gcloud/pubsub/test_subscription.py index 8350556dd165..9d18d28eb3fe 100644 --- a/gcloud/pubsub/test_subscription.py +++ b/gcloud/pubsub/test_subscription.py @@ -485,6 +485,7 @@ def test_delete_w_alternate_client(self): self.assertEqual(req['path'], '/%s' % SUB_PATH) def test_get_iam_policy_w_bound_client(self): + from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE OWNER1 = 'user:phred@example.com' OWNER2 = 'group:cloud-logs@google.com' WRITER1 = 'domain:google.com' @@ -495,9 +496,9 @@ def test_get_iam_policy_w_bound_client(self): 'etag': 'DEADBEEF', 'version': 17, 'bindings': [ - {'role': 'roles/owner', 'members': [OWNER1, OWNER2]}, - {'role': 'roles/writer', 'members': [WRITER1, WRITER2]}, - {'role': 'roles/reader', 'members': [READER1, READER2]}, + {'role': _OWNER_ROLE, 'members': [OWNER1, OWNER2]}, + {'role': _WRITER_ROLE, 'members': [WRITER1, WRITER2]}, + {'role': _READER_ROLE, 'members': [READER1, READER2]}, ], } PROJECT = 'PROJECT' @@ -556,6 +557,7 @@ def test_get_iam_policy_w_alternate_client(self): self.assertEqual(req['path'], '/%s' % PATH) def test_set_iam_policy_w_bound_client(self): + from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE from gcloud.pubsub.iam import Policy OWNER1 = 'group:cloud-logs@google.com' OWNER2 = 'user:phred@example.com' @@ -567,9 +569,9 @@ def test_set_iam_policy_w_bound_client(self): 'etag': 'DEADBEEF', 'version': 17, 'bindings': [ - {'role': 'roles/owner', 'members': [OWNER1, OWNER2]}, - {'role': 'roles/writer', 'members': [WRITER1, WRITER2]}, - {'role': 'roles/reader', 'members': [READER1, READER2]}, + {'role': _OWNER_ROLE, 'members': [OWNER1, OWNER2]}, + {'role': _WRITER_ROLE, 'members': [WRITER1, WRITER2]}, + {'role': _READER_ROLE, 'members': [READER1, READER2]}, ], } RESPONSE = POLICY.copy() diff --git a/gcloud/pubsub/test_topic.py b/gcloud/pubsub/test_topic.py index bb63a0c46834..fc9684c5f829 100644 --- a/gcloud/pubsub/test_topic.py +++ b/gcloud/pubsub/test_topic.py @@ -602,11 +602,12 @@ def test_set_iam_policy_w_alternate_client(self): self.assertEqual(req['data'], {}) def test_test_iam_permissions_w_bound_client(self): + from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE TOPIC_NAME = 'topic_name' PROJECT = 'PROJECT' PATH = 'projects/%s/topics/%s:testIamPermissions' % ( PROJECT, TOPIC_NAME) - ROLES = ['roles/reader', 'roles/writer', 'roles/owner'] + ROLES = [_READER_ROLE, _WRITER_ROLE, _OWNER_ROLE] REQUESTED = { 'permissions': ROLES, } @@ -627,11 +628,12 @@ def test_test_iam_permissions_w_bound_client(self): self.assertEqual(req['data'], REQUESTED) def test_test_iam_permissions_w_alternate_client(self): + from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE TOPIC_NAME = 'topic_name' PROJECT = 'PROJECT' PATH = 'projects/%s/topics/%s:testIamPermissions' % ( PROJECT, TOPIC_NAME) - ROLES = ['roles/reader', 'roles/writer', 'roles/owner'] + ROLES = [_READER_ROLE, _WRITER_ROLE, _OWNER_ROLE] REQUESTED = { 'permissions': ROLES, }