Skip to content

Commit

Permalink
ECS Fargate Support (#2483)
Browse files Browse the repository at this point in the history
* ecs: fargate implementation, acctests, docs #2483

* implement review

* tabs->spaces
* default value for launch_type
* randomize resource names in tests
* improve require_compatibilities doc

* r/aws_ecs_task_definition: add field execution_role_arn

* r/aws_ecs_service: add region for fargate test

* r/aws_ecs_task_definition: fix setting field twice and return error
  • Loading branch information
apricote authored and radeksimko committed Dec 5, 2017
1 parent 844674a commit 26d9e1d
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 0 deletions.
13 changes: 13 additions & 0 deletions aws/resource_aws_ecs_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ func resourceAwsEcsService() *schema.Resource {
Optional: true,
},

"launch_type": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Default: "EC2",
},

"iam_role": {
Type: schema.TypeString,
ForceNew: true,
Expand Down Expand Up @@ -206,6 +213,10 @@ func resourceAwsEcsServiceCreate(d *schema.ResourceData, meta interface{}) error
input.Cluster = aws.String(v.(string))
}

if v, ok := d.GetOk("launch_type"); ok {
input.LaunchType = aws.String(v.(string))
}

loadBalancers := expandEcsLoadBalancers(d.Get("load_balancer").(*schema.Set).List())
if len(loadBalancers) > 0 {
log.Printf("[DEBUG] Adding ECS load balancers: %s", loadBalancers)
Expand Down Expand Up @@ -342,6 +353,8 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error {

d.Set("desired_count", service.DesiredCount)

d.Set("launch_type", service.LaunchType)

// Save cluster in the same format
if strings.HasPrefix(d.Get("cluster").(string), "arn:"+meta.(*AWSClient).partition+":ecs:") {
d.Set("cluster", service.ClusterArn)
Expand Down
101 changes: 101 additions & 0 deletions aws/resource_aws_ecs_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,24 @@ func TestAccAWSEcsServiceWithPlacementConstraints_emptyExpression(t *testing.T)
})
}

func TestAccAWSEcsServiceWithLaunchTypeFargate(t *testing.T) {
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEcsServiceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEcsServiceWithLaunchTypeFargate(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEcsServiceExists("aws_ecs_service.main"),
resource.TestCheckResourceAttr("aws_ecs_service.main", "launch_type", "FARGATE"),
),
},
},
})
}

func TestAccAWSEcsServiceWithNetworkConfiguration(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -597,6 +615,89 @@ resource "aws_ecs_service" "mongo" {
`, rInt, rInt)
}

func testAccAWSEcsServiceWithLaunchTypeFargate(rInt int) string {
return fmt.Sprintf(`
provider "aws" {
region = "us-east-1"
}
data "aws_availability_zones" "available" {}
resource "aws_vpc" "main" {
cidr_block = "10.10.0.0/16"
}
resource "aws_subnet" "main" {
count = 2
cidr_block = "${cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)}"
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
vpc_id = "${aws_vpc.main.id}"
}
resource "aws_security_group" "allow_all_a" {
name = "allow_all_a_%d"
description = "Allow all inbound traffic"
vpc_id = "${aws_vpc.main.id}"
ingress {
protocol = "6"
from_port = 80
to_port = 8000
cidr_blocks = ["${aws_vpc.main.cidr_block}"]
}
}
resource "aws_security_group" "allow_all_b" {
name = "allow_all_b_%d"
description = "Allow all inbound traffic"
vpc_id = "${aws_vpc.main.id}"
ingress {
protocol = "6"
from_port = 80
to_port = 8000
cidr_blocks = ["${aws_vpc.main.cidr_block}"]
}
}
resource "aws_ecs_cluster" "main" {
name = "tf-ecs-cluster-%d"
}
resource "aws_ecs_task_definition" "mongo" {
family = "mongodb"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
container_definitions = <<DEFINITION
[
{
"cpu": 256,
"essential": true,
"image": "mongo:latest",
"memory": 512,
"name": "mongodb",
"networkMode": "awsvpc"
}
]
DEFINITION
}
resource "aws_ecs_service" "main" {
name = "tf-ecs-service-%d"
cluster = "${aws_ecs_cluster.main.id}"
task_definition = "${aws_ecs_task_definition.mongo.arn}"
desired_count = 1
launch_type = "FARGATE"
network_configuration {
security_groups = ["${aws_security_group.allow_all_a.id}", "${aws_security_group.allow_all_b.id}"]
subnets = ["${aws_subnet.main.*.id}"]
}
}
`, rInt, rInt, rInt, rInt)
}

var testAccAWSEcsService_withIamRole = `
resource "aws_ecs_cluster" "main" {
name = "terraformecstest11"
Expand Down
48 changes: 48 additions & 0 deletions aws/resource_aws_ecs_task_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func resourceAwsEcsTaskDefinition() *schema.Resource {
Computed: true,
},

"cpu": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"family": {
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -59,6 +65,18 @@ func resourceAwsEcsTaskDefinition() *schema.Resource {
ForceNew: true,
},

"execution_role_arn": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"memory": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"network_mode": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -109,6 +127,13 @@ func resourceAwsEcsTaskDefinition() *schema.Resource {
},
},
},

"requires_compatibilities": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
Expand Down Expand Up @@ -155,6 +180,18 @@ func resourceAwsEcsTaskDefinitionCreate(d *schema.ResourceData, meta interface{}
input.TaskRoleArn = aws.String(v.(string))
}

if v, ok := d.GetOk("execution_role_arn"); ok {
input.ExecutionRoleArn = aws.String(v.(string))
}

if v, ok := d.GetOk("cpu"); ok {
input.Cpu = aws.String(v.(string))
}

if v, ok := d.GetOk("memory"); ok {
input.Memory = aws.String(v.(string))
}

if v, ok := d.GetOk("network_mode"); ok {
input.NetworkMode = aws.String(v.(string))
}
Expand Down Expand Up @@ -185,6 +222,10 @@ func resourceAwsEcsTaskDefinitionCreate(d *schema.ResourceData, meta interface{}
input.PlacementConstraints = pc
}

if v, ok := d.GetOk("requires_compatibilities"); ok && v.(*schema.Set).Len() > 0 {
input.RequiresCompatibilities = expandStringList(v.(*schema.Set).List())
}

log.Printf("[DEBUG] Registering ECS task definition: %s", input)
out, err := conn.RegisterTaskDefinition(&input)
if err != nil {
Expand Down Expand Up @@ -231,12 +272,19 @@ func resourceAwsEcsTaskDefinitionRead(d *schema.ResourceData, meta interface{})
}

d.Set("task_role_arn", taskDefinition.TaskRoleArn)
d.Set("execution_role_arn", taskDefinition.ExecutionRoleArn)
d.Set("cpu", taskDefinition.Cpu)
d.Set("memory", taskDefinition.Memory)
d.Set("network_mode", taskDefinition.NetworkMode)
d.Set("volumes", flattenEcsVolumes(taskDefinition.Volumes))
if err := d.Set("placement_constraints", flattenPlacementConstraints(taskDefinition.PlacementConstraints)); err != nil {
log.Printf("[ERR] Error setting placement_constraints for (%s): %s", d.Id(), err)
}

if err := d.Set("requires_compatibilities", flattenStringList(taskDefinition.RequiresCompatibilities)); err != nil {
return err
}

return nil
}

Expand Down
135 changes: 135 additions & 0 deletions aws/resource_aws_ecs_task_definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,48 @@ func TestAccAWSEcsTaskDefinition_arrays(t *testing.T) {
})
}

