diff --git a/moto/autoscaling/models.py b/moto/autoscaling/models.py index 079b6bcbf..1a2db05c5 100644 --- a/moto/autoscaling/models.py +++ b/moto/autoscaling/models.py @@ -7,7 +7,7 @@ from moto.packages.boto.ec2.blockdevicemapping import ( from moto.ec2.exceptions import InvalidInstanceIdError 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.ec2 import ec2_backends from moto.elb import elb_backends @@ -70,6 +70,8 @@ class FakeScalingPolicy(BaseModel): as_name, scaling_adjustment, cooldown, + target_tracking_config, + step_adjustments, autoscaling_backend, ): self.name = name @@ -81,8 +83,14 @@ class FakeScalingPolicy(BaseModel): self.cooldown = cooldown else: self.cooldown = DEFAULT_COOLDOWN + self.target_tracking_config = target_tracking_config + self.step_adjustments = step_adjustments 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): if self.adjustment_type == "ExactCapacity": self.autoscaling_backend.set_desired_capacity( @@ -612,6 +620,7 @@ class AutoScalingBackend(BaseBackend): self.ec2_backend = ec2_backend self.elb_backend = elb_backend self.elbv2_backend = elbv2_backend + self.region = self.elbv2_backend.region_name def reset(self): ec2_backend = self.ec2_backend @@ -943,7 +952,15 @@ class AutoScalingBackend(BaseBackend): self.lifecycle_hooks.pop("%s_%s" % (as_name, name), None) 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( name, @@ -952,6 +969,8 @@ class AutoScalingBackend(BaseBackend): as_name, scaling_adjustment, cooldown, + target_tracking_config, + step_adjustments, self, ) diff --git a/moto/autoscaling/responses.py b/moto/autoscaling/responses.py index 2e5f1f462..4105a426d 100644 --- a/moto/autoscaling/responses.py +++ b/moto/autoscaling/responses.py @@ -273,13 +273,16 @@ class AutoScalingResponse(BaseResponse): return template.render() def put_scaling_policy(self): + params = self._get_params() policy = self.autoscaling_backend.create_autoscaling_policy( - name=self._get_param("PolicyName"), - policy_type=self._get_param("PolicyType"), - adjustment_type=self._get_param("AdjustmentType"), - as_name=self._get_param("AutoScalingGroupName"), + name=params.get("PolicyName"), + policy_type=params.get("PolicyType", "SimpleScaling"), + adjustment_type=params.get("AdjustmentType"), + as_name=params.get("AutoScalingGroupName"), scaling_adjustment=self._get_int_param("ScalingAdjustment"), 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) return template.render(policy=policy) @@ -801,14 +804,39 @@ DESCRIBE_SCALING_POLICIES_TEMPLATE = """ + {% if policy.target_tracking_config.get("PredefinedMetricSpecification", {}).get("ResourceLabel") %} + {{ policy.target_tracking_config.get("PredefinedMetricSpecification", {}).get("ResourceLabel") }} + {% endif %} + + {{ policy.target_tracking_config.get("TargetValue") }} + + {% endif %} + {% if policy.policy_type == 'StepScaling' %} + + {% for step in policy.step_adjustments %} + + {{ step.get("MetricIntervalLowerBound") }} + {{ step.get("MetricIntervalUpperBound") }} + {{ step.get("ScalingAdjustment") }} + + {% endfor %} + + {% endif %} {% endfor %} diff --git a/tests/test_autoscaling/test_autoscaling.py b/tests/test_autoscaling/test_autoscaling.py index 213ea22f5..e6627d811 100644 --- a/tests/test_autoscaling/test_autoscaling.py +++ b/tests/test_autoscaling/test_autoscaling.py @@ -17,6 +17,7 @@ from moto import ( mock_autoscaling_deprecated, mock_ec2, ) +from moto.core import ACCOUNT_ID from tests.helpers import requires_boto_gte from .utils import ( @@ -1597,7 +1598,16 @@ def test_autoscaling_describe_policies_boto3(): PolicyTypes=["SimpleScaling"], ) 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 @@ -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_autoscaling @mock_ec2 diff --git a/tests/test_autoscaling/utils.py b/tests/test_autoscaling/utils.py index b7fb75e6c..96a192747 100644 --- a/tests/test_autoscaling/utils.py +++ b/tests/test_autoscaling/utils.py @@ -4,14 +4,14 @@ from moto import mock_ec2, mock_ec2_deprecated @mock_ec2 -def setup_networking(): - ec2 = boto3.resource("ec2", region_name="us-east-1") +def setup_networking(region_name="us-east-1"): + ec2 = boto3.resource("ec2", region_name=region_name) vpc = ec2.create_vpc(CidrBlock="10.11.0.0/16") 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( - 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}