AutoScaling: create_auto_scaling_group() now supports the MixedInstancesPolicy-parameter (#5433)

This commit is contained in:
Bert Blommers 2022-08-30 10:04:12 +00:00 committed by GitHub
parent bd051f4f32
commit 2c98b7574b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 25 deletions

View File

@ -368,6 +368,8 @@ class FakeAutoScalingGroup(CloudFormationModel):
autoscaling_backend, autoscaling_backend,
ec2_backend, ec2_backend,
tags, tags,
mixed_instance_policy,
capacity_rebalance,
new_instances_protected_from_scale_in=False, new_instances_protected_from_scale_in=False,
): ):
self.autoscaling_backend = autoscaling_backend self.autoscaling_backend = autoscaling_backend
@ -376,6 +378,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
self._id = str(uuid4()) self._id = str(uuid4())
self.region = self.autoscaling_backend.region_name self.region = self.autoscaling_backend.region_name
self.account_id = self.autoscaling_backend.account_id self.account_id = self.autoscaling_backend.account_id
self.service_linked_role = f"arn:aws:iam::{self.account_id}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling"
self._set_azs_and_vpcs(availability_zones, vpc_zone_identifier) self._set_azs_and_vpcs(availability_zones, vpc_zone_identifier)
@ -385,7 +388,10 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.launch_template = None self.launch_template = None
self.launch_config = None self.launch_config = None
self._set_launch_configuration(launch_config_name, launch_template) self._set_launch_configuration(
launch_config_name, launch_template, mixed_instance_policy
)
self.mixed_instance_policy = mixed_instance_policy
self.default_cooldown = ( self.default_cooldown = (
default_cooldown if default_cooldown else DEFAULT_COOLDOWN default_cooldown if default_cooldown else DEFAULT_COOLDOWN
@ -395,6 +401,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.load_balancers = load_balancers self.load_balancers = load_balancers
self.target_group_arns = target_group_arns self.target_group_arns = target_group_arns
self.placement_group = placement_group self.placement_group = placement_group
self.capacity_rebalance = capacity_rebalance
self.termination_policies = termination_policies or ["Default"] self.termination_policies = termination_policies or ["Default"]
self.new_instances_protected_from_scale_in = ( self.new_instances_protected_from_scale_in = (
new_instances_protected_from_scale_in new_instances_protected_from_scale_in
@ -458,16 +465,29 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.availability_zones = availability_zones self.availability_zones = availability_zones
self.vpc_zone_identifier = vpc_zone_identifier self.vpc_zone_identifier = vpc_zone_identifier
def _set_launch_configuration(self, launch_config_name, launch_template): def _set_launch_configuration(
self, launch_config_name, launch_template, mixed_instance_policy
):
if launch_config_name: if launch_config_name:
self.launch_config = self.autoscaling_backend.launch_configurations[ self.launch_config = self.autoscaling_backend.launch_configurations[
launch_config_name launch_config_name
] ]
self.launch_config_name = launch_config_name self.launch_config_name = launch_config_name
if launch_template: if launch_template or mixed_instance_policy:
launch_template_id = launch_template.get("launch_template_id") if launch_template:
launch_template_name = launch_template.get("launch_template_name") launch_template_id = launch_template.get("launch_template_id")
launch_template_name = launch_template.get("launch_template_name")
self.launch_template_version = (
launch_template.get("version") or "$Default"
)
else:
spec = mixed_instance_policy["LaunchTemplate"][
"LaunchTemplateSpecification"
]
launch_template_id = spec.get("LaunchTemplateId")
launch_template_name = spec.get("LaunchTemplateName")
self.launch_template_version = spec.get("Version") or "$Default"
if not (launch_template_id or launch_template_name) or ( if not (launch_template_id or launch_template_name) or (
launch_template_id and launch_template_name launch_template_id and launch_template_name
@ -484,7 +504,6 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.launch_template = self.ec2_backend.get_launch_template_by_name( self.launch_template = self.ec2_backend.get_launch_template_by_name(
launch_template_name launch_template_name
) )
self.launch_template_version = launch_template.get("version") or "$Default"
@staticmethod @staticmethod
def __set_string_propagate_at_launch_booleans_on_tags(tags): def __set_string_propagate_at_launch_booleans_on_tags(tags):
@ -637,7 +656,9 @@ class FakeAutoScalingGroup(CloudFormationModel):
if max_size is not None and max_size < len(self.instance_states): if max_size is not None and max_size < len(self.instance_states):
desired_capacity = max_size desired_capacity = max_size
self._set_launch_configuration(launch_config_name, launch_template) self._set_launch_configuration(
launch_config_name, launch_template, mixed_instance_policy=None
)
if health_check_period is not None: if health_check_period is not None:
self.health_check_period = health_check_period self.health_check_period = health_check_period
@ -909,8 +930,10 @@ class AutoScalingBackend(BaseBackend):
placement_group, placement_group,
termination_policies, termination_policies,
tags, tags,
capacity_rebalance=False,
new_instances_protected_from_scale_in=False, new_instances_protected_from_scale_in=False,
instance_id=None, instance_id=None,
mixed_instance_policy=None,
): ):
max_size = self.make_int(max_size) max_size = self.make_int(max_size)
min_size = self.make_int(min_size) min_size = self.make_int(min_size)
@ -921,9 +944,13 @@ class AutoScalingBackend(BaseBackend):
else: else:
health_check_period = self.make_int(health_check_period) health_check_period = self.make_int(health_check_period)
# TODO: Add MixedInstancesPolicy once implemented.
# Verify only a single launch config-like parameter is provided. # Verify only a single launch config-like parameter is provided.
params = [launch_config_name, launch_template, instance_id] params = [
launch_config_name,
launch_template,
instance_id,
mixed_instance_policy,
]
num_params = sum([1 for param in params if param]) num_params = sum([1 for param in params if param])
if num_params != 1: if num_params != 1:
@ -962,6 +989,8 @@ class AutoScalingBackend(BaseBackend):
ec2_backend=self.ec2_backend, ec2_backend=self.ec2_backend,
tags=tags, tags=tags,
new_instances_protected_from_scale_in=new_instances_protected_from_scale_in, new_instances_protected_from_scale_in=new_instances_protected_from_scale_in,
mixed_instance_policy=mixed_instance_policy,
capacity_rebalance=capacity_rebalance,
) )
self.autoscaling_groups[name] = group self.autoscaling_groups[name] = group

View File

@ -80,6 +80,7 @@ class AutoScalingResponse(BaseResponse):
return template.render() return template.render()
def create_auto_scaling_group(self): def create_auto_scaling_group(self):
params = self._get_params()
self.autoscaling_backend.create_auto_scaling_group( self.autoscaling_backend.create_auto_scaling_group(
name=self._get_param("AutoScalingGroupName"), name=self._get_param("AutoScalingGroupName"),
availability_zones=self._get_multi_param("AvailabilityZones.member"), availability_zones=self._get_multi_param("AvailabilityZones.member"),
@ -89,6 +90,7 @@ class AutoScalingResponse(BaseResponse):
instance_id=self._get_param("InstanceId"), instance_id=self._get_param("InstanceId"),
launch_config_name=self._get_param("LaunchConfigurationName"), launch_config_name=self._get_param("LaunchConfigurationName"),
launch_template=self._get_dict_param("LaunchTemplate."), launch_template=self._get_dict_param("LaunchTemplate."),
mixed_instance_policy=params.get("MixedInstancesPolicy"),
vpc_zone_identifier=self._get_param("VPCZoneIdentifier"), vpc_zone_identifier=self._get_param("VPCZoneIdentifier"),
default_cooldown=self._get_int_param("DefaultCooldown"), default_cooldown=self._get_int_param("DefaultCooldown"),
health_check_period=self._get_int_param("HealthCheckGracePeriod"), health_check_period=self._get_int_param("HealthCheckGracePeriod"),
@ -98,6 +100,7 @@ class AutoScalingResponse(BaseResponse):
placement_group=self._get_param("PlacementGroup"), placement_group=self._get_param("PlacementGroup"),
termination_policies=self._get_multi_param("TerminationPolicies.member"), termination_policies=self._get_multi_param("TerminationPolicies.member"),
tags=self._get_list_prefix("Tags.member"), tags=self._get_list_prefix("Tags.member"),
capacity_rebalance=self._get_bool_param("CapacityRebalance"),
new_instances_protected_from_scale_in=self._get_bool_param( new_instances_protected_from_scale_in=self._get_bool_param(
"NewInstancesProtectedFromScaleIn", False "NewInstancesProtectedFromScaleIn", False
), ),
@ -748,6 +751,30 @@ DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE = """<DescribeAutoScalingGroupsResponse xml
<CreatedTime>2013-05-06T17:47:15.107Z</CreatedTime> <CreatedTime>2013-05-06T17:47:15.107Z</CreatedTime>
{% if group.launch_config_name %} {% if group.launch_config_name %}
<LaunchConfigurationName>{{ group.launch_config_name }}</LaunchConfigurationName> <LaunchConfigurationName>{{ group.launch_config_name }}</LaunchConfigurationName>
{% elif group.mixed_instance_policy %}
<MixedInstancesPolicy>
<LaunchTemplate>
<LaunchTemplateSpecification>
<LaunchTemplateId>{{ group.launch_template.id }}</LaunchTemplateId>
<Version>{{ group.launch_template_version }}</Version>
<LaunchTemplateName>{{ group.launch_template.name }}</LaunchTemplateName>
</LaunchTemplateSpecification>
{% if group.mixed_instance_policy.get("LaunchTemplate", {}).get("Overrides", []) %}
<Overrides>
{% for member in group.mixed_instance_policy.get("LaunchTemplate", {}).get("Overrides", []) %}
<member>
{% if member.get("InstanceType") %}
<InstanceType>{{ member.get("InstanceType") }}</InstanceType>
{% endif %}
{% if member.get("WeightedCapacity") %}
<WeightedCapacity>{{ member.get("WeightedCapacity") }}</WeightedCapacity>
{% endif %}
</member>
{% endfor %}
</Overrides>
{% endif %}
</LaunchTemplate>
</MixedInstancesPolicy>
{% elif group.launch_template %} {% elif group.launch_template %}
<LaunchTemplate> <LaunchTemplate>
<LaunchTemplateId>{{ group.launch_template.id }}</LaunchTemplateId> <LaunchTemplateId>{{ group.launch_template.id }}</LaunchTemplateId>
@ -777,6 +804,7 @@ DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE = """<DescribeAutoScalingGroupsResponse xml
{% endfor %} {% endfor %}
</Instances> </Instances>
<DesiredCapacity>{{ group.desired_capacity }}</DesiredCapacity> <DesiredCapacity>{{ group.desired_capacity }}</DesiredCapacity>
<CapacityRebalance>{{ 'true' if group.capacity_rebalance else 'false' }}</CapacityRebalance>
<AvailabilityZones> <AvailabilityZones>
{% for availability_zone in group.availability_zones %} {% for availability_zone in group.availability_zones %}
<member>{{ availability_zone }}</member> <member>{{ availability_zone }}</member>
@ -833,6 +861,7 @@ DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE = """<DescribeAutoScalingGroupsResponse xml
{% endfor %} {% endfor %}
</EnabledMetrics> </EnabledMetrics>
{% endif %} {% endif %}
<ServiceLinkedRoleARN>{{ group.service_linked_role }}</ServiceLinkedRoleARN>
</member> </member>
{% endfor %} {% endfor %}
</AutoScalingGroups> </AutoScalingGroups>

View File

@ -11,6 +11,14 @@ autoscaling:
- TestAccAutoScalingAttachment - TestAccAutoScalingAttachment
- TestAccAutoScalingGroupDataSource - TestAccAutoScalingGroupDataSource
- TestAccAutoScalingGroupTag - TestAccAutoScalingGroupTag
- TestAccAutoScalingGroup_basic
- TestAccAutoScalingGroup_disappears
- TestAccAutoScalingGroup_nameGenerated
- TestAccAutoScalingGroup_namePrefix
- TestAccAutoScalingGroup_enablingMetrics
- TestAccAutoScalingGroup_suspendingProcesses
- TestAccAutoScalingGroup_mixedInstancesPolicy
- TestAccAutoScalingGroup_MixedInstancesPolicy_capacityRebalance
- TestAccAutoScalingLaunchConfigurationDataSource - TestAccAutoScalingLaunchConfigurationDataSource
- TestAccAutoScalingLaunchConfiguration_ - TestAccAutoScalingLaunchConfiguration_
batch: batch:

View File

@ -61,22 +61,6 @@ def test_create_autoscaling_group_from_instance():
MinSize=1, MinSize=1,
MaxSize=3, MaxSize=3,
DesiredCapacity=2, DesiredCapacity=2,
Tags=[
{
"ResourceId": "test_asg",
"ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key",
"Value": "propagate-tag-value",
"PropagateAtLaunch": True,
},
{
"ResourceId": "test_asg",
"ResourceType": "auto-scaling-group",
"Key": "not-propogated-tag-key",
"Value": "not-propagate-tag-value",
"PropagateAtLaunch": False,
},
],
VPCZoneIdentifier=mocked_instance_with_networking["subnet1"], VPCZoneIdentifier=mocked_instance_with_networking["subnet1"],
NewInstancesProtectedFromScaleIn=False, NewInstancesProtectedFromScaleIn=False,
) )
@ -874,6 +858,116 @@ def test_create_autoscaling_policy_with_predictive_scaling_config():
) )
@mock_autoscaling
@mock_ec2
def test_create_auto_scaling_group_with_mixed_instances_policy():
mocked_networking = setup_networking(region_name="eu-west-1")
client = boto3.client("autoscaling", region_name="eu-west-1")
ec2_client = boto3.client("ec2", region_name="eu-west-1")
asg_name = "asg_test"
lt = ec2_client.create_launch_template(
LaunchTemplateName="launchie",
LaunchTemplateData={"ImageId": EXAMPLE_AMI_ID},
)["LaunchTemplate"]
client.create_auto_scaling_group(
MixedInstancesPolicy={
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "launchie",
"Version": "$DEFAULT",
}
}
},
AutoScalingGroupName=asg_name,
MinSize=2,
MaxSize=2,
VPCZoneIdentifier=mocked_networking["subnet1"],
)
# Assert we can describe MixedInstancesPolicy
response = client.describe_auto_scaling_groups(AutoScalingGroupNames=[asg_name])
group = response["AutoScalingGroups"][0]
group.should.have.key("MixedInstancesPolicy").equals(
{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateId": lt["LaunchTemplateId"],
"LaunchTemplateName": "launchie",
"Version": "$DEFAULT",
}
}
}
)
# Assert the LaunchTemplate is known for the resulting instances
response = client.describe_auto_scaling_instances()
len(response["AutoScalingInstances"]).should.equal(2)
for instance in response["AutoScalingInstances"]:
instance["LaunchTemplate"].should.equal(
{
"LaunchTemplateId": lt["LaunchTemplateId"],
"LaunchTemplateName": "launchie",
"Version": "$DEFAULT",
}
)
@mock_autoscaling
@mock_ec2
def test_create_auto_scaling_group_with_mixed_instances_policy_overrides():
mocked_networking = setup_networking(region_name="eu-west-1")
client = boto3.client("autoscaling", region_name="eu-west-1")
ec2_client = boto3.client("ec2", region_name="eu-west-1")
asg_name = "asg_test"
lt = ec2_client.create_launch_template(
LaunchTemplateName="launchie",
LaunchTemplateData={"ImageId": EXAMPLE_AMI_ID},
)["LaunchTemplate"]
client.create_auto_scaling_group(
MixedInstancesPolicy={
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "launchie",
"Version": "$DEFAULT",
},
"Overrides": [
{
"InstanceType": "t2.medium",
"WeightedCapacity": "50",
}
],
}
},
AutoScalingGroupName=asg_name,
MinSize=2,
MaxSize=2,
VPCZoneIdentifier=mocked_networking["subnet1"],
)
# Assert we can describe MixedInstancesPolicy
response = client.describe_auto_scaling_groups(AutoScalingGroupNames=[asg_name])
group = response["AutoScalingGroups"][0]
group.should.have.key("MixedInstancesPolicy").equals(
{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateId": lt["LaunchTemplateId"],
"LaunchTemplateName": "launchie",
"Version": "$DEFAULT",
},
"Overrides": [
{
"InstanceType": "t2.medium",
"WeightedCapacity": "50",
}
],
}
}
)
@mock_autoscaling @mock_autoscaling
def test_set_instance_protection(): def test_set_instance_protection():
mocked_networking = setup_networking() mocked_networking = setup_networking()

View File

@ -44,6 +44,20 @@ class TestAutoScalingGroup(TestCase):
group["TerminationPolicies"].should.equal(["Default"]) group["TerminationPolicies"].should.equal(["Default"])
group["Tags"].should.equal([]) group["Tags"].should.equal([])
def test_create_autoscaling_group__additional_params(self):
self.as_client.create_auto_scaling_group(
AutoScalingGroupName="tester_group",
MinSize=1,
MaxSize=2,
LaunchConfigurationName=self.lc_name,
CapacityRebalance=True,
VPCZoneIdentifier=self.mocked_networking["subnet1"],
)
group = self.as_client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
group["AutoScalingGroupName"].should.equal("tester_group")
group["CapacityRebalance"].should.equal(True)
def test_list_many_autoscaling_groups(self): def test_list_many_autoscaling_groups(self):
for i in range(51): for i in range(51):