diff --git a/.travis.yml b/.travis.yml index fd4c4b3..b12372d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ sudo: required dist: trusty -language: minimal +language: go + +go: + - 1.12.x services: - docker @@ -13,11 +16,22 @@ cache: directories: - .terraform +env: + global: + - GO111MODULE=on + - PATH=$PATH:$TRAVIS_BUILD_DIR/bin + + # AWS_CREDENTIALS_FILE + - secure: PV7+QiwAZyw0rQQS9hRyBtU+gqrLj2p0C0r9xUcAQD9QRQKI7rHHplXMLXmVd2btiM+TEjiRwT1E2JNCtOzOlogDQBMhSfY6JFPD9nd/akOiaUpFE2QRYXN5IyumS8dC/udwqe0KZ8ckp/yxOiryUKKfxPOiqvzxxD+2CiCm7W9Hsbd33bbm1d9NPfT8k4yCRJ7ggtSsw8qDUsdMPn68tztl+7eo64lW4SV2+YfmWr1GARrmLCtSB6+LICeJgJ33BDrHX2T17nj9ChRjS6gjn98xEvYm1w74/FD5DNMTOzr0sFwsh9D6RnMxNS6c5CCDdsz8DOzCLVqmpG0CsDyqax3orTPWaIj0RZ1u/dQmlb8encsTSemVJPkQbOVu4dAQ0LLKgD1ou3rGeTYFcgkYftzdXw29CKu9NqFOqfym0iZIg6tdTiwd52mB/MLnfNUKkuVJZLf5AVP1CxwaRJyWBb0zrzasBJD4LILqCQmt6p+PenDNvjv7yBSa/+Ol1A9/g95ZtgnkqIUfre9kE5hTZiSJvXZPjVLc5jrPydYOKvbauLJewDKOzvpq0H6hRkSVUVk7sDnfZwb3z4eJfs+B7DZdmz+ZfVOWeNQ1kJejXhp93QrR4Z7VZjFKrOFb4GJ5qj2zhTYtOGfaUEP5SUji61ywC/F74SgTiX/62NuydUU= + +before_install: ./ci/before_install.sh + script: - - ./bin/terraform init -input=false - - ./bin/terraform validate -check-variables=false . - - if [[ -n "$(./bin/terraform fmt -write=false)" ]]; then echo "Some terraform files need be formatted, run 'terraform fmt' to fix"; exit 1; fi - - ./bin/tflint --debug + - terraform init -input=false + - terraform validate -check-variables=false . + - if [[ -n "$(terraform fmt -write=false)" ]]; then echo "Some terraform files need be formatted, run 'terraform fmt' to fix"; exit 1; fi + - tflint --debug + - go test -v $(go list ./test/) notifications: email: false diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8c35a44 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "go.formatTool": "gofmt" +} diff --git a/bin/terraform b/bin/terraform index 6269439..99f408a 100755 --- a/bin/terraform +++ b/bin/terraform @@ -1,10 +1,16 @@ #!/usr/bin/env bash +AWS_PROFILE=${AWS_PROFILE:-"test"} +AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"us-east-1"} + exec docker run \ --name terraform-cli \ --interactive \ - --tty \ --rm \ --workdir /tmp/workspace/terraform \ + --volume "${HOME}/.aws":/root/.aws \ --volume "$(pwd)":/tmp/workspace/terraform \ + --volume "$(cd .. && pwd)":/tmp/workspace/fargate-module \ + --env AWS_PROFILE="${AWS_PROFILE}" \ + --env AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" \ hashicorp/terraform:0.11.11 "${@}" diff --git a/ci/before_install.sh b/ci/before_install.sh new file mode 100755 index 0000000..9229145 --- /dev/null +++ b/ci/before_install.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -o errexit +set -o pipefail + +# Extract AWS credentials file so we can use AWS stuff without exporting access key/secret +mkdir -p ~/.aws && echo ${AWS_CREDENTIALS_FILE} | base64 -d > ~/.aws/credentials diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..83566ae --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/strvcom/terraform-aws-fargate + +go 1.12 + +require ( + github.com/aws/aws-sdk-go v1.17.14 // indirect + github.com/boombuler/barcode v1.0.0 // indirect + github.com/go-sql-driver/mysql v1.4.1 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/gruntwork-io/terratest v0.14.2 + github.com/pquerna/otp v1.1.0 // indirect + github.com/stretchr/testify v1.3.0 // indirect + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect + golang.org/x/net v0.0.0-20190311031020-56fb01167e7d // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..32a4cdc --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/aws/aws-sdk-go v1.17.14 h1:IjqZDIQoLyZ48A93BxVrZOaIGgZPRi4nXt6WQUMJplY= +github.com/aws/aws-sdk-go v1.17.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gruntwork-io/terratest v0.14.2 h1:A9YUZZlXE/syTnIVeuqhqoyVO5CUJS5Kasvyr5IUsv8= +github.com/gruntwork-io/terratest v0.14.2/go.mod h1:NjUn6YXA5Skxt8Rs20t3isYx5Rl+EgvGB8/+RRXddqk= +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/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.1.0 h1:q2gMsMuMl3JzneUaAX1MRGxLvOG6bzXV51hivBaStf0= +github.com/pquerna/otp v1.1.0/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311031020-56fb01167e7d h1:vQJbQvu6+H699vOmHa20TEBI9nEqroRbMtf/9biIE3A= +golang.org/x/net v0.0.0-20190311031020-56fb01167e7d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/main.tf b/main.tf index 33076e8..a50ca6e 100644 --- a/main.tf +++ b/main.tf @@ -8,6 +8,8 @@ terraform { data "aws_availability_zones" "this" {} +data "aws_region" "current" {} + module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "1.46.0" @@ -87,7 +89,7 @@ data "template_file" "tasks" { container_port = "${lookup(var.services[element(keys(var.services), count.index)], "container_port")}" repository_url = "${element(aws_ecr_repository.this.*.repository_url, count.index)}" log_group = "${element(aws_cloudwatch_log_group.this.*.name, count.index)}" - region = "${var.region}" + region = "${var.region != "" ? var.region : data.aws_region.current.name}" } } @@ -197,7 +199,7 @@ resource "aws_lb" "this" { count = "${length(var.services) > 0 ? length(var.services) : 0}" name = "${var.name}-${terraform.workspace}-${element(keys(var.services), count.index)}-alb" - subnets = ["${module.vpc.public_subnets}"] + subnets = ["${slice(module.vpc.public_subnets, 0, length(data.aws_availability_zones.this.names))}"] security_groups = ["${aws_security_group.web.id}"] } @@ -470,7 +472,7 @@ data "template_file" "metric_dashboard" { template = "${file("${path.module}/metrics/basic-dashboard.json")}" vars { - region = "${var.region}" + region = "${var.region != "" ? var.region : data.aws_region.current.name}" alb_arn_suffix = "${element(aws_lb.this.*.arn_suffix, count.index)}" cluster_name = "${aws_ecs_cluster.this.name}" service_name = "${element(keys(var.services), count.index)}" diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 0000000..4ed9089 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,24 @@ +# Base +FROM node:alpine AS base +ENV HOME=/home/node +WORKDIR $HOME/app +RUN chown -R node:node $HOME/* +USER node +COPY package*.json ./ + +# Dependencies +FROM base AS dependencies +RUN npm set progress=false && npm config set depth 0 +RUN npm install --only=production +RUN cp -R node_modules prod_node_modules +RUN npm install + +# Release +FROM dependencies as release + +COPY --from=dependencies $HOME/app/prod_node_modules ./node_modules +COPY . . + +EXPOSE 3000 + +CMD ["node", "."] diff --git a/test/api.json b/test/api.json new file mode 100644 index 0000000..13452fa --- /dev/null +++ b/test/api.json @@ -0,0 +1,21 @@ +[ + { + "portMappings": [ + { + "hostPort": ${container_port}, + "protocol": "tcp", + "containerPort": ${container_port} + } + ], + "image": "${repository_url}:latest", + "name": "${container_name}", + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "${log_group}", + "awslogs-region": "${region}", + "awslogs-stream-prefix": "ecs" + } + } + } +] diff --git a/test/basic_test.go b/test/basic_test.go new file mode 100644 index 0000000..451b818 --- /dev/null +++ b/test/basic_test.go @@ -0,0 +1,40 @@ +package test + +import ( + "fmt" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/aws" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestBasicExample(t *testing.T) { + expectedName := fmt.Sprintf("fargate-%s", strings.ToLower(random.UniqueId())) + + // Pick a random Fargate-enabled AWS region to test in. This helps ensure the module works in all regions. + forbiddenRegions := []string{"sa-east-1", "eu-west-3", "eu-north-1"} + awsRegion := aws.GetRandomStableRegion(t, nil, forbiddenRegions) + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: ".", + + // Variables to pass to our Terraform code using -var options + Vars: map[string]interface{}{ + "name": expectedName, + }, + + // Environment variables to set when running Terraform + EnvVars: map[string]string{ + "AWS_DEFAULT_REGION": awsRegion, + }, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) +} diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..7a1804e --- /dev/null +++ b/test/index.js @@ -0,0 +1,9 @@ +const express = require('express') +const os = require('os') + +const hostname = os.hostname() +const app = express() + +app.listen(3000, () => console.log(`Example app listening on port 3000! Host: ${hostname}`)) + +app.get('/', async (req, res) => res.json({ hostname })) diff --git a/test/main.tf b/test/main.tf new file mode 100644 index 0000000..631a086 --- /dev/null +++ b/test/main.tf @@ -0,0 +1,33 @@ +terraform { + required_version = "~> 0.11.11" +} + +provider "aws" { + version = "~> 1.54.0" + profile = "test" +} + +variable "name" {} + +module "fargate" { + source = "../fargate-module" + + vpc_create_nat = false + + name = "${var.name}" + + services = { + api = { + task_definition = "api.json" + container_port = 3000 + cpu = "256" + memory = "512" + replicas = 3 + + registry_retention_count = 15 + logs_retention_days = 14 + } + } + + codepipeline_events_enabled = true +} diff --git a/variables.tf b/variables.tf index 3fdeb44..d1513ea 100644 --- a/variables.tf +++ b/variables.tf @@ -8,7 +8,7 @@ variable "name" { variable "region" { description = "AWS region" - default = "us-east-1" + default = "" } ## VPC variables