Skip to content

Commit

Permalink
Sets placement constraints on the TaskDefinition resource.
Browse files Browse the repository at this point in the history
Placement constraints on a service are not updateable, which means
making changes via CloudFormation would require a replacement of the ECS
service. Instead, we set the placement constraints on the task
definition, and update the existing service with the new task
definition.
  • Loading branch information
ejholmes committed Aug 9, 2017
1 parent 3b5357d commit 537490d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 18 deletions.
7 changes: 7 additions & 0 deletions scheduler/cloudformation/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ type PortMappingProperties struct {
HostPort interface{} `json:",omitempty"`
}

type PlacementConstraint struct {
Type interface{} `json:",omitempty"`
Expression interface{} `json:",omitempty"`
}

type ContainerDefinitionProperties struct {
Command interface{} `json:",omitempty"`
Cpu interface{} `json:",omitempty"`
Expand All @@ -20,12 +25,14 @@ type ContainerDefinitionProperties struct {
}

type TaskDefinitionProperties struct {
PlacementConstraints []*PlacementConstraint `json:",omitempty"`
ContainerDefinitions []*ContainerDefinitionProperties `json:",omitempty"`
Volumes []interface{}
TaskRoleArn interface{} `json:",omitempty"`
}

type CustomTaskDefinitionProperties struct {
PlacementConstraints []*PlacementConstraint `json:",omitempty"`
ContainerDefinitions []*ContainerDefinitionProperties `json:",omitempty"`
Family interface{} `json:",omitempty"`
ServiceToken interface{} `json:",omitempty"`
Expand Down
28 changes: 16 additions & 12 deletions scheduler/cloudformation/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ func (t *EmpireTemplate) addTaskDefinition(tmpl *troposphere.Template, app *twel
// running tasks.
taskRole := toInterface(taskRoleArn(app))

var placementConstraints []*PlacementConstraint
if v := p.ECS; v != nil {
if len(v.PlacementConstraints) > 0 {
for _, c := range v.PlacementConstraints {
placementConstraints = append(placementConstraints, &PlacementConstraint{
Type: c.Type,
Expression: c.Expression,
})
}
}
}

var taskDefinitionProperties interface{}
taskDefinitionType := taskDefinitionResourceType(app)
if taskDefinitionType == "Custom::ECSTaskDefinition" {
Expand All @@ -314,7 +326,8 @@ func (t *EmpireTemplate) addTaskDefinition(tmpl *troposphere.Template, app *twel
ContainerDefinitions: []*ContainerDefinitionProperties{
containerDefinition,
},
TaskRoleArn: taskRole,
TaskRoleArn: taskRole,
PlacementConstraints: placementConstraints,
}
} else {
containerDefinition.Environment = cd.Environment
Expand All @@ -323,7 +336,8 @@ func (t *EmpireTemplate) addTaskDefinition(tmpl *troposphere.Template, app *twel
ContainerDefinitions: []*ContainerDefinitionProperties{
containerDefinition,
},
TaskRoleArn: taskRole,
TaskRoleArn: taskRole,
PlacementConstraints: placementConstraints,
}
}

Expand Down Expand Up @@ -691,16 +705,6 @@ func (t *EmpireTemplate) addService(tmpl *troposphere.Template, app *twelvefacto
"ServiceToken": t.CustomResourcesTopic,
}
if v := p.ECS; v != nil {
if len(v.PlacementConstraints) > 0 {
var placementConstraints []interface{}
for _, c := range v.PlacementConstraints {
placementConstraints = append(placementConstraints, map[string]interface{}{
"Type": c.Type,
"Expression": c.Expression,
})
}
serviceProperties["PlacementConstraints"] = placementConstraints
}
if len(v.PlacementStrategy) > 0 {
var placementStrategy []interface{}
for _, c := range v.PlacementStrategy {
Expand Down
12 changes: 6 additions & 6 deletions scheduler/cloudformation/templates/ecs-extra.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,6 @@
}
}
],
"PlacementConstraints": [
{
"Expression": "attribute:ecs.instance-type =~ t2.*",
"Type": "memberOf"
}
],
"Role": "ecsServiceRole",
"ServiceName": "acme-inc-web",
"ServiceToken": "sns topic arn",
Expand All @@ -222,6 +216,12 @@
},
"webTaskDefinition": {
"Properties": {
"PlacementConstraints": [
{
"Type": "memberOf",
"Expression": "attribute:ecs.instance-type =~ t2.*"
}
],
"ContainerDefinitions": [
{
"Command": [
Expand Down
11 changes: 11 additions & 0 deletions server/cloudformation/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ type ECSTaskDefinitionProperties struct {
Family *string
TaskRoleArn *string
ContainerDefinitions []ContainerDefinition
PlacementConstraints []ECSPlacementConstraint
}

func (p *ECSTaskDefinitionProperties) ReplacementHash() (uint64, error) {
Expand Down Expand Up @@ -360,10 +361,20 @@ func (p *ECSTaskDefinitionResource) register(properties *ECSTaskDefinitionProper
if properties.Family != nil {
family = aws.String(fmt.Sprintf("%s-%s", *properties.Family, postfix))
}

var placementConstraints []*ecs.TaskDefinitionPlacementConstraint
for _, v := range properties.PlacementConstraints {
placementConstraints = append(placementConstraints, &ecs.TaskDefinitionPlacementConstraint{
Type: v.Type,
Expression: v.Expression,
})
}

resp, err := p.ecs.RegisterTaskDefinition(&ecs.RegisterTaskDefinitionInput{
Family: family,
TaskRoleArn: properties.TaskRoleArn,
ContainerDefinitions: containerDefinitions,
PlacementConstraints: placementConstraints,
})
if err != nil {
return "", fmt.Errorf("error creating task definition: %v", err)
Expand Down
12 changes: 12 additions & 0 deletions server/cloudformation/ecs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,12 @@ func TestECSTaskDefinition_Create(t *testing.T) {
},
},
},
PlacementConstraints: []*ecs.TaskDefinitionPlacementConstraint{
{
Type: aws.String("memberOf"),
Expression: aws.String("attribute:instance-type =~ t1.micro"),
},
},
}).Return(&ecs.RegisterTaskDefinitionOutput{
TaskDefinition: &ecs.TaskDefinition{
TaskDefinitionArn: aws.String("arn:aws:ecs:us-east-1:012345678901:task-definition/acme-inc-web"),
Expand All @@ -422,6 +428,12 @@ func TestECSTaskDefinition_Create(t *testing.T) {
},
},
},
PlacementConstraints: []ECSPlacementConstraint{
{
Type: aws.String("memberOf"),
Expression: aws.String("attribute:instance-type =~ t1.micro"),
},
},
},
})
assert.NoError(t, err)
Expand Down

0 comments on commit 537490d

Please sign in to comment.