Skip to content

Commit aea1e6d

Browse files
committed
Merge pull request #1641 from tseaver/pubsub-topic-set_iam_policy
Add 'Topic.set_iam_policy' API wrapper.
2 parents b162a81 + ba7f87c commit aea1e6d

3 files changed

Lines changed: 121 additions & 3 deletions

File tree

docs/pubsub-usage.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ Fetch the IAM policy for a topic:
8383
>>> policy.readers
8484
['domain:example.com']
8585

86+
Update the IAM policy for a topic:
87+
88+
.. doctest::
89+
90+
>>> from gcloud import pubsub
91+
>>> client = pubsub.Client()
92+
>>> topic = client.topic('topic_name')
93+
>>> policy = topic.get_iam_policy() # API request
94+
>>> policy.writers.add(policy.group('editors-list@example.com'))
95+
>>> topic.set_iam_policy(policy) # API request
96+
8697

8798
Publish messages to a topic
8899
---------------------------

gcloud/pubsub/test_topic.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ def test_list_subscriptions_missing_key(self):
453453
self.assertEqual(req['query_params'], {})
454454

455455
def test_get_iam_policy_w_bound_client(self):
456+
from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE
456457
OWNER1 = 'user:phred@example.com'
457458
OWNER2 = 'group:cloud-logs@google.com'
458459
WRITER1 = 'domain:google.com'
@@ -463,9 +464,9 @@ def test_get_iam_policy_w_bound_client(self):
463464
'etag': 'DEADBEEF',
464465
'version': 17,
465466
'bindings': [
466-
{'role': 'roles/owner', 'members': [OWNER1, OWNER2]},
467-
{'role': 'roles/writer', 'members': [WRITER1, WRITER2]},
468-
{'role': 'roles/reader', 'members': [READER1, READER2]},
467+
{'role': _OWNER_ROLE, 'members': [OWNER1, OWNER2]},
468+
{'role': _WRITER_ROLE, 'members': [WRITER1, WRITER2]},
469+
{'role': _READER_ROLE, 'members': [READER1, READER2]},
469470
],
470471
}
471472
TOPIC_NAME = 'topic_name'
@@ -519,6 +520,87 @@ def test_get_iam_policy_w_alternate_client(self):
519520
self.assertEqual(req['method'], 'GET')
520521
self.assertEqual(req['path'], '/%s' % PATH)
521522

