├── .gitignore ├── LICENCE ├── README.md ├── alpha-service ├── alpha-service.tfvars ├── deploy.bash ├── modules.tf ├── modules │ ├── alb-listener │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── autoscaling │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── ecs-service │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── task-definition.json │ │ └── variables.tf └── variables.tf ├── base-infra ├── base-infra.tfvars ├── deploy.bash ├── modules.tf ├── modules │ ├── alb │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── bastion │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── ecs-cluster │ │ ├── datadog-agent-task-definition.json │ │ ├── hello-world-task-definition.json │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── ecs-instances │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── user-data.tpl │ │ └── variables.tf │ ├── security │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── policies │ │ │ ├── ecs-instance-policy.json │ │ │ ├── ecs-instance.json │ │ │ ├── ecs-service-autoscale.json │ │ │ └── ecs-service.json │ │ └── variables.tf │ └── vpc │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── outputs.tf └── variables.tf ├── beta-service ├── .babelrc ├── .eslintignore ├── .eslintrc ├── beta-service.tfvars ├── deploy.bash ├── modules.tf ├── modules │ ├── alb-listener │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── autoscaling │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── ecs-service │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── task-definition.json │ │ └── variables.tf └── variables.tf ├── bitbucket-pipelines.yml ├── charlie-service ├── .babelrc ├── .eslintignore ├── .eslintrc ├── charlie-service.tfvars ├── deploy.bash ├── modules.tf ├── modules │ ├── alb-listener │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── autoscaling │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── ecs-service │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── task-definition.json │ │ └── variables.tf └── variables.tf └── demo-api ├── .babelrc ├── .eslintignore ├── .eslintrc ├── Dockerfile ├── README.md ├── package.json ├── src ├── healthcheck │ └── index.js ├── index.js ├── items │ └── index.js ├── middleware │ └── index.js └── utils.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | npm-debug.log 3 | yarn-error.log 4 | node_modules/ 5 | .DS_Store 6 | .terraform 7 | *.tfstate 8 | *.tfstate.backup 9 | .history/ -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jordan Hornblow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform-ecs-autoscale-alb 2 | 3 | Amazon [EC2 Container Service (ECS)](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/) is a highly scalable, fast, container management service that makes it easy to run, stop, and manage Docker containers on a cluster of EC2 instances (called container instances). 4 | 5 | The idea behind ECS is to create a cluster (a group of container instances managed by ECS), define what Docker containers we want to run (with configuration for each container) and ECS will take care of deploying those containers across the cluster, rolling out new versions and integrating with other AWS infrastructure/services. 6 | 7 | A task definition is required to run a Docker container on an ECS cluster. A task definition specifies various parameters such as which Docker image(s) to use and the repository in which the image is stored, how much CPU and memory to use for the container, which environment variables should be passed to the container when it starts, which logging driver to use (awslogs, syslog etc.). 8 | 9 | --- 10 | 11 | This repo contains Terraform configuration for an ECS cluster running three services (alpha, beta and charlie) with instance and service autoscaling configured at 80% CPU and memory (min and max autoscaling limits can be configured). The three services are sitting behind an Application Load Balancer (ALB) with path based routing set up. 12 | 13 | As far as I could tell the ALB doesn't currently support URL Rewriting so I've had to manually perform this at the application level. 14 | 15 | The code for the demo API is in the [/demo-api](../master/demo-api/) directory and is built and hosted on Docker Hub. 16 | 17 | [![Dockerhub badge](http://dockeri.co/image/jch254/ecs-demo-api)](https://hub.docker.com/r/jch254/ecs-demo-api) 18 | 19 | 20 | ## Base-infra components: 21 | 22 | + VPC 23 | + Public and private subnets 24 | + Internet Gateway 25 | + NAT Gateways 26 | + ALB in public subnet with Route53 record 27 | + ECS cluster 28 | + ECS container instances in private subnet with autoscaling configured (running Datadog agent and NGINX serving a default index.html for ALB default action on boot) 29 | + Bastion instance in public subnet (in ASG with a fixed size of one). This only allows SSH access for a specific IP address. 30 | 31 | ## Service components 32 | 33 | - ECS service with autoscaling configured 34 | - ALB listener and target group 35 | 36 | ## Deploying via Bitbucket Pipelines 37 | 38 | Deployment to AWS is automated via Bitbucket Pipelines. 39 | 40 | **Before running pipeline for the first time you must:** 41 | 42 | 1. Enable Bitbucket Pipelines for repository 43 | 1. Create an S3 bucket named 'your-terraform-remote-state' for Terraform remote state via console or CLI 44 | 1. Create a Bitbucket Pipelines IAM user with the required permissions 45 | 1. Set up the following account-level Bitbucket Pipelines environment variables in Bitbucket UI: 46 | - AWS_ACCESS_KEY_ID = PIPELINES_USER_ACCESS_KEY_ID 47 | - AWS_SECRET_ACCESS_KEY = PIPELINES_USER_SECRET_ACCESS_KEY 48 | 1. Set up the following repository-level Bitbucket Pipelines environment variables in Bitbucket UI: 49 | - TF_VAR_ssh_allowed_ip = YOUR_IP 50 | - TF_VAR_acm_arn = YOUR_ACM_CERT_ARN 51 | - TF_VAR_route53_zone_id = YOUR_R53_ZONE_ID 52 | - TF_VAR_datadog_api_key = YOUR_DATADOG_API_KEY 53 | - TF_VAR_key_pair_name = YOUR_KEY_PAIR_NAME 54 | - TF_VAR_bastion_key_pair_name = YOUR_KEY_PAIR_NAME 55 | 1. Edit configuration in the .tfvars file in [/base-infra](../master/base-infra/), [/alpha-service](../master/alpha-service/), [/beta-service](../master/beta-service/) and [/charlie-service](../master/charlie-service/) directories with required values. 56 | 1. Update deploy.bash file in [/base-infra](../master/base-infra/), [/alpha-service](../master/alpha-service/), [/beta-service](../master/beta-service/) and [/charlie-service] (../master/charlie-service/) directories with your remote state bucket name. 57 | 1. Uncomment steps in [/bitbucket-pipelines.yml](../master/bitbucket-pipelines.yml) and commit to repository to trigger the pipeline 58 | 59 | Refer to deploy.bash files for manual deployment steps. 60 | 61 | - TODO: Add comments throughout infra code 62 | -------------------------------------------------------------------------------- /alpha-service/alpha-service.tfvars: -------------------------------------------------------------------------------- 1 | region = "ap-southeast-2" 2 | 3 | service_name = "alpha-service" 4 | 5 | container_port = "80" 6 | 7 | docker_image = "jch254/ecs-demo-api" 8 | 9 | docker_tag = "latest" 10 | -------------------------------------------------------------------------------- /alpha-service/deploy.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | cd alpha-service 4 | 5 | terraform remote config -backend=s3 \ 6 | -backend-config="bucket=603-terraform-remote-state" \ 7 | -backend-config="key=terraform-ecs-autoscale-alb/alpha-service.tfstate" \ 8 | -backend-config="region=ap-southeast-2" \ 9 | -backend-config="encrypt=true" 10 | 11 | terraform get --update 12 | terraform plan -var-file alpha-service.tfvars 13 | terraform apply -var-file alpha-service.tfvars 14 | 15 | cd .. 16 | -------------------------------------------------------------------------------- /alpha-service/modules.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | data "terraform_remote_state" "base_remote_state" { 6 | backend = "s3" 7 | config { 8 | bucket = "603-terraform-remote-state" 9 | key = "terraform-ecs-autoscale-alb/base-infra.tfstate" 10 | region = "${var.region}" 11 | } 12 | } 13 | 14 | module "alb_listener" { 15 | source = "./modules/alb-listener" 16 | 17 | service_name = "${var.service_name}" 18 | container_port = "${var.container_port}" 19 | vpc_id = "${data.terraform_remote_state.base_remote_state.vpc_id}" 20 | alb_listener_arn = "${data.terraform_remote_state.base_remote_state.alb_listener_arn}" 21 | } 22 | 23 | module "ecs_service" { 24 | source = "./modules/ecs-service" 25 | 26 | service_name = "${var.service_name}" 27 | docker_image = "${var.docker_image}" 28 | docker_tag = "${var.docker_tag}" 29 | container_cpu = "${var.container_cpu}" 30 | container_memory = "${var.container_memory}" 31 | container_port = "${var.container_port}" 32 | region = "${var.region}" 33 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 34 | desired_count = "${var.min_capacity}" 35 | ecs_service_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_role_arn}" 36 | target_group_arn = "${module.alb_listener.target_group_arn}" 37 | } 38 | 39 | module "autoscaling" { 40 | source = "./modules/autoscaling" 41 | 42 | service_name = "${var.service_name}" 43 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 44 | ecs_service_autoscale_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_autoscale_role_arn}" 45 | min_capacity = "${var.min_capacity}" 46 | max_capacity = "${var.max_capacity}" 47 | } 48 | -------------------------------------------------------------------------------- /alpha-service/modules/alb-listener/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_alb_target_group" "alpha_service_tg" { 2 | name = "${replace(var.service_name, "/(.{0,28})(.*)/", "$1")}-tg" 3 | 4 | protocol = "HTTP" 5 | port = "${var.container_port}" 6 | vpc_id = "${var.vpc_id}" 7 | 8 | health_check { 9 | path = "/ping" 10 | } 11 | } 12 | 13 | resource "aws_alb_listener_rule" "alpha_service_listener" { 14 | listener_arn = "${var.alb_listener_arn}" 15 | priority = 100 16 | 17 | action { 18 | type = "forward" 19 | target_group_arn = "${aws_alb_target_group.alpha_service_tg.arn}" 20 | } 21 | 22 | condition { 23 | field = "path-pattern" 24 | values = ["/alpha/*"] 25 | } 26 | 27 | depends_on = ["aws_alb_target_group.alpha_service_tg"] 28 | } 29 | -------------------------------------------------------------------------------- /alpha-service/modules/alb-listener/outputs.tf: -------------------------------------------------------------------------------- 1 | output "target_group_arn" { 2 | value = "${aws_alb_target_group.alpha_service_tg.arn}" 3 | } 4 | -------------------------------------------------------------------------------- /alpha-service/modules/alb-listener/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "container_port" { 6 | description = "Port that service will listen on" 7 | } 8 | 9 | variable "vpc_id" { 10 | description = "Id of the VPC where service should be deployed" 11 | } 12 | 13 | variable "alb_listener_arn" { 14 | description = "ARN of ALB listener" 15 | } 16 | -------------------------------------------------------------------------------- /alpha-service/modules/autoscaling/main.tf: -------------------------------------------------------------------------------- 1 | # A CloudWatch alarm that moniors CPU utilization of containers for scaling up 2 | resource "aws_cloudwatch_metric_alarm" "alpha_service_cpu_high" { 3 | alarm_name = "${var.service_name}-cpu-utilization-above-80" 4 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling up" 5 | comparison_operator = "GreaterThanOrEqualToThreshold" 6 | evaluation_periods = "1" 7 | metric_name = "CPUUtilization" 8 | namespace = "AWS/ECS" 9 | period = "120" 10 | statistic = "Average" 11 | threshold = "80" 12 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 13 | 14 | dimensions { 15 | ClusterName = "${var.cluster_name}" 16 | ServiceName = "${var.service_name}" 17 | } 18 | } 19 | 20 | # A CloudWatch alarm that monitors CPU utilization of containers for scaling down 21 | resource "aws_cloudwatch_metric_alarm" "alpha_service_cpu_low" { 22 | alarm_name = "${var.service_name}-cpu-utilization-below-5" 23 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling down" 24 | comparison_operator = "LessThanThreshold" 25 | evaluation_periods = "1" 26 | metric_name = "CPUUtilization" 27 | namespace = "AWS/ECS" 28 | period = "120" 29 | statistic = "Average" 30 | threshold = "5" 31 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 32 | 33 | dimensions { 34 | ClusterName = "${var.cluster_name}" 35 | ServiceName = "${var.service_name}" 36 | } 37 | } 38 | 39 | # A CloudWatch alarm that monitors memory utilization of containers for scaling up 40 | resource "aws_cloudwatch_metric_alarm" "alpha_service_memory_high" { 41 | alarm_name = "${var.service_name}-memory-utilization-above-80" 42 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling up" 43 | comparison_operator = "GreaterThanOrEqualToThreshold" 44 | evaluation_periods = "1" 45 | metric_name = "MemoryUtilization" 46 | namespace = "AWS/ECS" 47 | period = "120" 48 | statistic = "Average" 49 | threshold = "80" 50 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 51 | 52 | dimensions { 53 | ClusterName = "${var.cluster_name}" 54 | ServiceName = "${var.service_name}" 55 | } 56 | } 57 | 58 | # A CloudWatch alarm that monitors memory utilization of containers for scaling down 59 | resource "aws_cloudwatch_metric_alarm" "alpha_service_memory_low" { 60 | alarm_name = "${var.service_name}-memory-utilization-below-5" 61 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling down" 62 | comparison_operator = "LessThanThreshold" 63 | evaluation_periods = "1" 64 | metric_name = "MemoryUtilization" 65 | namespace = "AWS/ECS" 66 | period = "120" 67 | statistic = "Average" 68 | threshold = "5" 69 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 70 | 71 | dimensions { 72 | ClusterName = "${var.cluster_name}" 73 | ServiceName = "${var.service_name}" 74 | } 75 | } 76 | 77 | resource "aws_appautoscaling_target" "target" { 78 | resource_id = "service/${var.cluster_name}/${var.service_name}" 79 | role_arn = "${var.ecs_service_autoscale_role_arn}" 80 | scalable_dimension = "ecs:service:DesiredCount" 81 | min_capacity = "${var.min_capacity}" 82 | max_capacity = "${var.max_capacity}" 83 | } 84 | 85 | resource "aws_appautoscaling_policy" "scale_up" { 86 | name = "${var.service_name}-scale-up" 87 | resource_id = "service/${var.cluster_name}/${var.service_name}" 88 | scalable_dimension = "ecs:service:DesiredCount" 89 | adjustment_type = "ChangeInCapacity" 90 | cooldown = 120 91 | metric_aggregation_type = "Average" 92 | 93 | step_adjustment { 94 | metric_interval_lower_bound = 0 95 | scaling_adjustment = 1 96 | } 97 | 98 | depends_on = ["aws_appautoscaling_target.target"] 99 | } 100 | 101 | resource "aws_appautoscaling_policy" "scale_down" { 102 | name = "${var.service_name}-scale-down" 103 | resource_id = "service/${var.cluster_name}/${var.service_name}" 104 | scalable_dimension = "ecs:service:DesiredCount" 105 | adjustment_type = "ChangeInCapacity" 106 | cooldown = 120 107 | metric_aggregation_type = "Average" 108 | 109 | step_adjustment { 110 | metric_interval_upper_bound = 0 111 | scaling_adjustment = -1 112 | } 113 | 114 | depends_on = ["aws_appautoscaling_target.target"] 115 | } 116 | -------------------------------------------------------------------------------- /alpha-service/modules/autoscaling/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jch254/terraform-ecs-autoscale-alb/ee36aa5462166922123252be846c338b9bd9f1b6/alpha-service/modules/autoscaling/outputs.tf -------------------------------------------------------------------------------- /alpha-service/modules/autoscaling/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "cluster_name" { 6 | description = "Name of ECS cluster" 7 | } 8 | 9 | variable "ecs_service_autoscale_role_arn" { 10 | description = "ARN of IAM role for ECS service autoscaling" 11 | } 12 | 13 | variable "min_capacity" { 14 | description = "Minimum number of containers to run" 15 | } 16 | 17 | variable "max_capacity" { 18 | description = "Minimum number of containers to run" 19 | } 20 | -------------------------------------------------------------------------------- /alpha-service/modules/ecs-service/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "alpha_service_lg" { 2 | name = "${var.service_name}" 3 | } 4 | 5 | data "template_file" "task_definition" { 6 | template = "${file("${path.module}/task-definition.json")}" 7 | 8 | vars { 9 | service_name = "${var.service_name}" 10 | docker_image = "${var.docker_image}" 11 | docker_tag = "${var.docker_tag}" 12 | container_cpu = "${var.container_cpu}" 13 | container_memory = "${var.container_memory}" 14 | container_port = "${var.container_port}" 15 | log_group_name = "${aws_cloudwatch_log_group.alpha_service_lg.name}" 16 | log_group_region = "${var.region}" 17 | } 18 | } 19 | 20 | # The ECS task that specifies what Docker containers we need to run the service 21 | resource "aws_ecs_task_definition" "alpha_service" { 22 | family = "${var.service_name}" 23 | container_definitions = "${data.template_file.task_definition.rendered}" 24 | } 25 | 26 | # A long-running ECS service for the alpha_service task 27 | resource "aws_ecs_service" "alpha_service" { 28 | name = "${var.service_name}" 29 | cluster = "${var.cluster_name}" 30 | task_definition = "${aws_ecs_task_definition.alpha_service.arn}" 31 | desired_count = "${var.desired_count}" 32 | deployment_minimum_healthy_percent = 50 33 | deployment_maximum_percent = 100 34 | iam_role = "${var.ecs_service_role_arn}" 35 | 36 | load_balancer { 37 | target_group_arn = "${var.target_group_arn}" 38 | container_name = "${var.service_name}" 39 | container_port = "${var.container_port}" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /alpha-service/modules/ecs-service/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service_name" { 2 | value = "${aws_ecs_service.alpha_service.name}" 3 | } 4 | -------------------------------------------------------------------------------- /alpha-service/modules/ecs-service/task-definition.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${service_name}", 4 | "image": "${docker_image}:${docker_tag}", 5 | "cpu": ${container_cpu}, 6 | "memory": ${container_memory}, 7 | "essential": true, 8 | "portMappings": [ 9 | { 10 | "containerPort": ${container_port} 11 | } 12 | ], 13 | "logConfiguration": { 14 | "logDriver": "awslogs", 15 | "options": { 16 | "awslogs-group": "${log_group_name}", 17 | "awslogs-region": "${log_group_region}" 18 | } 19 | }, 20 | "environment": [ 21 | { "name": "SERVICE_NAME", "value": "${service_name}"} 22 | ] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /alpha-service/modules/ecs-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "docker_image" { 6 | description = "Docker image to run" 7 | } 8 | 9 | variable "docker_tag" { 10 | description = "Tag of docker image to run" 11 | } 12 | 13 | variable "container_cpu" { 14 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 15 | } 16 | 17 | variable "container_memory" { 18 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 19 | } 20 | 21 | variable "container_port" { 22 | description = "Port that service will listen on" 23 | } 24 | 25 | variable "region" { 26 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 27 | } 28 | 29 | variable "cluster_name" { 30 | description = "Name of ECS cluster" 31 | } 32 | 33 | variable "desired_count" { 34 | description = "Initial number of containers to run" 35 | } 36 | 37 | variable "ecs_service_role_arn" { 38 | description = "ARN of IAM role for ECS service" 39 | } 40 | 41 | variable "target_group_arn" { 42 | description = "ARN of ALB target group for service" 43 | } 44 | -------------------------------------------------------------------------------- /alpha-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 3 | } 4 | 5 | variable "service_name" { 6 | description = "Name of service" 7 | } 8 | 9 | variable "container_port" { 10 | description = "Port that service will listen on" 11 | } 12 | 13 | variable "docker_image" { 14 | description = "Docker image to run" 15 | } 16 | 17 | variable "docker_tag" { 18 | description = "Tag of docker image to run" 19 | } 20 | 21 | variable "container_cpu" { 22 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 23 | default = "256" 24 | } 25 | 26 | variable "container_memory" { 27 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 28 | default = "256" 29 | } 30 | 31 | variable "min_capacity" { 32 | description = "Minimum number of containers to run" 33 | default = 2 34 | } 35 | 36 | variable "max_capacity" { 37 | description = "Minimum number of containers to run" 38 | default = 6 39 | } 40 | -------------------------------------------------------------------------------- /base-infra/base-infra.tfvars: -------------------------------------------------------------------------------- 1 | region = "ap-southeast-2" 2 | 3 | alb_dns_name = "ecs-demo.jch254.com" 4 | 5 | cluster_name = "ecs-demo" 6 | 7 | instance_type = "t2.micro" 8 | 9 | bastion_instance_type = "t2.micro" 10 | -------------------------------------------------------------------------------- /base-infra/deploy.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | cd base-infra 4 | 5 | terraform remote config -backend=s3 \ 6 | -backend-config="bucket=603-terraform-remote-state" \ 7 | -backend-config="key=terraform-ecs-autoscale-alb/base-infra.tfstate" \ 8 | -backend-config="region=ap-southeast-2" \ 9 | -backend-config="encrypt=true" 10 | 11 | terraform get --update 12 | terraform plan -var-file base-infra.tfvars 13 | terraform apply -var-file base-infra.tfvars 14 | 15 | cd .. 16 | -------------------------------------------------------------------------------- /base-infra/modules.tf: -------------------------------------------------------------------------------- 1 | # Configure the AWS Provider 2 | provider "aws" { 3 | region = "${var.region}" 4 | } 5 | 6 | module "vpc" { 7 | source = "./modules/vpc" 8 | 9 | region = "${var.region}" 10 | az_count = "${var.az_count}" 11 | } 12 | 13 | module "security" { 14 | source = "./modules/security" 15 | 16 | vpc_id = "${module.vpc.vpc_id}" 17 | vpc_cidr_block = "${module.vpc.vpc_cidr_block}" 18 | ssh_allowed_ip = "${var.ssh_allowed_ip}" 19 | } 20 | 21 | module "bastion" { 22 | source = "./modules/bastion" 23 | 24 | instance_type = "${var.bastion_instance_type}" 25 | key_pair_name = "${var.bastion_key_pair_name}" 26 | security_group_internal_id = "${module.security.internal_id}" 27 | security_group_ssh_id = "${module.security.ssh_id}" 28 | ami = "${lookup(var.bastion_ami, var.region)}" 29 | bastion_subnet_ids = "${module.vpc.subnet_public_ids}" 30 | } 31 | 32 | module "alb" { 33 | source = "./modules/alb" 34 | 35 | security_group_internal_id = "${module.security.internal_id}" 36 | security_group_inbound_id = "${module.security.inbound_id}" 37 | alb_subnet_ids = "${module.vpc.subnet_public_ids}" 38 | vpc_id = "${module.vpc.vpc_id}" 39 | acm_arn = "${var.acm_arn}" 40 | route53_zone_id = "${var.route53_zone_id}" 41 | alb_dns_name = "${var.alb_dns_name}" 42 | } 43 | 44 | module "ecs_cluster" { 45 | source = "./modules/ecs-cluster" 46 | 47 | cluster_name = "${var.cluster_name}" 48 | datadog_api_key = "${var.datadog_api_key}" 49 | } 50 | 51 | module "ecs_instances" { 52 | source = "./modules/ecs-instances" 53 | 54 | cluster_name = "${module.ecs_cluster.cluster_name}" 55 | dd_agent_task_name = "${module.ecs_cluster.dd_agent_task_name}" 56 | hello_world_task_name = "${module.ecs_cluster.hello_world_task_name}" 57 | instance_type = "${var.instance_type}" 58 | key_pair_name = "${var.key_pair_name}" 59 | instance_profile_name = "${module.security.ecs_instance_profile_name}" 60 | security_group_ecs_instance_id = "${module.security.internal_id}" 61 | ami = "${lookup(var.ami, var.region)}" 62 | asg_min = "${var.asg_min}" 63 | asg_max = "${var.asg_max}" 64 | ecs_cluster_subnet_ids = "${module.vpc.subnet_private_ids}" 65 | target_group_arn = "${module.alb.target_group_arn}" 66 | } 67 | -------------------------------------------------------------------------------- /base-infra/modules/alb/main.tf: -------------------------------------------------------------------------------- 1 | # Application load balancer that distributes load between the instances 2 | resource "aws_alb" "instance_alb" { 3 | name = "instance-alb" 4 | internal = false 5 | 6 | security_groups = [ 7 | "${var.security_group_internal_id}", 8 | "${var.security_group_inbound_id}", 9 | ] 10 | 11 | subnets = ["${split(",", var.alb_subnet_ids)}"] 12 | } 13 | 14 | # Default ALB target group that defines the default port/protocol the instances will listen on 15 | resource "aws_alb_target_group" "instance_tg" { 16 | name = "instance-tg" 17 | protocol = "HTTP" 18 | port = "80" 19 | vpc_id = "${var.vpc_id}" 20 | 21 | health_check { 22 | path = "/" 23 | } 24 | } 25 | 26 | # ALB listener that checks for connection requests from clients using the port/protocol specified 27 | # These requests are then forwarded to one or more target groups, based on the rules defined 28 | resource "aws_alb_listener" "instance_listener" { 29 | load_balancer_arn = "${aws_alb.instance_alb.arn}" 30 | port = "443" 31 | protocol = "HTTPS" 32 | ssl_policy = "ELBSecurityPolicy-2015-05" 33 | certificate_arn = "${var.acm_arn}" 34 | 35 | default_action { 36 | target_group_arn = "${aws_alb_target_group.instance_tg.arn}" 37 | type = "forward" 38 | } 39 | 40 | depends_on = ["aws_alb_target_group.instance_tg"] 41 | } 42 | 43 | # Route 53 DNS record for the application load balancer 44 | resource "aws_route53_record" "alb_record" { 45 | zone_id = "${var.route53_zone_id}" 46 | name = "${var.alb_dns_name}" 47 | type = "A" 48 | 49 | alias { 50 | name = "${aws_alb.instance_alb.dns_name}" 51 | zone_id = "${aws_alb.instance_alb.zone_id}" 52 | evaluate_target_health = false 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /base-infra/modules/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_listener_arn" { 2 | value = "${aws_alb_listener.instance_listener.arn}" 3 | } 4 | 5 | output "target_group_arn" { 6 | value = "${aws_alb_target_group.instance_tg.arn}" 7 | } 8 | -------------------------------------------------------------------------------- /base-infra/modules/alb/variables.tf: -------------------------------------------------------------------------------- 1 | variable "security_group_internal_id" { 2 | description = "Id of security group allowing internal traffic" 3 | } 4 | 5 | variable "security_group_inbound_id" { 6 | description = "Id of security group allowing inbound traffic" 7 | } 8 | 9 | variable "alb_subnet_ids" { 10 | description = "Comma-separated list of subnets where ALB should be deployed" 11 | } 12 | 13 | variable "vpc_id" { 14 | description = "Id of VPC where ALB will live" 15 | } 16 | 17 | variable "acm_arn" { 18 | description = "ARN of ACM SSL certificate" 19 | } 20 | 21 | variable "route53_zone_id" { 22 | description = "Route 53 Hosted Zone ID" 23 | } 24 | 25 | variable "alb_dns_name" { 26 | description = "DNS name for ALB" 27 | } 28 | -------------------------------------------------------------------------------- /base-infra/modules/bastion/main.tf: -------------------------------------------------------------------------------- 1 | # Launch configuration for each bastion 2 | resource "aws_launch_configuration" "bastion" { 3 | name_prefix = "bastion-" 4 | instance_type = "${var.instance_type}" 5 | key_name = "${var.key_pair_name}" 6 | associate_public_ip_address = true 7 | image_id = "${var.ami}" 8 | 9 | security_groups = [ 10 | "${var.security_group_internal_id}", 11 | "${var.security_group_ssh_id}", 12 | ] 13 | 14 | # Important note: whenever using a launch configuration with an auto scaling 15 | # group, you must set create_before_destroy = true. However, as soon as you 16 | # set create_before_destroy = true in one resource, you must also set it in 17 | # every resource that it depends on, or we'll get an error about cyclic 18 | # dependencies (especially when removing resources). For more info, see: 19 | # 20 | # https://www.terraform.io/docs/providers/aws/r/launch_configuration.html 21 | # https://terraform.io/docs/configuration/resources.html 22 | lifecycle { create_before_destroy = true } 23 | } 24 | 25 | # Autoscaling group that specifies how we want to scale the bastions 26 | resource "aws_autoscaling_group" "bastion" { 27 | name = "bastion" 28 | min_size = "1" 29 | max_size = "1" 30 | launch_configuration = "${aws_launch_configuration.bastion.name}" 31 | vpc_zone_identifier = ["${split(",", var.bastion_subnet_ids)}"] 32 | health_check_type = "EC2" 33 | 34 | lifecycle { create_before_destroy = true } 35 | 36 | tag { 37 | key = "Name" 38 | value = "bastion" 39 | propagate_at_launch = true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /base-infra/modules/bastion/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jch254/terraform-ecs-autoscale-alb/ee36aa5462166922123252be846c338b9bd9f1b6/base-infra/modules/bastion/outputs.tf -------------------------------------------------------------------------------- /base-infra/modules/bastion/variables.tf: -------------------------------------------------------------------------------- 1 | variable "instance_type" { 2 | description = "Instance type of bastion instance (e.g. t2.micro)" 3 | } 4 | 5 | variable "key_pair_name" { 6 | description = "Name of the Key Pair that can be used to access bastion instance" 7 | } 8 | 9 | variable "ami" { 10 | description = "AMI of bastion instance" 11 | } 12 | 13 | variable "security_group_internal_id" { 14 | description = "Id of security group allowing internal traffic" 15 | } 16 | 17 | variable "security_group_ssh_id" { 18 | description = "Id of security group allowing SSH traffic" 19 | } 20 | 21 | variable "bastion_subnet_ids" { 22 | description = "Comma-separated list of subnets where bastion instance should be deployed" 23 | } 24 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-cluster/datadog-agent-task-definition.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "environment": [ 4 | { 5 | "name": "API_KEY", 6 | "value": "${datadog_api_key}" 7 | } 8 | ], 9 | "mountPoints": [ 10 | { 11 | "sourceVolume": "docker_sock", 12 | "containerPath": "/var/run/docker.sock", 13 | "readOnly": false 14 | }, 15 | { 16 | "sourceVolume": "proc", 17 | "containerPath": "/host/proc", 18 | "readOnly": true 19 | }, 20 | { 21 | "sourceVolume": "cgroup", 22 | "containerPath": "/host/sys/fs/cgroup", 23 | "readOnly": true 24 | } 25 | ], 26 | "name": "dd-agent", 27 | "image": "datadog/docker-dd-agent:ecs", 28 | "cpu": 10, 29 | "memory": 128, 30 | "essential": true 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-cluster/hello-world-task-definition.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "environment": [], 4 | "mountPoints": [ 5 | { 6 | "sourceVolume": "www", 7 | "containerPath": "/usr/share/nginx/html", 8 | "readOnly": true 9 | } 10 | ], 11 | "portMappings": [ 12 | { 13 | "containerPort": 80, 14 | "hostPort": 80 15 | } 16 | ], 17 | "name": "hello-world", 18 | "image": "nginx", 19 | "cpu": 10, 20 | "memory": 128, 21 | "essential": true 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-cluster/main.tf: -------------------------------------------------------------------------------- 1 | # The ECS Cluster 2 | resource "aws_ecs_cluster" "ecs_cluster" { 3 | name = "${var.cluster_name}" 4 | } 5 | 6 | # The Datadog agent task definition 7 | data "template_file" "datadog_agent_task_definition" { 8 | template = "${file("${path.module}/datadog-agent-task-definition.json")}" 9 | 10 | vars { 11 | datadog_api_key = "${var.datadog_api_key}" 12 | } 13 | } 14 | 15 | # The hello world task definition 16 | data "template_file" "hello_world_task_definition" { 17 | template = "${file("${path.module}/hello-world-task-definition.json")}" 18 | } 19 | 20 | # The ECS task that specifies which Docker container we need to run the Datadog agent container 21 | resource "aws_ecs_task_definition" "datadog_agent" { 22 | family = "dd-agent-task" 23 | volume { 24 | name = "docker_sock" 25 | host_path = "/var/run/docker.sock" 26 | } 27 | volume { 28 | name = "proc" 29 | host_path = "/proc/" 30 | } 31 | volume { 32 | name = "cgroup" 33 | host_path = "/cgroup/" 34 | } 35 | container_definitions = "${data.template_file.datadog_agent_task_definition.rendered}" 36 | } 37 | 38 | # The ECS task that specifies which Docker container we need to run the hello world container 39 | resource "aws_ecs_task_definition" "hello_world" { 40 | family = "hello-world" 41 | volume { 42 | name = "www" 43 | host_path = "/var/www/html" 44 | } 45 | container_definitions = "${data.template_file.hello_world_task_definition.rendered}" 46 | } 47 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_name" { 2 | value = "${aws_ecs_cluster.ecs_cluster.name}" 3 | } 4 | 5 | output "dd_agent_task_name" { 6 | value = "${aws_ecs_task_definition.datadog_agent.family}" 7 | } 8 | 9 | output "hello_world_task_name" { 10 | value = "${aws_ecs_task_definition.hello_world.family}" 11 | } 12 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | description = "Name of ECS cluster" 3 | } 4 | 5 | variable "datadog_api_key" { 6 | description = "Datadog API key" 7 | } 8 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-instances/main.tf: -------------------------------------------------------------------------------- 1 | # User data template that specifies how to bootstrap each instance 2 | data "template_file" "user_data" { 3 | template = "${file("${path.module}/user-data.tpl")}" 4 | 5 | vars { 6 | cluster_name = "${var.cluster_name}" 7 | dd_agent_task_name = "${var.dd_agent_task_name}" 8 | hello_world_task_name = "${var.hello_world_task_name}" 9 | } 10 | } 11 | 12 | # The launch configuration for each EC2 Instance that will run in the cluster 13 | resource "aws_launch_configuration" "ecs_instance" { 14 | name_prefix = "${var.cluster_name}-instance-" 15 | instance_type = "${var.instance_type}" 16 | key_name = "${var.key_pair_name}" 17 | iam_instance_profile = "${var.instance_profile_name}" 18 | security_groups = ["${var.security_group_ecs_instance_id}"] 19 | image_id = "${var.ami}" 20 | user_data = "${data.template_file.user_data.rendered}" 21 | 22 | # Important note: whenever using a launch configuration with an auto scaling 23 | # group, you must set create_before_destroy = true. However, as soon as you 24 | # set create_before_destroy = true in one resource, you must also set it in 25 | # every resource that it depends on, or we'll get an error about cyclic 26 | # dependencies (especially when removing resources). For more info, see: 27 | # 28 | # https://www.terraform.io/docs/providers/aws/r/launch_configuration.html 29 | # https://terraform.io/docs/configuration/resources.html 30 | lifecycle { create_before_destroy = true } 31 | } 32 | 33 | # The auto scaling group that specifies how we want to scale the number of EC2 Instances in the cluster 34 | resource "aws_autoscaling_group" "ecs_cluster" { 35 | name = "${var.cluster_name}-instances" 36 | min_size = "${var.asg_min}" 37 | max_size = "${var.asg_max}" 38 | launch_configuration = "${aws_launch_configuration.ecs_instance.name}" 39 | vpc_zone_identifier = ["${split(",", var.ecs_cluster_subnet_ids)}"] 40 | health_check_type = "EC2" 41 | target_group_arns = ["${var.target_group_arn}"] 42 | 43 | lifecycle { create_before_destroy = true } 44 | 45 | tag { 46 | key = "Name" 47 | value = "${var.cluster_name}-instance" 48 | propagate_at_launch = true 49 | } 50 | } 51 | 52 | resource "aws_autoscaling_policy" "scale_up" { 53 | name = "${var.cluster_name}-instances-scale-up" 54 | scaling_adjustment = 1 55 | adjustment_type = "ChangeInCapacity" 56 | cooldown = 300 57 | autoscaling_group_name = "${aws_autoscaling_group.ecs_cluster.name}" 58 | } 59 | 60 | resource "aws_autoscaling_policy" "scale_down" { 61 | name = "${var.cluster_name}-instances-scale-down" 62 | scaling_adjustment = -1 63 | adjustment_type = "ChangeInCapacity" 64 | cooldown = 300 65 | autoscaling_group_name = "${aws_autoscaling_group.ecs_cluster.name}" 66 | } 67 | 68 | # A CloudWatch alarm that monitors CPU utilization of cluster instances for scaling up 69 | resource "aws_cloudwatch_metric_alarm" "ecs_cluster_instances_cpu_high" { 70 | alarm_name = "${var.cluster_name}-instances-CPU-Utilization-Above-80" 71 | alarm_description = "This alarm monitors ${var.cluster_name} instances CPU utilization for scaling up" 72 | comparison_operator = "GreaterThanOrEqualToThreshold" 73 | evaluation_periods = "1" 74 | metric_name = "CPUUtilization" 75 | namespace = "AWS/EC2" 76 | period = "300" 77 | statistic = "Average" 78 | threshold = "80" 79 | alarm_actions = ["${aws_autoscaling_policy.scale_up.arn}"] 80 | 81 | dimensions { 82 | AutoScalingGroupName = "${aws_autoscaling_group.ecs_cluster.name}" 83 | } 84 | } 85 | 86 | # A CloudWatch alarm that monitors CPU utilization of cluster instances for scaling down 87 | resource "aws_cloudwatch_metric_alarm" "ecs_cluster_instances_cpu_low" { 88 | alarm_name = "${var.cluster_name}-instances-CPU-Utilization-Below-5" 89 | alarm_description = "This alarm monitors ${var.cluster_name} instances CPU utilization for scaling down" 90 | comparison_operator = "LessThanThreshold" 91 | evaluation_periods = "1" 92 | metric_name = "CPUUtilization" 93 | namespace = "AWS/EC2" 94 | period = "300" 95 | statistic = "Average" 96 | threshold = "5" 97 | alarm_actions = ["${aws_autoscaling_policy.scale_down.arn}"] 98 | 99 | dimensions { 100 | AutoScalingGroupName = "${aws_autoscaling_group.ecs_cluster.name}" 101 | } 102 | } 103 | 104 | # A CloudWatch alarm that monitors memory utilization of cluster instances for scaling up 105 | resource "aws_cloudwatch_metric_alarm" "ecs_cluster_instances_memory_high" { 106 | alarm_name = "${var.cluster_name}-instances-Memory-Utilization-Above-80" 107 | alarm_description = "This alarm monitors ${var.cluster_name} instances memory utilization for scaling up" 108 | comparison_operator = "GreaterThanOrEqualToThreshold" 109 | evaluation_periods = "1" 110 | metric_name = "MemoryUtilization" 111 | namespace = "AWS/EC2" 112 | period = "300" 113 | statistic = "Average" 114 | threshold = "80" 115 | alarm_actions = ["${aws_autoscaling_policy.scale_down.arn}"] 116 | 117 | dimensions { 118 | AutoScalingGroupName = "${aws_autoscaling_group.ecs_cluster.name}" 119 | } 120 | } 121 | 122 | # A CloudWatch alarm that monitors memory utilization of cluster instances for scaling down 123 | resource "aws_cloudwatch_metric_alarm" "ecs_cluster_instances_memory_low" { 124 | alarm_name = "${var.cluster_name}-instances-Memory-Utilization-Below-5" 125 | alarm_description = "This alarm monitors ${var.cluster_name} instances memory utilization for scaling down" 126 | comparison_operator = "LessThanThreshold" 127 | evaluation_periods = "1" 128 | metric_name = "MemoryUtilization" 129 | namespace = "AWS/EC2" 130 | period = "300" 131 | statistic = "Average" 132 | threshold = "5" 133 | alarm_actions = ["${aws_autoscaling_policy.scale_down.arn}"] 134 | 135 | dimensions { 136 | AutoScalingGroupName = "${aws_autoscaling_group.ecs_cluster.name}" 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-instances/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jch254/terraform-ecs-autoscale-alb/ee36aa5462166922123252be846c338b9bd9f1b6/base-infra/modules/ecs-instances/outputs.tf -------------------------------------------------------------------------------- /base-infra/modules/ecs-instances/user-data.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 3 | 4 | echo Begin: user-data 5 | 6 | echo Begin: update and install packages 7 | yum update -y 8 | yum install -y aws-cli jq 9 | echo End: update and install packages 10 | 11 | echo Begin: create hello world index.html 12 | mkdir -p /var/www/html 13 | echo '

Hello world from ${cluster_name}!

' >> /var/www/html/index.html 14 | echo End: create hello world index.html 15 | 16 | echo Begin: start ECS 17 | cluster="${cluster_name}" 18 | echo ECS_CLUSTER=$cluster >> /etc/ecs/ecs.config 19 | start ecs 20 | until $(curl --output /dev/null --silent --head --fail http://localhost:51678/v1/metadata); do 21 | printf '.' 22 | sleep 1 23 | done 24 | echo End: start ECS 25 | 26 | echo Begin: set up datadog and hello world ECS tasks 27 | dd_task_def="${dd_agent_task_name}" 28 | hello_world_task_def="${hello_world_task_name}" 29 | instance_arn=$(curl -s http://localhost:51678/v1/metadata | jq -r '. | .ContainerInstanceArn' | awk -F/ '{print $NF}' ) 30 | az=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone) 31 | region=$${az:0:$${#az} - 1} 32 | 33 | echo " 34 | cluster=$cluster 35 | az=$az 36 | region=$region 37 | aws ecs start-task --cluster $cluster --task-definition $dd_task_def --container-instances $instance_arn --region $region 38 | aws ecs start-task --cluster $cluster --task-definition $hello_world_task_def --container-instances $instance_arn --region $region 39 | " >> /etc/rc.local 40 | echo End: set up datadog and hello world ECS tasks 41 | 42 | echo End: user-data 43 | -------------------------------------------------------------------------------- /base-infra/modules/ecs-instances/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | description = "Name of ECS cluster" 3 | } 4 | 5 | variable "dd_agent_task_name" { 6 | description = "Name of Datadog agent ECS task" 7 | } 8 | 9 | variable "hello_world_task_name" { 10 | description = "Name of 'hello world' ECS task" 11 | } 12 | 13 | variable "instance_type" { 14 | description = "Instance type of each EC2 instance in the ECS cluster" 15 | } 16 | 17 | variable "key_pair_name" { 18 | description = "Name of the Key Pair that can be used to SSH to each EC2 instance in the ECS cluster" 19 | } 20 | 21 | variable "instance_profile_name" { 22 | description = "Name of IAM instance profile for ECS instances" 23 | } 24 | 25 | variable "security_group_ecs_instance_id" { 26 | description = "Id of security group allowing internal traffic" 27 | } 28 | 29 | variable "ami" { 30 | description = "AMI of each EC2 instance in the ECS cluster" 31 | } 32 | 33 | variable "asg_min" { 34 | description = "Minimum number of EC2 instances to run in the ECS cluster" 35 | } 36 | 37 | variable "asg_max" { 38 | description = "Maximum number of EC2 instances to run in the ECS cluster" 39 | } 40 | 41 | variable "ecs_cluster_subnet_ids" { 42 | description = "Comma-separated list of subnets where EC2 instances should be deployed" 43 | } 44 | 45 | variable "target_group_arn" { 46 | default = "ALB Target group ARN" 47 | } 48 | -------------------------------------------------------------------------------- /base-infra/modules/security/main.tf: -------------------------------------------------------------------------------- 1 | # Security group allowing internal traffic (inside VPC) 2 | resource "aws_security_group" "internal" { 3 | vpc_id = "${var.vpc_id}" 4 | name = "internal" 5 | description = "Allow internal traffic" 6 | 7 | ingress { 8 | from_port = 0 9 | to_port = 65535 10 | protocol = "tcp" 11 | cidr_blocks = ["${var.vpc_cidr_block}"] 12 | } 13 | 14 | egress { 15 | from_port = 0 16 | to_port = 0 17 | protocol = "-1" 18 | cidr_blocks = ["0.0.0.0/0"] 19 | } 20 | 21 | tags { 22 | Name = "internal" 23 | } 24 | } 25 | 26 | # Security group allowing SSH traffic from a designated IP address (var.ssh_allowed_ip) 27 | resource "aws_security_group" "ssh" { 28 | vpc_id = "${var.vpc_id}" 29 | name = "ssh" 30 | description = "Allow SSH inbound traffic" 31 | 32 | ingress { 33 | from_port = 22 34 | to_port = 22 35 | protocol = "tcp" 36 | cidr_blocks = ["${var.ssh_allowed_ip}/32"] 37 | } 38 | 39 | egress { 40 | from_port = 0 41 | to_port = 0 42 | protocol = "-1" 43 | cidr_blocks = ["0.0.0.0/0"] 44 | } 45 | 46 | tags { 47 | Name = "ssh" 48 | } 49 | } 50 | 51 | # Security group allowing inbound HTTPS from anywhere 52 | resource "aws_security_group" "inbound" { 53 | vpc_id = "${var.vpc_id}" 54 | name = "inbound" 55 | description = "Allow inbound HTTPS traffic" 56 | 57 | ingress { 58 | from_port = 443 59 | to_port = 443 60 | protocol = "tcp" 61 | cidr_blocks = ["0.0.0.0/0"] 62 | } 63 | 64 | egress { 65 | from_port = 0 66 | to_port = 0 67 | protocol = "-1" 68 | cidr_blocks = ["0.0.0.0/0"] 69 | } 70 | 71 | tags { 72 | Name = "inbound" 73 | } 74 | } 75 | 76 | # An IAM instance profile we attach to the EC2 instances in the cluster 77 | resource "aws_iam_instance_profile" "ecs_instance" { 78 | name = "ecs-instance" 79 | roles = ["${aws_iam_role.ecs_instance.name}"] 80 | 81 | lifecycle { create_before_destroy = true } 82 | } 83 | 84 | # An IAM role that we attach to the EC2 Instances in the cluster 85 | resource "aws_iam_role" "ecs_instance" { 86 | name = "ecs-instance" 87 | assume_role_policy = "${file("${path.module}/policies/ecs-instance.json")}" 88 | 89 | lifecycle { create_before_destroy = true } 90 | } 91 | 92 | # IAM policy we add to ECS cluster instances that allows them to do their thing 93 | resource "aws_iam_role_policy" "ecs_instance_policy" { 94 | name = "ecs-instance-policy" 95 | role = "${aws_iam_role.ecs_instance.id}" 96 | policy = "${file("${path.module}/policies/ecs-instance-policy.json")}" 97 | 98 | lifecycle { create_before_destroy = true } 99 | } 100 | 101 | # An IAM Role that we attach to ECS services 102 | resource "aws_iam_role" "ecs_service" { 103 | name = "ecs-service" 104 | assume_role_policy = "${file("${path.module}/policies/ecs-service.json")}" 105 | } 106 | 107 | # Managed IAM Policy for ECS services to communicate with EC2 Instances 108 | resource "aws_iam_role_policy_attachment" "ecs_service" { 109 | role = "${aws_iam_role.ecs_service.name}" 110 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole" 111 | } 112 | 113 | # An IAM Role for ECS service autoscaling 114 | resource "aws_iam_role" "ecs_service_autoscale" { 115 | name = "ecs-service-autoscale" 116 | assume_role_policy = "${file("${path.module}/policies/ecs-service-autoscale.json")}" 117 | } 118 | 119 | # Managed IAM Policy for ECS service autoscaling 120 | resource "aws_iam_role_policy_attachment" "ecs_service_autoscale" { 121 | role = "${aws_iam_role.ecs_service_autoscale.name}" 122 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole" 123 | } 124 | -------------------------------------------------------------------------------- /base-infra/modules/security/outputs.tf: -------------------------------------------------------------------------------- 1 | output "internal_id" { 2 | value = "${aws_security_group.internal.id}" 3 | } 4 | 5 | output "inbound_id" { 6 | value = "${aws_security_group.inbound.id}" 7 | } 8 | 9 | output "ssh_id" { 10 | value = "${aws_security_group.ssh.id}" 11 | } 12 | 13 | output "ecs_instance_profile_name" { 14 | value = "${aws_iam_instance_profile.ecs_instance.name}" 15 | } 16 | 17 | output "ecs_service_role_arn" { 18 | value = "${aws_iam_role.ecs_service.arn}" 19 | } 20 | 21 | output "ecs_service_autoscale_role_arn" { 22 | value = "${aws_iam_role.ecs_service_autoscale.arn}" 23 | } 24 | -------------------------------------------------------------------------------- /base-infra/modules/security/policies/ecs-instance-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "ECS", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "ecs:CreateCluster", 9 | "ecs:DeregisterContainerInstance", 10 | "ecs:DiscoverPollEndpoint", 11 | "ecs:Poll", 12 | "ecs:RegisterContainerInstance", 13 | "ecs:StartTelemetrySession", 14 | "ecs:Submit*", 15 | "ecs:StartTask", 16 | "ecr:GetAuthorizationToken", 17 | "ecr:BatchCheckLayerAvailability", 18 | "ecr:GetDownloadUrlForLayer", 19 | "ecr:BatchGetImage", 20 | "logs:CreateLogGroup", 21 | "logs:CreateLogStream", 22 | "logs:PutLogEvents", 23 | "logs:DescribeLogStreams" 24 | ], 25 | "Resource": "*" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /base-infra/modules/security/policies/ecs-instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "EC2", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": "ec2.amazonaws.com" 9 | }, 10 | "Action": "sts:AssumeRole" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /base-infra/modules/security/policies/ecs-service-autoscale.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Autoscaling", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": "application-autoscaling.amazonaws.com" 9 | }, 10 | "Action": "sts:AssumeRole" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /base-infra/modules/security/policies/ecs-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "ECS", 6 | "Effect": "Allow", 7 | "Principal": { 8 | "Service": "ecs.amazonaws.com" 9 | }, 10 | "Action": "sts:AssumeRole" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /base-infra/modules/security/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "Id of VPC where security groups will live" 3 | } 4 | 5 | variable "vpc_cidr_block" { 6 | description = "The source CIDR block to allow traffic from" 7 | } 8 | 9 | variable "ssh_allowed_ip" { 10 | description = "IP address allowed to SSH to bastion instance" 11 | } 12 | -------------------------------------------------------------------------------- /base-infra/modules/vpc/main.tf: -------------------------------------------------------------------------------- 1 | # VPC 2 | resource "aws_vpc" "vpc" { 3 | cidr_block = "10.0.0.0/16" 4 | 5 | tags { 6 | Name = "vpc" 7 | } 8 | } 9 | 10 | # Internet gateway 11 | resource "aws_internet_gateway" "igw" { 12 | vpc_id = "${aws_vpc.vpc.id}" 13 | 14 | tags { 15 | Name = "igw" 16 | } 17 | } 18 | 19 | # Public subnets 20 | # These will get a 0/0 route to the IGW gateway as below 21 | # The az_count variable determines how many subnets will be created 22 | resource "aws_subnet" "public_subnet" { 23 | count = "${var.az_count}" 24 | vpc_id = "${aws_vpc.vpc.id}" 25 | cidr_block = "10.0.${count.index}.0/24" 26 | availability_zone = "${element(split(",", lookup(var.availability_zones, var.region)), count.index)}" 27 | map_public_ip_on_launch = true 28 | 29 | tags { 30 | Name = "Public-subnet-${count.index + 1}" 31 | } 32 | } 33 | 34 | # Public route table 35 | resource "aws_route_table" "public_rt" { 36 | vpc_id = "${aws_vpc.vpc.id}" 37 | 38 | tags { 39 | Name = "public-rt" 40 | } 41 | } 42 | 43 | # Attach a 0/0 route to the public route table going to the IGW 44 | resource "aws_route" "internet" { 45 | route_table_id = "${aws_route_table.public_rt.id}" 46 | destination_cidr_block = "0.0.0.0/0" 47 | gateway_id = "${aws_internet_gateway.igw.id}" 48 | 49 | depends_on = ["aws_internet_gateway.igw", "aws_route_table.public_rt"] 50 | } 51 | 52 | # Public subnet route table associations 53 | # The az_count variable determines how many associations will be created (one per public subnet) 54 | resource "aws_route_table_association" "public" { 55 | count = "${var.az_count}" 56 | subnet_id = "${element(aws_subnet.public_subnet.*.id, count.index)}" 57 | route_table_id = "${aws_route_table.public_rt.id}" 58 | } 59 | 60 | # The public route needs to be the default one (so that the default 61 | # route on the VPC goes to the IGW). 62 | resource "aws_main_route_table_association" "public" { 63 | vpc_id = "${aws_vpc.vpc.id}" 64 | route_table_id = "${aws_route_table.public_rt.id}" 65 | } 66 | 67 | # NAT gateway elastic IPs 68 | # The az_count variable determines how many EIPs will be created (one per private subnet) 69 | resource "aws_eip" "nat_gateway" { 70 | count = "${var.az_count}" 71 | vpc = true 72 | } 73 | 74 | # NAT gateways 75 | # The az_count variable determines how many NAT gateways will be created (one per public subnet) 76 | resource "aws_nat_gateway" "nat_gateway" { 77 | count = "${var.az_count}" 78 | allocation_id = "${element(aws_eip.nat_gateway.*.id, count.index)}" 79 | subnet_id = "${element(aws_subnet.public_subnet.*.id, count.index)}" 80 | depends_on = ["aws_internet_gateway.igw", "aws_eip.nat_gateway"] 81 | } 82 | 83 | # Private subnets 84 | # The az_count variable determines how many subnets will be created 85 | resource "aws_subnet" "private_subnet" { 86 | count = "${var.az_count}" 87 | vpc_id = "${aws_vpc.vpc.id}" 88 | cidr_block = "10.0.${100 + count.index}.0/24" 89 | availability_zone = "${element(split(",", lookup(var.availability_zones, var.region)), count.index)}" 90 | map_public_ip_on_launch = false 91 | 92 | tags { 93 | Name = "Private-subnet-${count.index + 1}" 94 | } 95 | } 96 | 97 | # Private route tables 98 | # The az_count variable determines how many route tables will be created 99 | # To get around the single-AZ nature of the current NAT Gateway implementation, 100 | # we define a route table per AZ 101 | resource "aws_route_table" "private_rt" { 102 | count = "${var.az_count}" 103 | vpc_id = "${aws_vpc.vpc.id}" 104 | 105 | tags { 106 | Name = "private-rt" 107 | } 108 | } 109 | 110 | # Attach 0/0 route to private route tables going to the NAT gateways 111 | resource "aws_route" "nat" { 112 | count = "${var.az_count}" 113 | route_table_id = "${element(aws_route_table.private_rt.*.id, count.index)}" 114 | destination_cidr_block = "0.0.0.0/0" 115 | nat_gateway_id = "${element(aws_nat_gateway.nat_gateway.*.id, count.index)}" 116 | } 117 | 118 | # Associate the private route tables with the private subnets 119 | resource "aws_route_table_association" "private" { 120 | count = "${var.az_count}" 121 | subnet_id = "${element(aws_subnet.private_subnet.*.id, count.index)}" 122 | route_table_id = "${element(aws_route_table.private_rt.*.id, count.index)}" 123 | } 124 | -------------------------------------------------------------------------------- /base-infra/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = "${aws_vpc.vpc.id}" 3 | } 4 | 5 | output "vpc_cidr_block" { 6 | value = "${aws_vpc.vpc.cidr_block}" 7 | } 8 | 9 | output "subnet_private_ids" { 10 | value = "${join(",", aws_subnet.private_subnet.*.id)}" 11 | } 12 | 13 | output "subnet_public_ids" { 14 | value = "${join(",", aws_subnet.public_subnet.*.id)}" 15 | } 16 | -------------------------------------------------------------------------------- /base-infra/modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 3 | } 4 | 5 | variable "az_count" { 6 | description = "The number of availailbilty zones to deploy across (must be minimum of two to use ALB)" 7 | } 8 | 9 | # Use "aws ec2 describe-availability-zones --region us-east-1" 10 | # to figure out the name of the AZs on every region as required 11 | variable "availability_zones" { 12 | description = "Availability zones by region" 13 | default = { 14 | "ap-southeast-2" = "ap-southeast-2a,ap-southeast-2b,ap-southeast-2c" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /base-infra/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = "${module.vpc.vpc_id}" 3 | } 4 | 5 | output "alb_listener_arn" { 6 | value = "${module.alb.alb_listener_arn}" 7 | } 8 | 9 | output "cluster_name" { 10 | value = "${module.ecs_cluster.cluster_name}" 11 | } 12 | 13 | output "ecs_service_role_arn" { 14 | value = "${module.security.ecs_service_role_arn}" 15 | } 16 | 17 | output "ecs_service_autoscale_role_arn" { 18 | value = "${module.security.ecs_service_autoscale_role_arn}" 19 | } 20 | -------------------------------------------------------------------------------- /base-infra/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 3 | } 4 | 5 | variable "az_count" { 6 | description = "The number of availailbilty zones to deploy across (must be minimum of two to use ALB)" 7 | default = 3 8 | } 9 | 10 | variable "ssh_allowed_ip" { 11 | description = "IP address allowed to SSH to bastion instance" 12 | } 13 | 14 | variable "acm_arn" { 15 | description = "ARN of ACM SSL certificate" 16 | } 17 | 18 | variable "route53_zone_id" { 19 | description = "Route 53 Hosted Zone ID" 20 | } 21 | 22 | variable "alb_dns_name" { 23 | description = "DNS name for ALB" 24 | } 25 | 26 | variable "cluster_name" { 27 | description = "Name of the ECS cluster" 28 | } 29 | 30 | variable "datadog_api_key" { 31 | description = "Datadog API key" 32 | } 33 | 34 | variable "instance_type" { 35 | description = "Instance type of each EC2 instance in the ECS cluster" 36 | } 37 | 38 | variable "key_pair_name" { 39 | description = "Name of the Key Pair that can be used to SSH to each EC2 instance in the ECS cluster" 40 | } 41 | 42 | variable "ami" { 43 | description = "AMI of each EC2 instance in the ECS cluster" 44 | # These are ids for Amazon's ECS-Optimized Linux AMI from: http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html 45 | # Note that the very first time, you have to accept the terms and conditions on that page or the EC2 instances will fail to launch! 46 | default = { 47 | us-east-1 = "ami-1924770e" 48 | ap-southeast-2 = "ami-73407d10" 49 | } 50 | } 51 | 52 | variable "asg_min" { 53 | description = "Minimum number of EC2 instances to run in the ECS cluster" 54 | default = 3 55 | } 56 | 57 | variable "asg_max" { 58 | description = "Maximum number of EC2 instances to run in the ECS cluster" 59 | default = 9 60 | } 61 | 62 | variable "bastion_instance_type" { 63 | description = "Instance type of bastion instance (e.g. t2.micro)" 64 | } 65 | 66 | variable "bastion_key_pair_name" { 67 | description = "Name of the Key Pair that can be used to access bastion instance" 68 | } 69 | 70 | variable "bastion_ami" { 71 | description = "AMI of bastion instance" 72 | # These are ids for Amazon's Linux AMI from: https://aws.amazon.com/amazon-linux-ami 73 | default = { 74 | us-east-1 = "ami-b73b63a0" 75 | ap-southeast-2 = "ami-db704cb8" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /beta-service/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2017-node7"] 3 | } 4 | -------------------------------------------------------------------------------- /beta-service/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /beta-service/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | extends: "airbnb-base", 3 | env: { 4 | node: true, 5 | es6: true 6 | }, 7 | parser: "babel-eslint", 8 | rules: { 9 | max-len: ["error", 120, 2] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /beta-service/beta-service.tfvars: -------------------------------------------------------------------------------- 1 | region = "ap-southeast-2" 2 | 3 | service_name = "beta-service" 4 | 5 | container_port = "80" 6 | 7 | docker_image = "jch254/ecs-demo-api" 8 | 9 | docker_tag = "latest" 10 | -------------------------------------------------------------------------------- /beta-service/deploy.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | cd beta-service 4 | 5 | terraform remote config -backend=s3 \ 6 | -backend-config="bucket=603-terraform-remote-state" \ 7 | -backend-config="key=terraform-ecs-autoscale-alb/beta-service.tfstate" \ 8 | -backend-config="region=ap-southeast-2" \ 9 | -backend-config="encrypt=true" 10 | 11 | terraform get --update 12 | terraform plan -var-file beta-service.tfvars 13 | terraform apply -var-file beta-service.tfvars 14 | 15 | cd .. 16 | -------------------------------------------------------------------------------- /beta-service/modules.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | data "terraform_remote_state" "base_remote_state" { 6 | backend = "s3" 7 | config { 8 | bucket = "603-terraform-remote-state" 9 | key = "terraform-ecs-autoscale-alb/base-infra.tfstate" 10 | region = "${var.region}" 11 | } 12 | } 13 | 14 | module "alb_listener" { 15 | source = "./modules/alb-listener" 16 | 17 | service_name = "${var.service_name}" 18 | container_port = "${var.container_port}" 19 | vpc_id = "${data.terraform_remote_state.base_remote_state.vpc_id}" 20 | alb_listener_arn = "${data.terraform_remote_state.base_remote_state.alb_listener_arn}" 21 | } 22 | 23 | module "ecs_service" { 24 | source = "./modules/ecs-service" 25 | 26 | service_name = "${var.service_name}" 27 | docker_image = "${var.docker_image}" 28 | docker_tag = "${var.docker_tag}" 29 | container_cpu = "${var.container_cpu}" 30 | container_memory = "${var.container_memory}" 31 | container_port = "${var.container_port}" 32 | region = "${var.region}" 33 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 34 | desired_count = "${var.min_capacity}" 35 | ecs_service_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_role_arn}" 36 | target_group_arn = "${module.alb_listener.target_group_arn}" 37 | } 38 | 39 | module "autoscaling" { 40 | source = "./modules/autoscaling" 41 | 42 | service_name = "${var.service_name}" 43 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 44 | ecs_service_autoscale_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_autoscale_role_arn}" 45 | min_capacity = "${var.min_capacity}" 46 | max_capacity = "${var.max_capacity}" 47 | } 48 | -------------------------------------------------------------------------------- /beta-service/modules/alb-listener/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_alb_target_group" "beta_service_tg" { 2 | name = "${replace(var.service_name, "/(.{0,28})(.*)/", "$1")}-tg" 3 | 4 | protocol = "HTTP" 5 | port = "${var.container_port}" 6 | vpc_id = "${var.vpc_id}" 7 | 8 | health_check { 9 | path = "/ping" 10 | } 11 | } 12 | 13 | resource "aws_alb_listener_rule" "beta_service_listener" { 14 | listener_arn = "${var.alb_listener_arn}" 15 | priority = 200 16 | 17 | action { 18 | type = "forward" 19 | target_group_arn = "${aws_alb_target_group.beta_service_tg.arn}" 20 | } 21 | 22 | condition { 23 | field = "path-pattern" 24 | values = ["/beta/*"] 25 | } 26 | 27 | depends_on = ["aws_alb_target_group.beta_service_tg"] 28 | } 29 | -------------------------------------------------------------------------------- /beta-service/modules/alb-listener/outputs.tf: -------------------------------------------------------------------------------- 1 | output "target_group_arn" { 2 | value = "${aws_alb_target_group.beta_service_tg.arn}" 3 | } 4 | -------------------------------------------------------------------------------- /beta-service/modules/alb-listener/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "container_port" { 6 | description = "Port that service will listen on" 7 | } 8 | 9 | variable "vpc_id" { 10 | description = "Id of the VPC where service should be deployed" 11 | } 12 | 13 | variable "alb_listener_arn" { 14 | description = "ARN of ALB listener" 15 | } 16 | -------------------------------------------------------------------------------- /beta-service/modules/autoscaling/main.tf: -------------------------------------------------------------------------------- 1 | # A CloudWatch alarm that moniors CPU utilization of containers for scaling up 2 | resource "aws_cloudwatch_metric_alarm" "beta_service_cpu_high" { 3 | alarm_name = "${var.service_name}-cpu-utilization-above-80" 4 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling up" 5 | comparison_operator = "GreaterThanOrEqualToThreshold" 6 | evaluation_periods = "1" 7 | metric_name = "CPUUtilization" 8 | namespace = "AWS/ECS" 9 | period = "120" 10 | statistic = "Average" 11 | threshold = "80" 12 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 13 | 14 | dimensions { 15 | ClusterName = "${var.cluster_name}" 16 | ServiceName = "${var.service_name}" 17 | } 18 | } 19 | 20 | # A CloudWatch alarm that monitors CPU utilization of containers for scaling down 21 | resource "aws_cloudwatch_metric_alarm" "beta_service_cpu_low" { 22 | alarm_name = "${var.service_name}-cpu-utilization-below-5" 23 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling down" 24 | comparison_operator = "LessThanThreshold" 25 | evaluation_periods = "1" 26 | metric_name = "CPUUtilization" 27 | namespace = "AWS/ECS" 28 | period = "120" 29 | statistic = "Average" 30 | threshold = "5" 31 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 32 | 33 | dimensions { 34 | ClusterName = "${var.cluster_name}" 35 | ServiceName = "${var.service_name}" 36 | } 37 | } 38 | 39 | # A CloudWatch alarm that monitors memory utilization of containers for scaling up 40 | resource "aws_cloudwatch_metric_alarm" "beta_service_memory_high" { 41 | alarm_name = "${var.service_name}-memory-utilization-above-80" 42 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling up" 43 | comparison_operator = "GreaterThanOrEqualToThreshold" 44 | evaluation_periods = "1" 45 | metric_name = "MemoryUtilization" 46 | namespace = "AWS/ECS" 47 | period = "120" 48 | statistic = "Average" 49 | threshold = "80" 50 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 51 | 52 | dimensions { 53 | ClusterName = "${var.cluster_name}" 54 | ServiceName = "${var.service_name}" 55 | } 56 | } 57 | 58 | # A CloudWatch alarm that monitors memory utilization of containers for scaling down 59 | resource "aws_cloudwatch_metric_alarm" "beta_service_memory_low" { 60 | alarm_name = "${var.service_name}-memory-utilization-below-5" 61 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling down" 62 | comparison_operator = "LessThanThreshold" 63 | evaluation_periods = "1" 64 | metric_name = "MemoryUtilization" 65 | namespace = "AWS/ECS" 66 | period = "120" 67 | statistic = "Average" 68 | threshold = "5" 69 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 70 | 71 | dimensions { 72 | ClusterName = "${var.cluster_name}" 73 | ServiceName = "${var.service_name}" 74 | } 75 | } 76 | 77 | resource "aws_appautoscaling_target" "target" { 78 | resource_id = "service/${var.cluster_name}/${var.service_name}" 79 | role_arn = "${var.ecs_service_autoscale_role_arn}" 80 | scalable_dimension = "ecs:service:DesiredCount" 81 | min_capacity = "${var.min_capacity}" 82 | max_capacity = "${var.max_capacity}" 83 | } 84 | 85 | resource "aws_appautoscaling_policy" "scale_up" { 86 | name = "${var.service_name}-scale-up" 87 | resource_id = "service/${var.cluster_name}/${var.service_name}" 88 | scalable_dimension = "ecs:service:DesiredCount" 89 | adjustment_type = "ChangeInCapacity" 90 | cooldown = 120 91 | metric_aggregation_type = "Average" 92 | 93 | step_adjustment { 94 | metric_interval_lower_bound = 0 95 | scaling_adjustment = 1 96 | } 97 | 98 | depends_on = ["aws_appautoscaling_target.target"] 99 | } 100 | 101 | resource "aws_appautoscaling_policy" "scale_down" { 102 | name = "${var.service_name}-scale-down" 103 | resource_id = "service/${var.cluster_name}/${var.service_name}" 104 | scalable_dimension = "ecs:service:DesiredCount" 105 | adjustment_type = "ChangeInCapacity" 106 | cooldown = 120 107 | metric_aggregation_type = "Average" 108 | 109 | step_adjustment { 110 | metric_interval_upper_bound = 0 111 | scaling_adjustment = -1 112 | } 113 | 114 | depends_on = ["aws_appautoscaling_target.target"] 115 | } 116 | -------------------------------------------------------------------------------- /beta-service/modules/autoscaling/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jch254/terraform-ecs-autoscale-alb/ee36aa5462166922123252be846c338b9bd9f1b6/beta-service/modules/autoscaling/outputs.tf -------------------------------------------------------------------------------- /beta-service/modules/autoscaling/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "cluster_name" { 6 | description = "Name of ECS cluster" 7 | } 8 | 9 | variable "ecs_service_autoscale_role_arn" { 10 | description = "ARN of IAM role for ECS service autoscaling" 11 | } 12 | 13 | variable "min_capacity" { 14 | description = "Minimum number of containers to run" 15 | } 16 | 17 | variable "max_capacity" { 18 | description = "Minimum number of containers to run" 19 | } 20 | -------------------------------------------------------------------------------- /beta-service/modules/ecs-service/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "beta_service_lg" { 2 | name = "${var.service_name}" 3 | } 4 | 5 | data "template_file" "task_definition" { 6 | template = "${file("${path.module}/task-definition.json")}" 7 | 8 | vars { 9 | service_name = "${var.service_name}" 10 | docker_image = "${var.docker_image}" 11 | docker_tag = "${var.docker_tag}" 12 | container_cpu = "${var.container_cpu}" 13 | container_memory = "${var.container_memory}" 14 | container_port = "${var.container_port}" 15 | log_group_name = "${aws_cloudwatch_log_group.beta_service_lg.name}" 16 | log_group_region = "${var.region}" 17 | } 18 | } 19 | 20 | # The ECS task that specifies what Docker containers we need to run the service 21 | resource "aws_ecs_task_definition" "beta_service" { 22 | family = "${var.service_name}" 23 | container_definitions = "${data.template_file.task_definition.rendered}" 24 | } 25 | 26 | # A long-running ECS service for the beta_service task 27 | resource "aws_ecs_service" "beta_service" { 28 | name = "${var.service_name}" 29 | cluster = "${var.cluster_name}" 30 | task_definition = "${aws_ecs_task_definition.beta_service.arn}" 31 | desired_count = "${var.desired_count}" 32 | deployment_minimum_healthy_percent = 50 33 | deployment_maximum_percent = 100 34 | iam_role = "${var.ecs_service_role_arn}" 35 | 36 | load_balancer { 37 | target_group_arn = "${var.target_group_arn}" 38 | container_name = "${var.service_name}" 39 | container_port = "${var.container_port}" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /beta-service/modules/ecs-service/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service_name" { 2 | value = "${aws_ecs_service.beta_service.name}" 3 | } 4 | -------------------------------------------------------------------------------- /beta-service/modules/ecs-service/task-definition.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${service_name}", 4 | "image": "${docker_image}:${docker_tag}", 5 | "cpu": ${container_cpu}, 6 | "memory": ${container_memory}, 7 | "essential": true, 8 | "portMappings": [ 9 | { 10 | "containerPort": ${container_port} 11 | } 12 | ], 13 | "logConfiguration": { 14 | "logDriver": "awslogs", 15 | "options": { 16 | "awslogs-group": "${log_group_name}", 17 | "awslogs-region": "${log_group_region}" 18 | } 19 | }, 20 | "environment": [ 21 | { "name": "SERVICE_NAME", "value": "${service_name}"} 22 | ] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /beta-service/modules/ecs-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "docker_image" { 6 | description = "Docker image to run" 7 | } 8 | 9 | variable "docker_tag" { 10 | description = "Tag of docker image to run" 11 | } 12 | 13 | variable "container_cpu" { 14 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 15 | } 16 | 17 | variable "container_memory" { 18 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 19 | } 20 | 21 | variable "container_port" { 22 | description = "Port that service will listen on" 23 | } 24 | 25 | variable "region" { 26 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 27 | } 28 | 29 | variable "cluster_name" { 30 | description = "Name of ECS cluster" 31 | } 32 | 33 | variable "desired_count" { 34 | description = "Initial number of containers to run" 35 | } 36 | 37 | variable "ecs_service_role_arn" { 38 | description = "ARN of IAM role for ECS service" 39 | } 40 | 41 | variable "target_group_arn" { 42 | description = "ARN of ALB target group for service" 43 | } 44 | -------------------------------------------------------------------------------- /beta-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 3 | } 4 | 5 | variable "service_name" { 6 | description = "Name of service" 7 | } 8 | 9 | variable "container_port" { 10 | description = "Port that service will listen on" 11 | } 12 | 13 | variable "docker_image" { 14 | description = "Docker image to run" 15 | } 16 | 17 | variable "docker_tag" { 18 | description = "Tag of docker image to run" 19 | } 20 | 21 | variable "container_cpu" { 22 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 23 | default = "256" 24 | } 25 | 26 | variable "container_memory" { 27 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 28 | default = "256" 29 | } 30 | 31 | variable "min_capacity" { 32 | description = "Minimum number of containers to run" 33 | default = 2 34 | } 35 | 36 | variable "max_capacity" { 37 | description = "Minimum number of containers to run" 38 | default = 6 39 | } 40 | -------------------------------------------------------------------------------- /bitbucket-pipelines.yml: -------------------------------------------------------------------------------- 1 | image: jch254/docker-node-terraform-aws:latest 2 | pipelines: 3 | branches: 4 | master: 5 | - step: 6 | script: 7 | - echo 'Refer to README.md for instructions and then uncomment steps below' 8 | #- ./base-infra/deploy.bash 9 | #- ./alpha-service/deploy.bash 10 | #- ./beta-service/deploy.bash 11 | #- ./charlie-service/deploy.bash 12 | -------------------------------------------------------------------------------- /charlie-service/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2017-node7"] 3 | } 4 | -------------------------------------------------------------------------------- /charlie-service/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /charlie-service/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | extends: "airbnb-base", 3 | env: { 4 | node: true, 5 | es6: true 6 | }, 7 | parser: "babel-eslint", 8 | rules: { 9 | max-len: ["error", 120, 2] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /charlie-service/charlie-service.tfvars: -------------------------------------------------------------------------------- 1 | region = "ap-southeast-2" 2 | 3 | service_name = "charlie-service" 4 | 5 | container_port = "80" 6 | 7 | docker_image = "jch254/ecs-demo-api" 8 | 9 | docker_tag = "latest" 10 | -------------------------------------------------------------------------------- /charlie-service/deploy.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | cd charlie-service 4 | 5 | terraform remote config -backend=s3 \ 6 | -backend-config="bucket=603-terraform-remote-state" \ 7 | -backend-config="key=terraform-ecs-autoscale-alb/charlie-service.tfstate" \ 8 | -backend-config="region=ap-southeast-2" \ 9 | -backend-config="encrypt=true" 10 | 11 | terraform get --update 12 | terraform plan -var-file charlie-service.tfvars 13 | terraform apply -var-file charlie-service.tfvars 14 | 15 | cd .. 16 | -------------------------------------------------------------------------------- /charlie-service/modules.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | data "terraform_remote_state" "base_remote_state" { 6 | backend = "s3" 7 | config { 8 | bucket = "603-terraform-remote-state" 9 | key = "terraform-ecs-autoscale-alb/base-infra.tfstate" 10 | region = "${var.region}" 11 | } 12 | } 13 | 14 | module "alb_listener" { 15 | source = "./modules/alb-listener" 16 | 17 | service_name = "${var.service_name}" 18 | container_port = "${var.container_port}" 19 | vpc_id = "${data.terraform_remote_state.base_remote_state.vpc_id}" 20 | alb_listener_arn = "${data.terraform_remote_state.base_remote_state.alb_listener_arn}" 21 | } 22 | 23 | module "ecs_service" { 24 | source = "./modules/ecs-service" 25 | 26 | service_name = "${var.service_name}" 27 | docker_image = "${var.docker_image}" 28 | docker_tag = "${var.docker_tag}" 29 | container_cpu = "${var.container_cpu}" 30 | container_memory = "${var.container_memory}" 31 | container_port = "${var.container_port}" 32 | region = "${var.region}" 33 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 34 | desired_count = "${var.min_capacity}" 35 | ecs_service_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_role_arn}" 36 | target_group_arn = "${module.alb_listener.target_group_arn}" 37 | } 38 | 39 | module "autoscaling" { 40 | source = "./modules/autoscaling" 41 | 42 | service_name = "${var.service_name}" 43 | cluster_name = "${data.terraform_remote_state.base_remote_state.cluster_name}" 44 | ecs_service_autoscale_role_arn = "${data.terraform_remote_state.base_remote_state.ecs_service_autoscale_role_arn}" 45 | min_capacity = "${var.min_capacity}" 46 | max_capacity = "${var.max_capacity}" 47 | } 48 | -------------------------------------------------------------------------------- /charlie-service/modules/alb-listener/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_alb_target_group" "charlie_service_tg" { 2 | name = "${replace(var.service_name, "/(.{0,28})(.*)/", "$1")}-tg" 3 | 4 | protocol = "HTTP" 5 | port = "${var.container_port}" 6 | vpc_id = "${var.vpc_id}" 7 | 8 | health_check { 9 | path = "/ping" 10 | } 11 | } 12 | 13 | resource "aws_alb_listener_rule" "charlie_service_listener" { 14 | listener_arn = "${var.alb_listener_arn}" 15 | priority = 300 16 | 17 | action { 18 | type = "forward" 19 | target_group_arn = "${aws_alb_target_group.charlie_service_tg.arn}" 20 | } 21 | 22 | condition { 23 | field = "path-pattern" 24 | values = ["/charlie/*"] 25 | } 26 | 27 | depends_on = ["aws_alb_target_group.charlie_service_tg"] 28 | } 29 | -------------------------------------------------------------------------------- /charlie-service/modules/alb-listener/outputs.tf: -------------------------------------------------------------------------------- 1 | output "target_group_arn" { 2 | value = "${aws_alb_target_group.charlie_service_tg.arn}" 3 | } 4 | -------------------------------------------------------------------------------- /charlie-service/modules/alb-listener/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "container_port" { 6 | description = "Port that service will listen on" 7 | } 8 | 9 | variable "vpc_id" { 10 | description = "Id of the VPC where service should be deployed" 11 | } 12 | 13 | variable "alb_listener_arn" { 14 | description = "ARN of ALB listener" 15 | } 16 | -------------------------------------------------------------------------------- /charlie-service/modules/autoscaling/main.tf: -------------------------------------------------------------------------------- 1 | # A CloudWatch alarm that moniors CPU utilization of containers for scaling up 2 | resource "aws_cloudwatch_metric_alarm" "charlie_service_cpu_high" { 3 | alarm_name = "${var.service_name}-cpu-utilization-above-80" 4 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling up" 5 | comparison_operator = "GreaterThanOrEqualToThreshold" 6 | evaluation_periods = "1" 7 | metric_name = "CPUUtilization" 8 | namespace = "AWS/ECS" 9 | period = "120" 10 | statistic = "Average" 11 | threshold = "80" 12 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 13 | 14 | dimensions { 15 | ClusterName = "${var.cluster_name}" 16 | ServiceName = "${var.service_name}" 17 | } 18 | } 19 | 20 | # A CloudWatch alarm that monitors CPU utilization of containers for scaling down 21 | resource "aws_cloudwatch_metric_alarm" "charlie_service_cpu_low" { 22 | alarm_name = "${var.service_name}-cpu-utilization-below-5" 23 | alarm_description = "This alarm monitors ${var.service_name} CPU utilization for scaling down" 24 | comparison_operator = "LessThanThreshold" 25 | evaluation_periods = "1" 26 | metric_name = "CPUUtilization" 27 | namespace = "AWS/ECS" 28 | period = "120" 29 | statistic = "Average" 30 | threshold = "5" 31 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 32 | 33 | dimensions { 34 | ClusterName = "${var.cluster_name}" 35 | ServiceName = "${var.service_name}" 36 | } 37 | } 38 | 39 | # A CloudWatch alarm that monitors memory utilization of containers for scaling up 40 | resource "aws_cloudwatch_metric_alarm" "charlie_service_memory_high" { 41 | alarm_name = "${var.service_name}-memory-utilization-above-80" 42 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling up" 43 | comparison_operator = "GreaterThanOrEqualToThreshold" 44 | evaluation_periods = "1" 45 | metric_name = "MemoryUtilization" 46 | namespace = "AWS/ECS" 47 | period = "120" 48 | statistic = "Average" 49 | threshold = "80" 50 | alarm_actions = ["${aws_appautoscaling_policy.scale_up.arn}"] 51 | 52 | dimensions { 53 | ClusterName = "${var.cluster_name}" 54 | ServiceName = "${var.service_name}" 55 | } 56 | } 57 | 58 | # A CloudWatch alarm that monitors memory utilization of containers for scaling down 59 | resource "aws_cloudwatch_metric_alarm" "charlie_service_memory_low" { 60 | alarm_name = "${var.service_name}-memory-utilization-below-5" 61 | alarm_description = "This alarm monitors ${var.service_name} memory utilization for scaling down" 62 | comparison_operator = "LessThanThreshold" 63 | evaluation_periods = "1" 64 | metric_name = "MemoryUtilization" 65 | namespace = "AWS/ECS" 66 | period = "120" 67 | statistic = "Average" 68 | threshold = "5" 69 | alarm_actions = ["${aws_appautoscaling_policy.scale_down.arn}"] 70 | 71 | dimensions { 72 | ClusterName = "${var.cluster_name}" 73 | ServiceName = "${var.service_name}" 74 | } 75 | } 76 | 77 | resource "aws_appautoscaling_target" "target" { 78 | resource_id = "service/${var.cluster_name}/${var.service_name}" 79 | role_arn = "${var.ecs_service_autoscale_role_arn}" 80 | scalable_dimension = "ecs:service:DesiredCount" 81 | min_capacity = "${var.min_capacity}" 82 | max_capacity = "${var.max_capacity}" 83 | } 84 | 85 | resource "aws_appautoscaling_policy" "scale_up" { 86 | name = "${var.service_name}-scale-up" 87 | resource_id = "service/${var.cluster_name}/${var.service_name}" 88 | scalable_dimension = "ecs:service:DesiredCount" 89 | adjustment_type = "ChangeInCapacity" 90 | cooldown = 120 91 | metric_aggregation_type = "Average" 92 | 93 | step_adjustment { 94 | metric_interval_lower_bound = 0 95 | scaling_adjustment = 1 96 | } 97 | 98 | depends_on = ["aws_appautoscaling_target.target"] 99 | } 100 | 101 | resource "aws_appautoscaling_policy" "scale_down" { 102 | name = "${var.service_name}-scale-down" 103 | resource_id = "service/${var.cluster_name}/${var.service_name}" 104 | scalable_dimension = "ecs:service:DesiredCount" 105 | adjustment_type = "ChangeInCapacity" 106 | cooldown = 120 107 | metric_aggregation_type = "Average" 108 | 109 | step_adjustment { 110 | metric_interval_upper_bound = 0 111 | scaling_adjustment = -1 112 | } 113 | 114 | depends_on = ["aws_appautoscaling_target.target"] 115 | } 116 | -------------------------------------------------------------------------------- /charlie-service/modules/autoscaling/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jch254/terraform-ecs-autoscale-alb/ee36aa5462166922123252be846c338b9bd9f1b6/charlie-service/modules/autoscaling/outputs.tf -------------------------------------------------------------------------------- /charlie-service/modules/autoscaling/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "cluster_name" { 6 | description = "Name of ECS cluster" 7 | } 8 | 9 | variable "ecs_service_autoscale_role_arn" { 10 | description = "ARN of IAM role for ECS service autoscaling" 11 | } 12 | 13 | variable "min_capacity" { 14 | description = "Minimum number of containers to run" 15 | } 16 | 17 | variable "max_capacity" { 18 | description = "Minimum number of containers to run" 19 | } 20 | -------------------------------------------------------------------------------- /charlie-service/modules/ecs-service/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "charlie_service_lg" { 2 | name = "${var.service_name}" 3 | } 4 | 5 | data "template_file" "task_definition" { 6 | template = "${file("${path.module}/task-definition.json")}" 7 | 8 | vars { 9 | service_name = "${var.service_name}" 10 | docker_image = "${var.docker_image}" 11 | docker_tag = "${var.docker_tag}" 12 | container_cpu = "${var.container_cpu}" 13 | container_memory = "${var.container_memory}" 14 | container_port = "${var.container_port}" 15 | log_group_name = "${aws_cloudwatch_log_group.charlie_service_lg.name}" 16 | log_group_region = "${var.region}" 17 | } 18 | } 19 | 20 | # The ECS task that specifies what Docker containers we need to run the service 21 | resource "aws_ecs_task_definition" "charlie_service" { 22 | family = "${var.service_name}" 23 | container_definitions = "${data.template_file.task_definition.rendered}" 24 | } 25 | 26 | # A long-running ECS service for the charlie_service task 27 | resource "aws_ecs_service" "charlie_service" { 28 | name = "${var.service_name}" 29 | cluster = "${var.cluster_name}" 30 | task_definition = "${aws_ecs_task_definition.charlie_service.arn}" 31 | desired_count = "${var.desired_count}" 32 | deployment_minimum_healthy_percent = 50 33 | deployment_maximum_percent = 100 34 | iam_role = "${var.ecs_service_role_arn}" 35 | 36 | load_balancer { 37 | target_group_arn = "${var.target_group_arn}" 38 | container_name = "${var.service_name}" 39 | container_port = "${var.container_port}" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /charlie-service/modules/ecs-service/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service_name" { 2 | value = "${aws_ecs_service.charlie_service.name}" 3 | } 4 | -------------------------------------------------------------------------------- /charlie-service/modules/ecs-service/task-definition.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${service_name}", 4 | "image": "${docker_image}:${docker_tag}", 5 | "cpu": ${container_cpu}, 6 | "memory": ${container_memory}, 7 | "essential": true, 8 | "portMappings": [ 9 | { 10 | "containerPort": ${container_port} 11 | } 12 | ], 13 | "logConfiguration": { 14 | "logDriver": "awslogs", 15 | "options": { 16 | "awslogs-group": "${log_group_name}", 17 | "awslogs-region": "${log_group_region}" 18 | } 19 | }, 20 | "environment": [ 21 | { "name": "SERVICE_NAME", "value": "${service_name}"} 22 | ] 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /charlie-service/modules/ecs-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "service_name" { 2 | description = "Name of service" 3 | } 4 | 5 | variable "docker_image" { 6 | description = "Docker image to run" 7 | } 8 | 9 | variable "docker_tag" { 10 | description = "Tag of docker image to run" 11 | } 12 | 13 | variable "container_cpu" { 14 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 15 | } 16 | 17 | variable "container_memory" { 18 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 19 | } 20 | 21 | variable "container_port" { 22 | description = "Port that service will listen on" 23 | } 24 | 25 | variable "region" { 26 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 27 | } 28 | 29 | variable "cluster_name" { 30 | description = "Name of ECS cluster" 31 | } 32 | 33 | variable "desired_count" { 34 | description = "Initial number of containers to run" 35 | } 36 | 37 | variable "ecs_service_role_arn" { 38 | description = "ARN of IAM role for ECS service" 39 | } 40 | 41 | variable "target_group_arn" { 42 | description = "ARN of ALB target group for service" 43 | } 44 | -------------------------------------------------------------------------------- /charlie-service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region to deploy to (e.g. ap-southeast-2)" 3 | } 4 | 5 | variable "service_name" { 6 | description = "Name of service" 7 | } 8 | 9 | variable "container_port" { 10 | description = "Port that service will listen on" 11 | } 12 | 13 | variable "docker_image" { 14 | description = "Docker image to run" 15 | } 16 | 17 | variable "docker_tag" { 18 | description = "Tag of docker image to run" 19 | } 20 | 21 | variable "container_cpu" { 22 | description = "The number of cpu units to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 23 | default = "256" 24 | } 25 | 26 | variable "container_memory" { 27 | description = "The number of MiB of memory to reserve for the container. See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html" 28 | default = "256" 29 | } 30 | 31 | variable "min_capacity" { 32 | description = "Minimum number of containers to run" 33 | default = 2 34 | } 35 | 36 | variable "max_capacity" { 37 | description = "Minimum number of containers to run" 38 | default = 6 39 | } 40 | -------------------------------------------------------------------------------- /demo-api/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2017-node7"] 3 | } 4 | -------------------------------------------------------------------------------- /demo-api/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /demo-api/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | extends: "airbnb-base", 3 | env: { 4 | node: true, 5 | es6: true 6 | }, 7 | parser: "babel-eslint", 8 | rules: { 9 | max-len: ["error", 120, 2], 10 | no-param-reassign: 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:latest 2 | 3 | RUN apk add --update python ca-certificates 4 | 5 | RUN npm install -g yarn@0.17.3 6 | 7 | WORKDIR /app 8 | 9 | COPY package.json . 10 | COPY yarn.lock . 11 | RUN yarn install 12 | 13 | COPY .babelrc . 14 | COPY src src 15 | RUN yarn run build 16 | 17 | ENV NODE_ENV=production 18 | ENV PORT=80 19 | 20 | EXPOSE ${PORT}/tcp 21 | 22 | ENTRYPOINT ["node", "--harmony", "dist"] 23 | -------------------------------------------------------------------------------- /demo-api/README.md: -------------------------------------------------------------------------------- 1 | # Demo-api 2 | 3 | Demo API powered by Node.js/Express. 4 | 5 | ## Running development server (with live-reloading) 6 | 7 | 1. Run the following commands in the app's root directory then submit requests to http://localhost:3000 8 | 9 | ``` 10 | yarn install 11 | yarn run dev 12 | ``` 13 | 14 | ## Running production version in Docker container 15 | 1. Run the following commands in the app's root directory then submit requests to http://localhost:YOUR_CONTAINER_PORT. YOUR_CONTAINER_ID will be returned by the `docker run` command and YOUR_CONTAINER_PORT will be returned by the `docker port` command. 16 | 17 | ``` 18 | docker build -t demo-api . 19 | docker run -d -P demo-api 20 | docker port YOUR_CONTAINER_ID 21 | ``` 22 | -------------------------------------------------------------------------------- /demo-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Crud-service", 3 | "version": "1.0.0", 4 | "description": "ECS demo CRUD service", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "dev": "nodemon -w src --exec \"babel-node src\"", 8 | "build": "babel src -s -D -d dist", 9 | "lint": "eslint src" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/jch254/terraform-ecs-autoscale-alb" 14 | }, 15 | "author": "Jordan Hornblow ", 16 | "license": "MIT", 17 | "dependencies": { 18 | "body-parser": "^1.15.2", 19 | "chance": "^1.0.4", 20 | "cors": "^2.8.1", 21 | "express": "^4.14.0", 22 | "express-winston": "^2.0.0", 23 | "helmet": "^3.1.0", 24 | "json-inflector": "^1.1.0", 25 | "winston": "^2.3.0" 26 | }, 27 | "devDependencies": { 28 | "babel-cli": "^6.18.0", 29 | "babel-core": "^6.18.2", 30 | "babel-eslint": "^7.1.0", 31 | "babel-preset-es2017-node7": "^0.4.1", 32 | "eslint": "^3.9.1", 33 | "eslint-config-airbnb-base": "9.0.0", 34 | "eslint-plugin-import": "^2.1.0", 35 | "nodemon": "^1.11.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo-api/src/healthcheck/index.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | export default () => { 4 | const api = Router(); 5 | 6 | api.get('/ping', (req, res) => { 7 | res.sendStatus(200); 8 | }); 9 | 10 | api.get('/healthcheck', (req, res) => { 11 | res.json({ message: 'All good' }); 12 | }); 13 | 14 | return api; 15 | }; 16 | -------------------------------------------------------------------------------- /demo-api/src/index.js: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | import express, { Router } from 'express'; 3 | import winston from 'winston'; 4 | 5 | import middleware from './middleware'; 6 | import healthcheck from './healthcheck'; 7 | import items from './items'; 8 | import { urlRewriter, topLevelErrorHandler } from './utils'; 9 | 10 | const app = express(); 11 | const api = Router(); 12 | 13 | app.server = http.createServer(app); 14 | 15 | if (process.env.NODE_ENV === 'production') { 16 | app.use(urlRewriter); 17 | } 18 | 19 | app.use(api.get('/', (req, res) => { 20 | res.json({ message: `Hello world from ${process.env.SERVICE_NAME || 'demo-api'}` }); 21 | })); 22 | 23 | app.use(middleware()); 24 | app.use(healthcheck()); 25 | app.use(items()); 26 | app.use(topLevelErrorHandler); 27 | 28 | app.server.listen(process.env.PORT || 3000); 29 | winston.info(`${process.env.SERVICE_NAME || 'demo-api'} started on port ${process.env.PORT || 3000}`); 30 | 31 | export default app; 32 | -------------------------------------------------------------------------------- /demo-api/src/items/index.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | export default () => { 4 | const api = Router(); 5 | 6 | api.get('/items', (req, res) => { 7 | res.json({ items: [{ id: 23, name: 'Cheers cobber' }] }); 8 | }); 9 | 10 | return api; 11 | }; 12 | -------------------------------------------------------------------------------- /demo-api/src/middleware/index.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import winston from 'winston'; 3 | import expressWinston from 'express-winston'; 4 | import helmet from 'helmet'; 5 | import bodyParser from 'body-parser'; 6 | import cors from 'cors'; 7 | import inflector from 'json-inflector'; 8 | 9 | export default () => { 10 | const api = Router(); 11 | 12 | const inflectorOptions = { 13 | request: 'camelize', 14 | response: 'camelizeLower', 15 | }; 16 | 17 | api.use(cors()); 18 | api.use(helmet()); 19 | api.use(bodyParser.json()); 20 | api.use(inflector(inflectorOptions)); 21 | 22 | winston.remove(winston.transports.Console); 23 | winston.add(winston.transports.Console, { timestamp: true, colorize: true }); 24 | 25 | // Log all requests 26 | api.use(expressWinston.logger({ 27 | transports: [ 28 | new winston.transports.Console({ 29 | timestamp: true, 30 | colorize: true, 31 | }), 32 | ], 33 | expressFormat: true, 34 | })); 35 | 36 | return api; 37 | }; 38 | -------------------------------------------------------------------------------- /demo-api/src/utils.js: -------------------------------------------------------------------------------- 1 | import winston from 'winston'; 2 | 3 | // Removes load balancer path from URL 4 | const urlRewriter = (req, res, next) => { 5 | if (req.url.match(/\//g).length >= 2) { 6 | const urlParts = req.url.split('/').filter(p => p.length !== 0); 7 | 8 | req.url = `/${urlParts.slice(1).join('/')}`; 9 | } 10 | 11 | next(); 12 | }; 13 | 14 | const topLevelErrorHandler = (err, req, res, next) => { 15 | if (res.headersSent) { 16 | return next(err); 17 | } 18 | 19 | winston.error(`${err.message}`); 20 | 21 | if (!err.status) { 22 | winston.error(err.stack); 23 | } 24 | 25 | return res.status(err.status || 500).json({ message: err.responseMessage || 'Matrix glitch' }); 26 | }; 27 | 28 | const createErrorResponse = (message, statusCode) => { 29 | const err = new Error(message); 30 | 31 | err.responseMessage = message; 32 | err.status = statusCode; 33 | 34 | return err; 35 | }; 36 | 37 | 38 | export { urlRewriter, topLevelErrorHandler, createErrorResponse }; 39 | -------------------------------------------------------------------------------- /demo-api/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | abbrev@1: 4 | version "1.0.9" 5 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" 6 | 7 | accepts@~1.3.3: 8 | version "1.3.3" 9 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" 10 | dependencies: 11 | mime-types "~2.1.11" 12 | negotiator "0.6.1" 13 | 14 | acorn-jsx@^3.0.0: 15 | version "3.0.1" 16 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 17 | dependencies: 18 | acorn "^3.0.4" 19 | 20 | acorn@^3.0.4: 21 | version "3.3.0" 22 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 23 | 24 | acorn@^4.0.1: 25 | version "4.0.3" 26 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.3.tgz#1a3e850b428e73ba6b09d1cc527f5aaad4d03ef1" 27 | 28 | ajv-keywords@^1.0.0: 29 | version "1.1.1" 30 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.1.1.tgz#02550bc605a3e576041565628af972e06c549d50" 31 | 32 | ajv@^4.7.0: 33 | version "4.8.2" 34 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.8.2.tgz#65486936ca36fea39a1504332a78bebd5d447bdc" 35 | dependencies: 36 | co "^4.6.0" 37 | json-stable-stringify "^1.0.1" 38 | 39 | ansi-escapes@^1.1.0: 40 | version "1.4.0" 41 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" 42 | 43 | ansi-regex@^2.0.0: 44 | version "2.0.0" 45 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" 46 | 47 | ansi-styles@^2.2.1: 48 | version "2.2.1" 49 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 50 | 51 | ansi-styles@~1.0.0: 52 | version "1.0.0" 53 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" 54 | 55 | anymatch@^1.3.0: 56 | version "1.3.0" 57 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" 58 | dependencies: 59 | arrify "^1.0.0" 60 | micromatch "^2.1.5" 61 | 62 | aproba@^1.0.3: 63 | version "1.0.4" 64 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" 65 | 66 | are-we-there-yet@~1.1.2: 67 | version "1.1.2" 68 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" 69 | dependencies: 70 | delegates "^1.0.0" 71 | readable-stream "^2.0.0 || ^1.1.13" 72 | 73 | argparse@^1.0.7: 74 | version "1.0.9" 75 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" 76 | dependencies: 77 | sprintf-js "~1.0.2" 78 | 79 | arr-diff@^2.0.0: 80 | version "2.0.0" 81 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 82 | dependencies: 83 | arr-flatten "^1.0.1" 84 | 85 | arr-flatten@^1.0.1: 86 | version "1.0.1" 87 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" 88 | 89 | array-flatten@1.1.1: 90 | version "1.1.1" 91 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 92 | 93 | array-union@^1.0.1: 94 | version "1.0.2" 95 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 96 | dependencies: 97 | array-uniq "^1.0.1" 98 | 99 | array-uniq@^1.0.1: 100 | version "1.0.3" 101 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 102 | 103 | array-unique@^0.2.1: 104 | version "0.2.1" 105 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 106 | 107 | arrify@^1.0.0: 108 | version "1.0.1" 109 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 110 | 111 | asn1@~0.2.3: 112 | version "0.2.3" 113 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" 114 | 115 | assert-plus@^0.2.0: 116 | version "0.2.0" 117 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" 118 | 119 | assert-plus@^1.0.0: 120 | version "1.0.0" 121 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" 122 | 123 | async-each@^1.0.0: 124 | version "1.0.1" 125 | resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" 126 | 127 | async@~1.0.0: 128 | version "1.0.0" 129 | resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" 130 | 131 | asynckit@^0.4.0: 132 | version "0.4.0" 133 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 134 | 135 | aws-sign2@~0.6.0: 136 | version "0.6.0" 137 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" 138 | 139 | aws4@^1.2.1: 140 | version "1.5.0" 141 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" 142 | 143 | babel-cli@^6.18.0: 144 | version "6.18.0" 145 | resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.18.0.tgz#92117f341add9dead90f6fa7d0a97c0cc08ec186" 146 | dependencies: 147 | babel-core "^6.18.0" 148 | babel-polyfill "^6.16.0" 149 | babel-register "^6.18.0" 150 | babel-runtime "^6.9.0" 151 | commander "^2.8.1" 152 | convert-source-map "^1.1.0" 153 | fs-readdir-recursive "^1.0.0" 154 | glob "^5.0.5" 155 | lodash "^4.2.0" 156 | output-file-sync "^1.1.0" 157 | path-is-absolute "^1.0.0" 158 | slash "^1.0.0" 159 | source-map "^0.5.0" 160 | v8flags "^2.0.10" 161 | optionalDependencies: 162 | chokidar "^1.0.0" 163 | 164 | babel-code-frame@^6.16.0: 165 | version "6.16.0" 166 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de" 167 | dependencies: 168 | chalk "^1.1.0" 169 | esutils "^2.0.2" 170 | js-tokens "^2.0.0" 171 | 172 | babel-core@^6.18.0, babel-core@^6.18.2: 173 | version "6.18.2" 174 | resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.18.2.tgz#d8bb14dd6986fa4f3566a26ceda3964fa0e04e5b" 175 | dependencies: 176 | babel-code-frame "^6.16.0" 177 | babel-generator "^6.18.0" 178 | babel-helpers "^6.16.0" 179 | babel-messages "^6.8.0" 180 | babel-register "^6.18.0" 181 | babel-runtime "^6.9.1" 182 | babel-template "^6.16.0" 183 | babel-traverse "^6.18.0" 184 | babel-types "^6.18.0" 185 | babylon "^6.11.0" 186 | convert-source-map "^1.1.0" 187 | debug "^2.1.1" 188 | json5 "^0.5.0" 189 | lodash "^4.2.0" 190 | minimatch "^3.0.2" 191 | path-is-absolute "^1.0.0" 192 | private "^0.1.6" 193 | slash "^1.0.0" 194 | source-map "^0.5.0" 195 | 196 | babel-eslint@^7.1.0: 197 | version "7.1.0" 198 | resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.0.tgz#d506a5174ba224e25a2d17e128e2ba8987139ddc" 199 | dependencies: 200 | babel-traverse "^6.15.0" 201 | babel-types "^6.15.0" 202 | babylon "^6.11.2" 203 | lodash.pickby "^4.6.0" 204 | 205 | babel-generator@^6.18.0: 206 | version "6.18.0" 207 | resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.18.0.tgz#e4f104cb3063996d9850556a45aae4a022060a07" 208 | dependencies: 209 | babel-messages "^6.8.0" 210 | babel-runtime "^6.9.0" 211 | babel-types "^6.18.0" 212 | detect-indent "^4.0.0" 213 | jsesc "^1.3.0" 214 | lodash "^4.2.0" 215 | source-map "^0.5.0" 216 | 217 | babel-helper-function-name@^6.18.0: 218 | version "6.18.0" 219 | resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz#68ec71aeba1f3e28b2a6f0730190b754a9bf30e6" 220 | dependencies: 221 | babel-helper-get-function-arity "^6.18.0" 222 | babel-runtime "^6.0.0" 223 | babel-template "^6.8.0" 224 | babel-traverse "^6.18.0" 225 | babel-types "^6.18.0" 226 | 227 | babel-helper-get-function-arity@^6.18.0: 228 | version "6.18.0" 229 | resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz#a5b19695fd3f9cdfc328398b47dafcd7094f9f24" 230 | dependencies: 231 | babel-runtime "^6.0.0" 232 | babel-types "^6.18.0" 233 | 234 | babel-helpers@^6.16.0: 235 | version "6.16.0" 236 | resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.16.0.tgz#1095ec10d99279460553e67eb3eee9973d3867e3" 237 | dependencies: 238 | babel-runtime "^6.0.0" 239 | babel-template "^6.16.0" 240 | 241 | babel-messages@^6.8.0: 242 | version "6.8.0" 243 | resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" 244 | dependencies: 245 | babel-runtime "^6.0.0" 246 | 247 | babel-plugin-syntax-class-properties@^6.8.0: 248 | version "6.13.0" 249 | resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" 250 | 251 | babel-plugin-syntax-object-rest-spread@^6.8.0: 252 | version "6.13.0" 253 | resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" 254 | 255 | babel-plugin-syntax-trailing-function-commas@^6.13.0: 256 | version "6.13.0" 257 | resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz#2b84b7d53dd744f94ff1fad7669406274b23f541" 258 | 259 | babel-plugin-transform-class-properties@^6.18.0: 260 | version "6.18.0" 261 | resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.18.0.tgz#bc1266a39d4c8726e0bd7b15c56235177e6ede57" 262 | dependencies: 263 | babel-helper-function-name "^6.18.0" 264 | babel-plugin-syntax-class-properties "^6.8.0" 265 | babel-runtime "^6.9.1" 266 | 267 | babel-plugin-transform-es2015-modules-commonjs@^6.18.0: 268 | version "6.18.0" 269 | resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz#c15ae5bb11b32a0abdcc98a5837baa4ee8d67bcc" 270 | dependencies: 271 | babel-plugin-transform-strict-mode "^6.18.0" 272 | babel-runtime "^6.0.0" 273 | babel-template "^6.16.0" 274 | babel-types "^6.18.0" 275 | 276 | babel-plugin-transform-object-rest-spread@^6.16.0: 277 | version "6.16.0" 278 | resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.16.0.tgz#db441d56fffc1999052fdebe2e2f25ebd28e36a9" 279 | dependencies: 280 | babel-plugin-syntax-object-rest-spread "^6.8.0" 281 | babel-runtime "^6.0.0" 282 | 283 | babel-plugin-transform-strict-mode@^6.18.0: 284 | version "6.18.0" 285 | resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.18.0.tgz#df7cf2991fe046f44163dcd110d5ca43bc652b9d" 286 | dependencies: 287 | babel-runtime "^6.0.0" 288 | babel-types "^6.18.0" 289 | 290 | babel-polyfill@^6.16.0: 291 | version "6.16.0" 292 | resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.16.0.tgz#2d45021df87e26a374b6d4d1a9c65964d17f2422" 293 | dependencies: 294 | babel-runtime "^6.9.1" 295 | core-js "^2.4.0" 296 | regenerator-runtime "^0.9.5" 297 | 298 | babel-preset-es2017-node7@^0.4.1: 299 | version "0.4.1" 300 | resolved "https://registry.yarnpkg.com/babel-preset-es2017-node7/-/babel-preset-es2017-node7-0.4.1.tgz#df557eafe343bbc0b859ac3163fdfb0cf58108e5" 301 | dependencies: 302 | babel-plugin-syntax-trailing-function-commas "^6.13.0" 303 | babel-plugin-transform-class-properties "^6.18.0" 304 | babel-plugin-transform-es2015-modules-commonjs "^6.18.0" 305 | babel-plugin-transform-object-rest-spread "^6.16.0" 306 | 307 | babel-register@^6.18.0: 308 | version "6.18.0" 309 | resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.18.0.tgz#892e2e03865078dd90ad2c715111ec4449b32a68" 310 | dependencies: 311 | babel-core "^6.18.0" 312 | babel-runtime "^6.11.6" 313 | core-js "^2.4.0" 314 | home-or-tmp "^2.0.0" 315 | lodash "^4.2.0" 316 | mkdirp "^0.5.1" 317 | source-map-support "^0.4.2" 318 | 319 | babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.9.0, babel-runtime@^6.9.1: 320 | version "6.18.0" 321 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" 322 | dependencies: 323 | core-js "^2.4.0" 324 | regenerator-runtime "^0.9.5" 325 | 326 | babel-template@^6.16.0, babel-template@^6.8.0: 327 | version "6.16.0" 328 | resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" 329 | dependencies: 330 | babel-runtime "^6.9.0" 331 | babel-traverse "^6.16.0" 332 | babel-types "^6.16.0" 333 | babylon "^6.11.0" 334 | lodash "^4.2.0" 335 | 336 | babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0: 337 | version "6.18.0" 338 | resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.18.0.tgz#5aeaa980baed2a07c8c47329cd90c3b90c80f05e" 339 | dependencies: 340 | babel-code-frame "^6.16.0" 341 | babel-messages "^6.8.0" 342 | babel-runtime "^6.9.0" 343 | babel-types "^6.18.0" 344 | babylon "^6.11.0" 345 | debug "^2.2.0" 346 | globals "^9.0.0" 347 | invariant "^2.2.0" 348 | lodash "^4.2.0" 349 | 350 | babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0: 351 | version "6.18.0" 352 | resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.18.0.tgz#1f7d5a73474c59eb9151b2417bbff4e4fce7c3f8" 353 | dependencies: 354 | babel-runtime "^6.9.1" 355 | esutils "^2.0.2" 356 | lodash "^4.2.0" 357 | to-fast-properties "^1.0.1" 358 | 359 | babylon@^6.11.0, babylon@^6.11.2: 360 | version "6.13.1" 361 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.13.1.tgz#adca350e088f0467647157652bafead6ddb8dfdb" 362 | 363 | balanced-match@^0.4.1: 364 | version "0.4.2" 365 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 366 | 367 | bcrypt-pbkdf@^1.0.0: 368 | version "1.0.0" 369 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" 370 | dependencies: 371 | tweetnacl "^0.14.3" 372 | 373 | binary-extensions@^1.0.0: 374 | version "1.7.0" 375 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.7.0.tgz#6c1610db163abfb34edfe42fa423343a1e01185d" 376 | 377 | block-stream@*: 378 | version "0.0.9" 379 | resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" 380 | dependencies: 381 | inherits "~2.0.0" 382 | 383 | body-parser@^1.15.2: 384 | version "1.15.2" 385 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67" 386 | dependencies: 387 | bytes "2.4.0" 388 | content-type "~1.0.2" 389 | debug "~2.2.0" 390 | depd "~1.1.0" 391 | http-errors "~1.5.0" 392 | iconv-lite "0.4.13" 393 | on-finished "~2.3.0" 394 | qs "6.2.0" 395 | raw-body "~2.1.7" 396 | type-is "~1.6.13" 397 | 398 | boom@2.x.x: 399 | version "2.10.1" 400 | resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" 401 | dependencies: 402 | hoek "2.x.x" 403 | 404 | brace-expansion@^1.0.0: 405 | version "1.1.6" 406 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 407 | dependencies: 408 | balanced-match "^0.4.1" 409 | concat-map "0.0.1" 410 | 411 | braces@^1.8.2: 412 | version "1.8.5" 413 | resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 414 | dependencies: 415 | expand-range "^1.8.1" 416 | preserve "^0.2.0" 417 | repeat-element "^1.1.2" 418 | 419 | buffer-shims@^1.0.0: 420 | version "1.0.0" 421 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 422 | 423 | builtin-modules@^1.1.1: 424 | version "1.1.1" 425 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 426 | 427 | bytes@2.4.0: 428 | version "2.4.0" 429 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" 430 | 431 | caller-path@^0.1.0: 432 | version "0.1.0" 433 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 434 | dependencies: 435 | callsites "^0.2.0" 436 | 437 | callsites@^0.2.0: 438 | version "0.2.0" 439 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 440 | 441 | camelize@1.0.0: 442 | version "1.0.0" 443 | resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" 444 | 445 | caseless@~0.11.0: 446 | version "0.11.0" 447 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" 448 | 449 | chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: 450 | version "1.1.3" 451 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 452 | dependencies: 453 | ansi-styles "^2.2.1" 454 | escape-string-regexp "^1.0.2" 455 | has-ansi "^2.0.0" 456 | strip-ansi "^3.0.0" 457 | supports-color "^2.0.0" 458 | 459 | chalk@~0.4.0: 460 | version "0.4.0" 461 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" 462 | dependencies: 463 | ansi-styles "~1.0.0" 464 | has-color "~0.1.0" 465 | strip-ansi "~0.1.0" 466 | 467 | chance@^1.0.4: 468 | version "1.0.4" 469 | resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.4.tgz#2dbc3ea6ad0541f7f965bd0af25d1ca275c853c4" 470 | 471 | chokidar@^1.0.0, chokidar@^1.4.3: 472 | version "1.6.1" 473 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" 474 | dependencies: 475 | anymatch "^1.3.0" 476 | async-each "^1.0.0" 477 | glob-parent "^2.0.0" 478 | inherits "^2.0.1" 479 | is-binary-path "^1.0.0" 480 | is-glob "^2.0.0" 481 | path-is-absolute "^1.0.0" 482 | readdirp "^2.0.0" 483 | optionalDependencies: 484 | fsevents "^1.0.0" 485 | 486 | circular-json@^0.3.0: 487 | version "0.3.1" 488 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" 489 | 490 | cli-cursor@^1.0.1: 491 | version "1.0.2" 492 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" 493 | dependencies: 494 | restore-cursor "^1.0.1" 495 | 496 | cli-width@^2.0.0: 497 | version "2.1.0" 498 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" 499 | 500 | co@^4.6.0: 501 | version "4.6.0" 502 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 503 | 504 | code-point-at@^1.0.0: 505 | version "1.1.0" 506 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 507 | 508 | colors@1.0.x: 509 | version "1.0.3" 510 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 511 | 512 | combined-stream@^1.0.5, combined-stream@~1.0.5: 513 | version "1.0.5" 514 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" 515 | dependencies: 516 | delayed-stream "~1.0.0" 517 | 518 | commander@^2.8.1, commander@^2.9.0: 519 | version "2.9.0" 520 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 521 | dependencies: 522 | graceful-readlink ">= 1.0.0" 523 | 524 | concat-map@0.0.1: 525 | version "0.0.1" 526 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 527 | 528 | concat-stream@^1.4.6: 529 | version "1.5.2" 530 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" 531 | dependencies: 532 | inherits "~2.0.1" 533 | readable-stream "~2.0.0" 534 | typedarray "~0.0.5" 535 | 536 | configstore@^1.0.0: 537 | version "1.4.0" 538 | resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" 539 | dependencies: 540 | graceful-fs "^4.1.2" 541 | mkdirp "^0.5.0" 542 | object-assign "^4.0.1" 543 | os-tmpdir "^1.0.0" 544 | osenv "^0.1.0" 545 | uuid "^2.0.1" 546 | write-file-atomic "^1.1.2" 547 | xdg-basedir "^2.0.0" 548 | 549 | connect@3.4.1: 550 | version "3.4.1" 551 | resolved "https://registry.yarnpkg.com/connect/-/connect-3.4.1.tgz#a21361d3f4099ef761cda6dc4a973bb1ebb0a34d" 552 | dependencies: 553 | debug "~2.2.0" 554 | finalhandler "0.4.1" 555 | parseurl "~1.3.1" 556 | utils-merge "1.0.0" 557 | 558 | console-control-strings@^1.0.0, console-control-strings@~1.1.0: 559 | version "1.1.0" 560 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 561 | 562 | contains-path@^0.1.0: 563 | version "0.1.0" 564 | resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" 565 | 566 | content-disposition@0.5.1: 567 | version "0.5.1" 568 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" 569 | 570 | content-security-policy-builder@1.1.0: 571 | version "1.1.0" 572 | resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-1.1.0.tgz#d91f1b076236c119850c7dee9924bf55e05772b3" 573 | dependencies: 574 | dashify "^0.2.0" 575 | 576 | content-type@~1.0.2: 577 | version "1.0.2" 578 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" 579 | 580 | convert-source-map@^1.1.0: 581 | version "1.3.0" 582 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" 583 | 584 | cookie-signature@1.0.6: 585 | version "1.0.6" 586 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 587 | 588 | cookie@0.3.1: 589 | version "0.3.1" 590 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 591 | 592 | core-js@^2.4.0: 593 | version "2.4.1" 594 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" 595 | 596 | core-util-is@~1.0.0, core-util-is@1.0.2: 597 | version "1.0.2" 598 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 599 | 600 | cors@^2.8.1: 601 | version "2.8.1" 602 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.1.tgz#6181aa56abb45a2825be3304703747ae4e9d2383" 603 | dependencies: 604 | vary "^1" 605 | 606 | cryptiles@2.x.x: 607 | version "2.0.5" 608 | resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" 609 | dependencies: 610 | boom "2.x.x" 611 | 612 | cycle@1.0.x: 613 | version "1.0.3" 614 | resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" 615 | 616 | d@^0.1.1, d@~0.1.1: 617 | version "0.1.1" 618 | resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" 619 | dependencies: 620 | es5-ext "~0.10.2" 621 | 622 | dashdash@^1.12.0: 623 | version "1.14.0" 624 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.0.tgz#29e486c5418bf0f356034a993d51686a33e84141" 625 | dependencies: 626 | assert-plus "^1.0.0" 627 | 628 | dasherize@2.0.0: 629 | version "2.0.0" 630 | resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308" 631 | 632 | dashify@^0.2.0: 633 | version "0.2.2" 634 | resolved "https://registry.yarnpkg.com/dashify/-/dashify-0.2.2.tgz#6a07415a01c91faf4a32e38d9dfba71f61cb20fe" 635 | 636 | debug@^2.1.1, debug@^2.2.0, debug@~2.2.0, debug@2.2.0: 637 | version "2.2.0" 638 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 639 | dependencies: 640 | ms "0.7.1" 641 | 642 | deep-extend@~0.4.0: 643 | version "0.4.1" 644 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" 645 | 646 | deep-is@~0.1.3: 647 | version "0.1.3" 648 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 649 | 650 | del@^2.0.2: 651 | version "2.2.2" 652 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 653 | dependencies: 654 | globby "^5.0.0" 655 | is-path-cwd "^1.0.0" 656 | is-path-in-cwd "^1.0.0" 657 | object-assign "^4.0.1" 658 | pify "^2.0.0" 659 | pinkie-promise "^2.0.0" 660 | rimraf "^2.2.8" 661 | 662 | delayed-stream@~1.0.0: 663 | version "1.0.0" 664 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 665 | 666 | delegates@^1.0.0: 667 | version "1.0.0" 668 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 669 | 670 | depd@~1.1.0: 671 | version "1.1.0" 672 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" 673 | 674 | destroy@~1.0.4: 675 | version "1.0.4" 676 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 677 | 678 | detect-indent@^4.0.0: 679 | version "4.0.0" 680 | resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" 681 | dependencies: 682 | repeating "^2.0.0" 683 | 684 | dns-prefetch-control@0.1.0: 685 | version "0.1.0" 686 | resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" 687 | 688 | doctrine@^1.2.2, doctrine@1.5.0: 689 | version "1.5.0" 690 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" 691 | dependencies: 692 | esutils "^2.0.2" 693 | isarray "^1.0.0" 694 | 695 | dont-sniff-mimetype@1.0.0: 696 | version "1.0.0" 697 | resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz#5932890dc9f4e2f19e5eb02a20026e5e5efc8f58" 698 | 699 | duplexer@~0.1.1: 700 | version "0.1.1" 701 | resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" 702 | 703 | duplexify@^3.2.0: 704 | version "3.5.0" 705 | resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.0.tgz#1aa773002e1578457e9d9d4a50b0ccaaebcbd604" 706 | dependencies: 707 | end-of-stream "1.0.0" 708 | inherits "^2.0.1" 709 | readable-stream "^2.0.0" 710 | stream-shift "^1.0.0" 711 | 712 | ecc-jsbn@~0.1.1: 713 | version "0.1.1" 714 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" 715 | dependencies: 716 | jsbn "~0.1.0" 717 | 718 | ee-first@1.1.1: 719 | version "1.1.1" 720 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 721 | 722 | encodeurl@~1.0.1: 723 | version "1.0.1" 724 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" 725 | 726 | end-of-stream@1.0.0: 727 | version "1.0.0" 728 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e" 729 | dependencies: 730 | once "~1.3.0" 731 | 732 | es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: 733 | version "0.10.12" 734 | resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" 735 | dependencies: 736 | es6-iterator "2" 737 | es6-symbol "~3.1" 738 | 739 | es6-iterator@2: 740 | version "2.0.0" 741 | resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" 742 | dependencies: 743 | d "^0.1.1" 744 | es5-ext "^0.10.7" 745 | es6-symbol "3" 746 | 747 | es6-map@^0.1.3: 748 | version "0.1.4" 749 | resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.4.tgz#a34b147be224773a4d7da8072794cefa3632b897" 750 | dependencies: 751 | d "~0.1.1" 752 | es5-ext "~0.10.11" 753 | es6-iterator "2" 754 | es6-set "~0.1.3" 755 | es6-symbol "~3.1.0" 756 | event-emitter "~0.3.4" 757 | 758 | es6-promise@^3.0.2: 759 | version "3.3.1" 760 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" 761 | 762 | es6-set@~0.1.3: 763 | version "0.1.4" 764 | resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8" 765 | dependencies: 766 | d "~0.1.1" 767 | es5-ext "~0.10.11" 768 | es6-iterator "2" 769 | es6-symbol "3" 770 | event-emitter "~0.3.4" 771 | 772 | es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: 773 | version "3.1.0" 774 | resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" 775 | dependencies: 776 | d "~0.1.1" 777 | es5-ext "~0.10.11" 778 | 779 | es6-weak-map@^2.0.1: 780 | version "2.0.1" 781 | resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" 782 | dependencies: 783 | d "^0.1.1" 784 | es5-ext "^0.10.8" 785 | es6-iterator "2" 786 | es6-symbol "3" 787 | 788 | escape-html@~1.0.3: 789 | version "1.0.3" 790 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 791 | 792 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 793 | version "1.0.5" 794 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 795 | 796 | escope@^3.6.0: 797 | version "3.6.0" 798 | resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" 799 | dependencies: 800 | es6-map "^0.1.3" 801 | es6-weak-map "^2.0.1" 802 | esrecurse "^4.1.0" 803 | estraverse "^4.1.1" 804 | 805 | eslint-config-airbnb-base@9.0.0: 806 | version "9.0.0" 807 | resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-9.0.0.tgz#03e135562aa6c4d0d9f1bbda96d901326bf58ad2" 808 | 809 | eslint-import-resolver-node@^0.2.0: 810 | version "0.2.3" 811 | resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" 812 | dependencies: 813 | debug "^2.2.0" 814 | object-assign "^4.0.1" 815 | resolve "^1.1.6" 816 | 817 | eslint-module-utils@^1.0.0: 818 | version "1.0.0" 819 | resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-1.0.0.tgz#c4a57fd3a53efd8426cc2d5550aadab9bbd05fd0" 820 | dependencies: 821 | debug "2.2.0" 822 | pkg-dir "^1.0.0" 823 | 824 | eslint-plugin-import@^2.1.0: 825 | version "2.1.0" 826 | resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.1.0.tgz#20fe254aa7dac0d9fe617979f14b59f0f762e1b4" 827 | dependencies: 828 | builtin-modules "^1.1.1" 829 | contains-path "^0.1.0" 830 | debug "^2.2.0" 831 | doctrine "1.5.0" 832 | eslint-import-resolver-node "^0.2.0" 833 | eslint-module-utils "^1.0.0" 834 | has "^1.0.1" 835 | lodash.cond "^4.3.0" 836 | minimatch "^3.0.3" 837 | pkg-up "^1.0.0" 838 | 839 | eslint@^3.9.1: 840 | version "3.9.1" 841 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.9.1.tgz#5a8597706fc6048bc6061ac754d4a211d28f4f5b" 842 | dependencies: 843 | babel-code-frame "^6.16.0" 844 | chalk "^1.1.3" 845 | concat-stream "^1.4.6" 846 | debug "^2.1.1" 847 | doctrine "^1.2.2" 848 | escope "^3.6.0" 849 | espree "^3.3.1" 850 | estraverse "^4.2.0" 851 | esutils "^2.0.2" 852 | file-entry-cache "^2.0.0" 853 | glob "^7.0.3" 854 | globals "^9.2.0" 855 | ignore "^3.1.5" 856 | imurmurhash "^0.1.4" 857 | inquirer "^0.12.0" 858 | is-my-json-valid "^2.10.0" 859 | is-resolvable "^1.0.0" 860 | js-yaml "^3.5.1" 861 | json-stable-stringify "^1.0.0" 862 | levn "^0.3.0" 863 | lodash "^4.0.0" 864 | mkdirp "^0.5.0" 865 | natural-compare "^1.4.0" 866 | optionator "^0.8.2" 867 | path-is-inside "^1.0.1" 868 | pluralize "^1.2.1" 869 | progress "^1.1.8" 870 | require-uncached "^1.0.2" 871 | shelljs "^0.7.5" 872 | strip-bom "^3.0.0" 873 | strip-json-comments "~1.0.1" 874 | table "^3.7.8" 875 | text-table "~0.2.0" 876 | user-home "^2.0.0" 877 | 878 | espree@^3.3.1: 879 | version "3.3.2" 880 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.3.2.tgz#dbf3fadeb4ecb4d4778303e50103b3d36c88b89c" 881 | dependencies: 882 | acorn "^4.0.1" 883 | acorn-jsx "^3.0.0" 884 | 885 | esprima@^2.6.0: 886 | version "2.7.3" 887 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" 888 | 889 | esrecurse@^4.1.0: 890 | version "4.1.0" 891 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" 892 | dependencies: 893 | estraverse "~4.1.0" 894 | object-assign "^4.0.1" 895 | 896 | estraverse@^4.1.1, estraverse@^4.2.0: 897 | version "4.2.0" 898 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 899 | 900 | estraverse@~4.1.0: 901 | version "4.1.1" 902 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" 903 | 904 | esutils@^2.0.2: 905 | version "2.0.2" 906 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 907 | 908 | etag@~1.7.0: 909 | version "1.7.0" 910 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" 911 | 912 | event-emitter@~0.3.4: 913 | version "0.3.4" 914 | resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.4.tgz#8d63ddfb4cfe1fae3b32ca265c4c720222080bb5" 915 | dependencies: 916 | d "~0.1.1" 917 | es5-ext "~0.10.7" 918 | 919 | event-stream@~3.3.0: 920 | version "3.3.4" 921 | resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" 922 | dependencies: 923 | duplexer "~0.1.1" 924 | from "~0" 925 | map-stream "~0.1.0" 926 | pause-stream "0.0.11" 927 | split "0.3" 928 | stream-combiner "~0.0.4" 929 | through "~2.3.1" 930 | 931 | exit-hook@^1.0.0: 932 | version "1.1.1" 933 | resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" 934 | 935 | expand-brackets@^0.1.4: 936 | version "0.1.5" 937 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 938 | dependencies: 939 | is-posix-bracket "^0.1.0" 940 | 941 | expand-range@^1.8.1: 942 | version "1.8.2" 943 | resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 944 | dependencies: 945 | fill-range "^2.1.0" 946 | 947 | express-winston@^2.0.0: 948 | version "2.0.0" 949 | resolved "https://registry.yarnpkg.com/express-winston/-/express-winston-2.0.0.tgz#1100eda7e7f7a8f7c20a1ce1fa45de22168552b8" 950 | dependencies: 951 | chalk "~0.4.0" 952 | lodash "~4.11.1" 953 | 954 | express@^4.14.0: 955 | version "4.14.0" 956 | resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" 957 | dependencies: 958 | accepts "~1.3.3" 959 | array-flatten "1.1.1" 960 | content-disposition "0.5.1" 961 | content-type "~1.0.2" 962 | cookie "0.3.1" 963 | cookie-signature "1.0.6" 964 | debug "~2.2.0" 965 | depd "~1.1.0" 966 | encodeurl "~1.0.1" 967 | escape-html "~1.0.3" 968 | etag "~1.7.0" 969 | finalhandler "0.5.0" 970 | fresh "0.3.0" 971 | merge-descriptors "1.0.1" 972 | methods "~1.1.2" 973 | on-finished "~2.3.0" 974 | parseurl "~1.3.1" 975 | path-to-regexp "0.1.7" 976 | proxy-addr "~1.1.2" 977 | qs "6.2.0" 978 | range-parser "~1.2.0" 979 | send "0.14.1" 980 | serve-static "~1.11.1" 981 | type-is "~1.6.13" 982 | utils-merge "1.0.0" 983 | vary "~1.1.0" 984 | 985 | extend@~3.0.0: 986 | version "3.0.0" 987 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" 988 | 989 | extglob@^0.3.1: 990 | version "0.3.2" 991 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 992 | dependencies: 993 | is-extglob "^1.0.0" 994 | 995 | extsprintf@1.0.2: 996 | version "1.0.2" 997 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" 998 | 999 | eyes@0.1.x: 1000 | version "0.1.8" 1001 | resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" 1002 | 1003 | fast-levenshtein@~2.0.4: 1004 | version "2.0.5" 1005 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.5.tgz#bd33145744519ab1c36c3ee9f31f08e9079b67f2" 1006 | 1007 | figures@^1.3.5: 1008 | version "1.7.0" 1009 | resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" 1010 | dependencies: 1011 | escape-string-regexp "^1.0.5" 1012 | object-assign "^4.1.0" 1013 | 1014 | file-entry-cache@^2.0.0: 1015 | version "2.0.0" 1016 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 1017 | dependencies: 1018 | flat-cache "^1.2.1" 1019 | object-assign "^4.0.1" 1020 | 1021 | filename-regex@^2.0.0: 1022 | version "2.0.0" 1023 | resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" 1024 | 1025 | fill-range@^2.1.0: 1026 | version "2.2.3" 1027 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" 1028 | dependencies: 1029 | is-number "^2.1.0" 1030 | isobject "^2.0.0" 1031 | randomatic "^1.1.3" 1032 | repeat-element "^1.1.2" 1033 | repeat-string "^1.5.2" 1034 | 1035 | finalhandler@0.4.1: 1036 | version "0.4.1" 1037 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.4.1.tgz#85a17c6c59a94717d262d61230d4b0ebe3d4a14d" 1038 | dependencies: 1039 | debug "~2.2.0" 1040 | escape-html "~1.0.3" 1041 | on-finished "~2.3.0" 1042 | unpipe "~1.0.0" 1043 | 1044 | finalhandler@0.5.0: 1045 | version "0.5.0" 1046 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" 1047 | dependencies: 1048 | debug "~2.2.0" 1049 | escape-html "~1.0.3" 1050 | on-finished "~2.3.0" 1051 | statuses "~1.3.0" 1052 | unpipe "~1.0.0" 1053 | 1054 | find-up@^1.0.0: 1055 | version "1.1.2" 1056 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 1057 | dependencies: 1058 | path-exists "^2.0.0" 1059 | pinkie-promise "^2.0.0" 1060 | 1061 | flat-cache@^1.2.1: 1062 | version "1.2.1" 1063 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.1.tgz#6c837d6225a7de5659323740b36d5361f71691ff" 1064 | dependencies: 1065 | circular-json "^0.3.0" 1066 | del "^2.0.2" 1067 | graceful-fs "^4.1.2" 1068 | write "^0.2.1" 1069 | 1070 | for-in@^0.1.5: 1071 | version "0.1.6" 1072 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" 1073 | 1074 | for-own@^0.1.4: 1075 | version "0.1.4" 1076 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" 1077 | dependencies: 1078 | for-in "^0.1.5" 1079 | 1080 | forever-agent@~0.6.1: 1081 | version "0.6.1" 1082 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" 1083 | 1084 | form-data@~2.1.1: 1085 | version "2.1.1" 1086 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.1.tgz#4adf0342e1a79afa1e84c8c320a9ffc82392a1f3" 1087 | dependencies: 1088 | asynckit "^0.4.0" 1089 | combined-stream "^1.0.5" 1090 | mime-types "^2.1.12" 1091 | 1092 | forwarded@~0.1.0: 1093 | version "0.1.0" 1094 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" 1095 | 1096 | frameguard@3.0.0: 1097 | version "3.0.0" 1098 | resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.0.0.tgz#7bcad469ee7b96e91d12ceb3959c78235a9272e9" 1099 | 1100 | fresh@0.3.0: 1101 | version "0.3.0" 1102 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" 1103 | 1104 | from@~0: 1105 | version "0.1.3" 1106 | resolved "https://registry.yarnpkg.com/from/-/from-0.1.3.tgz#ef63ac2062ac32acf7862e0d40b44b896f22f3bc" 1107 | 1108 | fs-readdir-recursive@^1.0.0: 1109 | version "1.0.0" 1110 | resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" 1111 | 1112 | fs.realpath@^1.0.0: 1113 | version "1.0.0" 1114 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 1115 | 1116 | fsevents@^1.0.0: 1117 | version "1.0.15" 1118 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.15.tgz#fa63f590f3c2ad91275e4972a6cea545fb0aae44" 1119 | dependencies: 1120 | nan "^2.3.0" 1121 | node-pre-gyp "^0.6.29" 1122 | 1123 | fstream-ignore@~1.0.5: 1124 | version "1.0.5" 1125 | resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" 1126 | dependencies: 1127 | fstream "^1.0.0" 1128 | inherits "2" 1129 | minimatch "^3.0.0" 1130 | 1131 | fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: 1132 | version "1.0.10" 1133 | resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" 1134 | dependencies: 1135 | graceful-fs "^4.1.2" 1136 | inherits "~2.0.0" 1137 | mkdirp ">=0.5 0" 1138 | rimraf "2" 1139 | 1140 | function-bind@^1.0.2: 1141 | version "1.1.0" 1142 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" 1143 | 1144 | gauge@~2.6.0: 1145 | version "2.6.0" 1146 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" 1147 | dependencies: 1148 | aproba "^1.0.3" 1149 | console-control-strings "^1.0.0" 1150 | has-color "^0.1.7" 1151 | has-unicode "^2.0.0" 1152 | object-assign "^4.1.0" 1153 | signal-exit "^3.0.0" 1154 | string-width "^1.0.1" 1155 | strip-ansi "^3.0.1" 1156 | wide-align "^1.1.0" 1157 | 1158 | generate-function@^2.0.0: 1159 | version "2.0.0" 1160 | resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" 1161 | 1162 | generate-object-property@^1.1.0: 1163 | version "1.2.0" 1164 | resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" 1165 | dependencies: 1166 | is-property "^1.0.0" 1167 | 1168 | getpass@^0.1.1: 1169 | version "0.1.6" 1170 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" 1171 | dependencies: 1172 | assert-plus "^1.0.0" 1173 | 1174 | glob-base@^0.3.0: 1175 | version "0.3.0" 1176 | resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 1177 | dependencies: 1178 | glob-parent "^2.0.0" 1179 | is-glob "^2.0.0" 1180 | 1181 | glob-parent@^2.0.0: 1182 | version "2.0.0" 1183 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 1184 | dependencies: 1185 | is-glob "^2.0.0" 1186 | 1187 | glob@^5.0.5: 1188 | version "5.0.15" 1189 | resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 1190 | dependencies: 1191 | inflight "^1.0.4" 1192 | inherits "2" 1193 | minimatch "2 || 3" 1194 | once "^1.3.0" 1195 | path-is-absolute "^1.0.0" 1196 | 1197 | glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: 1198 | version "7.1.1" 1199 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 1200 | dependencies: 1201 | fs.realpath "^1.0.0" 1202 | inflight "^1.0.4" 1203 | inherits "2" 1204 | minimatch "^3.0.2" 1205 | once "^1.3.0" 1206 | path-is-absolute "^1.0.0" 1207 | 1208 | globals@^9.0.0, globals@^9.2.0: 1209 | version "9.12.0" 1210 | resolved "https://registry.yarnpkg.com/globals/-/globals-9.12.0.tgz#992ce90828c3a55fa8f16fada177adb64664cf9d" 1211 | 1212 | globby@^5.0.0: 1213 | version "5.0.0" 1214 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 1215 | dependencies: 1216 | array-union "^1.0.1" 1217 | arrify "^1.0.0" 1218 | glob "^7.0.3" 1219 | object-assign "^4.0.1" 1220 | pify "^2.0.0" 1221 | pinkie-promise "^2.0.0" 1222 | 1223 | got@^3.2.0: 1224 | version "3.3.1" 1225 | resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" 1226 | dependencies: 1227 | duplexify "^3.2.0" 1228 | infinity-agent "^2.0.0" 1229 | is-redirect "^1.0.0" 1230 | is-stream "^1.0.0" 1231 | lowercase-keys "^1.0.0" 1232 | nested-error-stacks "^1.0.0" 1233 | object-assign "^3.0.0" 1234 | prepend-http "^1.0.0" 1235 | read-all-stream "^3.0.0" 1236 | timed-out "^2.0.0" 1237 | 1238 | graceful-fs@^4.1.2, graceful-fs@^4.1.4: 1239 | version "4.1.10" 1240 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.10.tgz#f2d720c22092f743228775c75e3612632501f131" 1241 | 1242 | "graceful-readlink@>= 1.0.0": 1243 | version "1.0.1" 1244 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 1245 | 1246 | har-validator@~2.0.6: 1247 | version "2.0.6" 1248 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" 1249 | dependencies: 1250 | chalk "^1.1.1" 1251 | commander "^2.9.0" 1252 | is-my-json-valid "^2.12.4" 1253 | pinkie-promise "^2.0.0" 1254 | 1255 | has-ansi@^2.0.0: 1256 | version "2.0.0" 1257 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 1258 | dependencies: 1259 | ansi-regex "^2.0.0" 1260 | 1261 | has-color@^0.1.7, has-color@~0.1.0: 1262 | version "0.1.7" 1263 | resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" 1264 | 1265 | has-unicode@^2.0.0: 1266 | version "2.0.1" 1267 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 1268 | 1269 | has@^1.0.1: 1270 | version "1.0.1" 1271 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" 1272 | dependencies: 1273 | function-bind "^1.0.2" 1274 | 1275 | hawk@~3.1.3: 1276 | version "3.1.3" 1277 | resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" 1278 | dependencies: 1279 | boom "2.x.x" 1280 | cryptiles "2.x.x" 1281 | hoek "2.x.x" 1282 | sntp "1.x.x" 1283 | 1284 | helmet-csp@2.1.0: 1285 | version "2.1.0" 1286 | resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.1.0.tgz#c0fbff8d9e8f3bbff2b83dc7fed3d47143184040" 1287 | dependencies: 1288 | camelize "1.0.0" 1289 | content-security-policy-builder "1.1.0" 1290 | dasherize "2.0.0" 1291 | lodash.reduce "4.6.0" 1292 | platform "1.3.1" 1293 | 1294 | helmet@^3.1.0: 1295 | version "3.1.0" 1296 | resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.1.0.tgz#64449547398e51b063fe1c75e7cb0274a557ea09" 1297 | dependencies: 1298 | connect "3.4.1" 1299 | dns-prefetch-control "0.1.0" 1300 | dont-sniff-mimetype "1.0.0" 1301 | frameguard "3.0.0" 1302 | helmet-csp "2.1.0" 1303 | hide-powered-by "1.0.0" 1304 | hpkp "2.0.0" 1305 | hsts "2.0.0" 1306 | ienoopen "1.0.0" 1307 | nocache "2.0.0" 1308 | referrer-policy "1.0.0" 1309 | x-xss-protection "1.0.0" 1310 | 1311 | hide-powered-by@1.0.0: 1312 | version "1.0.0" 1313 | resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" 1314 | 1315 | hoek@2.x.x: 1316 | version "2.16.3" 1317 | resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" 1318 | 1319 | home-or-tmp@^2.0.0: 1320 | version "2.0.0" 1321 | resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" 1322 | dependencies: 1323 | os-homedir "^1.0.0" 1324 | os-tmpdir "^1.0.1" 1325 | 1326 | hpkp@2.0.0: 1327 | version "2.0.0" 1328 | resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672" 1329 | 1330 | hsts@2.0.0: 1331 | version "2.0.0" 1332 | resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.0.0.tgz#a52234c6070decf214b2b6b70bb144d07e4776c7" 1333 | dependencies: 1334 | core-util-is "1.0.2" 1335 | 1336 | http-errors@~1.5.0: 1337 | version "1.5.0" 1338 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.0.tgz#b1cb3d8260fd8e2386cad3189045943372d48211" 1339 | dependencies: 1340 | inherits "2.0.1" 1341 | setprototypeof "1.0.1" 1342 | statuses ">= 1.3.0 < 2" 1343 | 1344 | http-signature@~1.1.0: 1345 | version "1.1.1" 1346 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" 1347 | dependencies: 1348 | assert-plus "^0.2.0" 1349 | jsprim "^1.2.2" 1350 | sshpk "^1.7.0" 1351 | 1352 | iconv-lite@0.4.13: 1353 | version "0.4.13" 1354 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" 1355 | 1356 | ienoopen@1.0.0: 1357 | version "1.0.0" 1358 | resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.0.0.tgz#346a428f474aac8f50cf3784ea2d0f16f62bda6b" 1359 | 1360 | ignore-by-default@^1.0.0: 1361 | version "1.0.1" 1362 | resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" 1363 | 1364 | ignore@^3.1.5: 1365 | version "3.2.0" 1366 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435" 1367 | 1368 | imurmurhash@^0.1.4: 1369 | version "0.1.4" 1370 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 1371 | 1372 | infinity-agent@^2.0.0: 1373 | version "2.0.3" 1374 | resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216" 1375 | 1376 | inflection@^1.8.0: 1377 | version "1.10.0" 1378 | resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" 1379 | 1380 | inflight@^1.0.4: 1381 | version "1.0.6" 1382 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1383 | dependencies: 1384 | once "^1.3.0" 1385 | wrappy "1" 1386 | 1387 | inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@2: 1388 | version "2.0.3" 1389 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 1390 | 1391 | inherits@2.0.1: 1392 | version "2.0.1" 1393 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 1394 | 1395 | ini@~1.3.0: 1396 | version "1.3.4" 1397 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" 1398 | 1399 | inquirer@^0.12.0: 1400 | version "0.12.0" 1401 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" 1402 | dependencies: 1403 | ansi-escapes "^1.1.0" 1404 | ansi-regex "^2.0.0" 1405 | chalk "^1.0.0" 1406 | cli-cursor "^1.0.1" 1407 | cli-width "^2.0.0" 1408 | figures "^1.3.5" 1409 | lodash "^4.3.0" 1410 | readline2 "^1.0.1" 1411 | run-async "^0.1.0" 1412 | rx-lite "^3.1.2" 1413 | string-width "^1.0.1" 1414 | strip-ansi "^3.0.0" 1415 | through "^2.3.6" 1416 | 1417 | interpret@^1.0.0: 1418 | version "1.0.1" 1419 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" 1420 | 1421 | invariant@^2.2.0: 1422 | version "2.2.1" 1423 | resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" 1424 | dependencies: 1425 | loose-envify "^1.0.0" 1426 | 1427 | ipaddr.js@1.1.1: 1428 | version "1.1.1" 1429 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" 1430 | 1431 | is-binary-path@^1.0.0: 1432 | version "1.0.1" 1433 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" 1434 | dependencies: 1435 | binary-extensions "^1.0.0" 1436 | 1437 | is-buffer@^1.0.2: 1438 | version "1.1.4" 1439 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" 1440 | 1441 | is-dotfile@^1.0.0: 1442 | version "1.0.2" 1443 | resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" 1444 | 1445 | is-equal-shallow@^0.1.3: 1446 | version "0.1.3" 1447 | resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 1448 | dependencies: 1449 | is-primitive "^2.0.0" 1450 | 1451 | is-extendable@^0.1.1: 1452 | version "0.1.1" 1453 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 1454 | 1455 | is-extglob@^1.0.0: 1456 | version "1.0.0" 1457 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 1458 | 1459 | is-finite@^1.0.0: 1460 | version "1.0.2" 1461 | resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" 1462 | dependencies: 1463 | number-is-nan "^1.0.0" 1464 | 1465 | is-fullwidth-code-point@^1.0.0: 1466 | version "1.0.0" 1467 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 1468 | dependencies: 1469 | number-is-nan "^1.0.0" 1470 | 1471 | is-fullwidth-code-point@^2.0.0: 1472 | version "2.0.0" 1473 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 1474 | 1475 | is-glob@^2.0.0, is-glob@^2.0.1: 1476 | version "2.0.1" 1477 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 1478 | dependencies: 1479 | is-extglob "^1.0.0" 1480 | 1481 | is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: 1482 | version "2.15.0" 1483 | resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" 1484 | dependencies: 1485 | generate-function "^2.0.0" 1486 | generate-object-property "^1.1.0" 1487 | jsonpointer "^4.0.0" 1488 | xtend "^4.0.0" 1489 | 1490 | is-npm@^1.0.0: 1491 | version "1.0.0" 1492 | resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" 1493 | 1494 | is-number@^2.0.2, is-number@^2.1.0: 1495 | version "2.1.0" 1496 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 1497 | dependencies: 1498 | kind-of "^3.0.2" 1499 | 1500 | is-path-cwd@^1.0.0: 1501 | version "1.0.0" 1502 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 1503 | 1504 | is-path-in-cwd@^1.0.0: 1505 | version "1.0.0" 1506 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" 1507 | dependencies: 1508 | is-path-inside "^1.0.0" 1509 | 1510 | is-path-inside@^1.0.0: 1511 | version "1.0.0" 1512 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" 1513 | dependencies: 1514 | path-is-inside "^1.0.1" 1515 | 1516 | is-posix-bracket@^0.1.0: 1517 | version "0.1.1" 1518 | resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 1519 | 1520 | is-primitive@^2.0.0: 1521 | version "2.0.0" 1522 | resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 1523 | 1524 | is-property@^1.0.0: 1525 | version "1.0.2" 1526 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" 1527 | 1528 | is-redirect@^1.0.0: 1529 | version "1.0.0" 1530 | resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" 1531 | 1532 | is-resolvable@^1.0.0: 1533 | version "1.0.0" 1534 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" 1535 | dependencies: 1536 | tryit "^1.0.1" 1537 | 1538 | is-stream@^1.0.0: 1539 | version "1.1.0" 1540 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 1541 | 1542 | is-typedarray@~1.0.0: 1543 | version "1.0.0" 1544 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 1545 | 1546 | isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: 1547 | version "1.0.0" 1548 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 1549 | 1550 | isobject@^2.0.0: 1551 | version "2.1.0" 1552 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 1553 | dependencies: 1554 | isarray "1.0.0" 1555 | 1556 | isstream@~0.1.2, isstream@0.1.x: 1557 | version "0.1.2" 1558 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 1559 | 1560 | jodid25519@^1.0.0: 1561 | version "1.0.2" 1562 | resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" 1563 | dependencies: 1564 | jsbn "~0.1.0" 1565 | 1566 | js-tokens@^2.0.0: 1567 | version "2.0.0" 1568 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" 1569 | 1570 | js-yaml@^3.5.1: 1571 | version "3.6.1" 1572 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" 1573 | dependencies: 1574 | argparse "^1.0.7" 1575 | esprima "^2.6.0" 1576 | 1577 | jsbn@~0.1.0: 1578 | version "0.1.0" 1579 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" 1580 | 1581 | jsesc@^1.3.0: 1582 | version "1.3.0" 1583 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" 1584 | 1585 | json-inflector: 1586 | version "1.1.0" 1587 | resolved "https://registry.yarnpkg.com/json-inflector/-/json-inflector-1.1.0.tgz#bcacb816423ffe49f6b9e37db4e99ede29d9f1d4" 1588 | dependencies: 1589 | inflection "^1.8.0" 1590 | 1591 | json-schema@0.2.3: 1592 | version "0.2.3" 1593 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" 1594 | 1595 | json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: 1596 | version "1.0.1" 1597 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" 1598 | dependencies: 1599 | jsonify "~0.0.0" 1600 | 1601 | json-stringify-safe@~5.0.1: 1602 | version "5.0.1" 1603 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 1604 | 1605 | json5@^0.5.0: 1606 | version "0.5.0" 1607 | resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.0.tgz#9b20715b026cbe3778fd769edccd822d8332a5b2" 1608 | 1609 | jsonify@~0.0.0: 1610 | version "0.0.0" 1611 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 1612 | 1613 | jsonpointer@^4.0.0: 1614 | version "4.0.0" 1615 | resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" 1616 | 1617 | jsprim@^1.2.2: 1618 | version "1.3.1" 1619 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" 1620 | dependencies: 1621 | extsprintf "1.0.2" 1622 | json-schema "0.2.3" 1623 | verror "1.3.6" 1624 | 1625 | kind-of@^3.0.2: 1626 | version "3.0.4" 1627 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74" 1628 | dependencies: 1629 | is-buffer "^1.0.2" 1630 | 1631 | latest-version@^1.0.0: 1632 | version "1.0.1" 1633 | resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb" 1634 | dependencies: 1635 | package-json "^1.0.0" 1636 | 1637 | levn@^0.3.0, levn@~0.3.0: 1638 | version "0.3.0" 1639 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 1640 | dependencies: 1641 | prelude-ls "~1.1.2" 1642 | type-check "~0.3.2" 1643 | 1644 | lodash._baseassign@^3.0.0: 1645 | version "3.2.0" 1646 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 1647 | dependencies: 1648 | lodash._basecopy "^3.0.0" 1649 | lodash.keys "^3.0.0" 1650 | 1651 | lodash._basecopy@^3.0.0: 1652 | version "3.0.1" 1653 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 1654 | 1655 | lodash._bindcallback@^3.0.0: 1656 | version "3.0.1" 1657 | resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" 1658 | 1659 | lodash._createassigner@^3.0.0: 1660 | version "3.1.1" 1661 | resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" 1662 | dependencies: 1663 | lodash._bindcallback "^3.0.0" 1664 | lodash._isiterateecall "^3.0.0" 1665 | lodash.restparam "^3.0.0" 1666 | 1667 | lodash._getnative@^3.0.0: 1668 | version "3.9.1" 1669 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 1670 | 1671 | lodash._isiterateecall@^3.0.0: 1672 | version "3.0.9" 1673 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 1674 | 1675 | lodash.assign@^3.0.0: 1676 | version "3.2.0" 1677 | resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" 1678 | dependencies: 1679 | lodash._baseassign "^3.0.0" 1680 | lodash._createassigner "^3.0.0" 1681 | lodash.keys "^3.0.0" 1682 | 1683 | lodash.cond@^4.3.0: 1684 | version "4.5.2" 1685 | resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" 1686 | 1687 | lodash.defaults@^3.1.2: 1688 | version "3.1.2" 1689 | resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c" 1690 | dependencies: 1691 | lodash.assign "^3.0.0" 1692 | lodash.restparam "^3.0.0" 1693 | 1694 | lodash.isarguments@^3.0.0: 1695 | version "3.1.0" 1696 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 1697 | 1698 | lodash.isarray@^3.0.0: 1699 | version "3.0.4" 1700 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 1701 | 1702 | lodash.keys@^3.0.0: 1703 | version "3.1.2" 1704 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 1705 | dependencies: 1706 | lodash._getnative "^3.0.0" 1707 | lodash.isarguments "^3.0.0" 1708 | lodash.isarray "^3.0.0" 1709 | 1710 | lodash.pickby@^4.6.0: 1711 | version "4.6.0" 1712 | resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" 1713 | 1714 | lodash.reduce@4.6.0: 1715 | version "4.6.0" 1716 | resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" 1717 | 1718 | lodash.restparam@^3.0.0: 1719 | version "3.6.1" 1720 | resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" 1721 | 1722 | lodash@^4.0.0, lodash@^4.2.0, lodash@^4.3.0: 1723 | version "4.16.6" 1724 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" 1725 | 1726 | lodash@~4.11.1: 1727 | version "4.11.2" 1728 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.11.2.tgz#d6b4338b110a58e21dae5cebcfdbbfd2bc4cdb3b" 1729 | 1730 | loose-envify@^1.0.0: 1731 | version "1.3.0" 1732 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.0.tgz#6b26248c42f6d4fa4b0d8542f78edfcde35642a8" 1733 | dependencies: 1734 | js-tokens "^2.0.0" 1735 | 1736 | lowercase-keys@^1.0.0: 1737 | version "1.0.0" 1738 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" 1739 | 1740 | map-stream@~0.1.0: 1741 | version "0.1.0" 1742 | resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" 1743 | 1744 | media-typer@0.3.0: 1745 | version "0.3.0" 1746 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 1747 | 1748 | merge-descriptors@1.0.1: 1749 | version "1.0.1" 1750 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 1751 | 1752 | methods@~1.1.2: 1753 | version "1.1.2" 1754 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 1755 | 1756 | micromatch@^2.1.5: 1757 | version "2.3.11" 1758 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 1759 | dependencies: 1760 | arr-diff "^2.0.0" 1761 | array-unique "^0.2.1" 1762 | braces "^1.8.2" 1763 | expand-brackets "^0.1.4" 1764 | extglob "^0.3.1" 1765 | filename-regex "^2.0.0" 1766 | is-extglob "^1.0.0" 1767 | is-glob "^2.0.1" 1768 | kind-of "^3.0.2" 1769 | normalize-path "^2.0.1" 1770 | object.omit "^2.0.0" 1771 | parse-glob "^3.0.4" 1772 | regex-cache "^0.4.2" 1773 | 1774 | mime-db@~1.24.0: 1775 | version "1.24.0" 1776 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.24.0.tgz#e2d13f939f0016c6e4e9ad25a8652f126c467f0c" 1777 | 1778 | mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7: 1779 | version "2.1.12" 1780 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.12.tgz#152ba256777020dd4663f54c2e7bc26381e71729" 1781 | dependencies: 1782 | mime-db "~1.24.0" 1783 | 1784 | mime@1.3.4: 1785 | version "1.3.4" 1786 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 1787 | 1788 | minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3": 1789 | version "3.0.3" 1790 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 1791 | dependencies: 1792 | brace-expansion "^1.0.0" 1793 | 1794 | minimist@^1.2.0: 1795 | version "1.2.0" 1796 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 1797 | 1798 | minimist@0.0.8: 1799 | version "0.0.8" 1800 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1801 | 1802 | mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.1: 1803 | version "0.5.1" 1804 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 1805 | dependencies: 1806 | minimist "0.0.8" 1807 | 1808 | ms@0.7.1: 1809 | version "0.7.1" 1810 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 1811 | 1812 | mute-stream@0.0.5: 1813 | version "0.0.5" 1814 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" 1815 | 1816 | nan@^2.3.0: 1817 | version "2.4.0" 1818 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" 1819 | 1820 | natural-compare@^1.4.0: 1821 | version "1.4.0" 1822 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1823 | 1824 | negotiator@0.6.1: 1825 | version "0.6.1" 1826 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 1827 | 1828 | nested-error-stacks@^1.0.0: 1829 | version "1.0.2" 1830 | resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf" 1831 | dependencies: 1832 | inherits "~2.0.1" 1833 | 1834 | nocache@2.0.0: 1835 | version "2.0.0" 1836 | resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" 1837 | 1838 | node-pre-gyp@^0.6.29: 1839 | version "0.6.31" 1840 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.31.tgz#d8a00ddaa301a940615dbcc8caad4024d58f6017" 1841 | dependencies: 1842 | mkdirp "~0.5.1" 1843 | nopt "~3.0.6" 1844 | npmlog "^4.0.0" 1845 | rc "~1.1.6" 1846 | request "^2.75.0" 1847 | rimraf "~2.5.4" 1848 | semver "~5.3.0" 1849 | tar "~2.2.1" 1850 | tar-pack "~3.3.0" 1851 | 1852 | node-uuid@~1.4.7: 1853 | version "1.4.7" 1854 | resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" 1855 | 1856 | nodemon@^1.11.0: 1857 | version "1.11.0" 1858 | resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c" 1859 | dependencies: 1860 | chokidar "^1.4.3" 1861 | debug "^2.2.0" 1862 | es6-promise "^3.0.2" 1863 | ignore-by-default "^1.0.0" 1864 | lodash.defaults "^3.1.2" 1865 | minimatch "^3.0.0" 1866 | ps-tree "^1.0.1" 1867 | touch "1.0.0" 1868 | undefsafe "0.0.3" 1869 | update-notifier "0.5.0" 1870 | 1871 | nopt@~1.0.10: 1872 | version "1.0.10" 1873 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" 1874 | dependencies: 1875 | abbrev "1" 1876 | 1877 | nopt@~3.0.6: 1878 | version "3.0.6" 1879 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 1880 | dependencies: 1881 | abbrev "1" 1882 | 1883 | normalize-path@^2.0.1: 1884 | version "2.0.1" 1885 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" 1886 | 1887 | npmlog@^4.0.0: 1888 | version "4.0.0" 1889 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.0.tgz#e094503961c70c1774eb76692080e8d578a9f88f" 1890 | dependencies: 1891 | are-we-there-yet "~1.1.2" 1892 | console-control-strings "~1.1.0" 1893 | gauge "~2.6.0" 1894 | set-blocking "~2.0.0" 1895 | 1896 | number-is-nan@^1.0.0: 1897 | version "1.0.1" 1898 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1899 | 1900 | oauth-sign@~0.8.1: 1901 | version "0.8.2" 1902 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" 1903 | 1904 | object-assign@^3.0.0: 1905 | version "3.0.0" 1906 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" 1907 | 1908 | object-assign@^4.0.1, object-assign@^4.1.0: 1909 | version "4.1.0" 1910 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" 1911 | 1912 | object.omit@^2.0.0: 1913 | version "2.0.1" 1914 | resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" 1915 | dependencies: 1916 | for-own "^0.1.4" 1917 | is-extendable "^0.1.1" 1918 | 1919 | on-finished@~2.3.0: 1920 | version "2.3.0" 1921 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 1922 | dependencies: 1923 | ee-first "1.1.1" 1924 | 1925 | once@^1.3.0: 1926 | version "1.4.0" 1927 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1928 | dependencies: 1929 | wrappy "1" 1930 | 1931 | once@~1.3.0, once@~1.3.3: 1932 | version "1.3.3" 1933 | resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" 1934 | dependencies: 1935 | wrappy "1" 1936 | 1937 | onetime@^1.0.0: 1938 | version "1.1.0" 1939 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" 1940 | 1941 | optionator@^0.8.2: 1942 | version "0.8.2" 1943 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 1944 | dependencies: 1945 | deep-is "~0.1.3" 1946 | fast-levenshtein "~2.0.4" 1947 | levn "~0.3.0" 1948 | prelude-ls "~1.1.2" 1949 | type-check "~0.3.2" 1950 | wordwrap "~1.0.0" 1951 | 1952 | os-homedir@^1.0.0: 1953 | version "1.0.2" 1954 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1955 | 1956 | os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: 1957 | version "1.0.2" 1958 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1959 | 1960 | osenv@^0.1.0: 1961 | version "0.1.3" 1962 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.3.tgz#83cf05c6d6458fc4d5ac6362ea325d92f2754217" 1963 | dependencies: 1964 | os-homedir "^1.0.0" 1965 | os-tmpdir "^1.0.0" 1966 | 1967 | output-file-sync@^1.1.0: 1968 | version "1.1.2" 1969 | resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" 1970 | dependencies: 1971 | graceful-fs "^4.1.4" 1972 | mkdirp "^0.5.1" 1973 | object-assign "^4.1.0" 1974 | 1975 | package-json@^1.0.0: 1976 | version "1.2.0" 1977 | resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" 1978 | dependencies: 1979 | got "^3.2.0" 1980 | registry-url "^3.0.0" 1981 | 1982 | parse-glob@^3.0.4: 1983 | version "3.0.4" 1984 | resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 1985 | dependencies: 1986 | glob-base "^0.3.0" 1987 | is-dotfile "^1.0.0" 1988 | is-extglob "^1.0.0" 1989 | is-glob "^2.0.0" 1990 | 1991 | parseurl@~1.3.1: 1992 | version "1.3.1" 1993 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" 1994 | 1995 | path-exists@^2.0.0: 1996 | version "2.1.0" 1997 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 1998 | dependencies: 1999 | pinkie-promise "^2.0.0" 2000 | 2001 | path-is-absolute@^1.0.0: 2002 | version "1.0.1" 2003 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 2004 | 2005 | path-is-inside@^1.0.1: 2006 | version "1.0.2" 2007 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 2008 | 2009 | path-to-regexp@0.1.7: 2010 | version "0.1.7" 2011 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 2012 | 2013 | pause-stream@0.0.11: 2014 | version "0.0.11" 2015 | resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" 2016 | dependencies: 2017 | through "~2.3" 2018 | 2019 | pify@^2.0.0: 2020 | version "2.3.0" 2021 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 2022 | 2023 | pinkie-promise@^2.0.0: 2024 | version "2.0.1" 2025 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 2026 | dependencies: 2027 | pinkie "^2.0.0" 2028 | 2029 | pinkie@^2.0.0: 2030 | version "2.0.4" 2031 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 2032 | 2033 | pkg-dir@^1.0.0: 2034 | version "1.0.0" 2035 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" 2036 | dependencies: 2037 | find-up "^1.0.0" 2038 | 2039 | pkg-up@^1.0.0: 2040 | version "1.0.0" 2041 | resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" 2042 | dependencies: 2043 | find-up "^1.0.0" 2044 | 2045 | platform@1.3.1: 2046 | version "1.3.1" 2047 | resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.1.tgz#492210892335bd3131c0a08dda2d93ec3543e423" 2048 | 2049 | pluralize@^1.2.1: 2050 | version "1.2.1" 2051 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" 2052 | 2053 | prelude-ls@~1.1.2: 2054 | version "1.1.2" 2055 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 2056 | 2057 | prepend-http@^1.0.0: 2058 | version "1.0.4" 2059 | resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" 2060 | 2061 | preserve@^0.2.0: 2062 | version "0.2.0" 2063 | resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 2064 | 2065 | private@^0.1.6: 2066 | version "0.1.6" 2067 | resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1" 2068 | 2069 | process-nextick-args@~1.0.6: 2070 | version "1.0.7" 2071 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 2072 | 2073 | progress@^1.1.8: 2074 | version "1.1.8" 2075 | resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" 2076 | 2077 | proxy-addr@~1.1.2: 2078 | version "1.1.2" 2079 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" 2080 | dependencies: 2081 | forwarded "~0.1.0" 2082 | ipaddr.js "1.1.1" 2083 | 2084 | ps-tree@^1.0.1: 2085 | version "1.1.0" 2086 | resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" 2087 | dependencies: 2088 | event-stream "~3.3.0" 2089 | 2090 | punycode@^1.4.1: 2091 | version "1.4.1" 2092 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 2093 | 2094 | qs@~6.3.0: 2095 | version "6.3.0" 2096 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" 2097 | 2098 | qs@6.2.0: 2099 | version "6.2.0" 2100 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" 2101 | 2102 | randomatic@^1.1.3: 2103 | version "1.1.5" 2104 | resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b" 2105 | dependencies: 2106 | is-number "^2.0.2" 2107 | kind-of "^3.0.2" 2108 | 2109 | range-parser@~1.2.0: 2110 | version "1.2.0" 2111 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 2112 | 2113 | raw-body@~2.1.7: 2114 | version "2.1.7" 2115 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" 2116 | dependencies: 2117 | bytes "2.4.0" 2118 | iconv-lite "0.4.13" 2119 | unpipe "1.0.0" 2120 | 2121 | rc@^1.0.1, rc@~1.1.6: 2122 | version "1.1.6" 2123 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" 2124 | dependencies: 2125 | deep-extend "~0.4.0" 2126 | ini "~1.3.0" 2127 | minimist "^1.2.0" 2128 | strip-json-comments "~1.0.4" 2129 | 2130 | read-all-stream@^3.0.0: 2131 | version "3.1.0" 2132 | resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" 2133 | dependencies: 2134 | pinkie-promise "^2.0.0" 2135 | readable-stream "^2.0.0" 2136 | 2137 | readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@~2.1.4: 2138 | version "2.1.5" 2139 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" 2140 | dependencies: 2141 | buffer-shims "^1.0.0" 2142 | core-util-is "~1.0.0" 2143 | inherits "~2.0.1" 2144 | isarray "~1.0.0" 2145 | process-nextick-args "~1.0.6" 2146 | string_decoder "~0.10.x" 2147 | util-deprecate "~1.0.1" 2148 | 2149 | readable-stream@~2.0.0: 2150 | version "2.0.6" 2151 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 2152 | dependencies: 2153 | core-util-is "~1.0.0" 2154 | inherits "~2.0.1" 2155 | isarray "~1.0.0" 2156 | process-nextick-args "~1.0.6" 2157 | string_decoder "~0.10.x" 2158 | util-deprecate "~1.0.1" 2159 | 2160 | readdirp@^2.0.0: 2161 | version "2.1.0" 2162 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" 2163 | dependencies: 2164 | graceful-fs "^4.1.2" 2165 | minimatch "^3.0.2" 2166 | readable-stream "^2.0.2" 2167 | set-immediate-shim "^1.0.1" 2168 | 2169 | readline2@^1.0.1: 2170 | version "1.0.1" 2171 | resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" 2172 | dependencies: 2173 | code-point-at "^1.0.0" 2174 | is-fullwidth-code-point "^1.0.0" 2175 | mute-stream "0.0.5" 2176 | 2177 | rechoir@^0.6.2: 2178 | version "0.6.2" 2179 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 2180 | dependencies: 2181 | resolve "^1.1.6" 2182 | 2183 | referrer-policy@1.0.0: 2184 | version "1.0.0" 2185 | resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.0.0.tgz#f60eedc92f942b01a6118121ec932d66e8fd7e14" 2186 | 2187 | regenerator-runtime@^0.9.5: 2188 | version "0.9.6" 2189 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029" 2190 | 2191 | regex-cache@^0.4.2: 2192 | version "0.4.3" 2193 | resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" 2194 | dependencies: 2195 | is-equal-shallow "^0.1.3" 2196 | is-primitive "^2.0.0" 2197 | 2198 | registry-url@^3.0.0: 2199 | version "3.1.0" 2200 | resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" 2201 | dependencies: 2202 | rc "^1.0.1" 2203 | 2204 | repeat-element@^1.1.2: 2205 | version "1.1.2" 2206 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 2207 | 2208 | repeat-string@^1.5.2: 2209 | version "1.6.1" 2210 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 2211 | 2212 | repeating@^1.1.2: 2213 | version "1.1.3" 2214 | resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" 2215 | dependencies: 2216 | is-finite "^1.0.0" 2217 | 2218 | repeating@^2.0.0: 2219 | version "2.0.1" 2220 | resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" 2221 | dependencies: 2222 | is-finite "^1.0.0" 2223 | 2224 | request@^2.75.0: 2225 | version "2.78.0" 2226 | resolved "https://registry.yarnpkg.com/request/-/request-2.78.0.tgz#e1c8dec346e1c81923b24acdb337f11decabe9cc" 2227 | dependencies: 2228 | aws-sign2 "~0.6.0" 2229 | aws4 "^1.2.1" 2230 | caseless "~0.11.0" 2231 | combined-stream "~1.0.5" 2232 | extend "~3.0.0" 2233 | forever-agent "~0.6.1" 2234 | form-data "~2.1.1" 2235 | har-validator "~2.0.6" 2236 | hawk "~3.1.3" 2237 | http-signature "~1.1.0" 2238 | is-typedarray "~1.0.0" 2239 | isstream "~0.1.2" 2240 | json-stringify-safe "~5.0.1" 2241 | mime-types "~2.1.7" 2242 | node-uuid "~1.4.7" 2243 | oauth-sign "~0.8.1" 2244 | qs "~6.3.0" 2245 | stringstream "~0.0.4" 2246 | tough-cookie "~2.3.0" 2247 | tunnel-agent "~0.4.1" 2248 | 2249 | require-uncached@^1.0.2: 2250 | version "1.0.3" 2251 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 2252 | dependencies: 2253 | caller-path "^0.1.0" 2254 | resolve-from "^1.0.0" 2255 | 2256 | resolve-from@^1.0.0: 2257 | version "1.0.1" 2258 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 2259 | 2260 | resolve@^1.1.6: 2261 | version "1.1.7" 2262 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 2263 | 2264 | restore-cursor@^1.0.1: 2265 | version "1.0.1" 2266 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" 2267 | dependencies: 2268 | exit-hook "^1.0.0" 2269 | onetime "^1.0.0" 2270 | 2271 | rimraf@^2.2.8, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: 2272 | version "2.5.4" 2273 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" 2274 | dependencies: 2275 | glob "^7.0.5" 2276 | 2277 | run-async@^0.1.0: 2278 | version "0.1.0" 2279 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" 2280 | dependencies: 2281 | once "^1.3.0" 2282 | 2283 | rx-lite@^3.1.2: 2284 | version "3.1.2" 2285 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" 2286 | 2287 | semver-diff@^2.0.0: 2288 | version "2.1.0" 2289 | resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" 2290 | dependencies: 2291 | semver "^5.0.3" 2292 | 2293 | semver@^5.0.3, semver@~5.3.0: 2294 | version "5.3.0" 2295 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" 2296 | 2297 | send@0.14.1: 2298 | version "0.14.1" 2299 | resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" 2300 | dependencies: 2301 | debug "~2.2.0" 2302 | depd "~1.1.0" 2303 | destroy "~1.0.4" 2304 | encodeurl "~1.0.1" 2305 | escape-html "~1.0.3" 2306 | etag "~1.7.0" 2307 | fresh "0.3.0" 2308 | http-errors "~1.5.0" 2309 | mime "1.3.4" 2310 | ms "0.7.1" 2311 | on-finished "~2.3.0" 2312 | range-parser "~1.2.0" 2313 | statuses "~1.3.0" 2314 | 2315 | serve-static@~1.11.1: 2316 | version "1.11.1" 2317 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" 2318 | dependencies: 2319 | encodeurl "~1.0.1" 2320 | escape-html "~1.0.3" 2321 | parseurl "~1.3.1" 2322 | send "0.14.1" 2323 | 2324 | set-blocking@~2.0.0: 2325 | version "2.0.0" 2326 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 2327 | 2328 | set-immediate-shim@^1.0.1: 2329 | version "1.0.1" 2330 | resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" 2331 | 2332 | setprototypeof@1.0.1: 2333 | version "1.0.1" 2334 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" 2335 | 2336 | shelljs@^0.7.5: 2337 | version "0.7.5" 2338 | resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675" 2339 | dependencies: 2340 | glob "^7.0.0" 2341 | interpret "^1.0.0" 2342 | rechoir "^0.6.2" 2343 | 2344 | signal-exit@^3.0.0: 2345 | version "3.0.1" 2346 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" 2347 | 2348 | slash@^1.0.0: 2349 | version "1.0.0" 2350 | resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" 2351 | 2352 | slice-ansi@0.0.4: 2353 | version "0.0.4" 2354 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" 2355 | 2356 | slide@^1.1.5: 2357 | version "1.1.6" 2358 | resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" 2359 | 2360 | sntp@1.x.x: 2361 | version "1.0.9" 2362 | resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" 2363 | dependencies: 2364 | hoek "2.x.x" 2365 | 2366 | source-map-support@^0.4.2: 2367 | version "0.4.6" 2368 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.6.tgz#32552aa64b458392a85eab3b0b5ee61527167aeb" 2369 | dependencies: 2370 | source-map "^0.5.3" 2371 | 2372 | source-map@^0.5.0, source-map@^0.5.3: 2373 | version "0.5.6" 2374 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" 2375 | 2376 | split@0.3: 2377 | version "0.3.3" 2378 | resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" 2379 | dependencies: 2380 | through "2" 2381 | 2382 | sprintf-js@~1.0.2: 2383 | version "1.0.3" 2384 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 2385 | 2386 | sshpk@^1.7.0: 2387 | version "1.10.1" 2388 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.1.tgz#30e1a5d329244974a1af61511339d595af6638b0" 2389 | dependencies: 2390 | asn1 "~0.2.3" 2391 | assert-plus "^1.0.0" 2392 | dashdash "^1.12.0" 2393 | getpass "^0.1.1" 2394 | optionalDependencies: 2395 | bcrypt-pbkdf "^1.0.0" 2396 | ecc-jsbn "~0.1.1" 2397 | jodid25519 "^1.0.0" 2398 | jsbn "~0.1.0" 2399 | tweetnacl "~0.14.0" 2400 | 2401 | stack-trace@0.0.x: 2402 | version "0.0.9" 2403 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" 2404 | 2405 | "statuses@>= 1.3.0 < 2", statuses@~1.3.0: 2406 | version "1.3.0" 2407 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" 2408 | 2409 | stream-combiner@~0.0.4: 2410 | version "0.0.4" 2411 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" 2412 | dependencies: 2413 | duplexer "~0.1.1" 2414 | 2415 | stream-shift@^1.0.0: 2416 | version "1.0.0" 2417 | resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" 2418 | 2419 | string_decoder@~0.10.x: 2420 | version "0.10.31" 2421 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 2422 | 2423 | string-length@^1.0.0: 2424 | version "1.0.1" 2425 | resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" 2426 | dependencies: 2427 | strip-ansi "^3.0.0" 2428 | 2429 | string-width@^1.0.1: 2430 | version "1.0.2" 2431 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 2432 | dependencies: 2433 | code-point-at "^1.0.0" 2434 | is-fullwidth-code-point "^1.0.0" 2435 | strip-ansi "^3.0.0" 2436 | 2437 | string-width@^2.0.0: 2438 | version "2.0.0" 2439 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" 2440 | dependencies: 2441 | is-fullwidth-code-point "^2.0.0" 2442 | strip-ansi "^3.0.0" 2443 | 2444 | stringstream@~0.0.4: 2445 | version "0.0.5" 2446 | resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" 2447 | 2448 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 2449 | version "3.0.1" 2450 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 2451 | dependencies: 2452 | ansi-regex "^2.0.0" 2453 | 2454 | strip-ansi@~0.1.0: 2455 | version "0.1.1" 2456 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" 2457 | 2458 | strip-bom@^3.0.0: 2459 | version "3.0.0" 2460 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 2461 | 2462 | strip-json-comments@~1.0.1, strip-json-comments@~1.0.4: 2463 | version "1.0.4" 2464 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" 2465 | 2466 | supports-color@^2.0.0: 2467 | version "2.0.0" 2468 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 2469 | 2470 | table@^3.7.8: 2471 | version "3.8.3" 2472 | resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" 2473 | dependencies: 2474 | ajv "^4.7.0" 2475 | ajv-keywords "^1.0.0" 2476 | chalk "^1.1.1" 2477 | lodash "^4.0.0" 2478 | slice-ansi "0.0.4" 2479 | string-width "^2.0.0" 2480 | 2481 | tar-pack@~3.3.0: 2482 | version "3.3.0" 2483 | resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" 2484 | dependencies: 2485 | debug "~2.2.0" 2486 | fstream "~1.0.10" 2487 | fstream-ignore "~1.0.5" 2488 | once "~1.3.3" 2489 | readable-stream "~2.1.4" 2490 | rimraf "~2.5.1" 2491 | tar "~2.2.1" 2492 | uid-number "~0.0.6" 2493 | 2494 | tar@~2.2.1: 2495 | version "2.2.1" 2496 | resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" 2497 | dependencies: 2498 | block-stream "*" 2499 | fstream "^1.0.2" 2500 | inherits "2" 2501 | 2502 | text-table@~0.2.0: 2503 | version "0.2.0" 2504 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 2505 | 2506 | through@^2.3.6, through@~2.3, through@~2.3.1, through@2: 2507 | version "2.3.8" 2508 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 2509 | 2510 | timed-out@^2.0.0: 2511 | version "2.0.0" 2512 | resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" 2513 | 2514 | to-fast-properties@^1.0.1: 2515 | version "1.0.2" 2516 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" 2517 | 2518 | touch@1.0.0: 2519 | version "1.0.0" 2520 | resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" 2521 | dependencies: 2522 | nopt "~1.0.10" 2523 | 2524 | tough-cookie@~2.3.0: 2525 | version "2.3.2" 2526 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" 2527 | dependencies: 2528 | punycode "^1.4.1" 2529 | 2530 | tryit@^1.0.1: 2531 | version "1.0.3" 2532 | resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" 2533 | 2534 | tunnel-agent@~0.4.1: 2535 | version "0.4.3" 2536 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" 2537 | 2538 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: 2539 | version "0.14.3" 2540 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.3.tgz#3da382f670f25ded78d7b3d1792119bca0b7132d" 2541 | 2542 | type-check@~0.3.2: 2543 | version "0.3.2" 2544 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 2545 | dependencies: 2546 | prelude-ls "~1.1.2" 2547 | 2548 | type-is@~1.6.13: 2549 | version "1.6.13" 2550 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" 2551 | dependencies: 2552 | media-typer "0.3.0" 2553 | mime-types "~2.1.11" 2554 | 2555 | typedarray@~0.0.5: 2556 | version "0.0.6" 2557 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 2558 | 2559 | uid-number@~0.0.6: 2560 | version "0.0.6" 2561 | resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" 2562 | 2563 | undefsafe@0.0.3: 2564 | version "0.0.3" 2565 | resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" 2566 | 2567 | unpipe@~1.0.0, unpipe@1.0.0: 2568 | version "1.0.0" 2569 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 2570 | 2571 | update-notifier@0.5.0: 2572 | version "0.5.0" 2573 | resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" 2574 | dependencies: 2575 | chalk "^1.0.0" 2576 | configstore "^1.0.0" 2577 | is-npm "^1.0.0" 2578 | latest-version "^1.0.0" 2579 | repeating "^1.1.2" 2580 | semver-diff "^2.0.0" 2581 | string-length "^1.0.0" 2582 | 2583 | user-home@^1.1.1: 2584 | version "1.1.1" 2585 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" 2586 | 2587 | user-home@^2.0.0: 2588 | version "2.0.0" 2589 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" 2590 | dependencies: 2591 | os-homedir "^1.0.0" 2592 | 2593 | util-deprecate@~1.0.1: 2594 | version "1.0.2" 2595 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 2596 | 2597 | utils-merge@1.0.0: 2598 | version "1.0.0" 2599 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" 2600 | 2601 | uuid@^2.0.1: 2602 | version "2.0.3" 2603 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" 2604 | 2605 | v8flags@^2.0.10: 2606 | version "2.0.11" 2607 | resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" 2608 | dependencies: 2609 | user-home "^1.1.1" 2610 | 2611 | vary@^1, vary@~1.1.0: 2612 | version "1.1.0" 2613 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" 2614 | 2615 | verror@1.3.6: 2616 | version "1.3.6" 2617 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" 2618 | dependencies: 2619 | extsprintf "1.0.2" 2620 | 2621 | wide-align@^1.1.0: 2622 | version "1.1.0" 2623 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" 2624 | dependencies: 2625 | string-width "^1.0.1" 2626 | 2627 | winston@^2.3.0: 2628 | version "2.3.0" 2629 | resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.0.tgz#207faaab6fccf3fe493743dd2b03dbafc7ceb78c" 2630 | dependencies: 2631 | async "~1.0.0" 2632 | colors "1.0.x" 2633 | cycle "1.0.x" 2634 | eyes "0.1.x" 2635 | isstream "0.1.x" 2636 | stack-trace "0.0.x" 2637 | 2638 | wordwrap@~1.0.0: 2639 | version "1.0.0" 2640 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 2641 | 2642 | wrappy@1: 2643 | version "1.0.2" 2644 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 2645 | 2646 | write-file-atomic@^1.1.2: 2647 | version "1.2.0" 2648 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.2.0.tgz#14c66d4e4cb3ca0565c28cf3b7a6f3e4d5938fab" 2649 | dependencies: 2650 | graceful-fs "^4.1.2" 2651 | imurmurhash "^0.1.4" 2652 | slide "^1.1.5" 2653 | 2654 | write@^0.2.1: 2655 | version "0.2.1" 2656 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 2657 | dependencies: 2658 | mkdirp "^0.5.1" 2659 | 2660 | x-xss-protection@1.0.0: 2661 | version "1.0.0" 2662 | resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.0.0.tgz#898afb93869b24661cf9c52f9ee8db8ed0764dd9" 2663 | 2664 | xdg-basedir@^2.0.0: 2665 | version "2.0.0" 2666 | resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" 2667 | dependencies: 2668 | os-homedir "^1.0.0" 2669 | 2670 | xtend@^4.0.0: 2671 | version "4.0.1" 2672 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 2673 | 2674 | --------------------------------------------------------------------------------