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_spot_instance_info: add new module for describing spot instance requests #487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8e2b4f3
Added initial work: ec2_spot_instance_info module
mandar242 Aug 30, 2021
39d41e0
Capture params from playbook
mandar242 Aug 31, 2021
10d9a97
Fix Filters case, add request call
mandar242 Aug 31, 2021
0c03f75
Adding basic descriptions
mandar242 Sep 1, 2021
4de8dbf
Minor description update
mandar242 Sep 1, 2021
8006494
Sanity fixes
mandar242 Sep 1, 2021
ed06ed6
Capture params from playbook
mandar242 Aug 31, 2021
0545927
Fix Filters case, add request call
mandar242 Aug 31, 2021
f952982
Adding basic descriptions
mandar242 Sep 1, 2021
23f3300
Minor description update
mandar242 Sep 1, 2021
e8a715f
bug fix, add return block
mandar242 Sep 2, 2021
b67d94b
Bugfix: handle multiple instance in response, Added filter example
mandar242 Sep 2, 2021
b0cf0a2
Fix exit_json response
mandar242 Sep 2, 2021
aa2cd95
Sanity fixes
mandar242 Sep 7, 2021
ac41431
Add integration test
mandar242 Sep 8, 2021
889b55a
Remove dry run
mandar242 Sep 8, 2021
cf0ac0d
Add paginator, remove next_token & max_result, add integration test
mandar242 Sep 8, 2021
e79efc9
Add integration test, example
mandar242 Sep 8, 2021
8cd9822
Add changelogs fragment, integration test
mandar242 Sep 9, 2021
b877e27
Correcting changelogs fragment category
mandar242 Sep 9, 2021
14297ea
Modified filter list, changed key, check_mode, integration test based…
mandar242 Sep 9, 2021
aee2d43
Modify cleanup
mandar242 Sep 10, 2021
69e4b17
Improved assertion readability
mandar242 Sep 10, 2021
e848556
Remove assertion from cleanup
mandar242 Sep 11, 2021
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,2 @@
minor_changes:
- ec2_spot_instance_info - Added a new module that describes the specified Spot Instance requests (https://github.com/ansible-collections/amazon.aws/pull/487).
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ action_groups:
- ec2_snapshot
- ec2_snapshot_info
- ec2_spot_instance
- ec2_spot_instance_info
- ec2_tag
- ec2_tag_info
- ec2_vol
Expand Down
168 changes: 168 additions & 0 deletions plugins/modules/ec2_spot_instance_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/python
# This file is part of Ansible
# GNU General Public License v3.0+ (see COPYING or https://wwww.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
---
module: ec2_spot_instance_info
version_added: 2.0.0
short_description: Gather information about ec2 spot instance requests
description:
- Describes the specified Spot Instance requests.
author:
- Mandar Vijay Kulkarni (@mandar242)
options:
filters:
description:
- A dict of filters to apply. Each dict item consists of a filter key and a filter value.
- Filter names and values are case sensitive.
- See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSpotInstanceRequests.html) for possible filters.
required: false
default: {}
type: dict
spot_instance_request_ids:
description:
- One or more Spot Instance request IDs.
required: false
type: list
elements: str

extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.

- name: describe the Spot Instance requests based on request IDs
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- sir-12345678

- name: describe the Spot Instance requests and filter results based on instance type
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- sir-12345678
- sir-13579246
- sir-87654321
filters:
launch.instance-type: t3.medium

- name: describe the Spot requests filtered using multiple filters
amazon.aws.ec2_spot_instance_info:
filters:
state: active
launch.block-device-mapping.device-name: /dev/sdb

