cloudformation_facts: don't fail on nonexistent stack - fixes #23419 (#23758)

* Allow cloudformation_facts to exit gracefully if stack does not exist

make cloudformation_facts pep8

remove from legacy files

remove unnecessary if statement

Allow cloudformation_facts to exit gracefully if stack does not exist version 2

fix documentation errors

add an example for a hard-fail if a stack doesn't exist

* Remove extra whitespace

* Use the .response attribute since .message isn't present with Python 3

* Don't fail if no stack name is provided and no stacks exist.
This commit is contained in:
Sloane Hertel 2017-10-26 15:18:31 -04:00 committed by Ryan Brown
parent 6a6ea663ea
commit c714da7fac
2 changed files with 26 additions and 14 deletions

View file

@ -91,6 +91,13 @@ EXAMPLES = '''
stack_resources: true stack_resources: true
stack_policy: true stack_policy: true
# Fail if the stack doesn't exist
- name: try to get facts about a stack but fail if it doesn't exist
cloudformation_facts:
stack_name: nonexistent-stack
all_facts: yes
failed_when: cloudformation['nonexistent-stack'] is undefined
# Example dictionary outputs for stack_outputs, stack_parameters and stack_resources: # Example dictionary outputs for stack_outputs, stack_parameters and stack_resources:
# "stack_outputs": { # "stack_outputs": {
# "ApplicationDatabaseName": "dazvlpr01xj55a.ap-southeast-2.rds.amazonaws.com", # "ApplicationDatabaseName": "dazvlpr01xj55a.ap-southeast-2.rds.amazonaws.com",
@ -113,38 +120,38 @@ EXAMPLES = '''
RETURN = ''' RETURN = '''
stack_description: stack_description:
description: Summary facts about the stack description: Summary facts about the stack
returned: always returned: if the stack exists
type: dict type: dict
stack_outputs: stack_outputs:
description: Dictionary of stack outputs keyed by the value of each output 'OutputKey' parameter and corresponding value of each description: Dictionary of stack outputs keyed by the value of each output 'OutputKey' parameter and corresponding value of each
output 'OutputValue' parameter output 'OutputValue' parameter
returned: always returned: if the stack exists
type: dict type: dict
stack_parameters: stack_parameters:
description: Dictionary of stack parameters keyed by the value of each parameter 'ParameterKey' parameter and corresponding value of description: Dictionary of stack parameters keyed by the value of each parameter 'ParameterKey' parameter and corresponding value of
each parameter 'ParameterValue' parameter each parameter 'ParameterValue' parameter
returned: always returned: if the stack exists
type: dict type: dict
stack_events: stack_events:
description: All stack events for the stack description: All stack events for the stack
returned: only if all_facts or stack_events is true returned: only if all_facts or stack_events is true and the stack exists
type: list type: list
stack_policy: stack_policy:
description: Describes the stack policy for the stack description: Describes the stack policy for the stack
returned: only if all_facts or stack_policy is true returned: only if all_facts or stack_policy is true and the stack exists
type: dict type: dict
stack_template: stack_template:
description: Describes the stack template for the stack description: Describes the stack template for the stack
returned: only if all_facts or stack_template is true returned: only if all_facts or stack_template is true and the stack exists
type: dict type: dict
stack_resource_list: stack_resource_list:
description: Describes stack resources for the stack description: Describes stack resources for the stack
returned: only if all_facts or stack_resourses is true returned: only if all_facts or stack_resourses is true and the stack exists
type: list type: list
stack_resources: stack_resources:
description: Dictionary of stack resources keyed by the value of each resource 'LogicalResourceId' parameter and corresponding value of each description: Dictionary of stack resources keyed by the value of each resource 'LogicalResourceId' parameter and corresponding value of each
resource 'PhysicalResourceId' parameter resource 'PhysicalResourceId' parameter
returned: only if all_facts or stack_resourses is true returned: only if all_facts or stack_resourses is true and the stack exists
type: dict type: dict
''' '''
@ -159,6 +166,7 @@ try:
except ImportError: except ImportError:
HAS_BOTO3 = False HAS_BOTO3 = False
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ec2 import (get_aws_connection_info, ec2_argument_spec, boto3_conn, from ansible.module_utils.ec2 import (get_aws_connection_info, ec2_argument_spec, boto3_conn,
camel_dict_to_snake_dict, AWSRetry, boto3_tag_list_to_ansible_dict) camel_dict_to_snake_dict, AWSRetry, boto3_tag_list_to_ansible_dict)
@ -191,11 +199,14 @@ class CloudFormationServiceManager:
kwargs = {'StackName': stack_name} if stack_name else {} kwargs = {'StackName': stack_name} if stack_name else {}
func = partial(self.client.describe_stacks, **kwargs) func = partial(self.client.describe_stacks, **kwargs)
response = self.paginated_response(func, 'Stacks') response = self.paginated_response(func, 'Stacks')
if response: if response is not None:
return response return response
self.module.fail_json(msg="Error describing stack(s) - an empty response was returned") self.module.fail_json(msg="Error describing stack(s) - an empty response was returned")
except Exception as e: except Exception as e:
self.module.fail_json(msg="Error describing stack(s) - " + str(e), exception=traceback.format_exc()) if 'does not exist' in e.response['Error']['Message']:
# missing stack, don't bail.
return {}
self.module.fail_json(msg="Error describing stack - " + to_native(e), exception=traceback.format_exc())
def list_stack_resources(self, stack_name): def list_stack_resources(self, stack_name):
try: try:
@ -243,6 +254,7 @@ class CloudFormationServiceManager:
return result return result
return result + self.paginated_response(func, result_key, next_token) return result + self.paginated_response(func, result_key, next_token)
def to_dict(items, key, value): def to_dict(items, key, value):
''' Transforms a list of items to a Key/Value dictionary ''' ''' Transforms a list of items to a Key/Value dictionary '''
if items: if items:
@ -250,6 +262,7 @@ def to_dict(items, key, value):
else: else:
return dict() return dict()
def main(): def main():
argument_spec = ec2_argument_spec() argument_spec = ec2_argument_spec()
argument_spec.update(dict( argument_spec.update(dict(

View file

@ -12,7 +12,6 @@ lib/ansible/modules/cloud/openstack/_os_server_actions.py
lib/ansible/modules/cloud/ovirt/_ovirt_affinity_groups.py lib/ansible/modules/cloud/ovirt/_ovirt_affinity_groups.py
lib/ansible/modules/cloud/amazon/aws_kms.py lib/ansible/modules/cloud/amazon/aws_kms.py
lib/ansible/modules/cloud/amazon/cloudformation.py lib/ansible/modules/cloud/amazon/cloudformation.py
lib/ansible/modules/cloud/amazon/cloudformation_facts.py
lib/ansible/modules/cloud/amazon/cloudfront_facts.py lib/ansible/modules/cloud/amazon/cloudfront_facts.py
lib/ansible/modules/cloud/amazon/dynamodb_table.py lib/ansible/modules/cloud/amazon/dynamodb_table.py
lib/ansible/modules/cloud/amazon/ec2_ami_copy.py lib/ansible/modules/cloud/amazon/ec2_ami_copy.py