Autoscaling - add support for TargetTracking/StepAdjustments in scaling policy (#4449)

This commit is contained in:
Bert Blommers 2021-10-20 17:49:23 +00:00 committed by GitHub
parent ecd8d2478f
commit fff69b9faa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 13 deletions

View File

@ -7,7 +7,7 @@ from moto.packages.boto.ec2.blockdevicemapping import (
from moto.ec2.exceptions import InvalidInstanceIdError from moto.ec2.exceptions import InvalidInstanceIdError
from collections import OrderedDict from collections import OrderedDict
from moto.core import BaseBackend, BaseModel, CloudFormationModel from moto.core import ACCOUNT_ID, BaseBackend, BaseModel, CloudFormationModel
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.elb import elb_backends from moto.elb import elb_backends
@ -70,6 +70,8 @@ class FakeScalingPolicy(BaseModel):
as_name, as_name,
scaling_adjustment, scaling_adjustment,
cooldown, cooldown,
target_tracking_config,
step_adjustments,
autoscaling_backend, autoscaling_backend,
): ):
self.name = name self.name = name
@ -81,8 +83,14 @@ class FakeScalingPolicy(BaseModel):
self.cooldown = cooldown self.cooldown = cooldown
else: else:
self.cooldown = DEFAULT_COOLDOWN self.cooldown = DEFAULT_COOLDOWN
self.target_tracking_config = target_tracking_config
self.step_adjustments = step_adjustments
self.autoscaling_backend = autoscaling_backend self.autoscaling_backend = autoscaling_backend
@property
def arn(self):
return f"arn:aws:autoscaling:{self.autoscaling_backend.region}:{ACCOUNT_ID}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/{self.as_name}:policyName/{self.name}"
def execute(self): def execute(self):
if self.adjustment_type == "ExactCapacity": if self.adjustment_type == "ExactCapacity":
self.autoscaling_backend.set_desired_capacity( self.autoscaling_backend.set_desired_capacity(
@ -612,6 +620,7 @@ class AutoScalingBackend(BaseBackend):
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
self.elb_backend = elb_backend self.elb_backend = elb_backend
self.elbv2_backend = elbv2_backend self.elbv2_backend = elbv2_backend
self.region = self.elbv2_backend.region_name
def reset(self): def reset(self):
ec2_backend = self.ec2_backend ec2_backend = self.ec2_backend
@ -943,7 +952,15 @@ class AutoScalingBackend(BaseBackend):
self.lifecycle_hooks.pop("%s_%s" % (as_name, name), None) self.lifecycle_hooks.pop("%s_%s" % (as_name, name), None)
def create_autoscaling_policy( def create_autoscaling_policy(
self, name, policy_type, adjustment_type, as_name, scaling_adjustment, cooldown self,
name,
policy_type,
adjustment_type,
as_name,
scaling_adjustment,
cooldown,
target_tracking_config,
step_adjustments,
): ):
policy = FakeScalingPolicy( policy = FakeScalingPolicy(
name, name,
@ -952,6 +969,8 @@ class AutoScalingBackend(BaseBackend):
as_name, as_name,
scaling_adjustment, scaling_adjustment,
cooldown, cooldown,
target_tracking_config,
step_adjustments,
self, self,
) )

View File

@ -273,13 +273,16 @@ class AutoScalingResponse(BaseResponse):
return template.render() return template.render()
def put_scaling_policy(self): def put_scaling_policy(self):
params = self._get_params()
policy = self.autoscaling_backend.create_autoscaling_policy( policy = self.autoscaling_backend.create_autoscaling_policy(
name=self._get_param("PolicyName"), name=params.get("PolicyName"),
policy_type=self._get_param("PolicyType"), policy_type=params.get("PolicyType", "SimpleScaling"),
adjustment_type=self._get_param("AdjustmentType"), adjustment_type=params.get("AdjustmentType"),
as_name=self._get_param("AutoScalingGroupName"), as_name=params.get("AutoScalingGroupName"),
scaling_adjustment=self._get_int_param("ScalingAdjustment"), scaling_adjustment=self._get_int_param("ScalingAdjustment"),
cooldown=self._get_int_param("Cooldown"), cooldown=self._get_int_param("Cooldown"),
target_tracking_config=params.get("TargetTrackingConfiguration", {}),
step_adjustments=params.get("StepAdjustments", []),
) )
template = self.response_template(CREATE_SCALING_POLICY_TEMPLATE) template = self.response_template(CREATE_SCALING_POLICY_TEMPLATE)
return template.render(policy=policy) return template.render(policy=policy)
@ -801,14 +804,39 @@ DESCRIBE_SCALING_POLICIES_TEMPLATE = """<DescribePoliciesResponse xmlns="http://
<ScalingPolicies> <ScalingPolicies>
{% for policy in policies %} {% for policy in policies %}
<member> <member>
<PolicyARN>arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c322 <PolicyARN>{{ policy.arn }}</PolicyARN>
761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/my-test-asg:policyName/MyScaleDownPolicy</PolicyARN>
<AdjustmentType>{{ policy.adjustment_type }}</AdjustmentType> <AdjustmentType>{{ policy.adjustment_type }}</AdjustmentType>
{% if policy.scaling_adjustment %}
<ScalingAdjustment>{{ policy.scaling_adjustment }}</ScalingAdjustment> <ScalingAdjustment>{{ policy.scaling_adjustment }}</ScalingAdjustment>
{% endif %}
<PolicyName>{{ policy.name }}</PolicyName> <PolicyName>{{ policy.name }}</PolicyName>
<PolicyType>{{ policy.policy_type }}</PolicyType> <PolicyType>{{ policy.policy_type }}</PolicyType>
<AutoScalingGroupName>{{ policy.as_name }}</AutoScalingGroupName> <AutoScalingGroupName>{{ policy.as_name }}</AutoScalingGroupName>
{% if policy.policy_type == 'SimpleScaling' %}
<Cooldown>{{ policy.cooldown }}</Cooldown> <Cooldown>{{ policy.cooldown }}</Cooldown>
{% endif %}
{% if policy.policy_type == 'TargetTrackingScaling' %}
<TargetTrackingConfiguration>
<PredefinedMetricSpecification>
<PredefinedMetricType>{{ policy.target_tracking_config.get("PredefinedMetricSpecification", {}).get("PredefinedMetricType", "") }}</PredefinedMetricType>
{% if policy.target_tracking_config.get("PredefinedMetricSpecification", {}).get("ResourceLabel") %}
<ResourceLabel>{{ policy.target_tracking_config.get("PredefinedMetricSpecification", {}).get("ResourceLabel") }}</ResourceLabel>
{% endif %}
</PredefinedMetricSpecification>
<TargetValue>{{ policy.target_tracking_config.get("TargetValue") }}</TargetValue>
</TargetTrackingConfiguration>
{% endif %}
{% if policy.policy_type == 'StepScaling' %}
<StepAdjustments>
{% for step in policy.step_adjustments %}
<entry>
<MetricIntervalLowerBound>{{ step.get("MetricIntervalLowerBound") }}</MetricIntervalLowerBound>
<MetricIntervalUpperBound>{{ step.get("MetricIntervalUpperBound") }}</MetricIntervalUpperBound>
<ScalingAdjustment>{{ step.get("ScalingAdjustment") }}</ScalingAdjustment>
</entry>
{% endfor %}
</StepAdjustments>
{% endif %}
<Alarms/> <Alarms/>
</member> </member>
{% endfor %} {% endfor %}

View File

@ -17,6 +17,7 @@ from moto import (
mock_autoscaling_deprecated, mock_autoscaling_deprecated,
mock_ec2, mock_ec2,
) )
from moto.core import ACCOUNT_ID
from tests.helpers import requires_boto_gte from tests.helpers import requires_boto_gte
from .utils import ( from .utils import (
@ -1597,7 +1598,16 @@ def test_autoscaling_describe_policies_boto3():
PolicyTypes=["SimpleScaling"], PolicyTypes=["SimpleScaling"],
) )
response["ScalingPolicies"].should.have.length_of(1) response["ScalingPolicies"].should.have.length_of(1)
response["ScalingPolicies"][0]["PolicyName"].should.equal("test_policy_down") policy = response["ScalingPolicies"][0]
policy["PolicyType"].should.equal("SimpleScaling")
policy["AdjustmentType"].should.equal("PercentChangeInCapacity")
policy["ScalingAdjustment"].should.equal(-10)
policy["Cooldown"].should.equal(60)
policy["PolicyARN"].should.equal(
f"arn:aws:autoscaling:us-east-1:{ACCOUNT_ID}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/test_asg:policyName/test_policy_down"
)
policy["PolicyName"].should.equal("test_policy_down")
policy.shouldnt.have.key("TargetTrackingConfiguration")
@mock_elb @mock_elb
@ -1677,6 +1687,114 @@ def test_detach_one_instance_decrement():
) )
@mock_autoscaling
@mock_ec2
def test_create_autoscaling_policy_with_policytype__targettrackingscaling():
mocked_networking = setup_networking(region_name="us-west-1")
client = boto3.client("autoscaling", region_name="us-west-1")
configuration_name = "test"
asg_name = "asg_test"
client.create_launch_configuration(
LaunchConfigurationName=configuration_name,
ImageId=EXAMPLE_AMI_ID,
InstanceType="m1.small",
)
client.create_auto_scaling_group(
LaunchConfigurationName=configuration_name,
AutoScalingGroupName=asg_name,
MinSize=1,
MaxSize=2,
VPCZoneIdentifier=mocked_networking["subnet1"],
)
client.put_scaling_policy(
AutoScalingGroupName=asg_name,
PolicyName=configuration_name,
PolicyType="TargetTrackingScaling",
EstimatedInstanceWarmup=100,
TargetTrackingConfiguration={
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageNetworkIn",
},
"TargetValue": 1000000.0,
},
)
resp = client.describe_policies(AutoScalingGroupName=asg_name)
policy = resp["ScalingPolicies"][0]
policy.should.have.key("PolicyName").equals(configuration_name)
policy.should.have.key("PolicyARN").equals(
f"arn:aws:autoscaling:us-west-1:{ACCOUNT_ID}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/{asg_name}:policyName/{configuration_name}"
)
policy.should.have.key("PolicyType").equals("TargetTrackingScaling")
policy.should.have.key("TargetTrackingConfiguration").should.equal(
{
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageNetworkIn",
},
"TargetValue": 1000000.0,
}
)
policy.shouldnt.have.key("ScalingAdjustment")
policy.shouldnt.have.key("Cooldown")
@mock_autoscaling
@mock_ec2
def test_create_autoscaling_policy_with_policytype__stepscaling():
mocked_networking = setup_networking(region_name="eu-west-1")
client = boto3.client("autoscaling", region_name="eu-west-1")
launch_config_name = "lg_name"
asg_name = "asg_test"
client.create_launch_configuration(
LaunchConfigurationName=launch_config_name,
ImageId=EXAMPLE_AMI_ID,
InstanceType="m1.small",
)
client.create_auto_scaling_group(
LaunchConfigurationName=launch_config_name,
AutoScalingGroupName=asg_name,
MinSize=1,
MaxSize=2,
VPCZoneIdentifier=mocked_networking["subnet1"],
)
client.put_scaling_policy(
AutoScalingGroupName=asg_name,
PolicyName=launch_config_name,
PolicyType="StepScaling",
StepAdjustments=[
{
"MetricIntervalLowerBound": 2,
"MetricIntervalUpperBound": 8,
"ScalingAdjustment": 1,
}
],
)
resp = client.describe_policies(AutoScalingGroupName=asg_name)
policy = resp["ScalingPolicies"][0]
policy.should.have.key("PolicyName").equals(launch_config_name)
policy.should.have.key("PolicyARN").equals(
f"arn:aws:autoscaling:eu-west-1:{ACCOUNT_ID}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/{asg_name}:policyName/{launch_config_name}"
)
policy.should.have.key("PolicyType").equals("StepScaling")
policy.should.have.key("StepAdjustments").equal(
[
{
"MetricIntervalLowerBound": 2,
"MetricIntervalUpperBound": 8,
"ScalingAdjustment": 1,
}
]
)
policy.shouldnt.have.key("TargetTrackingConfiguration")
policy.shouldnt.have.key("ScalingAdjustment")
policy.shouldnt.have.key("Cooldown")
@mock_elb @mock_elb
@mock_autoscaling @mock_autoscaling
@mock_ec2 @mock_ec2

View File

@ -4,14 +4,14 @@ from moto import mock_ec2, mock_ec2_deprecated
@mock_ec2 @mock_ec2
def setup_networking(): def setup_networking(region_name="us-east-1"):
ec2 = boto3.resource("ec2", region_name="us-east-1") ec2 = boto3.resource("ec2", region_name=region_name)
vpc = ec2.create_vpc(CidrBlock="10.11.0.0/16") vpc = ec2.create_vpc(CidrBlock="10.11.0.0/16")
subnet1 = ec2.create_subnet( subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="10.11.1.0/24", AvailabilityZone="us-east-1a" VpcId=vpc.id, CidrBlock="10.11.1.0/24", AvailabilityZone=f"{region_name}a"
) )
subnet2 = ec2.create_subnet( subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="10.11.2.0/24", AvailabilityZone="us-east-1b" VpcId=vpc.id, CidrBlock="10.11.2.0/24", AvailabilityZone=f"{region_name}b"
) )
return {"vpc": vpc.id, "subnet1": subnet1.id, "subnet2": subnet2.id} return {"vpc": vpc.id, "subnet1": subnet1.id, "subnet2": subnet2.id}