diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index 2e3678fb846e..d6b07127727d 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -83,6 +83,17 @@ Fetch the IAM policy for a topic: >>> 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 + Publish messages to a topic --------------------------- diff --git a/gcloud/pubsub/test_topic.py b/gcloud/pubsub/test_topic.py index 595ab33284cf..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' @@ -519,6 +520,87 @@ 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 + 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' + WRITER2 = 'user:phred@example.com' + READER1 = 'serviceAccount:1234-abcdef@service.example.com' + READER2 = 'user:phred@example.com' + POLICY = { + 'etag': 'DEADBEEF', + 'version': 17, + 'bindings': [ + {'role': _OWNER_ROLE, 'members': [OWNER1, OWNER2]}, + {'role': _WRITER_ROLE, 'members': [WRITER1, WRITER2]}, + {'role': _READER_ROLE, '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.