Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix "random" ECS task placement strategy #6176

Merged
merged 2 commits into from
Oct 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions aws/resource_aws_ecs_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,11 +648,14 @@ func flattenPlacementStrategyDeprecated(pss []*ecs.PlacementStrategy) []map[stri
for _, ps := range pss {
c := make(map[string]interface{})
c["type"] = *ps.Type
c["field"] = *ps.Field

// for some fields the API requires lowercase for creation but will return uppercase on query
if *ps.Field == "MEMORY" || *ps.Field == "CPU" {
c["field"] = strings.ToLower(*ps.Field)
if ps.Field != nil {
c["field"] = *ps.Field

// for some fields the API requires lowercase for creation but will return uppercase on query
if *ps.Field == "MEMORY" || *ps.Field == "CPU" {
c["field"] = strings.ToLower(*ps.Field)
}
}

results = append(results, c)
Expand All @@ -664,40 +667,48 @@ func expandPlacementStrategy(s []interface{}) ([]*ecs.PlacementStrategy, error)
if len(s) == 0 {
return nil, nil
}
ps := make([]*ecs.PlacementStrategy, 0)
pss := make([]*ecs.PlacementStrategy, 0)
for _, raw := range s {
p := raw.(map[string]interface{})
t := p["type"].(string)
f := p["field"].(string)
if err := validateAwsEcsPlacementStrategy(t, f); err != nil {
return nil, err
}
ps = append(ps, &ecs.PlacementStrategy{
Type: aws.String(t),
Field: aws.String(f),
})
ps := &ecs.PlacementStrategy{
Type: aws.String(t),
}
if f != "" {
// Field must be omitted (i.e. not empty string) for random strategy
ps.Field = aws.String(f)
}
pss = append(pss, ps)
}
return ps, nil
return pss, nil
}

func expandPlacementStrategyDeprecated(s *schema.Set) ([]*ecs.PlacementStrategy, error) {
if len(s.List()) == 0 {
return nil, nil
}
ps := make([]*ecs.PlacementStrategy, 0)
pss := make([]*ecs.PlacementStrategy, 0)
for _, raw := range s.List() {
p := raw.(map[string]interface{})
t := p["type"].(string)
f := p["field"].(string)
if err := validateAwsEcsPlacementStrategy(t, f); err != nil {
return nil, err
}
ps = append(ps, &ecs.PlacementStrategy{
Type: aws.String(t),
Field: aws.String(f),
})
ps := &ecs.PlacementStrategy{
Type: aws.String(t),
}
if f != "" {
// Field must be omitted (i.e. not empty string) for random strategy
ps.Field = aws.String(f)
}
pss = append(pss, ps)
}
return ps, nil
return pss, nil
}

func flattenPlacementStrategy(pss []*ecs.PlacementStrategy) []interface{} {
Expand All @@ -708,11 +719,14 @@ func flattenPlacementStrategy(pss []*ecs.PlacementStrategy) []interface{} {
for _, ps := range pss {
c := make(map[string]interface{})
c["type"] = *ps.Type
c["field"] = *ps.Field

// for some fields the API requires lowercase for creation but will return uppercase on query
if *ps.Field == "MEMORY" || *ps.Field == "CPU" {
c["field"] = strings.ToLower(*ps.Field)
if ps.Field != nil {
c["field"] = *ps.Field

// for some fields the API requires lowercase for creation but will return uppercase on query
if *ps.Field == "MEMORY" || *ps.Field == "CPU" {
c["field"] = strings.ToLower(*ps.Field)
}
}

results = append(results, c)
Expand Down
42 changes: 42 additions & 0 deletions aws/resource_aws_ecs_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,15 @@ func TestAccAWSEcsService_withPlacementStrategy(t *testing.T) {
resource.TestCheckResourceAttr("aws_ecs_service.mongo", "ordered_placement_strategy.0.field", "memory"),
),
},
{
Config: testAccAWSEcsServiceWithRandomPlacementStrategy(clusterName, tdName, svcName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEcsServiceExists("aws_ecs_service.mongo", &service),
resource.TestCheckResourceAttr("aws_ecs_service.mongo", "ordered_placement_strategy.#", "1"),
resource.TestCheckResourceAttr("aws_ecs_service.mongo", "ordered_placement_strategy.0.type", "random"),
resource.TestCheckResourceAttr("aws_ecs_service.mongo", "ordered_placement_strategy.0.field", ""),
),
},
{
Config: testAccAWSEcsServiceWithMultiPlacementStrategy(clusterName, tdName, svcName),
Check: resource.ComposeTestCheckFunc(
Expand Down Expand Up @@ -997,6 +1006,39 @@ resource "aws_ecs_service" "mongo" {
`, clusterName, tdName, svcName)
}

func testAccAWSEcsServiceWithRandomPlacementStrategy(clusterName, tdName, svcName string) string {
return fmt.Sprintf(`
resource "aws_ecs_cluster" "default" {
name = "%s"
}

resource "aws_ecs_task_definition" "mongo" {
family = "%s"
container_definitions = <<DEFINITION
[
{
"cpu": 128,
"essential": true,
"image": "mongo:latest",
"memory": 128,
"name": "mongodb"
}
]
DEFINITION
}

resource "aws_ecs_service" "mongo" {
name = "%s"
cluster = "${aws_ecs_cluster.default.id}"
task_definition = "${aws_ecs_task_definition.mongo.arn}"
desired_count = 1
ordered_placement_strategy {
type = "random"
}
}
`, clusterName, tdName, svcName)
}

func testAccAWSEcsServiceWithMultiPlacementStrategy(clusterName, tdName, svcName string) string {
return fmt.Sprintf(`
resource "aws_ecs_cluster" "default" {
Expand Down
7 changes: 5 additions & 2 deletions aws/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -785,8 +785,11 @@ func validateAwsDynamoDbGlobalTableName(v interface{}, k string) (ws []string, e
func validateAwsEcsPlacementStrategy(stratType, stratField string) error {
switch stratType {
case "random":
// random does not need the field attribute set, could error, but it isn't read at the API level
return nil
// random requires the field attribute to be unset.
if stratField != "" {
return fmt.Errorf("Random type requires the field attribute to be unset. Got: %s",
stratField)
}
case "spread":
// For the spread placement strategy, valid values are instanceId
// (or host, which has the same effect), or any platform or custom attribute
Expand Down