New module: ec2_instance (#35749)

New module: ec2_instance

Integration tests for ec2_instance
pull/35891/merge
Ryan Brown 7 years ago committed by Sloane Hertel
parent 663c410da4
commit e71c6d8e17

@ -48,7 +48,7 @@ additional methods for connecting to AWS using the standard module arguments
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
from ansible.module_utils.ec2 import HAS_BOTO3, camel_dict_to_snake_dict, ec2_argument_spec from ansible.module_utils.ec2 import HAS_BOTO3, camel_dict_to_snake_dict, ec2_argument_spec, boto3_conn, get_aws_connection_info
import traceback import traceback
# We will also export HAS_BOTO3 so end user modules can use it. # We will also export HAS_BOTO3 so end user modules can use it.
@ -112,6 +112,22 @@ class AnsibleAWSModule(object):
def fail_json(self, *args, **kwargs): def fail_json(self, *args, **kwargs):
return self._module.fail_json(*args, **kwargs) return self._module.fail_json(*args, **kwargs)
def debug(self, *args, **kwargs):
return self._module.debug(*args, **kwargs)
def warn(self, *args, **kwargs):
return self._module.warn(*args, **kwargs)
def client(self, service):
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
return boto3_conn(self, conn_type='client', resource=service,
region=region, endpoint=ec2_url, **aws_connect_kwargs)
def resource(self, service):
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
return boto3_conn(self, conn_type='resource', resource=service,
region=region, endpoint=ec2_url, **aws_connect_kwargs)
def fail_json_aws(self, exception, msg=None): def fail_json_aws(self, exception, msg=None):
"""call fail_json with processed exception """call fail_json with processed exception
@ -142,6 +158,3 @@ class AnsibleAWSModule(object):
else: else:
self._module.fail_json(msg=message, exception=last_traceback, self._module.fail_json(msg=message, exception=last_traceback,
**camel_dict_to_snake_dict(response)) **camel_dict_to_snake_dict(response))
def warn(self, msg):
self._module.warn(msg)

@ -449,7 +449,7 @@ def ansible_dict_to_boto3_filter_list(filters_dict):
Args: Args:
filters_dict (dict): Dict of AWS filters. filters_dict (dict): Dict of AWS filters.
Basic Usage: Basic Usage:
>>> filters = {'some-aws-id', 'i-01234567'} >>> filters = {'some-aws-id': 'i-01234567'}
>>> ansible_dict_to_boto3_filter_list(filters) >>> ansible_dict_to_boto3_filter_list(filters)
{ {
'some-aws-id': 'i-01234567' 'some-aws-id': 'i-01234567'
@ -776,8 +776,8 @@ def map_complex_type(complex_type, type_map):
def compare_aws_tags(current_tags_dict, new_tags_dict, purge_tags=True): def compare_aws_tags(current_tags_dict, new_tags_dict, purge_tags=True):
""" """
Compare two dicts of AWS tags. Dicts are expected to of been created using 'boto3_tag_list_to_ansible_dict' helper function. Compare two dicts of AWS tags. Dicts are expected to of been created using 'boto3_tag_list_to_ansible_dict' helper function.
Two dicts are returned - the first is tags to be set, the second is any tags to remove. Since the AWS APIs differ t Two dicts are returned - the first is tags to be set, the second is any tags to remove. Since the AWS APIs differ
hese may not be able to be used out of the box. these may not be able to be used out of the box.
:param current_tags_dict: :param current_tags_dict:
:param new_tags_dict: :param new_tags_dict:

File diff suppressed because it is too large Load Diff

@ -0,0 +1,20 @@
---
# defaults file for ec2_instance
ec2_instance_name: '{{resource_prefix}}-node'
ec2_instance_owner: 'integration-run-{{resource_prefix}}'
ec2_ami_image:
# https://wiki.centos.org/Cloud/AWS collected 2018-01-10
ap-northeast-1: ami-571e3c30
ap-northeast-2: ami-97cb19f9
ap-south-1: ami-11f0837e
ap-southeast-1: ami-30318f53
ap-southeast-2: ami-24959b47
ca-central-1: ami-daeb57be
eu-central-1: ami-7cbc6e13
eu-west-1: ami-0d063c6b
eu-west-2: ami-c22236a6
sa-east-1: ami-864f2dea
us-east-1: ami-ae7bfdb8
us-east-2: ami-9cbf9bf9
us-west-1: ami-7c280d1c
us-west-2: ami-0c2aba6c

@ -0,0 +1,3 @@
dependencies:
- prepare_tests
- setup_ec2

@ -0,0 +1,31 @@
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
- name: New instance with an extra block device
ec2_instance:
name: "{{ resource_prefix }}-test-ebs-vols"
image_id: "{{ ec2_ami_image[aws_region] }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
volumes:
- device_name: /dev/sdb
ebs:
volume_size: 20
delete_on_termination: true
volume_type: standard
tags:
TestId: "{{ resource_prefix }}"
instance_type: t2.micro
<<: *aws_connection_info
register: in_test_vpc
- assert:
that:
- in_test_vpc is not failed
- in_test_vpc is changed
- in_test_vpc.instances[0].block_device_mappings[0]
- in_test_vpc.instances[0].block_device_mappings[1]
- in_test_vpc.instances[0].block_device_mappings[1].device_name == '/dev/sdb'

@ -0,0 +1,23 @@
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
- name: Make instance in a default subnet of the VPC
ec2_instance:
name: "{{ resource_prefix }}-test-default-vpc"
image_id: "{{ ec2_ami_image[aws_region] }}"
tags:
TestId: "{{ resource_prefix }}"
security_groups: "{{ sg.group_id }}"
instance_type: t2.micro
<<: *aws_connection_info
register: in_default_vpc
- name: Terminate instance
ec2:
instance_ids: "{{ in_default_vpc.instance_ids }}"
state: absent
<<: *aws_connection_info

@ -0,0 +1,78 @@
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
# Make custom ENIs and attach via the `network` parameter
- ec2_eni:
delete_on_termination: true
subnet_id: "{{ testing_subnet_b.subnet.id }}"
security_groups:
- "{{ sg.group_id }}"
<<: *aws_connection_info
register: eni_a
- ec2_eni:
delete_on_termination: true
subnet_id: "{{ testing_subnet_b.subnet.id }}"
security_groups:
- "{{ sg.group_id }}"
<<: *aws_connection_info
register: eni_b
- name: Make instance in the testing subnet created in the test VPC
ec2_instance:
name: "{{ resource_prefix }}-test-eni-vpc"
network:
interfaces:
- id: "{{ eni_a.interface.id }}"
image_id: "{{ ec2_ami_image[aws_region] }}"
availability_zone: '{{ aws_region }}b'
tags:
TestId: "{{ resource_prefix }}"
instance_type: t2.micro
<<: *aws_connection_info
register: in_test_vpc
- name: Add a second interface
ec2_instance:
name: "{{ resource_prefix }}-test-eni-vpc"
network:
interfaces:
- id: "{{ eni_a.interface.id }}"
- id: "{{ eni_b.interface.id }}"
image_id: "{{ ec2_ami_image[aws_region] }}"
tags:
TestId: "{{ resource_prefix }}"
instance_type: t2.micro
<<: *aws_connection_info
- name: Terminate instance
ec2_instance:
filters:
tag:TestId: "{{ resource_prefix }}"
state: absent
<<: *aws_connection_info
register: result
- assert:
that: result.changed
- name: Terminate instance
ec2_instance:
instance_ids: "{{ in_test_vpc.instance_ids }}"
state: absent
<<: *aws_connection_info
register: result
- assert:
that: not result.changed
- ec2_eni:
eni_id: "{{ item }}"
state: absent
<<: *aws_connection_info
with_items:
- "{{ eni_a.interface.id }}"
- "{{ eni_b.interface.id }}"

@ -0,0 +1,204 @@
---
# A Note about ec2 environment variable name preference:
# - EC2_URL -> AWS_URL
# - EC2_ACCESS_KEY -> AWS_ACCESS_KEY_ID -> AWS_ACCESS_KEY
# - EC2_SECRET_KEY -> AWS_SECRET_ACCESS_KEY -> AWX_SECRET_KEY
# - EC2_REGION -> AWS_REGION
#
# - include: ../../setup_ec2/tasks/common.yml module_name: ec2_instance
- block:
# ============================================================
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
- name: Create VPC for use in testing
ec2_vpc_net:
name: "{{ resource_prefix }}-vpc"
cidr_block: 10.22.32.0/23
tags:
Name: Ansible ec2_instance Testing VPC
tenancy: default
<<: *aws_connection_info
register: testing_vpc
- name: Create internet gateway for use in testing
ec2_vpc_igw:
vpc_id: "{{ testing_vpc.vpc.id }}"
state: present
<<: *aws_connection_info
register: igw
- name: Create default subnet in zone A
ec2_vpc_subnet:
state: present
vpc_id: "{{ testing_vpc.vpc.id }}"
cidr: 10.22.32.0/24
az: "{{ aws_region }}a"
resource_tags:
Name: "{{ resource_prefix }}-subnet-a"
<<: *aws_connection_info
register: testing_subnet_a
- name: Create secondary subnet in zone B
ec2_vpc_subnet:
state: present
vpc_id: "{{ testing_vpc.vpc.id }}"
cidr: 10.22.33.0/24
az: "{{ aws_region }}b"
resource_tags:
Name: "{{ resource_prefix }}-subnet-b"
<<: *aws_connection_info
register: testing_subnet_b
- name: create routing rules
ec2_vpc_route_table:
vpc_id: "{{ testing_vpc.vpc.id }}"
tags:
created: "{{ resource_prefix }}-route"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
subnets:
- "{{ testing_subnet_a.subnet.id }}"
- "{{ testing_subnet_b.subnet.id }}"
<<: *aws_connection_info
- name: create a security group with the vpc
ec2_group:
name: "{{ resource_prefix }}-sg"
description: a security group for ansible tests
vpc_id: "{{ testing_vpc.vpc.id }}"
rules:
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 80
to_port: 80
cidr_ip: 0.0.0.0/0
<<: *aws_connection_info
register: sg
- include_tasks: tasks/termination_protection.yml
- include_tasks: tasks/tags_and_vpc_settings.yml
- include_tasks: tasks/external_resource_attach.yml
- include_tasks: tasks/block_devices.yml
- include_tasks: tasks/default_vpc_tests.yml
# ============================================================
always:
- name: remove any instances in the test VPC
ec2_instance:
filters:
vpc_id: "{{ testing_vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove ENIs
ec2_eni_facts:
filters:
vpc-id: "{{ testing_vpc.vpc.id }}"
<<: *aws_connection_info
register: enis
- name: delete all ENIs
ec2_eni:
eni_id: "{{ item.id }}"
state: absent
<<: *aws_connection_info
until: removed is not failed
with_items: "{{ enis.network_interfaces }}"
ignore_errors: yes
retries: 10
- name: remove the security group
ec2_group:
name: "{{ resource_prefix }}-sg"
description: a security group for ansible tests
vpc_id: "{{ testing_vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove routing rules
ec2_vpc_route_table:
state: absent
vpc_id: "{{ testing_vpc.vpc.id }}"
tags:
created: "{{ resource_prefix }}-route"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ igw.gateway_id }}"
subnets:
- "{{ testing_subnet_a.subnet.id }}"
- "{{ testing_subnet_b.subnet.id }}"
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove internet gateway
ec2_vpc_igw:
vpc_id: "{{ testing_vpc.vpc.id }}"
state: absent
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove subnet A
ec2_vpc_subnet:
state: absent
vpc_id: "{{ testing_vpc.vpc.id }}"
cidr: 10.22.32.0/24
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove subnet B
ec2_vpc_subnet:
state: absent
vpc_id: "{{ testing_vpc.vpc.id }}"
cidr: 10.22.33.0/24
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10
- name: remove the VPC
ec2_vpc_net:
name: "{{ resource_prefix }}-vpc"
cidr_block: 10.22.32.0/23
state: absent
tags:
Name: Ansible Testing VPC
tenancy: default
<<: *aws_connection_info
register: removed
until: removed is not failed
ignore_errors: yes
retries: 10

@ -0,0 +1,127 @@
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
- name: Make instance in the testing subnet created in the test VPC
ec2_instance:
name: "{{ resource_prefix }}-test-basic-vpc-create"
image_id: "{{ ec2_ami_image[aws_region] }}"
user_data: |
#cloud-config
package_upgrade: true
package_update: true
tags:
TestId: "{{ resource_prefix }}"
Something: else
security_groups: "{{ sg.group_id }}"
network:
source_dest_check: false
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
instance_type: t2.micro
<<: *aws_connection_info
register: in_test_vpc
- name: Try to re-make the instance, hopefully this shows changed=False
ec2_instance:
name: "{{ resource_prefix }}-test-basic-vpc-create"
image_id: "{{ ec2_ami_image[aws_region] }}"
user_data: |
#cloud-config
package_upgrade: true
package_update: true
tags:
TestId: "{{ resource_prefix }}"
Something: else
security_groups: "{{ sg.group_id }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
instance_type: t2.micro
<<: *aws_connection_info
register: remake_in_test_vpc
- name: "Remaking the same instance resulted in no changes"
assert:
that: not remake_in_test_vpc.changed
- name: check that instance IDs match anyway
assert:
that: 'remake_in_test_vpc.instance_ids[0] == in_test_vpc.instance_ids[0]'
- name: check that source_dest_check was set to false
assert:
that: 'not remake_in_test_vpc.instances[0].source_dest_check'
- name: Alter it by adding tags
ec2_instance:
name: "{{ resource_prefix }}-test-basic-vpc-create"
image_id: "{{ ec2_ami_image[aws_region] }}"
tags:
TestId: "{{ resource_prefix }}"
Another: thing
security_groups: "{{ sg.group_id }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
instance_type: t2.micro
<<: *aws_connection_info
register: add_another_tag
- ec2_instance_facts:
instance_ids: "{{ add_another_tag.instance_ids }}"
<<: *aws_connection_info
register: check_tags
- name: "Remaking the same instance resulted in no changes"
assert:
that:
- check_tags.instances[0].tags.Another == 'thing'
- check_tags.instances[0].tags.Something == 'else'
- name: Purge a tag
ec2_instance:
name: "{{ resource_prefix }}-test-basic-vpc-create"
image_id: "{{ ec2_ami_image[aws_region] }}"
purge_tags: true
tags:
TestId: "{{ resource_prefix }}"
Another: thing
security_groups: "{{ sg.group_id }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
instance_type: t2.micro
<<: *aws_connection_info
- ec2_instance_facts:
instance_ids: "{{ add_another_tag.instance_ids }}"
<<: *aws_connection_info
register: check_tags
- name: "Remaking the same instance resulted in no changes"
assert:
that:
- "'Something' not in check_tags.instances[0].tags"
- name: Terminate instance
ec2_instance:
filters:
tag:TestId: "{{ resource_prefix }}"
state: absent
<<: *aws_connection_info
register: result
- assert:
that: result.changed
- name: Terminate instance
ec2_instance:
instance_ids: "{{ in_test_vpc.instance_ids }}"
state: absent
<<: *aws_connection_info
register: result
- assert:
that: not result.changed
- name: check that subnet-default public IP rule was followed
assert:
that:
- in_test_vpc.instances[0].public_dns_name == ""
- in_test_vpc.instances[0].private_ip_address.startswith("10.22.33")
- in_test_vpc.instances[0].subnet_id == testing_subnet_b.subnet.id
- name: check that tags were applied
assert:
that:
- in_test_vpc.instances[0].tags.Name.startswith(resource_prefix)
- in_test_vpc.instances[0].state.name == 'running'

@ -0,0 +1,57 @@
- name: set connection information for all tasks
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: true
- name: Make termination-protected instance in the testing subnet created in the test VPC
ec2_instance:
name: "{{ resource_prefix }}-test-protected-instance-in-vpc"
image_id: "{{ ec2_ami_image[aws_region] }}"
tags:
TestId: "{{ resource_prefix }}"
security_groups: "{{ sg.group_id }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
termination_protection: true
instance_type: t2.micro
<<: *aws_connection_info
register: in_test_vpc
- name: Try to terminate the instance
ec2_instance:
state: absent
name: "{{ resource_prefix }}-test-protected-instance-in-vpc"
image_id: "{{ ec2_ami_image[aws_region] }}"
tags:
TestId: "{{ resource_prefix }}"
security_groups: "{{ sg.group_id }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
termination_protection: true
instance_type: t2.micro
<<: *aws_connection_info
register: bad_terminate
ignore_errors: yes
- name: Cannot terminate protected instance
assert:
that:
- bad_terminate is failed
- name: Alter termination protection setting
ec2_instance:
name: "{{ resource_prefix }}-test-protected-instance-in-vpc"
image_id: "{{ ec2_ami_image[aws_region] }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
termination_protection: false
instance_type: t2.micro
<<: *aws_connection_info
- name: Try to terminate the instance again (should work)
ec2_instance:
name: "{{ resource_prefix }}-test-protected-instance-in-vpc"
image_id: "{{ ec2_ami_image[aws_region] }}"
vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
instance_type: t2.micro
state: absent
<<: *aws_connection_info
register: terminate_results
- assert:
that: terminate_results is not failed
Loading…
Cancel
Save