Skip to content

Commit

Permalink
ec2_spot_instance: add parameter to enable terminating spot instances…
Browse files Browse the repository at this point in the history
… when cancelling request (ansible-collections#1402)

ec2_spot_instance: add parameter to enable terminating spot instances when cancelling request

SUMMARY

This PR adds a new parameter terminate_instances (true | false) to enable terminating spot instances when cancelling request.
Can be used only when state=absent.

Fixes ansible-collections#1360
ISSUE TYPE


Feature Pull Request

COMPONENT NAME

ec2_spot_instance
ADDITIONAL INFORMATION

Reviewed-by: Bikouo Aubin
Reviewed-by: Mandar Kulkarni <[email protected]>
Reviewed-by: Alina Buzachis
Reviewed-by: Mark Chappell
  • Loading branch information
mandar242 authored Mar 8, 2023
1 parent b8dd905 commit aa86240
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- ec2_spot_instance - add parameter ``terminate_instances`` to support terminate instances associated with spot requests. (https://github.com/ansible-collections/amazon.aws/pull/1402).
30 changes: 27 additions & 3 deletions plugins/modules/ec2_spot_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@
- List of strings with IDs of spot requests to be cancelled
type: list
elements: str
terminate_instances:
description:
- Boolean value to set whether or not to terminate instances associated to spot request.
- Can be used only when I(state=absent).
default: False
type: bool
version_added: 5.4.0
extends_documentation_fragment:
- amazon.aws.common.modules
- amazon.aws.region.modules
Expand Down Expand Up @@ -523,13 +530,25 @@ def cancel_spot_instance_requests(module, connection):
msg='Would have cancelled Spot request {0}'.format(spot_instance_request_ids))

connection.cancel_spot_instance_requests(aws_retry=True, SpotInstanceRequestIds=module.params.get('spot_instance_request_ids'))

if module.params.get("terminate_instances") is True:
associated_instances = [request["InstanceId"] for request in requests_exist["SpotInstanceRequests"]]
terminate_associated_instances(connection, module, associated_instances)

module.exit_json(changed=changed, msg='Cancelled Spot request {0}'.format(module.params.get('spot_instance_request_ids')))
else:
module.exit_json(changed=changed, msg='Spot request not found or already cancelled')
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Error while cancelling the spot instance request')


def terminate_associated_instances(connection, module, instance_ids):
try:
connection.terminate_instances(aws_retry=True, InstanceIds=instance_ids)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json(e, msg="Unable to terminate instances")


def main():
network_interface_options = dict(
associate_public_ip_address=dict(type='bool'),
Expand Down Expand Up @@ -601,16 +620,21 @@ def main():
tags=dict(type='dict'),
# valid_from=dict(type='datetime', default=datetime.datetime.now()),
# valid_until=dict(type='datetime', default=(datetime.datetime.now() + datetime.timedelta(minutes=60))
spot_instance_request_ids=dict(type='list', elements='str'),
spot_instance_request_ids=dict(type="list", elements="str"),
terminate_instances=dict(type="bool", default="False"),
)

module = AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True
)

connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
state = module.params["state"]

state = module.params['state']
if module.params.get("terminate_instances") and state != "absent":
module.fail_json("terminate_instances can only be used when state is absent.")

connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())

if state == 'present':
request_spot_instances(module, connection)
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/targets/ec2_spot_instance/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@

# ============================================================

- name: Run tests for termianting associated instances
import_tasks: terminate_associated_instances.yml

# Assert that spot instance request is created
- name: Create simple spot instance request
ec2_spot_instance:
Expand Down Expand Up @@ -253,7 +256,6 @@
- fake_cancel_result is not changed
- '"Spot request not found or already cancelled" in fake_cancel_result.msg'


always:

# ============================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
- block:

# Spot instance request creation
- name: Simple Spot Request Creation
amazon.aws.ec2_spot_instance:
launch_specification:
image_id: "{{ ec2_ami_id }}"
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.micro"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
register: create_result

# Get instance ID of associated spot instance request
- name: Get info about the spot instance request created
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ create_result.spot_request.spot_instance_request_id }}"
register: spot_instance_info_result
retries: 5
until: spot_instance_info_result.spot_request[0].instance_id is defined

- name: Pause to allow instance launch
pause:
seconds: 60

- name: Get instance ID of the instance associated with above spot instance request
set_fact:
instance_id_1: "{{ spot_instance_info_result.spot_request[0].instance_id }}"

- name: Check state of instance - BEFORE request cancellation
amazon.aws.ec2_instance_info:
instance_ids: ["{{ instance_id_1 }}"]
register: instance_info_result

# Cancel spot instance request
- name: Spot Request Termination
amazon.aws.ec2_spot_instance:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
state: absent

# Verify that instance is not terminated and still running
- name: Check state of instance - AFTER request cancellation
amazon.aws.ec2_instance_info:
instance_ids: ["{{ instance_id_1 }}"]
register: instance_info_result

- assert:
that: instance_info_result.instances[0].state.name == 'running'

#==========================================================================

# Spot instance request creation
- name: Simple Spot Request Creation
amazon.aws.ec2_spot_instance:
launch_specification:
image_id: "{{ ec2_ami_id }}"
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.micro"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
register: create_result

# Get instance ID of associated spot instance request
- name: Get info about the spot instance request created
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ create_result.spot_request.spot_instance_request_id }}"
register: spot_instance_info_result
retries: 5
until: spot_instance_info_result.spot_request[0].instance_id is defined

- name: Pause to allow instance launch
pause:
seconds: 60

- name: Get instance ID of the instance associated with above spot instance request
set_fact:
instance_id_2: "{{ spot_instance_info_result.spot_request[0].instance_id }}"

- name: Check state of instance - BEFORE request cancellation
amazon.aws.ec2_instance_info:
instance_ids: ["{{ instance_id_2 }}"]
register: instance_info_result

# Cancel spot instance request
- name: Spot Request Termination
amazon.aws.ec2_spot_instance:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
state: absent
terminate_instances: true

- name: wait for instance to terminate
pause:
seconds: 60

# Verify that instance is terminated or shutting-down
- name: Check state of instance - AFTER request cancellation
amazon.aws.ec2_instance_info:
instance_ids: ["{{ instance_id_2 }}"]
register: instance_info_result

- assert:
that: instance_info_result.instances[0].state.name in ['terminated', 'shutting-down']

0 comments on commit aa86240

Please sign in to comment.