├── .github
└── workflows
│ └── terraform.yml
├── .gitignore
├── 01-cloud-and-iac
└── README.md
├── 02-overview
├── README.md
└── main.tf
├── 03-basics
├── README.md
├── aws-backend
│ └── main.tf
├── terraform-cloud-backend
│ └── main.tf
└── web-app
│ ├── README.md
│ ├── architecture.png
│ └── main.tf
├── 04-variables-and-outputs
├── examples
│ ├── README.md
│ ├── another-variable-file.tfvars
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfvars
│ └── variables.tf
└── web-app
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfvars
│ └── variables.tf
├── 05-language-features
└── README.md
├── 06-organization-and-modules
├── README.md
├── consul
│ ├── README.md
│ └── main.tf
├── web-app-module
│ ├── compute.tf
│ ├── database.tf
│ ├── dns.tf
│ ├── main.tf
│ ├── networking.tf
│ ├── outputs.tf
│ ├── storage.tf
│ └── variables.tf
└── web-app
│ └── main.tf
├── 07-managing-multiple-environments
├── file-structure
│ ├── README.md
│ ├── global
│ │ └── main.tf
│ ├── production
│ │ └── main.tf
│ └── staging
│ │ └── main.tf
└── workspaces
│ ├── README.md
│ └── main.tf
├── 08-testing
├── deployed
│ ├── README.md
│ ├── production
│ │ └── main.tf
│ └── staging
│ │ └── main.tf
├── examples
│ ├── README.md
│ └── hello-world
│ │ └── main.tf
├── modules
│ ├── README.md
│ └── hello-world
│ │ ├── README.md
│ │ └── instance.tf
└── tests
│ ├── README.md
│ ├── bash
│ └── hello_world_test.sh
│ ├── static
│ └── README.md
│ └── terratest
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── hello_world_test.go
├── 09-developer-workflows
└── README.md
└── README.md
/.github/workflows/terraform.yml:
--------------------------------------------------------------------------------
1 | name: "Terraform"
2 |
3 | on:
4 | # Uncomment to enable staging deploy from main
5 | # push:
6 | # branches:
7 | # - main
8 | release:
9 | types: [published]
10 | pull_request:
11 |
12 | jobs:
13 | terraform:
14 | name: "Terraform"
15 | runs-on: ubuntu-latest
16 | env:
17 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
18 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
19 | defaults:
20 | run:
21 | working-directory: 07-managing-multiple-environments/file-structure/staging
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v2
25 |
26 | - name: Setup Terraform
27 | uses: hashicorp/setup-terraform@v1
28 | with:
29 | terraform_version: 1.0.1
30 | terraform_wrapper: false
31 |
32 | - name: Terraform Format
33 | id: fmt
34 | run: terraform fmt -check
35 |
36 | - name: Terraform Init
37 | id: init
38 | run: terraform init
39 |
40 | - name: Terraform Plan
41 | id: plan
42 | if: github.event_name == 'pull_request'
43 | # Route 53 zone must already exist for this to succeed!
44 | run: terraform plan -var db_pass=${{secrets.DB_PASS }} -no-color
45 | continue-on-error: true
46 |
47 | - uses: actions/github-script@0.9.0
48 | if: github.event_name == 'pull_request'
49 | env:
50 | PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
51 | with:
52 | github-token: ${{ secrets.GITHUB_TOKEN }}
53 | script: |
54 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
55 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
56 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
57 |
58 | Show Plan
59 |
60 | \`\`\`${process.env.PLAN}\`\`\`
61 |
62 |
63 |
64 | *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
65 |
66 |
67 | github.issues.createComment({
68 | issue_number: context.issue.number,
69 | owner: context.repo.owner,
70 | repo: context.repo.repo,
71 | body: output
72 | })
73 |
74 | - name: Terraform Plan Status
75 | if: steps.plan.outcome == 'failure'
76 | run: exit 1
77 |
78 | - uses: actions/setup-go@v2
79 | with:
80 | go-version: '^1.15.5'
81 |
82 | - name : Terratest Execution
83 | if: github.event_name == 'pull_request'
84 | working-directory: 08-testing/tests/terratest
85 | run: |
86 | go test . -v timeout 10m
87 |
88 | - name: Check tag
89 | id: check-tag
90 | run: |
91 | if [[ ${{ github.ref }} =~ ^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo ::set-output name=environment::production
92 | elif [[ ${{ github.ref }} == 'refs/heads/main' ]]; then echo ::set-output name=environment::staging
93 | else echo ::set-output name=environment::unknown
94 | fi
95 |
96 | - name: Terraform Apply Global
97 | if: github.event_name == 'push' || github.event_name == 'release'
98 | working-directory: 07-managing-multiple-environments/file-structure/global
99 | run: |
100 | terraform init
101 | terraform apply -auto-approve
102 |
103 | - name: Terraform Apply Staging
104 | if: steps.check-tag.outputs.environment == 'staging' && github.event_name == 'push'
105 | run: terraform apply -var db_pass=${{secrets.DB_PASS }} -auto-approve
106 |
107 | - name: Terraform Apply Production
108 | if: steps.check-tag.outputs.environment == 'production' && github.event_name == 'release'
109 | working-directory: 07-managing-multiple-environments/file-structure/production
110 | run: |
111 | terraform init
112 | terraform apply -var db_pass=${{secrets.DB_PASS }} -auto-approve
113 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.terraform*
2 | *.tfstate*
--------------------------------------------------------------------------------
/01-cloud-and-iac/README.md:
--------------------------------------------------------------------------------
1 | # 01 - Evolution of Cloud + Infrastructure as Code
2 |
3 | This module doesn't have any corresponding code.
--------------------------------------------------------------------------------
/02-overview/README.md:
--------------------------------------------------------------------------------
1 | ## 02 - Overview + Setup
2 |
3 | ## Install Terraform
4 |
5 | Official installation instructions from HashiCorp: https://learn.hashicorp.com/tutorials/terraform/install-cli
6 |
7 | ## AWS Account Setup
8 |
9 | AWS Terraform provider documentation: https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication
10 |
11 | 1) create non-root AWS user
12 | 2) Add the necessary IAM roles (e.g. AmazonEC2FullAccess)
13 | 3) Save Access key + secret key (or use AWS CLI `aws configure` -- https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)
14 |
15 | ## Hello World
16 |
17 | `./main.tf` contains minimal configuration to provision an EC2 instance.
18 |
19 | 1) `aws configure`
20 | 2) `terraform init`
21 | 3) `terraform plan`
22 | 4) `terraform apply`
23 |
--------------------------------------------------------------------------------
/02-overview/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~> 3.0"
6 | }
7 | }
8 | }
9 |
10 | provider "aws" {
11 | region = "us-east-1"
12 | }
13 |
14 | resource "aws_instance" "example" {
15 | ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
16 | instance_type = "t2.micro"
17 | }
18 |
--------------------------------------------------------------------------------
/03-basics/README.md:
--------------------------------------------------------------------------------
1 | ## 03 - Basics
2 |
3 | ## Remote Backends
4 |
5 | Remote backends enable storage of TF state in a remote, location to enable secure collaboration.
6 |
7 | ### Terraform Cloud
8 |
9 | https://www.terraform.io/cloud
10 |
11 | `./terraform-cloud-backend/main.tf`
12 |
13 | ### AWS S3 + Dynamo DB
14 |
15 | Steps to initialize backend in AWS and manage it with Terraform:
16 |
17 | 1) Use config from `./aws-backend/` (init, plan, apply) to provision s3 bucket and dynamoDB table with local state
18 | 2) Uncomment the remote backend configuration
19 | 3) Reinitialize with `terraform init`:
20 |
21 | ```
22 | Do you want to copy existing state to the new backend?
23 | Pre-existing state was found while migrating the previous "local" backend to the
24 | newly configured "s3" backend. No existing state was found in the newly
25 | configured "s3" backend. Do you want to copy this state to the new "s3"
26 | backend? Enter "yes" to copy and "no" to start with an empty state.
27 |
28 | Enter a value: yes
29 | ```
30 |
31 | Now the S3 bucket and dynamoDB table are mam and are able to be used as the state backend!
32 |
33 | ## Web-App
34 |
35 | Generic web application architecture including:
36 | - EC2 instances
37 | - S3 bucket
38 | - RDS instance
39 | - Load balancer
40 | - Route 53 DNS config
41 |
42 | This example will be refined and improved in later modules.
43 |
44 | ## Architecture
45 | 
--------------------------------------------------------------------------------
/03-basics/aws-backend/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | #############################################################
3 | ## AFTER RUNNING TERRAFORM APPLY (WITH LOCAL BACKEND)
4 | ## YOU WILL UNCOMMENT THIS CODE THEN RERUN TERRAFORM INIT
5 | ## TO SWITCH FROM LOCAL BACKEND TO REMOTE AWS BACKEND
6 | #############################################################
7 | # backend "s3" {
8 | # bucket = "devops-directive-tf-state" # REPLACE WITH YOUR BUCKET NAME
9 | # key = "03-basics/import-bootstrap/terraform.tfstate"
10 | # region = "us-east-1"
11 | # dynamodb_table = "terraform-state-locking"
12 | # encrypt = true
13 | # }
14 |
15 | required_providers {
16 | aws = {
17 | source = "hashicorp/aws"
18 | version = "~> 3.0"
19 | }
20 | }
21 | }
22 |
23 | provider "aws" {
24 | region = "us-east-1"
25 | }
26 |
27 | resource "aws_s3_bucket" "terraform_state" {
28 | bucket = "devops-directive-tf-state" # REPLACE WITH YOUR BUCKET NAME
29 | force_destroy = true
30 | }
31 |
32 | resource "aws_s3_bucket_versioning" "terraform_bucket_versioning" {
33 | bucket = aws_s3_bucket.terraform_state.id
34 | versioning_configuration {
35 | status = "Enabled"
36 | }
37 | }
38 |
39 | resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state_crypto_conf" {
40 | bucket = aws_s3_bucket.terraform_state.bucket
41 | rule {
42 | apply_server_side_encryption_by_default {
43 | sse_algorithm = "AES256"
44 | }
45 | }
46 | }
47 |
48 | resource "aws_dynamodb_table" "terraform_locks" {
49 | name = "terraform-state-locking"
50 | billing_mode = "PAY_PER_REQUEST"
51 | hash_key = "LockID"
52 | attribute {
53 | name = "LockID"
54 | type = "S"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/03-basics/terraform-cloud-backend/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | backend "remote" {
3 | organization = "devops-directive"
4 |
5 | workspaces {
6 | name = "devops-directive-terraform-course"
7 | }
8 | }
9 |
10 | required_providers {
11 | aws = {
12 | source = "hashicorp/aws"
13 | version = "~> 3.0"
14 | }
15 | }
16 | }
17 |
18 | provider "aws" {
19 | region = "us-east-1"
20 | }
21 |
--------------------------------------------------------------------------------
/03-basics/web-app/README.md:
--------------------------------------------------------------------------------
1 | ## DNS Nameservers
2 |
3 | Add AWS Nameservers so that Route 53 settings will be applied:
4 | - ns-1147.awsdns-15.org
5 | - ns-161.awsdns-20.com
6 | - ns-1629.awsdns-11.co.uk
7 | - ns-876.awsdns-45.net
8 |
9 | ## Architecture
10 | 
11 |
12 | ## Notes:
13 | - Had to add security group with IP of ec2 instance for inbound access (by default inbound traffic was blocked)
14 | - To connect to DB (`psql -U foo -d mydb -p 5432 -h terraform-20210127022433201300000001.cr2ub9wmsmpg.us-east-1.rds.amazonaws.com`)
15 |
--------------------------------------------------------------------------------
/03-basics/web-app/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidpalas/devops-directive-terraform-course/9949d314f3d3a2be54fae19d75546e912eb8a076/03-basics/web-app/architecture.png
--------------------------------------------------------------------------------
/03-basics/web-app/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "03-basics/web-app/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | resource "aws_instance" "instance_1" {
25 | ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
26 | instance_type = "t2.micro"
27 | security_groups = [aws_security_group.instances.name]
28 | user_data = <<-EOF
29 | #!/bin/bash
30 | echo "Hello, World 1" > index.html
31 | python3 -m http.server 8080 &
32 | EOF
33 | }
34 |
35 | resource "aws_instance" "instance_2" {
36 | ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
37 | instance_type = "t2.micro"
38 | security_groups = [aws_security_group.instances.name]
39 | user_data = <<-EOF
40 | #!/bin/bash
41 | echo "Hello, World 2" > index.html
42 | python3 -m http.server 8080 &
43 | EOF
44 | }
45 |
46 | resource "aws_s3_bucket" "bucket" {
47 | bucket_prefix = "devops-directive-web-app-data"
48 | force_destroy = true
49 | }
50 |
51 | resource "aws_s3_bucket_versioning" "bucket_versioning" {
52 | bucket = aws_s3_bucket.bucket.id
53 | versioning_configuration {
54 | status = "Enabled"
55 | }
56 | }
57 |
58 | resource "aws_s3_bucket_server_side_encryption_configuration" "bucket_crypto_conf" {
59 | bucket = aws_s3_bucket.bucket.bucket
60 | rule {
61 | apply_server_side_encryption_by_default {
62 | sse_algorithm = "AES256"
63 | }
64 | }
65 | }
66 |
67 | data "aws_vpc" "default_vpc" {
68 | default = true
69 | }
70 |
71 | data "aws_subnet_ids" "default_subnet" {
72 | vpc_id = data.aws_vpc.default_vpc.id
73 | }
74 |
75 | resource "aws_security_group" "instances" {
76 | name = "instance-security-group"
77 | }
78 |
79 | resource "aws_security_group_rule" "allow_http_inbound" {
80 | type = "ingress"
81 | security_group_id = aws_security_group.instances.id
82 |
83 | from_port = 8080
84 | to_port = 8080
85 | protocol = "tcp"
86 | cidr_blocks = ["0.0.0.0/0"]
87 | }
88 |
89 | resource "aws_lb_listener" "http" {
90 | load_balancer_arn = aws_lb.load_balancer.arn
91 |
92 | port = 80
93 |
94 | protocol = "HTTP"
95 |
96 | # By default, return a simple 404 page
97 | default_action {
98 | type = "fixed-response"
99 |
100 | fixed_response {
101 | content_type = "text/plain"
102 | message_body = "404: page not found"
103 | status_code = 404
104 | }
105 | }
106 | }
107 |
108 | resource "aws_lb_target_group" "instances" {
109 | name = "example-target-group"
110 | port = 8080
111 | protocol = "HTTP"
112 | vpc_id = data.aws_vpc.default_vpc.id
113 |
114 | health_check {
115 | path = "/"
116 | protocol = "HTTP"
117 | matcher = "200"
118 | interval = 15
119 | timeout = 3
120 | healthy_threshold = 2
121 | unhealthy_threshold = 2
122 | }
123 | }
124 |
125 | resource "aws_lb_target_group_attachment" "instance_1" {
126 | target_group_arn = aws_lb_target_group.instances.arn
127 | target_id = aws_instance.instance_1.id
128 | port = 8080
129 | }
130 |
131 | resource "aws_lb_target_group_attachment" "instance_2" {
132 | target_group_arn = aws_lb_target_group.instances.arn
133 | target_id = aws_instance.instance_2.id
134 | port = 8080
135 | }
136 |
137 | resource "aws_lb_listener_rule" "instances" {
138 | listener_arn = aws_lb_listener.http.arn
139 | priority = 100
140 |
141 | condition {
142 | path_pattern {
143 | values = ["*"]
144 | }
145 | }
146 |
147 | action {
148 | type = "forward"
149 | target_group_arn = aws_lb_target_group.instances.arn
150 | }
151 | }
152 |
153 |
154 | resource "aws_security_group" "alb" {
155 | name = "alb-security-group"
156 | }
157 |
158 | resource "aws_security_group_rule" "allow_alb_http_inbound" {
159 | type = "ingress"
160 | security_group_id = aws_security_group.alb.id
161 |
162 | from_port = 80
163 | to_port = 80
164 | protocol = "tcp"
165 | cidr_blocks = ["0.0.0.0/0"]
166 |
167 | }
168 |
169 | resource "aws_security_group_rule" "allow_alb_all_outbound" {
170 | type = "egress"
171 | security_group_id = aws_security_group.alb.id
172 |
173 | from_port = 0
174 | to_port = 0
175 | protocol = "-1"
176 | cidr_blocks = ["0.0.0.0/0"]
177 |
178 | }
179 |
180 |
181 | resource "aws_lb" "load_balancer" {
182 | name = "web-app-lb"
183 | load_balancer_type = "application"
184 | subnets = data.aws_subnet_ids.default_subnet.ids
185 | security_groups = [aws_security_group.alb.id]
186 |
187 | }
188 |
189 | resource "aws_route53_zone" "primary" {
190 | name = "devopsdeployed.com"
191 | }
192 |
193 | resource "aws_route53_record" "root" {
194 | zone_id = aws_route53_zone.primary.zone_id
195 | name = "devopsdeployed.com"
196 | type = "A"
197 |
198 | alias {
199 | name = aws_lb.load_balancer.dns_name
200 | zone_id = aws_lb.load_balancer.zone_id
201 | evaluate_target_health = true
202 | }
203 | }
204 |
205 | resource "aws_db_instance" "db_instance" {
206 | allocated_storage = 20
207 | # This allows any minor version within the major engine_version
208 | # defined below, but will also result in allowing AWS to auto
209 | # upgrade the minor version of your DB. This may be too risky
210 | # in a real production environment.
211 | auto_minor_version_upgrade = true
212 | storage_type = "standard"
213 | engine = "postgres"
214 | engine_version = "12"
215 | instance_class = "db.t2.micro"
216 | name = "mydb"
217 | username = "foo"
218 | password = "foobarbaz"
219 | skip_final_snapshot = true
220 | }
221 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/README.md:
--------------------------------------------------------------------------------
1 | # Variables
2 |
3 | ## Variable block
4 |
5 | must define variable block
6 |
7 | ```
8 | variable "var_name" {
9 | type = string
10 | }
11 | ```
12 |
13 | ## Variable types
14 | - string
15 | - number
16 | - bool
17 | - list()
18 | - set()
19 | - map()
20 | - object({ = , ... })
21 | - tuple([, ...])
22 |
23 | ## Variable files
24 | `variables.tfvars` (or `.auto.tfvars`) automatically applied
25 |
26 | ## Apply default
27 | `terraform apply`
28 |
29 | ## Apply a different variable file
30 | `terraform apply -var-file=another-variable-file.tfvars`
31 |
32 | ## Passing Variable via Prompt
33 | If value not specified, Terraform will prompt for value. (this is okay for testing... but don't depend on it since you should be automating things!)
34 | ```
35 | var.db_pass
36 | password for database
37 |
38 | Enter a value:
39 | ```
40 |
41 | ## Passing Variables via CLI
42 | `terraform apply -var="db_pass=$DB_PASS_ENV_VAR"`
43 |
44 | # Local Variables
45 |
46 | Allows you to store the value of expression for reuse but doesn't allow for passing in values
47 | ```
48 | locals {
49 | extra_tag = "extra-tag"
50 | }
51 | ```
52 |
53 | # Output Variables
54 |
55 | Allows you to output some value (which might not be known ahead of time).
56 |
57 | For example it might be useful to know the IP address of a VM that was created:
58 |
59 | ```
60 | output "instance_ip_addr" {
61 | value = aws_instance.instance.private_ip
62 | }
63 | ```
64 |
65 | Sample output:
66 | ```
67 | db_instance_addr = "terraform-20210504182745335900000001.cr2ub9wmsmpg.us-east-1.rds.amazonaws.com"
68 | instance_ip_addr = "172.31.24.95"
69 | ```
70 |
71 | Will be output after `terraform apply` or `terraform output`
72 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/another-variable-file.tfvars:
--------------------------------------------------------------------------------
1 | instance_name = "hello-world-2"
2 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | backend "s3" {
3 | bucket = "devops-directive-tf-state"
4 | key = "04-variables-and-outputs/examples/terraform.tfstate"
5 | region = "us-east-1"
6 | dynamodb_table = "terraform-state-locking"
7 | encrypt = true
8 | }
9 |
10 | required_providers {
11 | aws = {
12 | source = "hashicorp/aws"
13 | version = "~> 3.0"
14 | }
15 | }
16 | }
17 |
18 | provider "aws" {
19 | region = "us-east-1"
20 | }
21 |
22 | locals {
23 | extra_tag = "extra-tag"
24 | }
25 |
26 | resource "aws_instance" "instance" {
27 | ami = var.ami
28 | instance_type = var.instance_type
29 |
30 | tags = {
31 | Name = var.instance_name
32 | ExtraTag = local.extra_tag
33 | }
34 | }
35 |
36 | resource "aws_db_instance" "db_instance" {
37 | allocated_storage = 20
38 | storage_type = "gp2"
39 | engine = "postgres"
40 | engine_version = "12"
41 | instance_class = "db.t2.micro"
42 | name = "mydb"
43 | username = var.db_user
44 | password = var.db_pass
45 | skip_final_snapshot = true
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/outputs.tf:
--------------------------------------------------------------------------------
1 | output "instance_ip_addr" {
2 | value = aws_instance.instance.private_ip
3 | }
4 |
5 | output "db_instance_addr" {
6 | value = aws_db_instance.db_instance.address
7 | }
8 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/terraform.tfvars:
--------------------------------------------------------------------------------
1 | instance_name = "hello-world"
2 | ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
3 | instance_type = "t2.micro"
--------------------------------------------------------------------------------
/04-variables-and-outputs/examples/variables.tf:
--------------------------------------------------------------------------------
1 | # should specify optional vs required
2 |
3 | variable "instance_name" {
4 | description = "Name of ec2 instance"
5 | type = string
6 | }
7 |
8 | variable "ami" {
9 | description = "Amazon machine image to use for ec2 instance"
10 | type = string
11 | default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
12 | }
13 |
14 | variable "instance_type" {
15 | description = "ec2 instance type"
16 | type = string
17 | default = "t2.micro"
18 | }
19 |
20 | variable "db_user" {
21 | description = "username for database"
22 | type = string
23 | default = "foo"
24 | }
25 |
26 | variable "db_pass" {
27 | description = "password for database"
28 | type = string
29 | sensitive = true
30 | }
31 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/web-app/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "04-variables-and-outputs/web-app/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 |
21 | provider "aws" {
22 | region = var.region
23 | }
24 |
25 | resource "aws_instance" "instance_1" {
26 | ami = var.ami
27 | instance_type = var.instance_type
28 | security_groups = [aws_security_group.instances.name]
29 | user_data = <<-EOF
30 | #!/bin/bash
31 | echo "Hello, World 1" > index.html
32 | python3 -m http.server 8080 &
33 | EOF
34 | }
35 |
36 | resource "aws_instance" "instance_2" {
37 | ami = var.ami
38 | instance_type = var.instance_type
39 | security_groups = [aws_security_group.instances.name]
40 | user_data = <<-EOF
41 | #!/bin/bash
42 | echo "Hello, World 2" > index.html
43 | python3 -m http.server 8080 &
44 | EOF
45 | }
46 |
47 | resource "aws_s3_bucket" "bucket" {
48 | bucket_prefix = var.bucket_prefix
49 | force_destroy = true
50 | }
51 |
52 | resource "aws_s3_bucket_versioning" "bucket_versioning" {
53 | bucket = aws_s3_bucket.bucket.id
54 | versioning_configuration {
55 | status = "Enabled"
56 | }
57 | }
58 |
59 | resource "aws_s3_bucket_server_side_encryption_configuration" "bucket_crypto_conf" {
60 | bucket = aws_s3_bucket.bucket.bucket
61 | rule {
62 | apply_server_side_encryption_by_default {
63 | sse_algorithm = "AES256"
64 | }
65 | }
66 | }
67 |
68 | data "aws_vpc" "default_vpc" {
69 | default = true
70 | }
71 |
72 | data "aws_subnet_ids" "default_subnet" {
73 | vpc_id = data.aws_vpc.default_vpc.id
74 | }
75 |
76 | resource "aws_security_group" "instances" {
77 | name = "instance-security-group"
78 | }
79 |
80 | resource "aws_security_group_rule" "allow_http_inbound" {
81 | type = "ingress"
82 | security_group_id = aws_security_group.instances.id
83 |
84 | from_port = 8080
85 | to_port = 8080
86 | protocol = "tcp"
87 | cidr_blocks = ["0.0.0.0/0"]
88 | }
89 |
90 | resource "aws_lb_listener" "http" {
91 | load_balancer_arn = aws_lb.load_balancer.arn
92 |
93 | port = 80
94 |
95 | protocol = "HTTP"
96 |
97 | # By default, return a simple 404 page
98 | default_action {
99 | type = "fixed-response"
100 |
101 | fixed_response {
102 | content_type = "text/plain"
103 | message_body = "404: page not found"
104 | status_code = 404
105 | }
106 | }
107 | }
108 |
109 | resource "aws_lb_target_group" "instances" {
110 | name = "example-target-group"
111 | port = 8080
112 | protocol = "HTTP"
113 | vpc_id = data.aws_vpc.default_vpc.id
114 |
115 | health_check {
116 | path = "/"
117 | protocol = "HTTP"
118 | matcher = "200"
119 | interval = 15
120 | timeout = 3
121 | healthy_threshold = 2
122 | unhealthy_threshold = 2
123 | }
124 | }
125 |
126 | resource "aws_lb_target_group_attachment" "instance_1" {
127 | target_group_arn = aws_lb_target_group.instances.arn
128 | target_id = aws_instance.instance_1.id
129 | port = 8080
130 | }
131 |
132 | resource "aws_lb_target_group_attachment" "instance_2" {
133 | target_group_arn = aws_lb_target_group.instances.arn
134 | target_id = aws_instance.instance_2.id
135 | port = 8080
136 | }
137 |
138 | resource "aws_lb_listener_rule" "instances" {
139 | listener_arn = aws_lb_listener.http.arn
140 | priority = 100
141 |
142 | condition {
143 | path_pattern {
144 | values = ["*"]
145 | }
146 | }
147 |
148 | action {
149 | type = "forward"
150 | target_group_arn = aws_lb_target_group.instances.arn
151 | }
152 | }
153 |
154 |
155 | resource "aws_security_group" "alb" {
156 | name = "alb-security-group"
157 | }
158 |
159 | resource "aws_security_group_rule" "allow_alb_http_inbound" {
160 | type = "ingress"
161 | security_group_id = aws_security_group.alb.id
162 |
163 | from_port = 80
164 | to_port = 80
165 | protocol = "tcp"
166 | cidr_blocks = ["0.0.0.0/0"]
167 |
168 | }
169 |
170 | resource "aws_security_group_rule" "allow_alb_all_outbound" {
171 | type = "egress"
172 | security_group_id = aws_security_group.alb.id
173 |
174 | from_port = 0
175 | to_port = 0
176 | protocol = "-1"
177 | cidr_blocks = ["0.0.0.0/0"]
178 |
179 | }
180 |
181 |
182 | resource "aws_lb" "load_balancer" {
183 | name = "web-app-lb"
184 | load_balancer_type = "application"
185 | subnets = data.aws_subnet_ids.default_subnet.ids
186 | security_groups = [aws_security_group.alb.id]
187 |
188 | }
189 |
190 | resource "aws_route53_zone" "primary" {
191 | name = var.domain
192 | }
193 |
194 | resource "aws_route53_record" "root" {
195 | zone_id = aws_route53_zone.primary.zone_id
196 | name = var.domain
197 | type = "A"
198 |
199 | alias {
200 | name = aws_lb.load_balancer.dns_name
201 | zone_id = aws_lb.load_balancer.zone_id
202 | evaluate_target_health = true
203 | }
204 | }
205 |
206 | resource "aws_db_instance" "db_instance" {
207 | allocated_storage = 20
208 | storage_type = "standard"
209 | engine = "postgres"
210 | engine_version = "12"
211 | instance_class = "db.t2.micro"
212 | name = var.db_name
213 | username = var.db_user
214 | password = var.db_pass
215 | skip_final_snapshot = true
216 | }
217 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/web-app/outputs.tf:
--------------------------------------------------------------------------------
1 | output "instance_1_ip_addr" {
2 | value = aws_instance.instance_1.public_ip
3 | }
4 |
5 | output "instance_2_ip_addr" {
6 | value = aws_instance.instance_2.public_ip
7 | }
8 |
9 | output "db_instance_addr" {
10 | value = aws_db_instance.db_instance.address
11 | }
12 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/web-app/terraform.tfvars:
--------------------------------------------------------------------------------
1 | bucket_prefix = "devops-directive-web-app-data"
2 | domain = "devopsdeployed.com"
3 | db_name = "mydb"
4 | db_user = "foo"
5 | # db_pass = "foobarbaz"
6 |
--------------------------------------------------------------------------------
/04-variables-and-outputs/web-app/variables.tf:
--------------------------------------------------------------------------------
1 | # General Variables
2 |
3 | variable "region" {
4 | description = "Default region for provider"
5 | type = string
6 | default = "us-east-1"
7 | }
8 |
9 | # EC2 Variables
10 |
11 | variable "ami" {
12 | description = "Amazon machine image to use for ec2 instance"
13 | type = string
14 | default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
15 | }
16 |
17 | variable "instance_type" {
18 | description = "ec2 instance type"
19 | type = string
20 | default = "t2.micro"
21 | }
22 |
23 | # S3 Variables
24 |
25 | variable "bucket_prefix" {
26 | description = "prefix of s3 bucket for app data"
27 | type = string
28 | }
29 |
30 | # Route 53 Variables
31 |
32 | variable "domain" {
33 | description = "Domain for website"
34 | type = string
35 | }
36 |
37 | # RDS Variables
38 |
39 | variable "db_name" {
40 | description = "Name of DB"
41 | type = string
42 | }
43 |
44 | variable "db_user" {
45 | description = "Username for DB"
46 | type = string
47 | }
48 |
49 | variable "db_pass" {
50 | description = "Password for DB"
51 | type = string
52 | sensitive = true
53 | }
54 |
55 |
56 |
--------------------------------------------------------------------------------
/05-language-features/README.md:
--------------------------------------------------------------------------------
1 | # Hashicorp Configuration Language Features
2 |
3 | The official documentation is the best reference for these: https://www.terraform.io/docs/language/index.html
4 |
5 | NOTE: ` ```py ` is used on code blocks to get highlighting since HCL isn't an allowable language.
6 |
7 | ## Expressions
8 |
9 | ### Strings
10 | ```py
11 | "foo" # literal string
12 |
13 | "foo ${var.bar}" # template string
14 | ```
15 |
16 | ### Operators
17 | ```py
18 | # Order of operations:
19 | !, - # (multiplication by -1)
20 | *, /, % # (modulo)
21 | +, - # (subtraction)
22 | >, >=, <, <= # (comparison)
23 | ==, != # (equality)
24 | && # (AND)
25 | || # (OR)
26 | ```
27 |
28 | ### Conditionals
29 |
30 | Ternary syntax can be used to conditionally set values based on other values.
31 |
32 | ```py
33 | condition ? true_val : false_val
34 |
35 | # For example
36 | var.a != "" ? var.a : "default-a"
37 | ```
38 |
39 | ### Other expression types:
40 | - For expressions
41 | - Splat expressions
42 | - Dynamic blocks
43 | - Type constraints
44 | - Version constraints
45 |
46 | ## Functions
47 | ```py
48 | # Numeric
49 | abs()
50 | ceil()
51 | floor()
52 | log()
53 | max()
54 | parseint() # parse as integer
55 | pow()
56 | signum() # sign of number
57 |
58 | # string
59 | chomp() # remove newlines at end
60 | format() # format number
61 | formatlist()
62 | indent()
63 | join()
64 | lower()
65 | regex()
66 | regexall()
67 | replace()
68 | split()
69 | strrev() # reverse string
70 | substr()
71 | title()
72 | trim()
73 | trimprefix()
74 | trimsuffix()
75 | trimspace()
76 | upper()
77 | ```
78 | ### Other function types:
79 | - Colleciton
80 | - Encoding
81 | - Filesystem
82 | - Date & Time
83 | - Hash & Crypto
84 | - IP Network
85 | - Type Conversion
86 |
87 | ## Meta-arguments
88 |
89 | Special arguments to control the behavior of resources and/or modules
90 |
91 | ### depends_on
92 |
93 | Allows specifying dependencies which do not manifest directly through consumption of data from another resource. For example if the creation order of two resources matters, the latter can be specified to depend on the former.
94 |
95 | ### count
96 |
97 | Allows for creation of multiple of a particular resource or module. This is most useful if each instance configuration is nearly identical.
98 |
99 | `count.index` can be referenced for each resource.
100 |
101 | `Count = 0` can also be used to prevent creation of a resource or modules. This is usually used in conjunction with conditional expression to selectively determine if the resource needs to be created.
102 |
103 | ### for_each
104 |
105 | Also allows for multiple of a particular resource or module but allows for more control across the instances by iterating over a list.
106 |
107 | ```json
108 | resource "some_resource" "example" {
109 | for_each = toset( ["foo", "bar", "baz"] )
110 | name = each.key
111 | }
112 | ```
113 |
114 | This would create three copies of `some_resource` with the name set to `"foo"`, `"bar"`, and `"baz"` respectively
115 |
116 | ### lifecycle
117 |
118 | Lifecycle meta-arguments control how Terraform treats particular resources.
119 |
120 | #### create_before_destroy
121 |
122 | Specifying `create_before_destroy = true` indicates that if the resource does need to be destroyed, Terraform should first provision its replacement before destroying the deprecated resource. This can be useful for things such as zero downtime deployments.
123 |
124 | ```json
125 | resource "some_resource" "example" {
126 | # ...
127 |
128 | lifecycle {
129 | create_before_destroy = true
130 | }
131 | }
132 | ```
133 |
134 | #### ignore_changes
135 |
136 | Sometimes an entity outside of terraform will automatically modify a resource (e.g. adding metadata, etc...). The `ignore_changes` argument allows you to ignore specific types of resource changes to prevent this from causing Terraform to attempt to revert those changes.
137 |
138 | #### prevent_destroy
139 |
140 | `prevent_destroy` provides an additional stopgap against accidentally destroying resources with terraform. If set to true, Terraform will reject any attempt to destroy that resource.
--------------------------------------------------------------------------------
/06-organization-and-modules/README.md:
--------------------------------------------------------------------------------
1 | ## Modifications
2 | - remove backend definition
3 | - remove provider definition
4 |
5 |
--------------------------------------------------------------------------------
/06-organization-and-modules/consul/README.md:
--------------------------------------------------------------------------------
1 | Uses a module from the terraform registry:
2 |
3 | https://github.com/hashicorp/terraform-aws-consul
--------------------------------------------------------------------------------
/06-organization-and-modules/consul/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "06-organization-and-modules/consul/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | ############################################################
25 | ##
26 | ## NOTE: if you are deploying this in your production setup
27 | ## follow the instructions in the github repo on how to modify
28 | ## deploying with the defaults here as an example of the power
29 | ## of modules.
30 | ##
31 | ## REPO: https://github.com/hashicorp/terraform-aws-consul
32 | ##
33 | ############################################################
34 | module "consul" {
35 | source = "git@github.com:hashicorp/terraform-aws-consul.git"
36 | }
37 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/compute.tf:
--------------------------------------------------------------------------------
1 | resource "aws_instance" "instance_1" {
2 | ami = var.ami
3 | instance_type = var.instance_type
4 | security_groups = [aws_security_group.instances.name]
5 | user_data = <<-EOF
6 | #!/bin/bash
7 | echo "Hello, World 1" > index.html
8 | python3 -m http.server 8080 &
9 | EOF
10 | }
11 |
12 | resource "aws_instance" "instance_2" {
13 | ami = var.ami
14 | instance_type = var.instance_type
15 | security_groups = [aws_security_group.instances.name]
16 | user_data = <<-EOF
17 | #!/bin/bash
18 | echo "Hello, World 2" > index.html
19 | python3 -m http.server 8080 &
20 | EOF
21 | }
22 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/database.tf:
--------------------------------------------------------------------------------
1 | resource "aws_db_instance" "db_instance" {
2 | allocated_storage = 20
3 | storage_type = "standard"
4 | engine = "postgres"
5 | engine_version = "12"
6 | instance_class = "db.t2.micro"
7 | name = var.db_name
8 | username = var.db_user
9 | password = var.db_pass
10 | skip_final_snapshot = true
11 | }
12 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/dns.tf:
--------------------------------------------------------------------------------
1 | resource "aws_route53_zone" "primary" {
2 | count = var.create_dns_zone ? 1 : 0
3 | name = var.domain
4 | }
5 |
6 | data "aws_route53_zone" "primary" {
7 | count = var.create_dns_zone ? 0 : 1
8 | name = var.domain
9 | }
10 |
11 | locals {
12 | dns_zone_id = var.create_dns_zone ? aws_route53_zone.primary[0].zone_id : data.aws_route53_zone.primary[0].zone_id
13 | subdomain = var.environment_name == "production" ? "" : "${var.environment_name}."
14 | }
15 |
16 | resource "aws_route53_record" "root" {
17 | zone_id = local.dns_zone_id
18 | name = "${local.subdomain}${var.domain}"
19 | type = "A"
20 |
21 | alias {
22 | name = aws_lb.load_balancer.dns_name
23 | zone_id = aws_lb.load_balancer.zone_id
24 | evaluate_target_health = true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~> 3.0"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/networking.tf:
--------------------------------------------------------------------------------
1 | data "aws_vpc" "default_vpc" {
2 | default = true
3 | }
4 |
5 | data "aws_subnet_ids" "default_subnet" {
6 | vpc_id = data.aws_vpc.default_vpc.id
7 | }
8 |
9 | resource "aws_security_group" "instances" {
10 | name = "${var.app_name}-${var.environment_name}-instance-security-group"
11 | }
12 |
13 | resource "aws_security_group_rule" "allow_http_inbound" {
14 | type = "ingress"
15 | security_group_id = aws_security_group.instances.id
16 |
17 | from_port = 8080
18 | to_port = 8080
19 | protocol = "tcp"
20 | cidr_blocks = ["0.0.0.0/0"]
21 | }
22 |
23 | resource "aws_lb_listener" "http" {
24 | load_balancer_arn = aws_lb.load_balancer.arn
25 |
26 | port = 80
27 |
28 | protocol = "HTTP"
29 |
30 | # By default, return a simple 404 page
31 | default_action {
32 | type = "fixed-response"
33 |
34 | fixed_response {
35 | content_type = "text/plain"
36 | message_body = "404: page not found"
37 | status_code = 404
38 | }
39 | }
40 | }
41 |
42 | resource "aws_lb_target_group" "instances" {
43 | name = "${var.app_name}-${var.environment_name}-tg"
44 | port = 8080
45 | protocol = "HTTP"
46 | vpc_id = data.aws_vpc.default_vpc.id
47 |
48 | health_check {
49 | path = "/"
50 | protocol = "HTTP"
51 | matcher = "200"
52 | interval = 15
53 | timeout = 3
54 | healthy_threshold = 2
55 | unhealthy_threshold = 2
56 | }
57 | }
58 |
59 | resource "aws_lb_target_group_attachment" "instance_1" {
60 | target_group_arn = aws_lb_target_group.instances.arn
61 | target_id = aws_instance.instance_1.id
62 | port = 8080
63 | }
64 |
65 | resource "aws_lb_target_group_attachment" "instance_2" {
66 | target_group_arn = aws_lb_target_group.instances.arn
67 | target_id = aws_instance.instance_2.id
68 | port = 8080
69 | }
70 |
71 | resource "aws_lb_listener_rule" "instances" {
72 | listener_arn = aws_lb_listener.http.arn
73 | priority = 100
74 |
75 | condition {
76 | path_pattern {
77 | values = ["*"]
78 | }
79 | }
80 |
81 | action {
82 | type = "forward"
83 | target_group_arn = aws_lb_target_group.instances.arn
84 | }
85 | }
86 |
87 |
88 | resource "aws_security_group" "alb" {
89 | name = "${var.app_name}-${var.environment_name}-alb-security-group"
90 | }
91 |
92 | resource "aws_security_group_rule" "allow_alb_http_inbound" {
93 | type = "ingress"
94 | security_group_id = aws_security_group.alb.id
95 |
96 | from_port = 80
97 | to_port = 80
98 | protocol = "tcp"
99 | cidr_blocks = ["0.0.0.0/0"]
100 |
101 | }
102 |
103 | resource "aws_security_group_rule" "allow_alb_all_outbound" {
104 | type = "egress"
105 | security_group_id = aws_security_group.alb.id
106 |
107 | from_port = 0
108 | to_port = 0
109 | protocol = "-1"
110 | cidr_blocks = ["0.0.0.0/0"]
111 |
112 | }
113 |
114 |
115 | resource "aws_lb" "load_balancer" {
116 | name = "${var.app_name}-${var.environment_name}-web-app-lb"
117 | load_balancer_type = "application"
118 | subnets = data.aws_subnet_ids.default_subnet.ids
119 | security_groups = [aws_security_group.alb.id]
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/outputs.tf:
--------------------------------------------------------------------------------
1 | output "instance_1_ip_addr" {
2 | value = aws_instance.instance_1.public_ip
3 | }
4 |
5 | output "instance_2_ip_addr" {
6 | value = aws_instance.instance_2.public_ip
7 | }
8 |
9 | output "db_instance_addr" {
10 | value = aws_db_instance.db_instance.address
11 | }
12 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/storage.tf:
--------------------------------------------------------------------------------
1 | resource "aws_s3_bucket" "bucket" {
2 | bucket_prefix = var.bucket_prefix
3 | force_destroy = true
4 | }
5 |
6 | resource "aws_s3_bucket_versioning" "bucket_versioning" {
7 | bucket = aws_s3_bucket.bucket.id
8 | versioning_configuration {
9 | status = "Enabled"
10 | }
11 | }
12 |
13 | resource "aws_s3_bucket_server_side_encryption_configuration" "bucket_crypto_conf" {
14 | bucket = aws_s3_bucket.bucket.bucket
15 | rule {
16 | apply_server_side_encryption_by_default {
17 | sse_algorithm = "AES256"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app-module/variables.tf:
--------------------------------------------------------------------------------
1 | # General Variables
2 |
3 | variable "region" {
4 | description = "Default region for provider"
5 | type = string
6 | default = "us-east-1"
7 | }
8 |
9 | variable "app_name" {
10 | description = "Name of the web application"
11 | type = string
12 | default = "web-app"
13 | }
14 |
15 | variable "environment_name" {
16 | description = "Deployment environment (dev/staging/production)"
17 | type = string
18 | default = "dev"
19 | }
20 |
21 | # EC2 Variables
22 |
23 | variable "ami" {
24 | description = "Amazon machine image to use for ec2 instance"
25 | type = string
26 | default = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
27 | }
28 |
29 | variable "instance_type" {
30 | description = "ec2 instance type"
31 | type = string
32 | default = "t2.micro"
33 | }
34 |
35 | # S3 Variables
36 |
37 | variable "bucket_prefix" {
38 | description = "prefix of s3 bucket for app data"
39 | type = string
40 | }
41 |
42 | # Route 53 Variables
43 |
44 | variable "create_dns_zone" {
45 | description = "If true, create new route53 zone, if false read existing route53 zone"
46 | type = bool
47 | default = false
48 | }
49 |
50 | variable "domain" {
51 | description = "Domain for website"
52 | type = string
53 | }
54 |
55 | # RDS Variables
56 |
57 | variable "db_name" {
58 | description = "Name of DB"
59 | type = string
60 | }
61 |
62 | variable "db_user" {
63 | description = "Username for DB"
64 | type = string
65 | }
66 |
67 | variable "db_pass" {
68 | description = "Password for DB"
69 | type = string
70 | sensitive = true
71 | }
72 |
73 |
74 |
--------------------------------------------------------------------------------
/06-organization-and-modules/web-app/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "06-organization-and-modules/web-app/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | variable "db_pass_1" {
25 | description = "password for database #1"
26 | type = string
27 | sensitive = true
28 | }
29 |
30 | variable "db_pass_2" {
31 | description = "password for database #2"
32 | type = string
33 | sensitive = true
34 | }
35 |
36 | module "web_app_1" {
37 | source = "../web-app-module"
38 |
39 | # Input Variables
40 | bucket_prefix = "web-app-1-data"
41 | domain = "devopsdeployed.com"
42 | app_name = "web-app-1"
43 | environment_name = "production"
44 | instance_type = "t2.micro"
45 | create_dns_zone = true
46 | db_name = "webapp1db"
47 | db_user = "foo"
48 | db_pass = var.db_pass_1
49 | }
50 |
51 | module "web_app_2" {
52 | source = "../web-app-module"
53 |
54 | # Input Variables
55 | bucket_prefix = "web-app-2-data"
56 | domain = "anotherdevopsdeployed.com"
57 | app_name = "web-app-2"
58 | environment_name = "production"
59 | instance_type = "t2.micro"
60 | create_dns_zone = true
61 | db_name = "webapp2db"
62 | db_user = "bar"
63 | db_pass = var.db_pass_2
64 | }
65 |
--------------------------------------------------------------------------------
/07-managing-multiple-environments/file-structure/README.md:
--------------------------------------------------------------------------------
1 | - Note about using separate AWS accounts (avoids prefix issues, improved IAM control)
2 | - Cover this in advanced section?
3 |
4 | ```
5 | provider “aws” {
6 | region = “us-east-1”
7 | assume_role {
8 | role_arn = “arn:aws:iam::123456789012:role/iac”
9 | }
10 | }
11 | ```
--------------------------------------------------------------------------------
/07-managing-multiple-environments/file-structure/global/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "07-managing-multiple-environments/global/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | # Route53 zone is shared across staging and production
25 | resource "aws_route53_zone" "primary" {
26 | name = "devopsdeployed.com"
27 | }
28 |
--------------------------------------------------------------------------------
/07-managing-multiple-environments/file-structure/production/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "07-managing-multiple-environments/production/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | variable "db_pass" {
25 | description = "password for database"
26 | type = string
27 | sensitive = true
28 | }
29 |
30 | locals {
31 | environment_name = "production"
32 | }
33 |
34 | module "web_app" {
35 | source = "../../../06-organization-and-modules/web-app-module"
36 |
37 | # Input Variables
38 | bucket_prefix = "web-app-data-${local.environment_name}"
39 | domain = "devopsdeployed.com"
40 | environment_name = local.environment_name
41 | instance_type = "t2.micro"
42 | create_dns_zone = false
43 | db_name = "${local.environment_name}mydb"
44 | db_user = "foo"
45 | db_pass = var.db_pass
46 | }
47 |
--------------------------------------------------------------------------------
/07-managing-multiple-environments/file-structure/staging/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "07-managing-multiple-environments/staging/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | variable "db_pass" {
25 | description = "password for database"
26 | type = string
27 | sensitive = true
28 | }
29 |
30 | locals {
31 | environment_name = "staging"
32 | }
33 |
34 | module "web_app" {
35 | source = "../../../06-organization-and-modules/web-app-module"
36 |
37 | # Input Variables
38 | bucket_prefix = "web-app-data-${local.environment_name}"
39 | domain = "devopsdeployed.com"
40 | environment_name = local.environment_name
41 | instance_type = "t2.micro"
42 | create_dns_zone = false
43 | db_name = "${local.environment_name}mydb"
44 | db_user = "foo"
45 | db_pass = var.db_pass
46 | }
47 |
--------------------------------------------------------------------------------
/07-managing-multiple-environments/workspaces/README.md:
--------------------------------------------------------------------------------
1 | Warning about manually switching environments
2 | ```
3 | terraform workspace new production
4 | terraform workspace list
5 | terraform workspace select staging
6 | ```
--------------------------------------------------------------------------------
/07-managing-multiple-environments/workspaces/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "07-managing-multiple-environments/workspaces/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 | variable "db_pass" {
25 | description = "password for database"
26 | type = string
27 | sensitive = true
28 | }
29 |
30 | locals {
31 | environment_name = terraform.workspace
32 | }
33 |
34 | module "web_app" {
35 | source = "../../06-organization-and-modules/web-app-module"
36 |
37 | # Input Variables
38 | bucket_prefix = "web-app-data-${local.environment_name}"
39 | domain = "devopsdeployed.com"
40 | environment_name = local.environment_name
41 | instance_type = "t2.micro"
42 | create_dns_zone = terraform.workspace == "production" ? true : false
43 | db_name = "${local.environment_name}mydb"
44 | db_user = "foo"
45 | db_pass = var.db_pass
46 | }
47 |
--------------------------------------------------------------------------------
/08-testing/deployed/README.md:
--------------------------------------------------------------------------------
1 | This is where the actual deployed configurations would live
--------------------------------------------------------------------------------
/08-testing/deployed/production/main.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidpalas/devops-directive-terraform-course/9949d314f3d3a2be54fae19d75546e912eb8a076/08-testing/deployed/production/main.tf
--------------------------------------------------------------------------------
/08-testing/deployed/staging/main.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidpalas/devops-directive-terraform-course/9949d314f3d3a2be54fae19d75546e912eb8a076/08-testing/deployed/staging/main.tf
--------------------------------------------------------------------------------
/08-testing/examples/README.md:
--------------------------------------------------------------------------------
1 | Examples using each module should be stored here.
--------------------------------------------------------------------------------
/08-testing/examples/hello-world/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | # Assumes s3 bucket and dynamo DB table already set up
3 | # See /code/03-basics/aws-backend
4 | backend "s3" {
5 | bucket = "devops-directive-tf-state"
6 | key = "08-testing/examples/hello-world/terraform.tfstate"
7 | region = "us-east-1"
8 | dynamodb_table = "terraform-state-locking"
9 | encrypt = true
10 | }
11 |
12 | required_providers {
13 | aws = {
14 | source = "hashicorp/aws"
15 | version = "~> 3.0"
16 | }
17 | }
18 | }
19 |
20 | provider "aws" {
21 | region = "us-east-1"
22 | }
23 |
24 |
25 | module "web_app" {
26 | source = "../../modules/hello-world"
27 | }
28 |
29 | output "instance_ip_addr" {
30 | value = module.web_app.instance_ip_addr
31 | }
32 |
33 | output "url" {
34 | value = "http://${module.web_app.instance_ip_addr}:8080"
35 | }
36 |
--------------------------------------------------------------------------------
/08-testing/modules/README.md:
--------------------------------------------------------------------------------
1 | Additional modules could be defined here.
--------------------------------------------------------------------------------
/08-testing/modules/hello-world/README.md:
--------------------------------------------------------------------------------
1 | Module that creates an EC2 instance running a python webserver and firewall rules to allow inbound traffic.
--------------------------------------------------------------------------------
/08-testing/modules/hello-world/instance.tf:
--------------------------------------------------------------------------------
1 | resource "aws_instance" "instance" {
2 | ami = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
3 | instance_type = "t2.micro"
4 | security_groups = [aws_security_group.instances.name]
5 | user_data = <<-EOF
6 | #!/bin/bash
7 | echo "Hello, World" > index.html
8 | python3 -m http.server 8080 &
9 | EOF
10 | }
11 |
12 | resource "aws_security_group" "instances" {
13 | name = "instance-security-group"
14 | }
15 |
16 | resource "aws_security_group_rule" "allow_http_inbound" {
17 | type = "ingress"
18 | security_group_id = aws_security_group.instances.id
19 |
20 | from_port = 8080
21 | to_port = 8080
22 | protocol = "tcp"
23 | cidr_blocks = ["0.0.0.0/0"]
24 | }
25 |
26 | output "instance_ip_addr" {
27 | value = aws_instance.instance.public_ip
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/08-testing/tests/README.md:
--------------------------------------------------------------------------------
1 | Automated tests go here!
--------------------------------------------------------------------------------
/08-testing/tests/bash/hello_world_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 |
4 | # Change directory to example
5 | cd ../../examples/hello-world
6 |
7 | # Create the resources
8 | terraform init
9 | terraform apply -auto-approve
10 |
11 | # Wait while the instance boots up
12 | # (Could also use a provisioner in the TF config to do this)
13 | sleep 60
14 |
15 | # Query the output, extract the IP and make a request
16 | terraform output -json |\
17 | jq -r '.instance_ip_addr.value' |\
18 | xargs -I {} curl http://{}:8080 -m 10
19 |
20 | # If request succeeds, destroy the resources
21 | terraform destroy -auto-approve
22 |
--------------------------------------------------------------------------------
/08-testing/tests/static/README.md:
--------------------------------------------------------------------------------
1 | # Static checks
2 |
3 | ## Built in
4 |
5 | ### Format
6 | Enforces style rules for your configurations.
7 | ```
8 | terraform fmt -check # checks if formatter would make chances
9 |
10 | terraform fmt # applies those changes
11 | ```
12 |
13 | ### Validate
14 | Checks that configuration are valid.
15 |
16 | Terraform init is required to use validate. If not working with a remote backend, `terraform init -backend=false` can be used.
17 | ```
18 | terraform validate
19 | ```
20 |
21 | ### Plan
22 | Looking at the resulting Terraform plan can help catch bugs.
23 | ```
24 | terraform plan
25 | ```
26 |
27 | ### Custom Validation Rules
28 | Enforce conditions on variables to prevent misuse
29 | ```
30 | variable "short_variable" {
31 | type = string
32 |
33 | validation {
34 | condition = length(var.short_variable) < 4
35 | error_message = "The short_variable value must be less than 4 characters!"
36 | }
37 | }
38 | ```
39 |
40 | ## External
41 |
42 | There are many 3rd party tools which can check Terraform configurations for potential issues and/or suggest best practices:
43 | - [tflint](https://github.com/terraform-linters/tflint)
44 | - [checkov](https://github.com/bridgecrewio/checkov)
45 | - [terrascan](https://github.com/accurics/terrascan)
46 | - [terraform-compliance](https://terraform-compliance.com/)
47 | - [snyk](https://support.snyk.io/hc/en-us/articles/360010916577-Scan-and-fix-security-issues-in-your-Terraform-files)
48 | - [Terraform Sentinel](https://www.terraform.io/docs/cloud/sentinel/index.html)
49 |
--------------------------------------------------------------------------------
/08-testing/tests/terratest/README.md:
--------------------------------------------------------------------------------
1 | How to run this test?
2 |
3 | download dependencies, then run the tests...
4 | ```
5 | go mod download
6 | go test -v --timeout 10m
7 | ```
8 |
--------------------------------------------------------------------------------
/08-testing/tests/terratest/go.mod:
--------------------------------------------------------------------------------
1 | module hello-world
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/gruntwork-io/terratest v0.34.8
7 | github.com/stretchr/testify v1.7.0
8 | )
9 |
--------------------------------------------------------------------------------
/08-testing/tests/terratest/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
8 | cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
9 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
10 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
14 | github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
15 | github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
16 | github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
17 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
18 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
19 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
20 | github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
21 | github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
22 | github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
23 | github.com/Azure/go-autorest/autorest v0.11.5/go.mod h1:foo3aIXRQ90zFve3r0QiDsrjGDUwWhKl0ZOQy1CT14k=
24 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
25 | github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
26 | github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
27 | github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
28 | github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
29 | github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
30 | github.com/Azure/go-autorest/autorest/azure/auth v0.5.1/go.mod h1:ea90/jvmnAwDrSooLH4sRIehEPtG/EPUXavDh31MnA4=
31 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s=
32 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
33 | github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
34 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
35 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
36 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
37 | github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
38 | github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
39 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
40 | github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
41 | github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
42 | github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
43 | github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
44 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
45 | github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
46 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
47 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
48 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
49 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
50 | github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
51 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
52 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
53 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
54 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
55 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
56 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
57 | github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
58 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
59 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
60 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
61 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
62 | github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
63 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
64 | github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
65 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
66 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
67 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
68 | github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
69 | github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
70 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
71 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
72 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
73 | github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
74 | github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
75 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
76 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
77 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
78 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
79 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
80 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
81 | github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
82 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
83 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
84 | github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
85 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
86 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
87 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
88 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
89 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
90 | github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
91 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
92 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
93 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
94 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
95 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
96 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
97 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
98 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
99 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
100 | github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
101 | github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
102 | github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
103 | github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
104 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
105 | github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
106 | github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
107 | github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
108 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
109 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
110 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
111 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
112 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
113 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
114 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
115 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
116 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
117 | github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
118 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
119 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
120 | github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
121 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
122 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
123 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
124 | github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
125 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
126 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
127 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
128 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
129 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
130 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
131 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
132 | github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
133 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
134 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
135 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
136 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
137 | github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
138 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
139 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
140 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
141 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
142 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
143 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
144 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
145 | github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
146 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
147 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
148 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
149 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
150 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
151 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
152 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
153 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
154 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
155 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
156 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
157 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
158 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
159 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
160 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
161 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
162 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
163 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
164 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
165 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
166 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
167 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
168 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
169 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
170 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
171 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
172 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
173 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
174 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
175 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
176 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
177 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
178 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
179 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
180 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
181 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
182 | github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk=
183 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
184 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
185 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
186 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
187 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
188 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
189 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
190 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
191 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
192 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
193 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
194 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
195 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
196 | github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
197 | github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
198 | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
199 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
200 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
201 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
202 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
203 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
204 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
205 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
206 | github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78=
207 | github.com/gruntwork-io/terratest v0.34.8 h1:JVa+OSTZ/FRIN2RVpHwQRceGruRhgvJ7pKyb7kbJ/Jc=
208 | github.com/gruntwork-io/terratest v0.34.8/go.mod h1:IBb+b5b7p34oZLfpz/ZADyn8TSKeWSBu+vQMmNeePLE=
209 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
210 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
211 | github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
212 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
213 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
214 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
215 | github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
216 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
217 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
218 | github.com/hashicorp/hcl/v2 v2.8.2 h1:wmFle3D1vu0okesm8BTLVDyJ6/OL9DCLUwn0b2OptiY=
219 | github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
220 | github.com/hashicorp/terraform-json v0.9.0 h1:WE7+Wt93W93feOiCligElSyS0tlDzwZUtJuDGIBr8zg=
221 | github.com/hashicorp/terraform-json v0.9.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE=
222 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
223 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
224 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
225 | github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
226 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
227 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
228 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
229 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
230 | github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
231 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
232 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
233 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
234 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
235 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
236 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
237 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
238 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
239 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
240 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
241 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
242 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
243 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
244 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
245 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
246 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
247 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
248 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
249 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
250 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
251 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
252 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
253 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
254 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
255 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
256 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
257 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
258 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
259 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
260 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
261 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
262 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
263 | github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
264 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
265 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
266 | github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
267 | github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
268 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
269 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
270 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
271 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
272 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
273 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
274 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
275 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
276 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
277 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
278 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
279 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
280 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
281 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
282 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
283 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
284 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
285 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
286 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
287 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
288 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
289 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
290 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
291 | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
292 | github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
293 | github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
294 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
295 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
296 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
297 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
298 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
299 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
300 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
301 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
302 | github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
303 | github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
304 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
305 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
306 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
307 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
308 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
309 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
310 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
311 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
312 | github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
313 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
314 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
315 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
316 | github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
317 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
318 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
319 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
320 | github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
321 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
322 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
323 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
324 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
325 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
326 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
327 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
328 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
329 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
330 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
331 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
332 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
333 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
334 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
335 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
336 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
337 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
338 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
339 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
340 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
341 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
342 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
343 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
344 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
345 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
346 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
347 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
348 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
349 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
350 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
351 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
352 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
353 | github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
354 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
355 | github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
356 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
357 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
358 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
359 | github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
360 | github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8=
361 | github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
362 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
363 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
364 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
365 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
366 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
367 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
368 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
369 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
370 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
371 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
372 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
373 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
374 | golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
375 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
376 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
377 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
378 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
379 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
380 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
381 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
382 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
383 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
384 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
385 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
386 | golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
387 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
388 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
389 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
390 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
391 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
392 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
393 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
394 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
395 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
396 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
397 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
398 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
399 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
400 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
401 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
402 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
403 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
404 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
405 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
406 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
407 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
408 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
409 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
410 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
411 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
412 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
413 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
414 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
415 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
416 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
417 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
418 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
419 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
420 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
421 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
422 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
423 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
424 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
425 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
426 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
427 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
428 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
429 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
430 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
431 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
432 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
433 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
434 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
435 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
436 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
437 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
438 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
439 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
440 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
441 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
442 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
443 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
444 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
445 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
446 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
447 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
448 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
449 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
450 | golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
451 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
452 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
453 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
454 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
455 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
456 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
457 | golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
458 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
459 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
460 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
461 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
462 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
463 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
464 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
465 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
466 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
467 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
468 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
469 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
470 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
471 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
472 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
473 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
474 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
475 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
476 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
477 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
478 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
479 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
480 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
481 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
482 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
483 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
484 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
485 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
486 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
487 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
488 | golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
489 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
490 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
491 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
492 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
493 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
494 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
495 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
496 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
497 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
498 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
499 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
500 | golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
501 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
502 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
503 | golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
504 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
505 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
506 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
507 | golang.org/x/tools v0.0.0-20191205215504-7b8c8591a921/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
508 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
509 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
510 | golang.org/x/tools v0.0.0-20201110201400-7099162a900a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
511 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
512 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
513 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
514 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
515 | gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
516 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
517 | gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
518 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
519 | google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
520 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
521 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
522 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
523 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
524 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
525 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
526 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
527 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
528 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
529 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
530 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
531 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
532 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
533 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
534 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
535 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
536 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
537 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
538 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
539 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
540 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
541 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
542 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
543 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
544 | google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
545 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
546 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
547 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
548 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
549 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
550 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
551 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
552 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
553 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
554 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
555 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
556 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
557 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
558 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
559 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
560 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
561 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
562 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
563 | gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
564 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
565 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
566 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
567 | gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
568 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
569 | gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
570 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
571 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
572 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
573 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
574 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
575 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
576 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
577 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
578 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
579 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
580 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
581 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
582 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
583 | k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
584 | k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs=
585 | k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
586 | k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
587 | k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
588 | k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
589 | k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM=
590 | k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE=
591 | k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
592 | k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
593 | k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls=
594 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
595 | k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
596 | k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
597 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
598 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
599 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
600 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
601 | k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
602 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
603 | k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
604 | k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8=
605 | k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
606 | k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
607 | modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
608 | modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
609 | modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
610 | modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
611 | modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
612 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
613 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
614 | sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
615 | sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
616 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
617 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
618 |
--------------------------------------------------------------------------------
/08-testing/tests/terratest/hello_world_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "crypto/tls"
5 | "fmt"
6 | "testing"
7 | "time"
8 |
9 | "github.com/gruntwork-io/terratest/modules/http-helper"
10 | "github.com/gruntwork-io/terratest/modules/terraform"
11 | )
12 |
13 | func TestTerraformHelloWorldExample(t *testing.T) {
14 | // retryable errors in terraform testing.
15 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
16 | TerraformDir: "../../examples/hello-world",
17 | })
18 |
19 | defer terraform.Destroy(t, terraformOptions)
20 |
21 | terraform.InitAndApply(t, terraformOptions)
22 |
23 | instanceURL := terraform.Output(t, terraformOptions, "url")
24 | tlsConfig := tls.Config{}
25 | maxRetries := 30
26 | timeBetweenRetries := 10 * time.Second
27 |
28 | http_helper.HttpGetWithRetryWithCustomValidation(
29 | t, instanceURL, &tlsConfig, maxRetries, timeBetweenRetries, validate,
30 | )
31 |
32 | }
33 |
34 | func validate(status int, body string) bool {
35 | fmt.Println(body)
36 | return status == 200
37 | }
38 |
--------------------------------------------------------------------------------
/09-developer-workflows/README.md:
--------------------------------------------------------------------------------
1 | - CI/CD
2 | - Multi account / project
3 | - Terragrunt
4 | - Test account + cloud nuke
5 |
6 | `.github/workflows contains CI/CD Workflows`
7 |
8 | https://github.com/hashicorp/learn-terraform-github-actions/blob/master/.github/workflows/terraform.yml
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DevOps Directive Terraform Course
2 |
3 | This is the companion repo to: [Complete Terraform Course - From BEGINNER to PRO! (Learn Infrastructure as Code)](https://www.youtube.com/watch?v=7xngnjfIlK4)
4 |
5 | [](https://www.youtube.com/watch?v=7xngnjfIlK4)
6 |
7 | ## 01 - Evolution of Cloud + Infrastructure as Code
8 |
9 | High level overview of the evolution of cloud computing and infrastructure as code.
10 |
11 | This module does not have any corresponding code.
12 |
13 | ## 02 - Overview + Setup
14 |
15 | Terraform overview and setup instructions.
16 |
17 | Includes basic `hello world` terraform config to provision a single AWS EC2 instance.
18 |
19 | ## 03 - Basics
20 |
21 | Covers main usage pattern, setting up remote backends (where the terraform state is stored) using terraform Cloud and AWS, and provides a naive implementation of a web application architecture.
22 |
23 | ## 04 - Variables and Outputs
24 |
25 | Introduces the concepts of variables which enable Terraform configurations to be flexible and composable. Refactors web application to use these features.
26 |
27 | ## 05 - Language Features
28 |
29 | Describes additional features of the Hashicorp Configuration Language (HCL).
30 |
31 | ## 06 - Organization and Modules
32 |
33 | Demonstrates how to structure terraform code into reuseable modules and how to instantiate/configure modules.
34 |
35 | ## 07 - Managing Multiple Environments
36 |
37 | Shows two methods for managing multiple environments (e.g. dev/staging/prodution) with Terraform.
38 |
39 | ## 08 - Testing
40 |
41 | Explains different types of testing (manual + automated) for Terraform modules and configurations.
42 |
43 | ## 09 - Developer Workflows + CI/CD
44 |
45 | Covers how teams can work together with Terraform and how to set up CI/CD pipelines to keep infrastructure environments up to date.
46 |
--------------------------------------------------------------------------------