From ce9aed9c52a2fcee6ff6de86b8a7baee4f5ac24b Mon Sep 17 00:00:00 2001 From: Rob White Date: Mon, 1 Feb 2016 21:21:00 +1100 Subject: [PATCH] Allow SNS topics to be created without subscriptions. Also added better error handling around boto calls. --- cloud/amazon/sns_topic.py | 75 +++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/cloud/amazon/sns_topic.py b/cloud/amazon/sns_topic.py index 71a66318290..5dd6d32ed59 100755 --- a/cloud/amazon/sns_topic.py +++ b/cloud/amazon/sns_topic.py @@ -132,8 +132,8 @@ import json import re try: - import boto import boto.sns + from boto.exception import BotoServerError HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -146,12 +146,15 @@ def canonicalize_endpoint(protocol, endpoint): return endpoint - -def get_all_topics(connection): +def get_all_topics(connection, module): next_token = None topics = [] while True: - response = connection.get_all_topics(next_token) + try: + response = connection.get_all_topics(next_token) + except BotoServerError, e: + module.fail_json(msg=e.message) + topics.extend(response['ListTopicsResponse']['ListTopicsResult']['Topics']) next_token = \ response['ListTopicsResponse']['ListTopicsResult']['NextToken'] @@ -160,15 +163,16 @@ def get_all_topics(connection): return [t['TopicArn'] for t in topics] -def arn_topic_lookup(connection, short_topic): +def arn_topic_lookup(connection, short_topic, module): # topic names cannot have colons, so this captures the full topic name - all_topics = get_all_topics(connection) + all_topics = get_all_topics(connection, module) lookup_topic = ':%s' % short_topic for topic in all_topics: if topic.endswith(lookup_topic): return topic return None + def main(): argument_spec = ec2_argument_spec() argument_spec.update( @@ -179,7 +183,7 @@ def main(): display_name=dict(type='str', required=False), policy=dict(type='dict', required=False), delivery_policy=dict(type='dict', required=False), - subscriptions=dict(type='list', required=False), + subscriptions=dict(default=[], type='list', required=False), purge_subscriptions=dict(type='bool', default=True), ) ) @@ -214,7 +218,7 @@ def main(): # topics cannot contain ':', so thats the decider if ':' in name: - all_topics = get_all_topics(connection) + all_topics = get_all_topics(connection, module) if name in all_topics: arn_topic = name elif state == 'absent': @@ -223,7 +227,7 @@ def main(): module.fail_json(msg="specified an ARN for a topic but it doesn't" " exist") else: - arn_topic = arn_topic_lookup(connection, name) + arn_topic = arn_topic_lookup(connection, name, module) if not arn_topic: if state == 'absent': module.exit_json(changed=False) @@ -234,15 +238,22 @@ def main(): changed=True topic_created = True - connection.create_topic(name) - arn_topic = arn_topic_lookup(connection, name) + try: + connection.create_topic(name) + except BotoServerError, e: + module.fail_json(msg=e.message) + arn_topic = arn_topic_lookup(connection, name, module) while not arn_topic: time.sleep(3) - arn_topic = arn_topic_lookup(connection, name) + arn_topic = arn_topic_lookup(connection, name, module) if arn_topic and state == "absent": if not check_mode: - connection.delete_topic(arn_topic) + try: + connection.delete_topic(arn_topic) + except BotoServerError, e: + module.fail_json(msg=e.message) + module.exit_json(changed=True) topic_attributes = connection.get_topic_attributes(arn_topic) \ @@ -252,30 +263,40 @@ def main(): changed = True attributes_set.append('display_name') if not check_mode: - connection.set_topic_attributes(arn_topic, 'DisplayName', - display_name) + try: + connection.set_topic_attributes(arn_topic, 'DisplayName', display_name) + except BotoServerError, e: + module.fail_json(msg=e.message) if policy and policy != json.loads(topic_attributes['policy']): changed = True attributes_set.append('policy') if not check_mode: - connection.set_topic_attributes(arn_topic, 'Policy', - json.dumps(policy)) + try: + connection.set_topic_attributes(arn_topic, 'Policy', json.dumps(policy)) + except BotoServerError, e: + module.fail_json(msg=e.message) if delivery_policy and ('DeliveryPolicy' not in topic_attributes or \ delivery_policy != json.loads(topic_attributes['DeliveryPolicy'])): changed = True attributes_set.append('delivery_policy') if not check_mode: - connection.set_topic_attributes(arn_topic, 'DeliveryPolicy', - json.dumps(delivery_policy)) + try: + connection.set_topic_attributes(arn_topic, 'DeliveryPolicy',json.dumps(delivery_policy)) + except BotoServerError, e: + module.fail_json(msg=e.message) next_token = None aws_subscriptions = [] while True: - response = connection.get_all_subscriptions_by_topic(arn_topic, + try: + response = connection.get_all_subscriptions_by_topic(arn_topic, next_token) + except BotoServerError, e: + module.fail_json(msg=e.message) + aws_subscriptions.extend(response['ListSubscriptionsByTopicResponse'] \ ['ListSubscriptionsByTopicResult']['Subscriptions']) next_token = response['ListSubscriptionsByTopicResponse'] \ @@ -286,6 +307,7 @@ def main(): desired_subscriptions = [(sub['protocol'], canonicalize_endpoint(sub['protocol'], sub['endpoint'])) for sub in subscriptions] + aws_subscriptions_list = [] for sub in aws_subscriptions: @@ -296,14 +318,20 @@ def main(): changed = True subscriptions_deleted.append(sub_key) if not check_mode: - connection.unsubscribe(sub['SubscriptionArn']) + try: + connection.unsubscribe(sub['SubscriptionArn']) + except BotoServerError, e: + module.fail_json(msg=e.message) for (protocol, endpoint) in desired_subscriptions: if (protocol, endpoint) not in aws_subscriptions_list: changed = True subscriptions_added.append(sub) if not check_mode: - connection.subscribe(arn_topic, protocol, endpoint) + try: + connection.subscribe(arn_topic, protocol, endpoint) + except BotoServerError, e: + module.fail_json(msg=e.message) module.exit_json(changed=changed, topic_created=topic_created, attributes_set=attributes_set, @@ -313,4 +341,5 @@ def main(): from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * -main() +if __name__ == '__main__': + main()