def _CreateTopic(self, pubsub_topic, service_account): """Assures that a topic exists, creating it if necessary. Also adds GCS as a publisher on that bucket, if necessary. Args: pubsub_topic: name of the Cloud Pub/Sub topic to use/create. service_account: the GCS service account that needs publish permission. Returns: true if we modified IAM permissions, otherwise false. """ pubsub_api = PubsubApi(logger=self.logger) # Verify that the Pub/Sub topic exists. If it does not, create it. try: pubsub_api.GetTopic(topic_name=pubsub_topic) self.logger.debug('Topic %s already exists', pubsub_topic) except NotFoundException: self.logger.debug('Creating topic %s', pubsub_topic) pubsub_api.CreateTopic(topic_name=pubsub_topic) self.logger.info('Created Cloud Pub/Sub topic %s', pubsub_topic) # Verify that the service account is in the IAM policy. policy = pubsub_api.GetTopicIamPolicy(topic_name=pubsub_topic) binding = Binding(role='roles/pubsub.publisher', members=['serviceAccount:%s' % service_account]) # This could be more extensive. We could, for instance, check for roles # that are stronger that pubsub.publisher, like owner. We could also # recurse up the hierarchy looking to see if there are project-level # permissions. This can get very complex very quickly, as the caller # may not necessarily have access to the project-level IAM policy. # There's no danger in double-granting permission just to make sure it's # there, though. if binding not in policy.bindings: policy.bindings.append(binding) # transactional safety via etag field. pubsub_api.SetTopicIamPolicy(topic_name=pubsub_topic, policy=policy) return True else: self.logger.debug( 'GCS already has publish permission to topic %s.', pubsub_topic) return False
def setUp(self): super(TestNotificationPubSub, self).setUp() self.pubsub_api = PubsubApi(logger=logging.getLogger()) self.created_topic = None
class TestNotificationPubSub(testcase.GsUtilIntegrationTestCase): """Integration tests for notification command (the Cloud Pub/Sub parts).""" def setUp(self): super(TestNotificationPubSub, self).setUp() self.pubsub_api = PubsubApi(logger=logging.getLogger()) self.created_topic = None def tearDown(self): super(TestNotificationPubSub, self).tearDown() # Cleanup any created topics. if self.created_topic: self.pubsub_api.DeleteTopic(self.created_topic) self.created_topic = None def _RegisterDefaultTopicCreation(self, bucket_name): """Records the name of a topic we expect to create, for cleanup.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') expected_topic_name = 'projects/%s/topics/%s' % ( PopulateProjectId(None), bucket_name) self.created_topic = expected_topic_name return expected_topic_name def test_list_new_bucket(self): """Tests listing notification configs on a new bucket.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') bucket_uri = self.CreateBucket() stdout = self.RunGsUtil([ 'notification', 'list', suri(bucket_uri)], return_stdout=True) self.assertFalse(stdout) def test_delete_with_no_notifications(self): """Tests deleting all notification configs when there are none.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') bucket_uri = self.CreateBucket() stdout = self.RunGsUtil([ 'notification', 'delete', suri(bucket_uri)], return_stdout=True) self.assertFalse(stdout) def test_create_basic(self): """Tests the create command succeeds in normal circumstances.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') bucket_uri = self.CreateBucket() topic_name = self._RegisterDefaultTopicCreation(bucket_uri.bucket_name) stderr = self.RunGsUtil( ['notification', 'create', '-f', 'json', suri(bucket_uri)], return_stderr=True) self.assertIn('Created notification', stderr) self.assertIn(topic_name, stderr) def test_list_one_entry(self): """Tests notification config listing with one entry.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') bucket_uri = self.CreateBucket() bucket_name = bucket_uri.bucket_name topic_name = self._RegisterDefaultTopicCreation(bucket_uri.bucket_name) self.RunGsUtil( ['notification', 'create', '-f', 'json', '-e', 'OBJECT_FINALIZE', '-e', 'OBJECT_DELETE', '-m', 'someKey:someValue', '-p', 'somePrefix', suri(bucket_uri)], return_stderr=True) stdout = self.RunGsUtil(['notification', 'list', suri(bucket_uri)], return_stdout=True) self.assertEquals( stdout, ('projects/_/buckets/{bucket_name}/notificationConfigs/1\n' '\tCloud Pub/Sub topic: {topic_name}\n' '\tCustom attributes:\n' '\t\tsomeKey: someValue\n' '\tFilters:\n' '\t\tEvent Types: OBJECT_FINALIZE, OBJECT_DELETE\n' '\t\tObject name prefix: \'somePrefix\'\n'.format( bucket_name=bucket_name, topic_name=topic_name))) def test_delete(self): """Tests the create command succeeds in normal circumstances.""" if self.test_api == ApiSelector.XML: return unittest.skip('Notifications only work with the JSON API.') bucket_uri = self.CreateBucket() self._RegisterDefaultTopicCreation(bucket_uri.bucket_name) self.RunGsUtil(['notification', 'create', '-f', 'json', suri(bucket_uri)]) self.RunGsUtil(['notification', 'delete', suri(bucket_uri)])