From d7dcdee04dd35d0e0d5da95db22fe03a734f9fb0 Mon Sep 17 00:00:00 2001 From: Tres Seaver <tseaver@palladion.com> Date: Sun, 20 Mar 2016 20:21:57 -0400 Subject: [PATCH 1/3] Add 'Topic.set_iam_policy' API wrapper. --- docs/pubsub-usage.rst | 4 +- gcloud/pubsub/test_topic.py | 80 +++++++++++++++++++++++++++++++++++++ gcloud/pubsub/topic.py | 25 ++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index 2e3678fb846e..ff76e5ee0994 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -66,7 +66,7 @@ Delete a topic: >>> topic = client.topic('topic_name') >>> topic.delete() # API request -Fetch the IAM policy for a topic: +Update the IAM policy for a topic: .. doctest:: @@ -82,6 +82,8 @@ Fetch the IAM policy for a topic: ['systemAccount:abc-1234@systemaccounts.example.com'] >>> policy.readers ['domain:example.com'] + >>> policy.writers.add(policy.group('editors-list@example.com')) + >>> topic.set_iam_policy(policy) # API request Publish messages to a topic diff --git a/gcloud/pubsub/test_topic.py b/gcloud/pubsub/test_topic.py index 595ab33284cf..51410f115f98 100644 --- a/gcloud/pubsub/test_topic.py +++ b/gcloud/pubsub/test_topic.py @@ -519,6 +519,86 @@ 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 + TOPIC_NAME = 'topic_name' + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + PATH = 'projects/%s/topics/%s:setIamPolicy' % (PROJECT, TOPIC_NAME) + + conn = _Connection(RESPONSE) + CLIENT = _Client(project=PROJECT, connection=conn) + topic = self._makeOne(TOPIC_NAME, client=CLIENT) + 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 = topic.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'} + TOPIC_NAME = 'topic_name' + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + PATH = 'projects/%s/topics/%s:setIamPolicy' % (PROJECT, TOPIC_NAME) + + conn1 = _Connection() + conn2 = _Connection(RESPONSE) + CLIENT1 = _Client(project=PROJECT, connection=conn1) + CLIENT2 = _Client(project=PROJECT, connection=conn2) + topic = self._makeOne(TOPIC_NAME, client=CLIENT1) + + policy = Policy() + new_policy = topic.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 TestBatch(unittest2.TestCase): diff --git a/gcloud/pubsub/topic.py b/gcloud/pubsub/topic.py index 2269993dc302..7b6387b1203b 100644 --- a/gcloud/pubsub/topic.py +++ b/gcloud/pubsub/topic.py @@ -278,6 +278,31 @@ def get_iam_policy(self, client=None): 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 topic. + + See: + https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/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 batch. + + :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) + class Batch(object): """Context manager: collect messages to publish via a single API call. From 0dd8f5d05828242e6a327f4fcf7eda1682a68ead Mon Sep 17 00:00:00 2001 From: Tres Seaver <tseaver@palladion.com> Date: Tue, 22 Mar 2016 12:04:10 -0400 Subject: [PATCH 2/3] Use role constants introduced in ebf5051e432af9e06bd8a41ce42a64b449755b34. --- gcloud/pubsub/test_topic.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/gcloud/pubsub/test_topic.py b/gcloud/pubsub/test_topic.py index 51410f115f98..5d58e627fffd 100644 --- a/gcloud/pubsub/test_topic.py +++ b/gcloud/pubsub/test_topic.py @@ -453,6 +453,7 @@ def test_list_subscriptions_missing_key(self): self.assertEqual(req['query_params'], {}) 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' @@ -463,9 +464,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]}, ], } TOPIC_NAME = 'topic_name' @@ -521,6 +522,7 @@ def test_get_iam_policy_w_alternate_client(self): def test_set_iam_policy_w_bound_client(self): from gcloud.pubsub.iam import Policy + from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE OWNER1 = 'group:cloud-logs@google.com' OWNER2 = 'user:phred@example.com' WRITER1 = 'domain:google.com' @@ -531,9 +533,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() From ba7f87c636b8177e8d84c25e0278e38dd14ae6a8 Mon Sep 17 00:00:00 2001 From: Tres Seaver <tseaver@palladion.com> Date: Tue, 22 Mar 2016 12:07:39 -0400 Subject: [PATCH 3/3] Split out 'Fetch' vs' Update' examples for topic IAM policy. Addresses: https://github.com/GoogleCloudPlatform/gcloud-python/pull/1641#discussion_r57014382 --- docs/pubsub-usage.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index ff76e5ee0994..d6b07127727d 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -66,7 +66,7 @@ Delete a topic: >>> topic = client.topic('topic_name') >>> topic.delete() # API request -Update the IAM policy for a topic: +Fetch the IAM policy for a topic: .. doctest:: @@ -82,6 +82,15 @@ Update the IAM policy for a topic: ['systemAccount:abc-1234@systemaccounts.example.com'] >>> policy.readers ['domain:example.com'] + +Update the IAM policy for a topic: + +.. doctest:: + + >>> from gcloud import pubsub + >>> client = pubsub.Client() + >>> topic = client.topic('topic_name') + >>> policy = topic.get_iam_policy() # API request >>> policy.writers.add(policy.group('editors-list@example.com')) >>> topic.set_iam_policy(policy) # API request