diff --git a/ecs/autoscaling.go b/ecs/autoscaling.go index 7a40938d7..927d614cc 100644 --- a/ecs/autoscaling.go +++ b/ecs/autoscaling.go @@ -17,6 +17,7 @@ package ecs import ( + "encoding/json" "fmt" applicationautoscaling2 "github.com/aws/aws-sdk-go/service/applicationautoscaling" @@ -26,13 +27,37 @@ import ( "github.com/compose-spec/compose-go/types" ) -func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resources awsResources, template *cloudformation.Template, service types.ServiceConfig) { +type autoscalingConfig struct { + Memory int `json:"memory,omitempty"` + CPU int `json:"cpu,omitempty"` + Min int `json:"min,omitempty"` + Max int `json:"max,omitempty"` +} + +func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resources awsResources, template *cloudformation.Template, service types.ServiceConfig) error { if service.Deploy == nil { - return + return nil } v, ok := service.Deploy.Extensions[extensionAutoScaling] if !ok { - return + return nil + } + + marshalled, err := json.Marshal(v) + if err != nil { + return err + } + var config autoscalingConfig + err = json.Unmarshal(marshalled, &config) + if err != nil { + return err + } + + if config.Memory != 0 && config.CPU != 0 { + return fmt.Errorf("%s can't be set with both cpu and memory targets", extensionAutoScaling) + } + if config.Max == 0 { + return fmt.Errorf("%s MUST define max replicas", extensionAutoScaling) } role := fmt.Sprintf("%sAutoScalingRole", normalizeResourceName(service.Name)) @@ -66,8 +91,8 @@ func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resource target := fmt.Sprintf("%sScalableTarget", normalizeResourceName(service.Name)) template.Resources[target] = &applicationautoscaling.ScalableTarget{ - MaxCapacity: 10, - MinCapacity: 0, + MaxCapacity: config.Max, + MinCapacity: config.Min, ResourceId: resourceID, RoleARN: cloudformation.GetAtt(role, "Arn"), ScalableDimension: applicationautoscaling2.ScalableDimensionEcsServiceDesiredCount, @@ -75,6 +100,15 @@ func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resource AWSCloudFormationDependsOn: []string{serviceResourceName(service.Name)}, } + var ( + metric = applicationautoscaling2.MetricTypeEcsserviceAverageCpuutilization + targetPercent = config.CPU + ) + if config.Memory != 0 { + metric = applicationautoscaling2.MetricTypeEcsserviceAverageMemoryUtilization + targetPercent = config.Memory + } + policy := fmt.Sprintf("%sScalingPolicy", normalizeResourceName(service.Name)) template.Resources[policy] = &applicationautoscaling.ScalingPolicy{ PolicyType: "TargetTrackingScaling", @@ -83,11 +117,12 @@ func (b *ecsAPIService) createAutoscalingPolicy(project *types.Project, resource StepScalingPolicyConfiguration: nil, TargetTrackingScalingPolicyConfiguration: &applicationautoscaling.ScalingPolicy_TargetTrackingScalingPolicyConfiguration{ PredefinedMetricSpecification: &applicationautoscaling.ScalingPolicy_PredefinedMetricSpecification{ - PredefinedMetricType: applicationautoscaling2.MetricTypeEcsserviceAverageCpuutilization, + PredefinedMetricType: metric, }, ScaleOutCooldown: 60, ScaleInCooldown: 60, - TargetValue: float64(v.(int)), + TargetValue: float64(targetPercent), }, } + return nil } diff --git a/ecs/autoscaling_test.go b/ecs/autoscaling_test.go index d91b768c8..270323162 100644 --- a/ecs/autoscaling_test.go +++ b/ecs/autoscaling_test.go @@ -29,13 +29,15 @@ services: foo: image: hello_world deploy: - x-aws-autoscaling: 75 + x-aws-autoscaling: + cpu: 75 + max: 10 `, useDefaultVPC) target := template.Resources["FooScalableTarget"].(*autoscaling.ScalableTarget) assert.Check(t, target != nil) + assert.Check(t, target.MaxCapacity == 10) //nolint:staticcheck + policy := template.Resources["FooScalingPolicy"].(*autoscaling.ScalingPolicy) - if policy == nil || policy.TargetTrackingScalingPolicyConfiguration == nil { - t.Fail() - } - assert.Check(t, policy.TargetTrackingScalingPolicyConfiguration.TargetValue == float64(75)) + assert.Check(t, policy != nil) + assert.Check(t, policy.TargetTrackingScalingPolicyConfiguration.TargetValue == float64(75)) //nolint:staticcheck } diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go index e284dd514..f098d99d8 100644 --- a/ecs/cloudformation.go +++ b/ecs/cloudformation.go @@ -83,7 +83,10 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c return nil, err } - b.createAutoscalingPolicy(project, resources, template, service) + err = b.createAutoscalingPolicy(project, resources, template, service) + if err != nil { + return nil, err + } } err = b.createCapacityProvider(ctx, project, template, resources)