Skip to content

Commit

Permalink
Merge pull request #1641 from tseaver/pubsub-topic-set_iam_policy
Browse files Browse the repository at this point in the history
Add 'Topic.set_iam_policy' API wrapper.
  • Loading branch information
tseaver committed Mar 22, 2016
2 parents b162a81 + ba7f87c commit aea1e6d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 3 deletions.
11 changes: 11 additions & 0 deletions docs/pubsub-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------------------
Expand Down
88 changes: 85 additions & 3 deletions gcloud/pubsub/test_topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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):

Expand Down
25 changes: 25 additions & 0 deletions gcloud/pubsub/topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit aea1e6d

Please sign in to comment.