523+
def test_set_iam_policy_w_bound_client(self):
524+
from gcloud.pubsub.iam import Policy
525+
from gcloud.pubsub.iam import _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE
526+
OWNER1 = 'group:cloud-logs@google.com'
527+
OWNER2 = 'user:phred@example.com'
528+
WRITER1 = 'domain:google.com'
529+
WRITER2 = 'user:phred@example.com'
530+
READER1 = 'serviceAccount:1234-abcdef@service.example.com'
531+
READER2 = 'user:phred@example.com'
532+
POLICY = {
533+
'etag': 'DEADBEEF',
534+
'version': 17,
535+
'bindings': [
536+
{'role': _OWNER_ROLE, 'members': [OWNER1, OWNER2]},
537+
{'role': _WRITER_ROLE, 'members': [WRITER1, WRITER2]},
538+
{'role': _READER_ROLE, 'members': [READER1, READER2]},
539+
],
540+
}
541+
RESPONSE = POLICY.copy()
542+
RESPONSE['etag'] = 'ABACABAF'
543+
RESPONSE['version'] = 18
544+
TOPIC_NAME = 'topic_name'
545+
PROJECT = 'PROJECT'
546+
TOPIC_NAME = 'topic_name'
547+
PATH = 'projects/%s/topics/%s:setIamPolicy' % (PROJECT, TOPIC_NAME)
548+
549+
conn = _Connection(RESPONSE)
550+
CLIENT = _Client(project=PROJECT, connection=conn)
551+
topic = self._makeOne(TOPIC_NAME, client=CLIENT)
552+
policy = Policy('DEADBEEF', 17)
553+
policy.owners.add(OWNER1)
554+
policy.owners.add(OWNER2)
555+
policy.writers.add(WRITER1)
556+
policy.writers.add(WRITER2)
557+
policy.readers.add(READER1)
558+
policy.readers.add(READER2)
559+
560+
new_policy = topic.set_iam_policy(policy)
561+
562+
self.assertEqual(new_policy.etag, 'ABACABAF')
563+
self.assertEqual(new_policy.version, 18)
564+
self.assertEqual(sorted(new_policy.owners), [OWNER1, OWNER2])
565+
self.assertEqual(sorted(new_policy.writers), [WRITER1, WRITER2])
566+
self.assertEqual(sorted(new_policy.readers), [READER1, READER2])
567+
568+
self.assertEqual(len(conn._requested), 1)
569+
req = conn._requested[0]
570+
self.assertEqual(req['method'], 'POST')
571+
self.assertEqual(req['path'], '/%s' % PATH)
572+
self.assertEqual(req['data'], POLICY)
573+
574+
def test_set_iam_policy_w_alternate_client(self):
575+
from gcloud.pubsub.iam import Policy
576+
RESPONSE = {'etag': 'ACAB'}
577+
TOPIC_NAME = 'topic_name'
578+
PROJECT = 'PROJECT'
579+
TOPIC_NAME = 'topic_name'
580+
PATH = 'projects/%s/topics/%s:setIamPolicy' % (PROJECT, TOPIC_NAME)
581+
582+
conn1 = _Connection()
583+
conn2 = _Connection(RESPONSE)
584+
CLIENT1 = _Client(project=PROJECT, connection=conn1)
585+
CLIENT2 = _Client(project=PROJECT, connection=conn2)
586+
topic = self._makeOne(TOPIC_NAME, client=CLIENT1)
587+
588+
policy = Policy()
589+
new_policy = topic.set_iam_policy(policy, client=CLIENT2)
590+
591+
self.assertEqual(new_policy.etag, 'ACAB')
592+
self.assertEqual(new_policy.version, None)
593+
self.assertEqual(sorted(new_policy.owners), [])
594+
self.assertEqual(sorted(new_policy.writers), [])
595+
self.assertEqual(sorted(new_policy.readers), [])
596+
597+
self.assertEqual(len(conn1._requested), 0)
598+
self.assertEqual(len(conn2._requested), 1)
599+
req = conn2._requested[0]
600+
self.assertEqual(req['method'], 'POST')
601+
self.assertEqual(req['path'], '/%s' % PATH)
602+
self.assertEqual(req['data'], {})
603+
522604

523605
class TestBatch(unittest2.TestCase):
524606

gcloud/pubsub/topic.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,31 @@ def get_iam_policy(self, client=None):
278278
resp = client.connection.api_request(method='GET', path=path)
279279
return Policy.from_api_repr(resp)
280280

281+
def set_iam_policy(self, policy, client=None):
282+
"""Update the IAM policy for the topic.
283+
284+
See:
285+
https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/setIamPolicy
286+
287+
:type policy: :class:`gcloud.pubsub.iam.Policy`
288+
:param policy: the new policy, typically fetched via
289+
:meth:`getIamPolicy` and updated in place.
290+
291+
:type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
292+
:param client: the client to use. If not passed, falls back to the
293+
``client`` stored on the current batch.
294+
295+
:rtype: :class:`gcloud.pubsub.iam.Policy`
296+
:returns: updated policy created from the resource returned by the
297+
``setIamPolicy`` API request.
298+
"""
299+
client = self._require_client(client)
300+
path = '%s:setIamPolicy' % (self.path,)
301+
resource = policy.to_api_repr()
302+
resp = client.connection.api_request(
303+
method='POST', path=path, data=resource)
304+
return Policy.from_api_repr(resp)
305+
281306

282307
class Batch(object):
283308
"""Context manager: collect messages to publish via a single API call.

0 commit comments

Comments
 (0)