[cloud][tests] fixtures for placebo to test AWS modules using boto3 (#21253)

* [cloud][tests] Create fixtures for using placebo to test boto3-using modules

* Use pytest's importorskip instead of manually skipping on missing deps

* Fix imports in cloudformation module

* Delete unused code

* Add maybe_sleep fixtures to speed up recorded test runs

* Build basic placebo-CFN tests

* Commit placebo recordings of basic stack operations

* Add placebo to test-requires

* Allow unit tests to run regardless of environment by setting a default region

* Use explicit relative import for Python 3 compat

* Use __name__ attribute that works on Python 2 and 3
pull/23196/head
Ryan Brown 7 years ago committed by GitHub
parent 5591d639f5
commit 2196fa0e95

@ -237,6 +237,8 @@ except ImportError:
# import a class, otherwise we'll use a fully qualified path
from ansible.module_utils.ec2 import AWSRetry
from ansible.module_utils.basic import AnsibleModule
import ansible.module_utils.ec2
def boto_exception(err):
'''generic error message handler'''
@ -250,17 +252,6 @@ def boto_exception(err):
return error
def boto_version_required(version_tuple):
parts = boto3.__version__.split('.')
boto_version = []
try:
for part in parts:
boto_version.append(int(part))
except ValueError:
boto_version.append(-1)
return tuple(boto_version) >= tuple(version_tuple)
def get_stack_events(cfn, stack_name):
'''This event data was never correct, it worked as a side effect. So the v2.3 format is different.'''
ret = {'events':[], 'log':[]}
@ -507,10 +498,6 @@ def main():
'directly to the CloudFormation API.')]
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import AnsibleModule
import ansible.module_utils.ec2
if __name__ == '__main__':
main()

@ -1,5 +1,6 @@
boto
boto3
placebo
jinja2
mock
nose