'''

RETURN = '''
spot_request:
description: The gathered information about specified spot instance requests.
returned: when success
type: dict
sample: {
"create_time": "2021-09-01T21:05:57+00:00",
"instance_id": "i-08877936b801ac475",
"instance_interruption_behavior": "terminate",
"launch_specification": {
"ebs_optimized": false,
"image_id": "ami-0443305dabd4be2bc",
"instance_type": "t2.medium",
"key_name": "zuul",
"monitoring": {
"enabled": false
},
"placement": {
"availability_zone": "us-east-2b"
},
"security_groups": [
{
"group_id": "sg-01f9833207d53b937",
"group_name": "default"
}
],
"subnet_id": "subnet-07d906b8358869bda"
},
"launched_availability_zone": "us-east-2b",
"product_description": "Linux/UNIX",
"spot_instance_request_id": "sir-c3cp9jsk",
"spot_price": "0.046400",
"state": "active",
"status": {
"code": "fulfilled",
"message": "Your spot request is fulfilled.",
"update_time": "2021-09-01T21:05:59+00:00"
},
"tags": {},
"type": "one-time",
"valid_until": "2021-09-08T21:05:57+00:00"
}
'''


try:
import botocore
except ImportError:
pass # Handled by AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list


def _describe_spot_instance_requests(connection, **params):
paginator = connection.get_paginator('describe_spot_instance_requests')
return paginator.paginate(**params).build_full_result()


def describe_spot_instance_requests(connection, module):

params = {}

if module.params.get('filters'):
params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get('filters'))
if module.params.get('spot_instance_request_ids'):
params['SpotInstanceRequestIds'] = module.params.get('spot_instance_request_ids')

try:
describe_spot_instance_requests_response = _describe_spot_instance_requests(connection, **params)['SpotInstanceRequests']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed to describe spot instance requests')

spot_request = []
for response_list_item in describe_spot_instance_requests_response:
spot_request.append(camel_dict_to_snake_dict(response_list_item))

if len(spot_request) == 0:
module.exit_json(msg='No spot requests found for specified options')

module.exit_json(spot_request=spot_request)


def main():

argument_spec = dict(
filters=dict(default={}, type='dict'),
spot_instance_request_ids=dict(default=[], type='list', elements='str'),
)
module = AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True
)
try:
connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed to connect to AWS')

describe_spot_instance_requests(connection, module)


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions tests/integration/targets/ec2_spot_instance/aliases
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
cloud/aws
ec2_spot_instance_info
62 changes: 59 additions & 3 deletions tests/integration/targets/ec2_spot_instance/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.medium"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
register: create_result

- name: Assert that result has changed and request has been created
Expand All @@ -90,6 +92,17 @@
- create_result.spot_request.spot_instance_request_id is defined
- create_result.spot_request.launch_specification.subnet_id == vpc_subnet_result.subnet.id

- name: Get info about the spot instance request created
ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ create_result.spot_request.spot_instance_request_id }}"
register: spot_instance_info_result

- name: Assert that the spot request created is open or active
assert:
that:
- spot_instance_info_result.spot_request[0].state in ['open', 'active']

- name: Create spot request with more complex options
ec2_spot_instance:
launch_specification:
Expand All @@ -104,6 +117,7 @@
volume_size: 5
network_interfaces:
- associate_public_ip_address: False
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
delete_on_termination: True
device_index: 0
placement:
Expand All @@ -117,6 +131,7 @@
snake_case: "hello_world"
"Title Case": "Hello World"
"lowercase spaced": "hello world"
ansible-test: "{{ resource_prefix }}"
register: complex_create_result

- assert:
Expand All @@ -135,7 +150,7 @@
- launch_spec.network_interfaces.0.device_index == 0
- launch_spec.network_interfaces.0.associate_public_ip_address == false
- launch_spec.network_interfaces.0.delete_on_termination == true
- spot_request_tags|length == 5
- spot_request_tags|length == 6
- spot_request_tags['camelCase'] == 'helloWorld'
- spot_request_tags['PascalCase'] == 'HelloWorld'
- spot_request_tags['snake_case'] == 'hello_world'
Expand All @@ -145,6 +160,39 @@
launch_spec: '{{ complex_create_result.spot_request.launch_specification }}'
spot_request_tags: '{{ complex_create_result.spot_request.tags }}'

- name: Get info about the complex spot instance request created
ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ complex_create_result.spot_request.spot_instance_request_id }}"
register: complex_info_result

- name: Assert that the complex spot request created is open/active and correct keys are set
assert:
that:
- complex_info_result.spot_request[0].state in ['open', 'active']
- complex_create_result.spot_request.spot_price == complex_info_result.spot_request[0].spot_price
- create_launch_spec.block_device_mappings[0].ebs.volume_size == info_launch_spec.block_device_mappings[0].ebs.volume_size
- create_launch_spec.block_device_mappings[0].ebs.volume_type == info_launch_spec.block_device_mappings[0].ebs.volume_type
- create_launch_spec.network_interfaces[0].delete_on_termination == info_launch_spec.network_interfaces[0].delete_on_termination
vars:
create_launch_spec: "{{ complex_create_result.spot_request.launch_specification }}"
info_launch_spec: "{{ complex_info_result.spot_request[0].launch_specification }}"

- name: Get info about the created spot instance requests and filter result based on provided filters
ec2_spot_instance_info:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
- '{{ complex_create_result.spot_request.spot_instance_request_id }}'
filters:
tag:ansible-test: "{{ resource_prefix }}"
launch.block-device-mapping.device-name: /dev/sdb
mandar242 marked this conversation as resolved.
Show resolved Hide resolved
register: spot_instance_info_filter_result

- name: Assert that the correct spot request was returned in the filtered result
assert:
that:
- spot_instance_info_filter_result.spot_request[0].spot_instance_request_id == complex_create_result.spot_request.spot_instance_request_id

# Assert check mode
- name: Create spot instance request (check_mode)
ec2_spot_instance:
Expand All @@ -153,6 +201,8 @@
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.medium"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
check_mode: True
register: check_create_result

Expand Down Expand Up @@ -221,14 +271,20 @@
filters:
vpc-id: "{{ vpc_result.vpc.id }}"

- name: get all spot requests created during test
ec2_spot_instance_info:
filters:
tag:ansible-test: "{{ resource_prefix }}"
register: spot_request_list

- name: remove spot instance requests
ec2_spot_instance:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
- '{{ complex_create_result.spot_request.spot_instance_request_id }}'
- '{{ item.spot_instance_request_id }}'
state: 'absent'
ignore_errors: true
retries: 5
with_items: "{{ spot_request_list.spot_request }}"

- name: remove the security group
ec2_group:
Expand Down