Skip to content

Commit

Permalink
Fully support mixed instance policy
Browse files Browse the repository at this point in the history
Previously, setting instances_distribution was not supported.
instances_distribution should be supported, to allow users to enable
spot instances within their mixed instance ASGs.

Note: The type and significance of the mixed_instance_policy has
changed. It now captures all of the mixed_instance_policy configuration
parameters, rather than just a list of instance types.

Fixes ansible-collections#231
  • Loading branch information
JacobHenner authored and goneri committed Feb 12, 2021
1 parent 2534412 commit 7bd3ac8
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 11 deletions.
112 changes: 106 additions & 6 deletions plugins/modules/ec2_asg.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,60 @@
- A list of instance_types.
type: list
elements: str
required: false
instances_distribution:
description:
- >-
Specifies the distribution of On-Demand Instances and Spot Instances, the maximum price
to pay for Spot Instances, and how the Auto Scaling group allocates instance types
to fulfill On-Demand and Spot capacity.
- 'See also U(https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_InstancesDistribution.html)'
required: false
type: dict
suboptions:
on_demand_allocation_strategy:
description:
- Indicates how to allocate instance types to fulfill On-Demand capacity.
type: str
required: false
on_demand_base_capacity:
description:
- >-
The minimum amount of the Auto Scaling group's capacity that must be fulfilled by On-Demand
Instances. This base portion is provisioned first as your group scales.
- >-
Default if not set is 0. If you leave it set to 0, On-Demand Instances are launched as a
percentage of the Auto Scaling group's desired capacity, per the OnDemandPercentageAboveBaseCapacity setting.
type: int
required: false
on_demand_percentage_above_base_capacity:
description:
- Controls the percentages of On-Demand Instances and Spot Instances for your additional capacity beyond OnDemandBaseCapacity.
- Default if not set is 100. If you leave it set to 100, the percentages are 100% for On-Demand Instances and 0% for Spot Instances.
- 'Valid range: 0 to 100'
type: int
required: false
spot_allocation_strategy:
description:
- Indicates how to allocate instances across Spot Instance pools.
type: str
required: false
spot_instance_pools:
description:
- >-
The number of Spot Instance pools across which to allocate your Spot Instances. The Spot pools are determined from
the different instance types in the Overrides array of LaunchTemplate. Default if not set is 2.
- Used only when the Spot allocation strategy is lowest-price.
- 'Valid Range: Minimum value of 1. Maximum value of 20.'
type: int
required: false
spot_max_price:
description:
- The maximum price per unit hour that you are willing to pay for a Spot Instance.
- If you leave the value of this parameter blank (which is the default), the maximum Spot price is set at the On-Demand price.
- To remove a value that you previously set, include the parameter but leave the value blank.
type: str
required: false
type: dict
placement_group:
description:
Expand Down Expand Up @@ -339,6 +393,9 @@
- t3a.large
- t3.large
- t2.large
instances_distribution:
on_demand_percentage_above_base_capacity: 0
spot_allocation_strategy: capacity-optimized
min_size: 1
max_size: 10
desired_capacity: 5
Expand Down Expand Up @@ -448,10 +505,32 @@
type: int
sample: 1
mixed_instance_policy:
description: Returns the list of instance types if a mixed instance policy is set.
description: Returns the dictionary representation of the mixed instance policy.
returned: success
type: list
sample: ["t3.micro", "t3a.micro"]
type: dict
sample: {
"InstancesDistribution": {
"OnDemandAllocationStrategy": "prioritized",
"OnDemandBaseCapacity": 0,
"OnDemandPercentageAboveBaseCapacity": 0,
"SpotAllocationStrategy": "capacity-optimized"
},
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateId": "lt-53c2425cffa544c23",
"LaunchTemplateName": "random-LaunchTemplate",
"Version": "2"
},
"Overrides": [
{
"InstanceType": "m5.xlarge"
},
{
"InstanceType": "m5a.xlarge"
},
]
}
}
pending_instances:
description: Number of instances in pending state
returned: success
Expand Down Expand Up @@ -526,6 +605,7 @@
'''

import time
import traceback

try:
import botocore
Expand All @@ -537,6 +617,7 @@
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import snake_dict_to_camel_dict

ASG_ATTRIBUTES = ('AvailabilityZones', 'DefaultCooldown', 'DesiredCapacity',
'HealthCheckGracePeriod', 'HealthCheckType', 'LaunchConfigurationName',
Expand Down Expand Up @@ -740,9 +821,7 @@ def get_properties(autoscaling_group):
properties['termination_policies'] = autoscaling_group.get('TerminationPolicies')
properties['target_group_arns'] = autoscaling_group.get('TargetGroupARNs')
properties['vpc_zone_identifier'] = autoscaling_group.get('VPCZoneIdentifier')
raw_mixed_instance_object = autoscaling_group.get('MixedInstancesPolicy')
if raw_mixed_instance_object:
properties['mixed_instances_policy'] = [x['InstanceType'] for x in raw_mixed_instance_object.get('LaunchTemplate').get('Overrides')]
properties['mixed_instances_policy'] = autoscaling_group.get('MixedInstancesPolicy')

metrics = autoscaling_group.get('EnabledMetrics')
if metrics:
Expand Down Expand Up @@ -792,6 +871,7 @@ def get_launch_object(connection, ec2_connection):

if mixed_instances_policy:
instance_types = mixed_instances_policy.get('instance_types', [])
instances_distribution = mixed_instances_policy.get('instances_distribution', {})
policy = {
'LaunchTemplate': {
'LaunchTemplateSpecification': launch_object['LaunchTemplate']
Expand All @@ -802,6 +882,14 @@ def get_launch_object(connection, ec2_connection):
for instance_type in instance_types:
instance_type_dict = {'InstanceType': instance_type}
policy['LaunchTemplate']['Overrides'].append(instance_type_dict)
if instances_distribution:
instances_distribution_params = dict(
(key.capitalize(), value) # To Pascal-case.
for key, value
in instances_distribution.items()
if value is not None
)
policy['InstancesDistribution'] = snake_dict_to_camel_dict(instances_distribution_params)
launch_object['MixedInstancesPolicy'] = policy
return launch_object

Expand Down Expand Up @@ -1661,6 +1749,18 @@ def main():
type='list',
elements='str'
),
instances_distribution=dict(
type='dict',
default=None,
options=dict(
on_demand_allocation_strategy=dict(type='str'),
on_demand_base_capacity=dict(type='int'),
on_demand_percentage_above_base_capacity=dict(type='int'),
spot_allocation_strategy=dict(type='str'),
spot_instance_pools=dict(type='int'),
spot_max_price=dict(type='str'),
)
)
)
),
placement_group=dict(type='str'),
Expand Down
14 changes: 9 additions & 5 deletions tests/integration/targets/ec2_asg/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@
until: status is finished
retries: 200
delay: 15

# we need a launch template, otherwise we cannot test the mixed instance policy
- name: create launch template for autoscaling group to test its mixed instance policy
ec2_launch_template:
Expand All @@ -648,7 +648,7 @@
- name: update autoscaling group with mixed-instance policy
ec2_asg:
name: "{{ resource_prefix }}-asg"
launch_template:
launch_template:
launch_template_name: "{{ resource_prefix }}-lt"
desired_capacity: 1
min_size: 1
Expand All @@ -659,14 +659,18 @@
instance_types:
- t3.micro
- t3a.micro
instances_distribution:
on_demand_percentage_above_base_capacity: 0
spot_allocation_strategy: capacity-optimized
wait_for_instances: yes
register: output

- assert:
that:
- "output.mixed_instances_policy | length == 2"
- "output.mixed_instances_policy[0] == 't3.micro'"
- "output.mixed_instances_policy[1] == 't3a.micro'"
- "output.mixed_instances_policy['LaunchTemplate']['Overrides'][0]['InstanceType'] == 't3.micro'"
- "output.mixed_instances_policy['LaunchTemplate']['Overrides'][1]['InstanceType'] == 't3a.micro'"
- "output.mixed_instances_policy['InstancesDistribution']['OnDemandPercentageAboveBaseCapacity'] == 0"
- "output.mixed_instances_policy['InstancesDistribution']['SpotallocationStrategy'] == 'capacity-optimized'"

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

Expand Down

0 comments on commit 7bd3ac8

Please sign in to comment.