diff --git a/aws/resource_aws_ecs_service.go b/aws/resource_aws_ecs_service.go index dd270ed05f5..dba91022ccb 100644 --- a/aws/resource_aws_ecs_service.go +++ b/aws/resource_aws_ecs_service.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) var taskDefinitionRE = regexp.MustCompile("^([a-zA-Z0-9_-]+):([0-9]+)$") @@ -205,6 +206,27 @@ func resourceAwsEcsService() *schema.Resource { }, }, }, + + "service_registries": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 65536), + }, + "registry_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + }, + }, + }, }, } } @@ -306,6 +328,23 @@ func resourceAwsEcsServiceCreate(d *schema.ResourceData, meta interface{}) error input.PlacementConstraints = pc } + serviceRegistries := d.Get("service_registries").(*schema.Set).List() + if len(serviceRegistries) > 0 { + srs := make([]*ecs.ServiceRegistry, 0, len(serviceRegistries)) + for _, v := range serviceRegistries { + raw := v.(map[string]interface{}) + sr := &ecs.ServiceRegistry{ + RegistryArn: aws.String(raw["registry_arn"].(string)), + } + if port, ok := raw["port"].(int); ok && port != 0 { + sr.Port = aws.Int64(int64(port)) + } + + srs = append(srs, sr) + } + input.ServiceRegistries = srs + } + log.Printf("[DEBUG] Creating ECS service: %s", input) // Retry due to AWS IAM & ECS eventual consistency @@ -446,6 +485,10 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("[ERR] Error setting network_configuration for (%s): %s", d.Id(), err) } + if err := d.Set("service_registries", flattenServiceRegistries(service.ServiceRegistries)); err != nil { + return fmt.Errorf("[ERR] Error setting service_registries for (%s): %s", d.Id(), err) + } + return nil } @@ -522,6 +565,23 @@ func flattenPlacementStrategy(pss []*ecs.PlacementStrategy) []map[string]interfa return results } +func flattenServiceRegistries(srs []*ecs.ServiceRegistry) []map[string]interface{} { + if len(srs) == 0 { + return nil + } + results := make([]map[string]interface{}, 0) + for _, sr := range srs { + c := map[string]interface{}{ + "registry_arn": aws.StringValue(sr.RegistryArn), + } + if sr.Port != nil { + c["port"] = int(aws.Int64Value(sr.Port)) + } + results = append(results, c) + } + return results +} + func resourceAwsEcsServiceUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn diff --git a/aws/resource_aws_ecs_service_test.go b/aws/resource_aws_ecs_service_test.go index 5d12bcc5088..27d4c87a7ec 100644 --- a/aws/resource_aws_ecs_service_test.go +++ b/aws/resource_aws_ecs_service_test.go @@ -102,6 +102,7 @@ func TestAccAWSEcsService_withARN(t *testing.T) { Config: testAccAWSEcsService(clusterName, tdName, svcName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEcsServiceExists("aws_ecs_service.mongo", &service), + resource.TestCheckResourceAttr("aws_ecs_service.mongo", "service_registries.#", "0"), ), }, @@ -109,6 +110,7 @@ func TestAccAWSEcsService_withARN(t *testing.T) { Config: testAccAWSEcsServiceModified(clusterName, tdName, svcName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEcsServiceExists("aws_ecs_service.mongo", &service), + resource.TestCheckResourceAttr("aws_ecs_service.mongo", "service_registries.#", "0"), ), }, }, @@ -611,6 +613,30 @@ func TestAccAWSEcsService_withLaunchTypeEC2AndNetworkConfiguration(t *testing.T) }) } +func TestAccAWSEcsService_withServiceRegistries(t *testing.T) { + var service ecs.Service + rString := acctest.RandString(8) + + clusterName := fmt.Sprintf("tf-acc-cluster-svc-w-ups-%s", rString) + tdName := fmt.Sprintf("tf-acc-td-svc-w-ups-%s", rString) + svcName := fmt.Sprintf("tf-acc-svc-w-ups-%s", rString) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcsService_withServiceRegistries(rString, clusterName, tdName, svcName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsServiceExists("aws_ecs_service.test", &service), + resource.TestCheckResourceAttr("aws_ecs_service.test", "service_registries.#", "1"), + ), + }, + }, + }) +} + func testAccCheckAWSEcsServiceDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ecsconn @@ -1688,3 +1714,84 @@ resource "aws_ecs_service" "main" { } `, sg1Name, sg2Name, clusterName, tdName, svcName, securityGroups) } + +func testAccAWSEcsService_withServiceRegistries(rName, clusterName, tdName, svcName string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "test" {} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "test" { + count = 2 + cidr_block = "${cidrsubnet(aws_vpc.test.cidr_block, 8, count.index)}" + availability_zone = "${data.aws_availability_zones.test.names[count.index]}" + vpc_id = "${aws_vpc.test.id}" +} + +resource "aws_security_group" "test" { + name = "tf-acc-sg-%s" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + } +} + +resource "aws_service_discovery_private_dns_namespace" "test" { + name = "tf-acc-sd-%s.terraform.local" + description = "test" + vpc = "${aws_vpc.test.id}" +} + +resource "aws_service_discovery_service" "test" { + name = "tf-acc-sd-%s" + dns_config { + namespace_id = "${aws_service_discovery_private_dns_namespace.test.id}" + dns_records { + ttl = 5 + type = "SRV" + } + } +} + +resource "aws_ecs_cluster" "test" { + name = "%s" +} + +resource "aws_ecs_task_definition" "test" { + family = "%s" + network_mode = "awsvpc" + container_definitions = < **Note:** As a result of an AWS limitation, a single `load_balancer` can be attached to the ECS service at most. See [related docs](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-load-balancing.html#load-balancing-concepts). @@ -106,6 +107,13 @@ Guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-query For more information, see [Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html) +## service_registries + +`service_registries` support the following: + +* `registry_arn` - (Required) The ARN of the Service Registry. The currently supported service registry is Amazon Route 53 Auto Naming Service(`aws_service_discovery_service`). For more information, see [Service](https://docs.aws.amazon.com/Route53/latest/APIReference/API_autonaming_Service.html) +* `port` - (Optional) The port value used if your Service Discovery service specified an SRV record. + ## Attributes Reference The following attributes are exported: @@ -122,4 +130,4 @@ ECS services can be imported using the `name` together with ecs cluster `name`, ``` $ terraform import aws_ecs_service.imported cluster-name/service-name -``` \ No newline at end of file +```