├── .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 | ![](./web-app/architecture.png) -------------------------------------------------------------------------------- /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 | ![](architecture.png) 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 | [![thumbnail](https://user-images.githubusercontent.com/1320389/154354937-98533608-2f42-44c1-8110-87f7e3f45085.jpeg)](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 | --------------------------------------------------------------------------------