@ -0,0 +1,203 @@
import os
import time
import mock
import pytest
boto3 = pytest.importorskip("boto3")
botocore = pytest.importorskip("botocore")
placebo = pytest.importorskip("placebo")
"""
Using Placebo to test modules using boto3:
This is an example test, using the placeboify fixture to test that a module
will fail if resources it depends on don't exist.
> from placebo_fixtures import placeboify, scratch_vpc
>
> def test_create_with_nonexistent_launch_config(placeboify):
> connection = placeboify.client('autoscaling')
> module = FakeModule('test-asg-created', None, min_size=0, max_size=0, desired_capacity=0)
> with pytest.raises(FailJSON) as excinfo:
> asg_module.create_autoscaling_group(connection, module)
> .... asserts based on module state/exceptions ....
In more advanced cases, use unrecorded resource fixtures to fill in ARNs/IDs of
things modules depend on, such as:
> def test_create_in_vpc(placeboify, scratch_vpc):
> connection = placeboify.client('autoscaling')
> module = FakeModule(name='test-asg-created',
> min_size=0, max_size=0, desired_capacity=0,
> availability_zones=[s['az'] for s in scratch_vpc['subnets']],
> vpc_zone_identifier=[s['id'] for s in scratch_vpc['subnets']],
> )
> ..... so on and so forth ....
"""
@pytest.fixture
def placeboify(request, monkeypatch):
"""This fixture puts a recording/replaying harness around `boto3_conn`
Placeboify patches the `boto3_conn` function in ec2 module_utils to return
a boto3 session that in recording or replaying mode, depending on the
PLACEBO_RECORD environment variable. Unset PLACEBO_RECORD (the common case
for just running tests) will put placebo in replay mode, set PLACEBO_RECORD
to any value to turn off replay & operate on real AWS resources.
The recorded sessions are stored in the test file's directory, under the
namespace `placebo_recordings/{testfile name}/{test function name}` to
distinguish them.
"""
session = boto3.Session(region_name='us-west-2')
recordings_path = os.path.join(
request.fspath.dirname,
'placebo_recordings',
request.fspath.basename.replace('.py', ''),
request.function.__name__
# remove the test_ prefix from the function & file name
).replace('test_', '')
try:
# make sure the directory for placebo test recordings is available
os.makedirs(recordings_path)
except OSError as e:
if e.errno != os.errno.EEXIST:
raise
pill = placebo.attach(session, data_path=recordings_path)
if os.getenv('PLACEBO_RECORD'):
pill.record()
else:
pill.playback()
def boto3_middleman_connection(module, conn_type, resource, region='us-west-2', **kwargs):
if conn_type != 'client':
#TODO support resource-based connections
raise ValueError('Mocker only supports client, not %s' % conn_type)
return session.client(resource, region_name=region)
import ansible.module_utils.ec2
monkeypatch.setattr(
ansible.module_utils.ec2,
'boto3_conn',
boto3_middleman_connection,
)
yield session
# tear down
pill.stop()
@pytest.fixture(scope='module')
def basic_launch_config():
"""Create an EC2 launch config whose creation *is not* recorded and return its name
This fixture is module-scoped, since launch configs are immutable and this
can be reused for many tests.
"""
if not os.getenv('PLACEBO_RECORD'):
yield 'pytest_basic_lc'
return
# use a *non recording* session to make the launch config
# since that's a prereq of the ec2_asg module, and isn't what
# we're testing.
asg = boto3.client('autoscaling')
asg.create_launch_configuration(
LaunchConfigurationName='pytest_basic_lc',
ImageId='ami-9be6f38c', # Amazon Linux 2016.09 us-east-1 AMI, can be any valid AMI
SecurityGroups=[],
UserData='#!/bin/bash\necho hello world',
InstanceType='t2.micro',
InstanceMonitoring={'Enabled': False},
AssociatePublicIpAddress=True
)
yield 'pytest_basic_lc'
try:
asg.delete_launch_configuration(LaunchConfigurationName='pytest_basic_lc')
except botocore.exceptions.ClientError as e:
if 'not found' in e.message:
return
raise
@pytest.fixture(scope='module')
def scratch_vpc():
if not os.getenv('PLACEBO_RECORD'):
yield {
'vpc_id': 'vpc-123456',
'cidr_range': '10.0.0.0/16',
'subnets': [
{
'id': 'subnet-123456',
'az': 'us-east-1d',
},
{
'id': 'subnet-654321',
'az': 'us-east-1e',
},
]
}
return
# use a *non recording* session to make the base VPC and subnets
ec2 = boto3.client('ec2')
vpc_resp = ec2.create_vpc(
CidrBlock='10.0.0.0/16',
AmazonProvidedIpv6CidrBlock=False,
)
subnets = (
ec2.create_subnet(
VpcId=vpc_resp['Vpc']['VpcId'],
CidrBlock='10.0.0.0/24',
),
ec2.create_subnet(
VpcId=vpc_resp['Vpc']['VpcId'],
CidrBlock='10.0.1.0/24',
)
)
time.sleep(3)
yield {
'vpc_id': vpc_resp['Vpc']['VpcId'],
'cidr_range': '10.0.0.0/16',
'subnets': [
{
'id': s['Subnet']['SubnetId'],
'az': s['Subnet']['AvailabilityZone'],
} for s in subnets
]
}
try:
for s in subnets:
try:
ec2.delete_subnet(SubnetId=s['Subnet']['SubnetId'])
except botocore.exceptions.ClientError as e:
if 'not found' in e.message:
continue
raise
ec2.delete_vpc(VpcId=vpc_resp['Vpc']['VpcId'])
except botocore.exceptions.ClientError as e:
if 'not found' in e.message:
return
raise
@pytest.fixture(scope='module')
def maybe_sleep():
"""If placebo is reading saved sessions, make sleep always take 0 seconds.
AWS modules often perform polling or retries, but when using recorded
sessions there's no reason to wait. We can still exercise retry and other
code paths without waiting for wall-clock time to pass."""
if not os.getenv('PLACEBO_RECORD'):
p = mock.patch('time.sleep', return_value=None)
p.start()
yield
p.stop()
else:
yield

