Skip to content

Commit

Permalink
Merge pull request #1645 from tseaver/pubsub-subscription-set_iam_policy
Browse files Browse the repository at this point in the history
Add 'Subscription.set_iam_policy' API wrapper.
  • Loading branch information
tseaver committed Mar 22, 2016
2 parents c2c9cff + 8cc3057 commit efc1d41
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 6 deletions.
12 changes: 12 additions & 0 deletions docs/pubsub-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
>>> subscription.set_iam_policy(policy) # API request
25 changes: 25 additions & 0 deletions gcloud/pubsub/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:`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
``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)
92 changes: 89 additions & 3 deletions gcloud/pubsub/test_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -555,6 +556,91 @@ 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 _OWNER_ROLE, _WRITER_ROLE, _READER_ROLE
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': _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
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):

Expand Down
6 changes: 4 additions & 2 deletions gcloud/pubsub/test_topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion gcloud/pubsub/topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit efc1d41

Please sign in to comment.