Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ec2_asg_lifecycle_hook: add integration tests #1048

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- ec2_asg_lifecycle_hook - add integration tests (https://github.com/ansible-collections/community.aws/pull/1048).
- ec2_asg_lifecycle_hook - module now returns info about Life Cycle Hook (https://github.com/ansible-collections/community.aws/pull/1048).
82 changes: 60 additions & 22 deletions plugins/modules/ec2_asg_lifecycle_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,50 @@
'''

RETURN = '''

---
auto_scaling_group_name:
description: The unique name of the auto scaling group
returned: success
type: str
sample: "myasg"
default_result:
description: Defines the action the Auto Scaling group should take when the lifecycle hook timeout elapses or if an unexpected failure occurs
returned: success
type: str
sample: CONTINUE
global_timeout:
description: The maximum time, in seconds, that an instance can remain in a Pending:Wait or Terminating:Wait state
returned: success
type: int
sample: 172800
heartbeat_timeout:
description: The maximum time, in seconds, that can elapse before the lifecycle hook times out
returned: success
type: int
sample: 3600
lifecycle_hook_name:
description: The name of the lifecycle hook
returned: success
type: str
sample: "mylifecyclehook"
lifecycle_transition:
description: The instance state to which lifecycle hook should be attached
returned: success
type: str
sample: "autoscaling:EC2_INSTANCE_LAUNCHING"
'''

from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule

try:
import botocore
except ImportError:
pass # handled by AnsibleAWSModule

from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict


def create_lifecycle_hook(connection, module):
changed = False

lch_name = module.params.get('lifecycle_hook_name')
asg_name = module.params.get('autoscaling_group_name')
Expand All @@ -120,6 +151,9 @@ def create_lifecycle_hook(connection, module):
heartbeat_timeout = module.params.get('heartbeat_timeout')
default_result = module.params.get('default_result')

return_object = {}
return_object['changed'] = False

lch_params = {
'LifecycleHookName': lch_name,
'AutoScalingGroupName': asg_name,
Expand Down Expand Up @@ -150,23 +184,26 @@ def create_lifecycle_hook(connection, module):
module.fail_json_aws(e, msg="Failed to get Lifecycle Hook")

if not existing_hook:
changed = True
else:
# GlobalTimeout is not configurable, but exists in response.
# Removing it helps to compare both dicts in order to understand
# what changes were done.
del(existing_hook[0]['GlobalTimeout'])
added, removed, modified, same = dict_compare(lch_params, existing_hook[0])
if added or removed or modified:
changed = True

if changed:
try:
return_object['changed'] = True
connection.put_lifecycle_hook(**lch_params)
return_object['lifecycle_hook_info'] = connection.describe_lifecycle_hooks(
AutoScalingGroupName=asg_name, LifecycleHookNames=[lch_name])['LifecycleHooks']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to create LifecycleHook")

return(changed)
else:
added, removed, modified, same = dict_compare(lch_params, existing_hook[0])
if modified:
try:
return_object['changed'] = True
connection.put_lifecycle_hook(**lch_params)
return_object['lifecycle_hook_info'] = connection.describe_lifecycle_hooks(
AutoScalingGroupName=asg_name, LifecycleHookNames=[lch_name])['LifecycleHooks']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to create LifecycleHook")

module.exit_json(**camel_dict_to_snake_dict(return_object))
mandar242 marked this conversation as resolved.
Show resolved Hide resolved


def dict_compare(d1, d2):
Expand All @@ -186,11 +223,13 @@ def dict_compare(d1, d2):


def delete_lifecycle_hook(connection, module):
changed = False

lch_name = module.params.get('lifecycle_hook_name')
asg_name = module.params.get('autoscaling_group_name')

return_object = {}
return_object['changed'] = False

try:
all_hooks = connection.describe_lifecycle_hooks(
AutoScalingGroupName=asg_name
Expand All @@ -207,13 +246,14 @@ def delete_lifecycle_hook(connection, module):

try:
connection.delete_lifecycle_hook(**lch_params)
changed = True
return_object['changed'] = True
return_object['lifecycle_hook_removed'] = {'LifecycleHookName': lch_name, 'AutoScalingGroupName': asg_name}
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to delete LifecycleHook")
else:
pass

return(changed)
module.exit_json(**camel_dict_to_snake_dict(return_object))


def main():
Expand All @@ -238,11 +278,9 @@ def main():
changed = False

if state == 'present':
changed = create_lifecycle_hook(connection, module)
create_lifecycle_hook(connection, module)
elif state == 'absent':
changed = delete_lifecycle_hook(connection, module)

module.exit_json(changed=changed)
delete_lifecycle_hook(connection, module)


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions tests/integration/targets/ec2_asg_lifecycle_hook/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cloud/aws
6 changes: 6 additions & 0 deletions tests/integration/targets/ec2_asg_lifecycle_hook/inventory
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tests]
create_update_delete

[all:vars]
ansible_connection=local
ansible_python_interpreter="{{ ansible_playbook_python }}"
40 changes: 40 additions & 0 deletions tests/integration/targets/ec2_asg_lifecycle_hook/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
# Beware: most of our tests here are run in parallel.
# To add new tests you'll need to add a new host to the inventory and a matching
# '{{ inventory_hostname }}'.yml file in roles/ec2_asg_lifecycle_hook/tasks/


# Prepare the VPC and figure out which AMI to use
- hosts: all
gather_facts: no
tasks:
- module_defaults:
group/aws:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}"
vars:
# We can't just use "run_once" because the facts don't propagate when
# running an 'include' that was run_once
setup_run_once: yes
block:
- include_role:
name: 'ec2_asg_lifecycle_hook'
tasks_from: env_setup.yml
rescue:
- include_role:
name: 'ec2_asg_lifecycle_hook'
tasks_from: env_cleanup.yml
run_once: yes
- fail:
msg: 'Environment preparation failed'
run_once: yes

# VPC should get cleaned up once all hosts have run
- hosts: all
gather_facts: no
strategy: free
serial: 6
roles:
- ec2_asg_lifecycle_hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
# defaults file for ec2_asg_lifecycle_hook
# Amazon Linux 2 AMI 2019.06.12 (HVM), GP2 Volume Type
ec2_ami_name: 'amzn2-ami-hvm-2.0.20190612-x86_64-gp2'
load_balancer_name: "{{ tiny_prefix }}-lb"
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
- name: Test create/update/delete AutoScalingGroups Lifecycle Hooks with ec2_asg_lifecycle_hook

block:
#----------------------------------------------------------------------
- name: create a launch configuration
ec2_lc:
name: "{{ resource_prefix }}-lc"
image_id: "{{ ec2_ami_image }}"
region: "{{ aws_region }}"
instance_type: t2.micro
assign_public_ip: yes
register: create_lc

- name: ensure that lc is created
assert:
that:
- create_lc is changed
- create_lc.failed is false

#----------------------------------------------------------------------
- name: create a AutoScalingGroup
ec2_asg:
name: "{{ resource_prefix }}-asg"
launch_config_name: "{{ resource_prefix }}-lc"
health_check_period: 60
health_check_type: ELB
replace_all_instances: yes
min_size: 1
max_size: 1
desired_capacity: 1
region: "{{ aws_region }}"
register: create_asg

- name: ensure that AutoScalingGroup is created
assert:
that:
- create_asg is changed
- create_asg.failed is false
- '"autoscaling:CreateAutoScalingGroup" in create_asg.resource_actions'

#----------------------------------------------------------------------

- name: Create lifecycle hook
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
transition: autoscaling:EC2_INSTANCE_LAUNCHING
heartbeat_timeout: 7000
default_result: ABANDON
state: present
register: output

- assert:
that:
- output is changed
- output is not failed
- '"lifecycle_hook_info" in output'
- output.lifecycle_hook_info[0].heartbeat_timeout == 7000

- name: Create lifecycle hook - Idempotency
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
transition: autoscaling:EC2_INSTANCE_LAUNCHING
heartbeat_timeout: 7000
default_result: ABANDON
state: present
register: output

- assert:
that:
- output is not changed
- output is not failed
- '"lifecycle_hook_info" not in output'

- name: Update lifecycle hook
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
transition: autoscaling:EC2_INSTANCE_LAUNCHING
heartbeat_timeout: 6000
default_result: ABANDON
state: present
register: output

- assert:
that:
- output is changed
- output is not failed
- '"lifecycle_hook_info" in output'
- output.lifecycle_hook_info[0].heartbeat_timeout == 6000

- name: Update lifecycle hook - Idempotency
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
transition: autoscaling:EC2_INSTANCE_LAUNCHING
heartbeat_timeout: 6000
default_result: ABANDON
state: present
register: output

- assert:
that:
- output is not changed
- output is not failed
- '"lifecycle_hook_info" not in output'

- name: Delete lifecycle hook
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
state: absent
register: output

- assert:
that:
- output is changed
- output is not failed
- '"lifecycle_hook_removed" in output'

- name: Delete lifecycle hook - Idempotency
community.aws.ec2_asg_lifecycle_hook:
region: "{{ aws_region }}"
autoscaling_group_name: "{{ resource_prefix }}-asg"
lifecycle_hook_name: "{{ resource_prefix }}-test-hook"
state: absent
register: output

- assert:
that:
- output is not changed
- output is not failed
- '"lifecycle_hook_removed" not in output'
Loading