@ -0,0 +1,17 @@
{
"status_code": 200,
"data": {
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "b5c37a4c-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "b5c37a4c-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:14 GMT",
"content-length": "393",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,16 @@
{
"status_code": 200,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d86831b6-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d86831b6-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:23:12 GMT",
"content-length": "212",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "b601e2e5-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "b601e2e5-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:14 GMT",
"content-length": "1097",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,100 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_COMPLETE-2017-03-22T16:23:01.740Z",
"ResourceStatus": "CREATE_COMPLETE",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 1,
"microsecond": 740000,
"year": 2017,
"day": 22,
"minute": 23
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d215d8ba-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d215d8ba-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "3144",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:23:01 GMT"
}
}
}
}

@ -0,0 +1,100 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_COMPLETE-2017-03-22T16:23:01.740Z",
"ResourceStatus": "CREATE_COMPLETE",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 1,
"microsecond": 740000,
"year": 2017,
"day": 22,
"minute": 23
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d532cfbf-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d532cfbf-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "3144",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:23:07 GMT"
}
}
}
}

@ -0,0 +1,119 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "d5bff430-0f1b-11e7-95e6-503ac931688d",
"ResourceStatus": "CREATE_COMPLETE",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 8,
"microsecond": 234000,
"year": 2017,
"day": 22,
"minute": 23
},
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_COMPLETE-2017-03-22T16:23:01.740Z",
"ResourceStatus": "CREATE_COMPLETE",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 1,
"microsecond": 740000,
"year": 2017,
"day": 22,
"minute": 23
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d855940c-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d855940c-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "3844",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:23:12 GMT"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "b9201348-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "b9201348-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:19 GMT",
"content-length": "1097",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "bc3f063b-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "bc3f063b-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:25 GMT",
"content-length": "1097",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "bf5d3682-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "bf5d3682-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:30 GMT",
"content-length": "1097",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c27bb4ee-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c27bb4ee-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:35 GMT",
"content-length": "1097",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,58 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c59c2e1c-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c59c2e1c-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:40 GMT",
"content-length": "1711",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,80 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c8bc5a17-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c8bc5a17-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "2471",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:22:46 GMT"
}
}
}
}

@ -0,0 +1,80 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "cbdb4cd1-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "cbdb4cd1-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "2471",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:22:51 GMT"
}
}
}
}

