diff --git a/README.md b/README.md index 114cc36..ae1aebf 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ Creates the following resources: * CloudWatch log group. * Security Groups for the ECS service. * ECS service. -* Task definition using `golang:1.12.5-alpine` (see below). +* Task definition using `golang:alpine` (see below). * Configurable associations with Network Load Balancers (NLB) and Application Load Balancers (ALB). -We create an initial task definition using the `golang:1.12.5-alpine` image as a way +We create an initial task definition using the `golang:alpine` image as a way to validate the initial infrastructure is working: visiting the site shows a simple Go hello world page. We expect deployments to manage the container definitions going forward, not Terraform. @@ -76,15 +76,15 @@ module "app_ecs_service" { | assign\_public\_ip | Whether this instance should be accessible from the public internet. Default is false. | bool | `"false"` | no | | associate\_alb | Whether to associate an Application Load Balancer \(ALB\) with the ECS service. | bool | `"false"` | no | | associate\_nlb | Whether to associate a Network Load Balancer \(NLB\) with the ECS service. | bool | `"false"` | no | -| cloudwatch\_alarm\_actions | The list of actions to take for cloudwatch alarms | list | `[]` | no | +| cloudwatch\_alarm\_actions | The list of actions to take for cloudwatch alarms | list(string) | `[]` | no | | cloudwatch\_alarm\_cpu\_enable | Enable the CPU Utilization CloudWatch metric alarm | bool | `"true"` | no | | cloudwatch\_alarm\_cpu\_threshold | The CPU Utilization threshold for the CloudWatch metric alarm | string | `"80"` | no | -| cloudwatch\_alarm\_mem\_enable | Enable the Memory Utilization CloudWatch metric alarm | string | `"true"` | no | +| cloudwatch\_alarm\_mem\_enable | Enable the Memory Utilization CloudWatch metric alarm | bool | `"true"` | no | | cloudwatch\_alarm\_mem\_threshold | The Memory Utilization threshold for the CloudWatch metric alarm | string | `"80"` | no | | cloudwatch\_alarm\_name | Generic name used for CPU and Memory Cloudwatch Alarms | string | `""` | no | -| container\_definitions | Container definitions provided as valid JSON document. Default uses golang:1.12.5-alpine running a simple hello world. | string | `""` | no | +| container\_definitions | Container definitions provided as valid JSON document. Default uses golang:alpine running a simple hello world. | string | `""` | no | | container\_health\_check\_port | An additional port on which the container can receive a health check. Zero means the container port can only receive a health check on the port set by the container\_port variable. | string | `"0"` | no | -| container\_image | The image of the container. | string | `"golang:1.12.5-alpine"` | no | +| container\_image | The image of the container. | string | `"golang:alpine"` | no | | container\_port | The port on which the container will receive traffic. | string | `"80"` | no | | ecr\_repo\_arns | The ARNs of the ECR repos. By default, allows all repositories. | list(string) | `[ "*" ]` | no | | ecs\_cluster | ECS cluster object for this task. | object | n/a | yes | diff --git a/examples/simple/main.tf b/examples/simple/main.tf index c2c6749..7409fdf 100644 --- a/examples/simple/main.tf +++ b/examples/simple/main.tf @@ -1,32 +1,53 @@ +locals { + environment = "test" + container_protocol = "HTTP" + container_port = "80" +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 2" + + name = var.test_name + cidr = "10.0.0.0/16" + azs = var.vpc_azs + + public_subnets = ["10.0.104.0/24", "10.0.105.0/24", "10.0.106.0/24"] +} # -# KMS Key +# KMS # + data "aws_caller_identity" "current" {} -data "aws_region" "current" {} data "aws_iam_policy_document" "cloudwatch_logs_allow_kms" { statement { sid = "Enable IAM User Permissions" effect = "Allow" + principals { type = "AWS" identifiers = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root", ] } + actions = [ "kms:*", ] resources = ["*"] } + statement { sid = "Allow logs KMS access" effect = "Allow" + principals { type = "Service" - identifiers = ["logs.${data.aws_region.current.name}.amazonaws.com"] + identifiers = ["logs.${var.region}.amazonaws.com"] } + actions = [ "kms:Encrypt*", "kms:Decrypt*", @@ -41,24 +62,8 @@ data "aws_iam_policy_document" "cloudwatch_logs_allow_kms" { resource "aws_kms_key" "main" { description = "Key for ECS log encryption" enable_key_rotation = true - policy = data.aws_iam_policy_document.cloudwatch_logs_allow_kms.json -} - -# -# ECS Service Module -# -module "app_ecs_service" { - source = "../../" - - name = var.ecs_service_name - environment = "test" - - kms_key_id = aws_kms_key.main.arn - - ecs_cluster = aws_ecs_cluster.main - ecs_vpc_id = aws_vpc.main.id - ecs_subnet_ids = [aws_subnet.main.id] + policy = data.aws_iam_policy_document.cloudwatch_logs_allow_kms.json } # @@ -66,27 +71,35 @@ module "app_ecs_service" { # resource "aws_ecs_cluster" "main" { - name = var.ecs_service_name + name = var.test_name } +module "ecs-service" { + source = "../../" -# -# VPC -# + name = var.test_name + environment = local.environment -resource "aws_vpc" "main" { - cidr_block = "10.0.0.0/16" + ecs_cluster = aws_ecs_cluster.main + ecs_subnet_ids = module.vpc.public_subnets + ecs_vpc_id = module.vpc.vpc_id + ecs_use_fargate = true + assign_public_ip = true - tags = { - Automation = "Terraform" - } + kms_key_id = aws_kms_key.main.arn } -resource "aws_subnet" "main" { - vpc_id = "${aws_vpc.main.id}" - cidr_block = "10.0.1.0/24" +# +# SG adjustment +# - tags = { - Automation = "Terraform" - } +resource "aws_security_group_rule" "ecs_allow_http" { + description = "Allow HTTP" + security_group_id = module.ecs-service.ecs_security_group_id + + type = "ingress" + from_port = local.container_port + to_port = local.container_port + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] } diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf index e6046d4..5e61846 100644 --- a/examples/simple/variables.tf +++ b/examples/simple/variables.tf @@ -1,4 +1,11 @@ -variable "ecs_service_name" { - description = "The name of the ECS service." - type = string +variable "region" { + type = string +} + +variable "test_name" { + type = string +} + +variable "vpc_azs" { + type = list(string) } diff --git a/go.mod b/go.mod index 2c787a6..5b88315 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,8 @@ module github.com/trussworks/terraform-aws-ecs-service go 1.13 -require github.com/gruntwork-io/terratest v0.23.3 +require ( + github.com/aws/aws-sdk-go v1.28.2 + github.com/gruntwork-io/terratest v0.23.3 + github.com/stretchr/testify v1.4.0 +) diff --git a/go.sum b/go.sum index 099708e..bd92915 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -20,35 +21,46 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aws/aws-sdk-go v1.23.8 h1:G/azJoBN0pnhB3B+0eeC4yyVFYIIad6bbzg6wwtImqk= github.com/aws/aws-sdk-go v1.23.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.28.2 h1:j5IXG9CdyLfcVfICqo1PXVv+rua+QQHbkXuvuU/JF+8= +github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 h1:skJKxRtNmevLqnayafdLe2AsenqRupVmzZSqrvb5caU= github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -56,8 +68,11 @@ github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c h1:jWtZjFEUE/Bz0IeIhqC github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gruntwork-io/gruntwork-cli v0.5.1 h1:mVmVsFubUSLSCO8bGigI63HXzvzkC0uWXzm4dd9pXRg= github.com/gruntwork-io/gruntwork-cli v0.5.1/go.mod h1:IBX21bESC1/LGoV7jhXKUnTQTZgQ6dYRsoj/VqxUSZQ= github.com/gruntwork-io/terratest v0.22.2 h1:rIuiDbe5Ur+EYePWtqtzsdXojUfJECXdD92hQRKY5sk= github.com/gruntwork-io/terratest v0.22.2/go.mod h1:veL2PxiS2Z5S63ERnyiIY/sxgmXJpZD8YZybiqvduwQ= @@ -70,9 +85,11 @@ github.com/gruntwork-io/terratest v0.23.3/go.mod h1:+fVff0FQYuRzCF3LKpKF9ac+4w38 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -86,20 +103,27 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -108,6 +132,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= +github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -133,6 +158,7 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -147,11 +173,14 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -178,6 +207,7 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -185,6 +215,9 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.0.0-20181110191121-a33c8200050f h1:BH667AnNr487/iTtY35X+m6c2S8HL02Rft1PFK93kmw= k8s.io/api v0.0.0-20181110191121-a33c8200050f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apimachinery v0.0.0-20190704094520-6f131bee5e2c h1:vdEIiO5B0/3EVwZboF6qyYn5kVDdvCbaGSzr7Rcx18A= k8s.io/apimachinery v0.0.0-20190704094520-6f131bee5e2c/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v0.0.0-20190704095228-386e588352a4 h1:hqylj4/yit+/eO496/Yhgy2YxxumFpSY94YDFX6lBoU= k8s.io/client-go v0.0.0-20190704095228-386e588352a4/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= diff --git a/main.tf b/main.tf index a391b6d..04bc220 100644 --- a/main.tf +++ b/main.tf @@ -6,10 +6,10 @@ * * CloudWatch log group. * * Security Groups for the ECS service. * * ECS service. - * * Task definition using `golang:1.12.5-alpine` (see below). + * * Task definition using `golang:alpine` (see below). * * Configurable associations with Network Load Balancers (NLB) and Application Load Balancers (ALB). * - * We create an initial task definition using the `golang:1.12.5-alpine` image as a way + * We create an initial task definition using the `golang:alpine` image as a way * to validate the initial infrastructure is working: visiting the site shows * a simple Go hello world page. We expect deployments to manage the container * definitions going forward, not Terraform. @@ -134,7 +134,7 @@ resource "aws_cloudwatch_log_group" "main" { } resource "aws_cloudwatch_metric_alarm" "alarm_cpu" { - count = "${var.cloudwatch_alarm_cpu_enable && (var.associate_alb || var.associate_nlb) ? 1 : 0}" + count = var.cloudwatch_alarm_cpu_enable && (var.associate_alb || var.associate_nlb) ? 1 : 0 alarm_name = "${local.cloudwatch_alarm_name}-cpu" alarm_description = "Monitors ECS CPU Utilization" @@ -155,7 +155,7 @@ resource "aws_cloudwatch_metric_alarm" "alarm_cpu" { } resource "aws_cloudwatch_metric_alarm" "alarm_mem" { - count = "${var.cloudwatch_alarm_cpu_enable && (var.associate_alb || var.associate_nlb) ? 1 : 0}" + count = var.cloudwatch_alarm_cpu_enable && (var.associate_alb || var.associate_nlb) ? 1 : 0 alarm_name = "${local.cloudwatch_alarm_name}-mem" alarm_description = "Monitors ECS CPU Utilization" @@ -176,7 +176,7 @@ resource "aws_cloudwatch_metric_alarm" "alarm_mem" { } resource "aws_cloudwatch_metric_alarm" "alarm_cpu_no_lb" { - count = "${var.cloudwatch_alarm_cpu_enable && ! (var.associate_alb || var.associate_nlb) ? 1 : 0}" + count = var.cloudwatch_alarm_cpu_enable && ! (var.associate_alb || var.associate_nlb) ? 1 : 0 alarm_name = "${local.cloudwatch_alarm_name}-cpu" alarm_description = "Monitors ECS CPU Utilization" @@ -197,7 +197,7 @@ resource "aws_cloudwatch_metric_alarm" "alarm_cpu_no_lb" { } resource "aws_cloudwatch_metric_alarm" "alarm_mem_no_lb" { - count = "${var.cloudwatch_alarm_cpu_enable && ! (var.associate_alb || var.associate_nlb) ? 1 : 0}" + count = var.cloudwatch_alarm_cpu_enable && ! (var.associate_alb || var.associate_nlb) ? 1 : 0 alarm_name = "${local.cloudwatch_alarm_name}-mem" alarm_description = "Monitors ECS CPU Utilization" diff --git a/test/terraform_aws_ecs_service_test.go b/test/terraform_aws_ecs_service_test.go index 2a54d64..6baae6b 100644 --- a/test/terraform_aws_ecs_service_test.go +++ b/test/terraform_aws_ecs_service_test.go @@ -1,25 +1,129 @@ package test import ( + "crypto/tls" "fmt" "strings" "testing" + "time" + awssdk "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/ecs" "github.com/gruntwork-io/terratest/modules/aws" + http_helper "github.com/gruntwork-io/terratest/modules/http-helper" "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/retry" "github.com/gruntwork-io/terratest/modules/terraform" + test_structure "github.com/gruntwork-io/terratest/modules/test-structure" + "github.com/stretchr/testify/require" ) +// Retrieve tasks associated with a cluster +func GetTasks(t *testing.T, region string, clusterName string) *ecs.ListTasksOutput { + taskList, err := GetTasksE(t, region, clusterName) + require.NoError(t, err) + return taskList +} + +func GetTasksE(t *testing.T, region string, clusterName string) (*ecs.ListTasksOutput, error) { + var returnTaskList *ecs.ListTasksOutput + ecsClient, err := aws.NewEcsClientE(t, region) + if err != nil { + return returnTaskList, err + } + + params := &ecs.ListTasksInput{ + Cluster: awssdk.String(clusterName), + } + + // Need to spin and wait to allow time for resources to get up + maxRetries := 3 + retryDuration, _ := time.ParseDuration("30s") + _, err = retry.DoWithRetryE(t, "Get tasks", maxRetries, retryDuration, + func() (string, error) { + tasks, _ := ecsClient.ListTasks(params) + + if len(tasks.TaskArns) == 0 { + return "Did not retrieve tasks", fmt.Errorf("We returned empty tasks %v", tasks.TaskArns) + } + returnTaskList = tasks + return "Retrieved tasks", nil + }, + ) + if err != nil { + return returnTaskList, err + } + + return returnTaskList, nil +} + +// Retrieve ENI from task ARNs and cluster +func GetEni(t *testing.T, region string, cluster string, taskArns []*string) *string { + eni, err := GetEniE(t, region, cluster, taskArns) + require.NoError(t, err) + return eni +} + +func GetEniE(t *testing.T, region string, cluster string, taskArns []*string) (*string, error) { + ecsClient, err := aws.NewEcsClientE(t, region) + if err != nil { + return nil, err + } + + params := &ecs.DescribeTasksInput{ + Cluster: awssdk.String(cluster), + Tasks: taskArns, + } + returnedTasks, err := ecsClient.DescribeTasks(params) + if err != nil { + return nil, err + } + + eniDetail := returnedTasks.Tasks[0].Attachments[0].Details[1].Value + return eniDetail, nil +} + +// Retrieve Public IP from ENI +func GetPublicIP(t *testing.T, region string, enis []string) *string { + publicIP, err := GetPublicIPE(t, region, enis) + require.NoError(t, err) + return publicIP +} + +func GetPublicIPE(t *testing.T, region string, enis []string) (*string, error) { + ec2Client := aws.NewEc2Client(t, region) + + params := &ec2.DescribeNetworkInterfacesInput{ + NetworkInterfaceIds: awssdk.StringSlice(enis), + } + eniDetail, err := ec2Client.DescribeNetworkInterfaces(params) + if err != nil { + return nil, err + } + + publicIP := eniDetail.NetworkInterfaces[0].Association.PublicIp + return publicIP, nil +} + func TestTerraformAwsEcsServiceSimple(t *testing.T) { t.Parallel() + tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, "../", "examples/simple") + ecsServiceName := fmt.Sprintf("terratest-simple-%s", strings.ToLower(random.UniqueId())) - awsRegion := aws.GetRandomStableRegion(t, nil, nil) + awsRegion := "us-west-2" + vpcAzs := aws.GetAvailabilityZones(t, awsRegion)[:3] terraformOptions := &terraform.Options{ - TerraformDir: "../examples/simple/", + // The path to where our Terraform code is located + TerraformDir: tempTestFolder, + + // Variables to pass to our Terraform code using -var options Vars: map[string]interface{}{ - "ecs_service_name": ecsServiceName, + "test_name": ecsServiceName, + "vpc_azs": vpcAzs, + "region": awsRegion, }, EnvVars: map[string]string{ "AWS_DEFAULT_REGION": awsRegion, @@ -29,4 +133,17 @@ func TestTerraformAwsEcsServiceSimple(t *testing.T) { defer terraform.Destroy(t, terraformOptions) terraform.InitAndApply(t, terraformOptions) + // Step through cluster and task to retrieve public IP of instance + tasksOutput := GetTasks(t, awsRegion, ecsServiceName) + singleTaskEni := GetEni(t, awsRegion, ecsServiceName, tasksOutput.TaskArns) + publicIP := GetPublicIP(t, awsRegion, []string{*singleTaskEni}) + + testURL := fmt.Sprintf("http://%v", *publicIP) + expectedText := "Hello, world!" + tlsConfig := tls.Config{} + maxRetries := 2 + timeBetweenRetries := 30 * time.Second + + http_helper.HttpGetWithRetry(t, testURL, &tlsConfig, 200, expectedText, maxRetries, timeBetweenRetries) + } diff --git a/variables.tf b/variables.tf index 2e66208..f70879a 100644 --- a/variables.tf +++ b/variables.tf @@ -11,12 +11,12 @@ variable "environment" { variable "cloudwatch_alarm_name" { description = "Generic name used for CPU and Memory Cloudwatch Alarms" default = "" - type = "string" + type = string } variable "cloudwatch_alarm_actions" { description = "The list of actions to take for cloudwatch alarms" - type = "list" + type = list(string) default = [] } @@ -29,19 +29,19 @@ variable "cloudwatch_alarm_cpu_enable" { variable "cloudwatch_alarm_cpu_threshold" { description = "The CPU Utilization threshold for the CloudWatch metric alarm" default = 80 - type = "string" + type = string } variable "cloudwatch_alarm_mem_enable" { description = "Enable the Memory Utilization CloudWatch metric alarm" - type = "string" default = true + type = bool } variable "cloudwatch_alarm_mem_threshold" { description = "The Memory Utilization threshold for the CloudWatch metric alarm" default = 80 - type = "string" + type = string } variable "logs_cloudwatch_retention" { @@ -130,7 +130,7 @@ variable "tasks_maximum_percent" { variable "container_image" { description = "The image of the container." - default = "golang:1.12.5-alpine" + default = "golang:alpine" type = string } @@ -147,7 +147,7 @@ variable "container_health_check_port" { } variable "container_definitions" { - description = "Container definitions provided as valid JSON document. Default uses golang:1.12.5-alpine running a simple hello world." + description = "Container definitions provided as valid JSON document. Default uses golang:alpine running a simple hello world." default = "" type = string }