From 10b8fabaaefcd978f88ddf6dfb80523929fee708 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof <nicolas.deloof@gmail.com> Date: Tue, 29 Sep 2020 15:11:31 +0200 Subject: [PATCH] Allow user to set ami/machine by Deploy.Placement.Constraint Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com> --- ecs/backend.go | 14 +++++------ ecs/cloudformation_test.go | 6 ++--- ecs/compatibility.go | 2 ++ ecs/ec2.go | 50 +++++++++++++++++++++++++++++++------- ecs/ec2_test.go | 49 +++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 ecs/ec2_test.go diff --git a/ecs/backend.go b/ecs/backend.go index ab5b3ddec..7e015db95 100644 --- a/ecs/backend.go +++ b/ecs/backend.go @@ -88,23 +88,23 @@ type ecsAPIService struct { aws API } -func (a *ecsAPIService) ContainerService() containers.Service { +func (b *ecsAPIService) ContainerService() containers.Service { return nil } -func (a *ecsAPIService) ComposeService() compose.Service { - return a +func (b *ecsAPIService) ComposeService() compose.Service { + return b } -func (a *ecsAPIService) SecretsService() secrets.Service { - return a +func (b *ecsAPIService) SecretsService() secrets.Service { + return b } -func (a *ecsAPIService) VolumeService() volumes.Service { +func (b *ecsAPIService) VolumeService() volumes.Service { return nil } -func (a *ecsAPIService) ResourceService() resources.Service { +func (b *ecsAPIService) ResourceService() resources.Service { return nil } diff --git a/ecs/cloudformation_test.go b/ecs/cloudformation_test.go index 06176822c..ff060292c 100644 --- a/ecs/cloudformation_test.go +++ b/ecs/cloudformation_test.go @@ -23,21 +23,19 @@ import ( "reflect" "testing" - "github.com/awslabs/goformation/v4/cloudformation/efs" - - "github.com/golang/mock/gomock" - "github.com/docker/compose-cli/api/compose" "github.com/aws/aws-sdk-go/service/elbv2" "github.com/awslabs/goformation/v4/cloudformation" "github.com/awslabs/goformation/v4/cloudformation/ec2" "github.com/awslabs/goformation/v4/cloudformation/ecs" + "github.com/awslabs/goformation/v4/cloudformation/efs" "github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2" "github.com/awslabs/goformation/v4/cloudformation/iam" "github.com/awslabs/goformation/v4/cloudformation/logs" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" + "github.com/golang/mock/gomock" "gotest.tools/v3/assert" "gotest.tools/v3/golden" ) diff --git a/ecs/compatibility.go b/ecs/compatibility.go index 4c42fc434..eb663a6ce 100644 --- a/ecs/compatibility.go +++ b/ecs/compatibility.go @@ -54,6 +54,8 @@ var compatibleComposeAttributes = []string{ "services.cap_drop", "services.depends_on", "services.deploy", + "services.deploy.placement", + "services.deploy.placement.constraints", "services.deploy.replicas", "services.deploy.resources.limits", "services.deploy.resources.limits.cpus", diff --git a/ecs/ec2.go b/ecs/ec2.go index fae76ed51..7438598ae 100644 --- a/ecs/ec2.go +++ b/ecs/ec2.go @@ -20,6 +20,7 @@ import ( "context" "encoding/base64" "fmt" + "strings" "github.com/awslabs/goformation/v4/cloudformation" "github.com/awslabs/goformation/v4/cloudformation/autoscaling" @@ -28,11 +29,22 @@ import ( "github.com/compose-spec/compose-go/types" ) +const ( + placementConstraintAMI = "node.ami == " + placementConstraintMachine = "node.machine == " +) + func (b *ecsAPIService) createCapacityProvider(ctx context.Context, project *types.Project, template *cloudformation.Template, resources awsResources) error { - var ec2 bool - for _, s := range project.Services { - if requireEC2(s) { + var ( + ec2 bool + ami string + machineType string + ) + for _, service := range project.Services { + if requireEC2(service) { ec2 = true + // TODO once we can assign a service to a CapacityProvider, we could run this _per service_ + ami, machineType = getUserDefinedMachine(service) break } } @@ -41,14 +53,20 @@ func (b *ecsAPIService) createCapacityProvider(ctx context.Context, project *typ return nil } - ami, err := b.aws.GetParameter(ctx, "/aws/service/ecs/optimized-ami/amazon-linux-2/gpu/recommended") - if err != nil { - return err + if ami == "" { + recommended, err := b.aws.GetParameter(ctx, "/aws/service/ecs/optimized-ami/amazon-linux-2/gpu/recommended") + if err != nil { + return err + } + ami = recommended } - machineType, err := guessMachineType(project) - if err != nil { - return err + if machineType == "" { + t, err := guessMachineType(project) + if err != nil { + return err + } + machineType = t } template.Resources["CapacityProvider"] = &ecs.CapacityProvider{ @@ -98,3 +116,17 @@ func (b *ecsAPIService) createCapacityProvider(ctx context.Context, project *typ return nil } + +func getUserDefinedMachine(s types.ServiceConfig) (ami string, machineType string) { + if s.Deploy != nil { + for _, s := range s.Deploy.Placement.Constraints { + if strings.HasPrefix(s, placementConstraintAMI) { + ami = s[len(placementConstraintAMI):] + } + if strings.HasPrefix(s, placementConstraintMachine) { + machineType = s[len(placementConstraintMachine):] + } + } + } + return ami, machineType +} diff --git a/ecs/ec2_test.go b/ecs/ec2_test.go new file mode 100644 index 000000000..db476c73d --- /dev/null +++ b/ecs/ec2_test.go @@ -0,0 +1,49 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package ecs + +import ( + "testing" + + "github.com/awslabs/goformation/v4/cloudformation/autoscaling" + "gotest.tools/v3/assert" +) + +func TestUserDefinedAMI(t *testing.T) { + template := convertYaml(t, ` +services: + test: + image: "image" + deploy: + placement: + constraints: + - "node.ami == ami123456789" + - "node.machine == t0.femto" + resources: + # devices: + # - capabilities: ["gpu"] + reservations: + memory: 8Gb + generic_resources: + - discrete_resource_spec: + kind: gpus + value: 1 +`, useDefaultVPC) + lc := template.Resources["LaunchConfiguration"].(*autoscaling.LaunchConfiguration) + assert.Check(t, lc.ImageId == "ami123456789") + assert.Check(t, lc.InstanceType == "t0.femto") +}