@ -0,0 +1,80 @@
{
"status_code": 200,
"data": {
"StackEvents": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:41.153Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 41,
"microsecond": 153000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "Resource creation Initiated",
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "ansible-test-basic-yaml-mybucket-ia65cw1pppku",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "MyBucket-CREATE_IN_PROGRESS-2017-03-22T16:22:39.863Z",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::S3::Bucket",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 39,
"microsecond": 863000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"ResourceProperties": "{}\n",
"PhysicalResourceId": "",
"LogicalResourceId": "MyBucket"
},
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"EventId": "b5caf440-0f1b-11e7-8ebb-503aca41a035",
"ResourceStatus": "CREATE_IN_PROGRESS",
"ResourceType": "AWS::CloudFormation::Stack",
"Timestamp": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"ResourceStatusReason": "User Initiated",
"StackName": "ansible-test-basic-yaml",
"PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"LogicalResourceId": "ansible-test-basic-yaml"
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "cef9cb47-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "cef9cb47-0f1b-11e7-8ccd-097b5e15ba0c",
"vary": "Accept-Encoding",
"content-length": "2471",
"content-type": "text/xml",
"date": "Wed, 22 Mar 2017 16:22:55 GMT"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"StackStatusReason": "User Initiated",
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "b5f14113-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "b5f14113-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:14 GMT",
"content-length": "869",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d205fa33-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d205fa33-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:23:01 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d52254fb-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d52254fb-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:23:07 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,43 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"Outputs": [
{
"OutputKey": "TheName",
"OutputValue": "ansible-test-basic-yaml-mybucket-ia65cw1pppku"
}
],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "d83fe923-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "d83fe923-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:23:11 GMT",
"content-length": "1008",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"StackStatusReason": "User Initiated",
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "b90efc42-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "b90efc42-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:19 GMT",
"content-length": "869",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"StackStatusReason": "User Initiated",
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "bc2d2be9-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "bc2d2be9-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:24 GMT",
"content-length": "869",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"StackStatusReason": "User Initiated",
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "bf4c6d9c-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "bf4c6d9c-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:30 GMT",
"content-length": "869",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,38 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"StackStatusReason": "User Initiated",
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c26a28ba-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c26a28ba-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:35 GMT",
"content-length": "869",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c589907a-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c589907a-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:40 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "c8aacde5-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "c8aacde5-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:46 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "cbca0fb7-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "cbca0fb7-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:51 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,37 @@
{
"status_code": 200,
"data": {
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/b5ca09e0-0f1b-11e7-8ebb-503aca41a035",
"Description": "Basic template that creates an S3 bucket",
"Tags": [],
"CreationTime": {
"hour": 16,
"__class__": "datetime",
"month": 3,
"second": 14,
"microsecond": 662000,
"year": 2017,
"day": 22,
"minute": 22
},
"StackName": "ansible-test-basic-yaml",
"NotificationARNs": [],
"StackStatus": "CREATE_IN_PROGRESS",
"DisableRollback": false
}
],
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 200,
"RequestId": "cee97795-0f1b-11e7-8ccd-097b5e15ba0c",
"HTTPHeaders": {
"x-amzn-requestid": "cee97795-0f1b-11e7-8ccd-097b5e15ba0c",
"date": "Wed, 22 Mar 2017 16:22:55 GMT",
"content-length": "807",
"content-type": "text/xml"
}
}
}
}

@ -0,0 +1,22 @@
{
"status_code": 400,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 400,
"RequestId": "d9042387-0f1b-11e7-8b5d-eb16a54c5dc1",
"HTTPHeaders": {
"x-amzn-requestid": "d9042387-0f1b-11e7-8b5d-eb16a54c5dc1",
"date": "Wed, 22 Mar 2017 16:23:13 GMT",
"content-length": "301",
"content-type": "text/xml",
"connection": "close"
}
},
"Error": {
"Message": "Stack [ansible-test-nonexist] does not exist",
"Code": "ValidationError",
"Type": "Sender"
}
}
}

@ -0,0 +1,22 @@
{
"status_code": 400,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 400,
"RequestId": "d9408f71-0f1b-11e7-8267-e1c161d41249",
"HTTPHeaders": {
"x-amzn-requestid": "d9408f71-0f1b-11e7-8267-e1c161d41249",
"date": "Wed, 22 Mar 2017 16:23:13 GMT",
"content-length": "301",
"content-type": "text/xml",
"connection": "close"
}
},
"Error": {
"Message": "Stack [ansible-test-nonexist] does not exist",
"Code": "ValidationError",
"Type": "Sender"
}
}
}

@ -0,0 +1,22 @@
{
"status_code": 400,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 400,
"RequestId": "d8c9b229-0f1b-11e7-9f76-f9c3e709b60b",
"HTTPHeaders": {
"x-amzn-requestid": "d8c9b229-0f1b-11e7-9f76-f9c3e709b60b",
"date": "Wed, 22 Mar 2017 16:23:12 GMT",
"content-length": "307",
"content-type": "text/xml",
"connection": "close"
}
},
"Error": {
"Message": "Stack with id ansible-test-nonexist does not exist",
"Code": "ValidationError",
"Type": "Sender"
}
}
}

@ -0,0 +1,22 @@
{
"status_code": 400,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 400,
"RequestId": "d97a3d8c-0f1b-11e7-8683-29a71b5248c0",
"HTTPHeaders": {
"x-amzn-requestid": "d97a3d8c-0f1b-11e7-8683-29a71b5248c0",
"date": "Wed, 22 Mar 2017 16:23:13 GMT",
"content-length": "307",
"content-type": "text/xml",
"connection": "close"
}
},
"Error": {
"Message": "Stack with id ansible-test-nonexist does not exist",
"Code": "ValidationError",
"Type": "Sender"
}
}
}