func TestAccAWSEcsTaskDefinition_Fargate(t *testing.T) {
var conf ecs.TaskDefinition
familyName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEcsTaskDefinitionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEcsTaskDefinitionFargate(familyName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEcsTaskDefinitionExists("aws_ecs_task_definition.fargate", &conf),
resource.TestCheckResourceAttr("aws_ecs_task_definition.fargate", "requires_compatibilities.#", "1"),
resource.TestCheckResourceAttr("aws_ecs_task_definition.fargate", "cpu", "256"),
resource.TestCheckResourceAttr("aws_ecs_task_definition.fargate", "memory", "512"),
),
},
},
})
}

func TestAccAWSEcsTaskDefinition_ExecutionRole(t *testing.T) {
var conf ecs.TaskDefinition
familyName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEcsTaskDefinitionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEcsTaskDefinitionExecutionRole(familyName, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEcsTaskDefinitionExists("aws_ecs_task_definition.fargate", &conf),
),
},
},
})
}

func testAccCheckEcsTaskDefinitionRecreated(t *testing.T,
before, after *ecs.TaskDefinition) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down Expand Up @@ -553,6 +595,99 @@ TASK_DEFINITION
`, familyName)
}

func testAccAWSEcsTaskDefinitionFargate(familyName string) string {
return fmt.Sprintf(`
resource "aws_ecs_task_definition" "fargate" {
family = "%s"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
container_definitions = <<TASK_DEFINITION
[
{
"name": "sleep",
"image": "busybox",
"cpu": 10,
"command": ["sleep","360"],
"memory": 10,
"essential": true
}
]
TASK_DEFINITION
}
`, familyName)
}

func testAccAWSEcsTaskDefinitionExecutionRole(familyName string, rInt int) string {
return fmt.Sprintf(`
resource "aws_iam_role" "role" {
name = "test-role-%d"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "policy" {
name = "test-policy-%d"
description = "A test policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "test-attach" {
role = "${aws_iam_role.role.name}"
policy_arn = "${aws_iam_policy.policy.arn}"
}
resource "aws_ecs_task_definition" "fargate" {
family = "%s"
execution_role_arn = "${aws_iam_role.role.arn}"
container_definitions = <<TASK_DEFINITION
[
{
"name": "sleep",
"image": "busybox",
"cpu": 10,
"command": ["sleep","360"],
"memory": 10,
"essential": true
}
]
TASK_DEFINITION
}
`, rInt, rInt, familyName)
}

var testAccAWSEcsTaskDefinitionWithScratchVolume = `
resource "aws_ecs_task_definition" "sleep" {
family = "terraform-acc-sc-volume-test"
Expand Down
Loading

0 comments on commit 26d9e1d

Please sign in to comment.