@ -0,0 +1,22 @@
{
"status_code": 400,
"data": {
"ResponseMetadata": {
"RetryAttempts": 0,
"HTTPStatusCode": 400,
"RequestId": "b57ef7f9-0f1b-11e7-909a-5b7ba00cf4e6",
"HTTPHeaders": {
"x-amzn-requestid": "b57ef7f9-0f1b-11e7-909a-5b7ba00cf4e6",
"date": "Wed, 22 Mar 2017 16:22:13 GMT",
"content-length": "320",
"content-type": "text/xml",
"connection": "close"
}
},
"Error": {
"Message": "Template format error: JSON not well-formed. (line 4, column 4)",
"Code": "ValidationError",
"Type": "Sender"
}
}
}

@ -0,0 +1,113 @@
# (c) 2017 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import pytest
from mock import patch
from . placebo_fixtures import placeboify, maybe_sleep
from ansible.modules.cloud.amazon import cloudformation as cfn_module
basic_yaml_tpl = """
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Basic template that creates an S3 bucket'
Resources:
MyBucket:
Type: "AWS::S3::Bucket"
Outputs:
TheName:
Value:
!Ref MyBucket
"""
bad_json_tpl = """{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Broken template, no comma here ->"
"Resources": {
"MyBucket": {
"Type": "AWS::S3::Bucket"
}
}
}"""
class FakeModule(object):
def __init__(self, **kwargs):
self.params = kwargs
def fail_json(self, *args, **kwargs):
self.exit_args = args
self.exit_kwargs = kwargs
raise Exception('FAIL')
def exit_json(self, *args, **kwargs):
self.exit_args = args
self.exit_kwargs = kwargs
raise Exception('EXIT')
def test_invalid_template_json(placeboify):
connection = placeboify.client('cloudformation')
params = {
'StackName': 'ansible-test-wrong-json',
'TemplateBody': bad_json_tpl,
}
m = FakeModule(disable_rollback=False)
with pytest.raises(Exception, message='Malformed JSON should cause the test to fail') as exc_info:
cfn_module.create_stack(m, params, connection)
assert exc_info.match('FAIL')
assert "ValidationError" in m.exit_kwargs['msg']
def test_basic_s3_stack(maybe_sleep, placeboify):
connection = placeboify.client('cloudformation')
params = {
'StackName': 'ansible-test-basic-yaml',
'TemplateBody': basic_yaml_tpl,
}
m = FakeModule(disable_rollback=False)
result = cfn_module.create_stack(m, params, connection)
assert result['changed']
assert len(result['events']) > 1
# require that the final recorded stack state was CREATE_COMPLETE
# events are retrieved newest-first, so 0 is the latest
assert 'CREATE_COMPLETE' in result['events'][0]
connection.delete_stack(StackName='ansible-test-basic-yaml')
def test_delete_nonexistent_stack(maybe_sleep, placeboify):
connection = placeboify.client('cloudformation')
result = cfn_module.stack_operation(connection, 'ansible-test-nonexist', 'DELETE')
assert result['changed']
assert 'Stack does not exist.' in result['log']
def test_get_nonexistent_stack(placeboify):
connection = placeboify.client('cloudformation')
assert cfn_module.get_stack_facts(connection, 'ansible-test-nonexist') is None
def test_missing_template_body(placeboify):
m = FakeModule()
with pytest.raises(Exception, message='Expected module to fail with no template') as exc_info:
cfn_module.create_stack(
module=m,
stack_params={},
cfn=None
)
assert exc_info.match('FAIL')
assert not m.exit_args
assert "Either 'template' or 'template_url' is required when the stack does not exist." == m.exit_kwargs['msg']
Loading…
Cancel
Save