├── .circleci └── config.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .pre-commit-config.yaml ├── CODEOWNERS ├── CONTRIBUTING.md ├── GRUNTWORK_PHILOSOPHY.md ├── LICENSE.txt ├── README.md ├── _docs ├── cloud-load-balancer-icon.png └── cloud-load-balancer.png ├── examples ├── http-multi-backend │ └── README.md ├── internal-load-balancer │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── startup_script.sh │ └── variables.tf ├── network-load-balancer │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── shared │ └── startup_script.sh ├── main.tf ├── modules ├── http-load-balancer │ ├── README.md │ ├── core-concepts.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── internal-load-balancer │ ├── README.md │ ├── core-concepts.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── network-load-balancer │ ├── README.md │ ├── core-concepts.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── outputs.tf ├── test ├── README.md ├── go.mod ├── go.sum ├── http_test.go ├── ilb_test.go ├── nlb_test.go ├── test_util.go └── validation │ └── validate_all_modules_and_examples_test.go └── variables.tf /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | defaults: &defaults 4 | machine: 5 | image: ubuntu-2004:202104-01 6 | 7 | env: &env 8 | environment: 9 | GRUNTWORK_INSTALLER_VERSION: v0.0.30 10 | TERRATEST_LOG_PARSER_VERSION: v0.30.4 11 | MODULE_CI_VERSION: v0.38.4 12 | TERRAFORM_VERSION: 1.0.3 13 | TERRAGRUNT_VERSION: NONE 14 | PACKER_VERSION: NONE 15 | GOLANG_VERSION: 1.16 16 | GO111MODULE: auto 17 | 18 | jobs: 19 | precommit: 20 | <<: *env 21 | docker: 22 | - image: circleci/python:3.8.1 23 | steps: 24 | - checkout 25 | 26 | - run: 27 | name: install dependencies 28 | command: | 29 | curl -Ls https://raw.githubusercontent.com/gruntwork-io/gruntwork-installer/master/bootstrap-gruntwork-installer.sh | bash /dev/stdin --version "${GRUNTWORK_INSTALLER_VERSION}" 30 | gruntwork-install --module-name "gruntwork-module-circleci-helpers" --repo "https://github.com/gruntwork-io/terraform-aws-ci" --tag "${MODULE_CI_VERSION}" 31 | configure-environment-for-gruntwork-module \ 32 | --terraform-version ${TERRAFORM_VERSION} \ 33 | --terragrunt-version NONE \ 34 | --packer-version NONE \ 35 | --go-version ${GOLANG_VERSION} 36 | # Fail the build if the pre-commit hooks don't pass. Note: if you run pre-commit install locally, these hooks will 37 | # execute automatically every time before you commit, ensuring the build never fails at this step! 38 | - run: 39 | command: | 40 | pip install pre-commit==1.21.0 cfgv==2.0.1 zipp==1.1.0 yapf 41 | go get golang.org/x/tools/cmd/goimports 42 | export GOPATH=~/go/bin && export PATH=$PATH:$GOPATH 43 | pre-commit install 44 | pre-commit run --all-files 45 | 46 | test: 47 | <<: *defaults 48 | <<: *env 49 | steps: 50 | - checkout 51 | - run: &install_gruntwork_tooling 52 | name: install gruntwork tooling 53 | command: | 54 | sudo apt-get -y update 55 | curl -Ls https://raw.githubusercontent.com/gruntwork-io/gruntwork-installer/master/bootstrap-gruntwork-installer.sh | bash /dev/stdin --version "${GRUNTWORK_INSTALLER_VERSION}" 56 | gruntwork-install --module-name "gruntwork-module-circleci-helpers" --repo "https://github.com/gruntwork-io/terraform-aws-ci" --tag "${MODULE_CI_VERSION}" 57 | gruntwork-install --module-name "git-helpers" --repo "https://github.com/gruntwork-io/terraform-aws-ci" --tag "${MODULE_CI_VERSION}" 58 | gruntwork-install --binary-name "terratest_log_parser" --repo "https://github.com/gruntwork-io/terratest" --tag "${TERRATEST_LOG_PARSER_VERSION}" 59 | configure-environment-for-gruntwork-module --go-src-path ./test --terraform-version ${TERRAFORM_VERSION} --terragrunt-version ${TERRAGRUNT_VERSION} --packer-version ${PACKER_VERSION} --go-version ${GOLANG_VERSION} 60 | 61 | - run: 62 | name: run tests 63 | command: | 64 | # required for gcloud to authenticate correctly 65 | echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=- 66 | gcloud --quiet config set project ${GOOGLE_PROJECT_ID} 67 | gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} 68 | # required for terraform and terratest to authenticate correctly 69 | echo $GCLOUD_SERVICE_KEY > /tmp/gcloud.json 70 | export GOOGLE_APPLICATION_CREDENTIALS="/tmp/gcloud.json" 71 | # run the tests 72 | mkdir -p /tmp/logs 73 | run-go-tests --path test --timeout 2h | tee /tmp/logs/all.log 74 | no_output_timeout: 1h 75 | 76 | - run: 77 | name: parse test output 78 | command: terratest_log_parser --testlog /tmp/logs/all.log --outputdir /tmp/logs 79 | when: always 80 | 81 | - store_artifacts: 82 | path: /tmp/logs 83 | - store_test_results: 84 | path: /tmp/logs 85 | 86 | workflows: 87 | version: 2 88 | test: 89 | jobs: 90 | - precommit: 91 | context: 92 | - GCP__automated-tests 93 | - GITHUB__PAT__gruntwork-ci 94 | filters: 95 | tags: 96 | only: /^v.*/ 97 | - test: 98 | context: 99 | - GCP__automated-tests 100 | - GITHUB__PAT__gruntwork-ci 101 | requires: 102 | - precommit 103 | filters: 104 | tags: 105 | only: /^v.*/ 106 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior including the relevant Terraform/Terragrunt/Packer version number and any code snippets and module inputs you used. 20 | 21 | ```hcl 22 | // paste code snippets here 23 | ``` 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Nice to have** 29 | - [ ] Terminal output 30 | - [ ] Screenshots 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Submit a feature request for this repo. 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Description 8 | 9 | 10 | 11 | ### Documentation 12 | 13 | 21 | 22 | 23 | 24 | ## TODOs 25 | 26 | Please ensure all of these TODOs are completed before asking for a review. 27 | 28 | - [ ] Ensure the branch is named correctly with the issue number. e.g: `feature/new-vpc-endpoints-955` or `bug/missing-count-param-434`. 29 | - [ ] Update the docs. 30 | - [ ] Keep the changes backward compatible where possible. 31 | - [ ] Run the pre-commit checks successfully. 32 | - [ ] Run the relevant tests successfully. 33 | - [ ] Ensure any 3rd party code adheres with our [license policy](https://www.notion.so/gruntwork/Gruntwork-licenses-and-open-source-usage-policy-f7dece1f780341c7b69c1763f22b1378) or delete this line if its not applicable. 34 | 35 | 36 | ## Related Issues 37 | 38 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Terraform files 2 | .terraform 3 | terraform.tfstate 4 | terraform.tfvars 5 | *.tfstate* 6 | .test-data 7 | 8 | # OS X files 9 | .history 10 | .DS_Store 11 | 12 | # IntelliJ files 13 | .idea_modules 14 | *.iml 15 | *.iws 16 | *.ipr 17 | .idea/ 18 | build/ 19 | */build/ 20 | out/ 21 | 22 | # Go best practices dictate that libraries should not include the vendor directory 23 | vendor 24 | 25 | 26 | # Ignore Terraform lock files, as we want to test the Terraform code in these repos with the latest provider 27 | # versions. 28 | .terraform.lock.hcl 29 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gruntwork-io/pre-commit 3 | rev: v0.1.10 4 | hooks: 5 | - id: terraform-fmt 6 | - id: goimports 7 | 8 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @marinalimeira @robmorgan @ina-stoyanova @gruntwork-io/maintenance-tier-3-orion 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | 6 | 7 | Contributions to this Module are very welcome! We follow a fairly standard [pull request process]( 8 | https://help.github.com/articles/about-pull-requests/) for contributions, subject to the following guidelines: 9 | 10 | 1. [File a GitHub issue](#file-a-github-issue) 11 | 1. [Update the documentation](#update-the-documentation) 12 | 1. [Update the tests](#update-the-tests) 13 | 1. [Update the code](#update-the-code) 14 | 1. [Create a pull request](#create-a-pull-request) 15 | 1. [Merge and release](#merge-and-release) 16 | 17 | ## File a GitHub issue 18 | 19 | Before starting any work, we recommend filing a GitHub issue in this repo. This is your chance to ask questions and 20 | get feedback from the maintainers and the community before you sink a lot of time into writing (possibly the wrong) 21 | code. If there is anything you're unsure about, just ask! 22 | 23 | ## Update the documentation 24 | 25 | We recommend updating the documentation *before* updating any code (see [Readme Driven 26 | Development](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)). This ensures the documentation 27 | stays up to date and allows you to think through the problem at a high level before you get lost in the weeds of 28 | coding. 29 | 30 | ## Update the tests 31 | 32 | We also recommend updating the automated tests *before* updating any code (see [Test Driven 33 | Development](https://en.wikipedia.org/wiki/Test-driven_development)). That means you add or update a test case, 34 | verify that it's failing with a clear error message, and *then* make the code changes to get that test to pass. This 35 | ensures the tests stay up to date and verify all the functionality in this Module, including whatever new 36 | functionality you're adding in your contribution. Check out the [tests](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/test) folder for instructions on running the 37 | automated tests. 38 | 39 | ## Update the code 40 | 41 | At this point, make your code changes and use your new test case to verify that everything is working. As you work, 42 | keep in mind two things: 43 | 44 | 1. Backwards compatibility 45 | 1. Downtime 46 | 47 | ### Backwards compatibility 48 | 49 | Please make every effort to avoid unnecessary backwards incompatible changes. With Terraform code, this means: 50 | 51 | 1. Do not delete, rename, or change the type of input variables. 52 | 1. If you add an input variable, it should have a `default`. 53 | 1. Do not delete, rename, or change the type of output variables. 54 | 1. Do not delete or rename a module in the `modules` folder. 55 | 56 | If a backwards incompatible change cannot be avoided, please make sure to call that out when you submit a pull request, 57 | explaining why the change is absolutely necessary. 58 | 59 | ### Downtime 60 | 61 | Bear in mind that the Terraform code in this Module is used by real companies to run real infrastructure in 62 | production, and certain types of changes could cause downtime. For example, consider the following: 63 | 64 | 1. If you rename a resource (e.g. `google_sql_database_instance "foo"` -> `google_sql_database_instance "bar"`), Terraform will see that as deleting 65 | the old resource and creating a new one. 66 | 1. If you change certain attributes of a resource (e.g. the `name` of an `google_compute_instance`), the cloud provider (e.g. Google) may 67 | treat that as an instruction to delete the old resource and a create a new one. 68 | 69 | Deleting certain types of resources (e.g. virtual servers, load balancers) can cause downtime, so when making code 70 | changes, think carefully about how to avoid that. For example, can you avoid downtime by using 71 | [create_before_destroy](https://www.terraform.io/docs/configuration/resources.html#create_before_destroy)? Or via 72 | the `terraform state` command? If so, make sure to note this in our pull request. If downtime cannot be avoided, 73 | please make sure to call that out when you submit a pull request. 74 | 75 | 76 | ### Formatting and pre-commit hooks 77 | 78 | You must run `terraform fmt` on the code before committing. You can configure your computer to do this automatically 79 | using pre-commit hooks managed using [pre-commit](http://pre-commit.com/): 80 | 81 | 1. [Install pre-commit](http://pre-commit.com/#install). E.g.: `brew install pre-commit`. 82 | 1. Install the hooks: `pre-commit install`. 83 | 84 | That's it! Now just write your code, and every time you commit, `terraform fmt` will be run on the files you're 85 | committing. 86 | 87 | 88 | ## Create a pull request 89 | 90 | [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) with your changes. Please make sure 91 | to include the following: 92 | 93 | 1. A description of the change, including a link to your GitHub issue. 94 | 1. The output of your automated test run, preferably in a [GitHub Gist](https://gist.github.com/). We cannot run 95 | automated tests for pull requests automatically due to [security 96 | concerns](https://circleci.com/docs/fork-pr-builds/#security-implications), so we need you to manually provide this 97 | test output so we can verify that everything is working. 98 | 1. Any notes on backwards incompatibility or downtime. 99 | 100 | ## Merge and release 101 | 102 | The maintainers for this repo will review your code and provide feedback. If everything looks good, they will merge the 103 | code and release a new version, which you'll be able to find in the [releases page](https://github.com/gruntwork-io/terraform-google-load-balancer/releases). -------------------------------------------------------------------------------- /GRUNTWORK_PHILOSOPHY.md: -------------------------------------------------------------------------------- 1 | # Gruntwork Philosophy 2 | 3 | At Gruntwork, we strive to accelerate the deployment of production grade infrastructure by prodiving a library of 4 | stable, reusable, and battle tested infrastructure as code organized into a series of [modules](#what-is-a-module) with 5 | [submodules](#what-is-a-submodule). Each module represents a particular set of infrastructure that is componentized into 6 | smaller pieces represented by the submodules within the module. By doing so, we have built a composable library that can 7 | be combined into building out everything from simple single service deployments to complicated microservice setups so 8 | that your infrastructure can grow with your business needs. Every module we provide is built with the [production grade 9 | infrastruture checklist](#production-grade-infrastructure-checklist) in mind, ensuring that the services you deploy are 10 | resilient, fault tolerant, and scalable. 11 | 12 | 13 | ## What is a Module? 14 | 15 | A Module is a reusable, tested, documented, configurable, best-practices definition of a single piece of Infrastructure 16 | (e.g., Docker cluster, VPC, Jenkins, Consul), written using a combination of [Terraform](https://www.terraform.io/), Go, 17 | and Bash. A module contains a set of automated tests, documentation, and examples that have been proven in production, 18 | providing the underlying infrastructure for [Gruntwork's customers](https://www.gruntwork.io/customers). 19 | 20 | Instead of figuring out the details of how to run a piece of infrastructure from scratch, you can reuse existing code 21 | that has been proven in production. And instead of maintaining all that infrastructure code yourself, you can leverage 22 | the work of the community to pick up infrastructure improvements through a version number bump. 23 | 24 | 25 | ## What is a Submodule? 26 | 27 | Each Infrastructure Module consists of one or more orthogonal Submodules that handle some specific aspect of that 28 | Infrastructure Module's functionality. Breaking the code up into multiple submodules makes it easier to reuse and 29 | compose to handle many different use cases. Although Modules are designed to provide an end to end solution to manage 30 | the relevant infrastructure by combining the Submodules defined in the Module, Submodules can be used independently for 31 | specific functionality that you need in your infrastructure code. 32 | 33 | 34 | ## Production Grade Infrastructure Checklist 35 | 36 | At Gruntwork, we have learned over the years that it is not enough to just get the services up and running in a publicly 37 | accessible space to call your application "production-ready." There are many more things to consider, and oftentimes 38 | many of these considerations are missing in the deployment plan of applications. These topics come up as afterthoughts, 39 | and are learned the hard way after the fact. That is why we codified all of them into a checklist that can be used as a 40 | reference to help ensure that they are considered before your application goes to production, and conscious decisions 41 | are made to neglect particular components if needed, as opposed to accidentally omitting them from consideration. 42 | 43 | 47 | 48 | | Task | Description | Example tools | 49 | |--------------------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------| 50 | | Install | Install the software binaries and all dependencies. | Bash, Chef, Ansible, Puppet | 51 | | Configure | Configure the software at runtime. Includes port settings, TLS certs, service discovery, leaders, followers, replication, etc. | Bash, Chef, Ansible, Puppet | 52 | | Provision | Provision the infrastructure. Includes EC2 instances, load balancers, network topology, security gr oups, IAM permissions, etc. | Terraform, CloudFormation | 53 | | Deploy | Deploy the service on top of the infrastructure. Roll out updates with no downtime. Includes blue-green, rolling, and canary deployments. | Scripts, Orchestration tools (ECS, k8s, Nomad) | 54 | | High availability | Withstand outages of individual processes, EC2 instances, services, Availability Zones, and regions. | Multi AZ, multi-region, replication, ASGs, ELBs | 55 | | Scalability | Scale up and down in response to load. Scale horizontally (more servers) and/or vertically (bigger servers). | ASGs, replication, sharding, caching, divide and conquer | 56 | | Performance | Optimize CPU, memory, disk, network, GPU, and usage. Includes query tuning, benchmarking, load testing, and profiling. | Dynatrace, valgrind, VisualVM, ab, Jmeter | 57 | | Networking | Configure static and dynamic IPs, ports, service discovery, firewalls, DNS, SSH access, and VPN access. | EIPs, ENIs, VPCs, NACLs, SGs, Route 53, OpenVPN | 58 | | Security | Encryption in transit (TLS) and on disk, authentication, authorization, secrets management, server hardening. | ACM, EBS Volumes, Cognito, Vault, CIS | 59 | | Metrics | Availability metrics, business metrics, app metrics, server metrics, events, observability, tracing, and alerting. | CloudWatch, DataDog, New Relic, Honeycomb | 60 | | Logs | Rotate logs on disk. Aggregate log data to a central location. | CloudWatch logs, ELK, Sumo Logic, Papertrail | 61 | | Backup and Restore | Make backups of DBs, caches, and other data on a scheduled basis. Replicate to separate region/account. | RDS, ElastiCache, ec2-snapper, Lambda | 62 | | Cost optimization | Pick proper instance types, use spot and reserved instances, use auto scaling, and nuke unused resources. | ASGs, spot instances, reserved instances | 63 | | Documentation | Document your code, architecture, and practices. Create playbooks to respond to incidents. | READMEs, wikis, Slack | 64 | | Tests | Write automated tests for your infrastructure code. Run tests after every commit and nightly. | Terratest | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Gruntwork, Inc 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Sunset notice 2 | 3 | We believe there is an opportunity to create a truly outstanding developer experience for deploying to the cloud, however developing this vision requires that we temporarily limit our focus to just one cloud. Gruntwork has hundreds of customers currently using AWS, so we have temporarily suspended our maintenance efforts on this repo. Once we have implemented and validated our vision for the developer experience on the cloud, we look forward to picking this up. In the meantime, you are welcome to use this code in accordance with the open source license, however we will not be responding to GitHub Issues or Pull Requests. 4 | 5 | If you wish to be the maintainer for this project, we are open to considering that. Please contact us at support@gruntwork.io 6 | 7 | --- 8 | 9 | 20 | 21 | # Cloud Load Balancer Modules 22 | 23 | [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gruntwork-io/terraform-google-load-balancer.svg?label=latest)](https://github.com/gruntwork-io/terraform-google-load-balancer/releases/latest) 24 | ![Terraform Version](https://img.shields.io/badge/tf-%3E%3D1.0.x-blue.svg) 25 | 26 | This repo contains modules to perform load balancing on [Google Cloud Platform (GCP)](https://cloud.google.com/) using [Google Cloud Load Balancing](https://cloud.google.com/load-balancing/). 27 | 28 | ## Cloud Load Balancer Architecture 29 | 30 | ![Cloud Load Balancer Architecture](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/_docs/cloud-load-balancer.png "Cloud Load Balancer Architecture") 31 | 32 | ## Features 33 | 34 | - Load balance HTTP and HTTPS traffic across multiple backend instances, across multiple regions with HTTP(S) Load Balancing. 35 | - Load balance internal TCP/UDP traffic with Internal Load Balancing 36 | - Load balance external TCP/UDP traffic with Network Load Balancing 37 | 38 | ## Learn 39 | 40 | This repo is a part of [the Gruntwork Infrastructure as Code Library](https://gruntwork.io/infrastructure-as-code-library/), a collection of reusable, battle-tested, production ready infrastructure code. If you’ve never used the Infrastructure as Code Library before, make sure to read [How to use the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/)! 41 | 42 | ### Core concepts 43 | 44 | - [What is Cloud Load Balancing](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer/core-concepts.md#what-is-cloud-load-balancing) 45 | - [HTTP(S) Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#https-load-balancer-terminology) 46 | - [Internal Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-load-balancer-terminology) 47 | - [Network Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/network-load-balancer/core-concepts.md#network-load-balancer-terminology) 48 | - [Cloud Load Balancing Documentation](https://cloud.google.com/load-balancing/) 49 | 50 | ### Repo organisation 51 | 52 | This repo has the following folder structure: 53 | 54 | - [root](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master): The root folder contains an example of how to deploy a HTTP Load Balancer with multiple backends. See [http-multi-backend example documentation](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples/http-multi-backend) for the documentation. 55 | 56 | - [modules](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules): This folder contains the main implementation code for this Module. 57 | 58 | The primary modules are: 59 | 60 | - [http-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer) is used to create an [HTTP(S) External Load Balancer](https://cloud.google.com/load-balancing/docs/https/). 61 | - [internal-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/internal-load-balancer) is used to create an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/). 62 | - [network-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/network-load-balancer) is used to create an [External TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/network/). 63 | 64 | - [examples](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): This folder contains examples of how to use the submodules. 65 | 66 | - [test](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/test): Automated tests for the submodules and examples. 67 | 68 | ## Deploy 69 | 70 | If you want to try this repo out for experimenting and learning, check out the following resources: 71 | 72 | - [examples folder](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): The `examples` folder contains sample code optimized for learning, experimenting, and testing. 73 | 74 | ## Manage 75 | 76 | ### Day-to-day operations 77 | 78 | - [How to configure a custom domain](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-a-custom-domain) 79 | - [How to configure SSL](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-ssl) 80 | - [How to configure access logging and monitoring](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-access-logging-and-monitoring) 81 | 82 | ## Support 83 | 84 | If you need help with this repo or anything else related to infrastructure or DevOps, Gruntwork offers [Commercial Support](https://gruntwork.io/support/) via Slack, email, and phone/video. If you’re already a Gruntwork customer, hop on Slack and ask away! If not, [subscribe now](https://www.gruntwork.io/pricing/). If you’re not sure, feel free to email us at [support@gruntwork.io](mailto:support@gruntwork.io). 85 | 86 | ## Contributions 87 | 88 | Contributions to this repo are very welcome and appreciated! If you find a bug or want to add a new feature or even contribute an entirely new module, we are very happy to accept pull requests, provide feedback, and run your changes through our automated test suite. 89 | 90 | Please see [Contributing to the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/#contributing-to-the-gruntwork-infrastructure-as-code-library) for instructions. 91 | 92 | ## License 93 | 94 | Please see [LICENSE](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/LICENSE.txt) for details on how the code in this repo is licensed. 95 | 96 | Copyright © 2019 Gruntwork, Inc. 97 | -------------------------------------------------------------------------------- /_docs/cloud-load-balancer-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gruntwork-io/terraform-google-load-balancer/9eaa4e497cd2b42c9f65ff611d17f0f71f678911/_docs/cloud-load-balancer-icon.png -------------------------------------------------------------------------------- /_docs/cloud-load-balancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gruntwork-io/terraform-google-load-balancer/9eaa4e497cd2b42c9f65ff611d17f0f71f678911/_docs/cloud-load-balancer.png -------------------------------------------------------------------------------- /examples/http-multi-backend/README.md: -------------------------------------------------------------------------------- 1 | # HTTP Load Balancer Example 2 | 3 | 6 | 7 | The root folder of this repo shows an example of how to use the [HTTP Load Balancer Module](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer) to create a [HTTP Cloud Load Balancer](https://cloud.google.com/load-balancing/docs/https/) with 8 | 9 | * HTTP listener 10 | * Backend Cloud Storage Bucket with sample files 11 | * Backend Service for an instance group with a single compute instance 12 | 13 | ## How do you run this example? 14 | 15 | To run this example, you need to: 16 | 17 | 1. Install [Terraform](https://www.terraform.io/). 18 | 1. Make sure you are in the root folder of the repo. 19 | 1. Open up `variables.tf` and fill in variables that don't have defaults. 20 | 1. `terraform init`. 21 | 1. `terraform plan`. 22 | 1. If the plan looks good, run `terraform apply`. 23 | 24 | When the templates are applied, Terraform will output the IP address of the load balancer. If you specified a custom domain name, you can connect using that. 25 | 26 | Note that it will take up to 10 minutes for the changes to propagate, so the load balancer and the backends might not be accessible until that. 27 | 28 | -------------------------------------------------------------------------------- /examples/internal-load-balancer/README.md: -------------------------------------------------------------------------------- 1 | # Internal Load Balancer Example 2 | 3 | 6 | 7 | This folder shows an example of how to use the [Internal Load Balancer Module](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer) to create an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/) with 8 | 9 | * Instance Group to route the requests to. The instance group has a single instance running a simple [Flask](http://flask.pocoo.org/) application 10 | on port 5000. 11 | * Compute instance with a public IP address to proxy requests to the load balancer. 12 | 13 | 14 | 15 | ## How do you run this example? 16 | 17 | To run this example, you need to: 18 | 19 | 1. Install [Terraform](https://www.terraform.io/). 20 | 1. Open up `variables.tf` and fill in variables that don't have defaults. 21 | 1. `terraform init`. 22 | 1. `terraform plan`. 23 | 1. If the plan looks good, run `terraform apply`. 24 | 25 | When the templates are applied, Terraform will output the public IP address of the Proxy Compute Instance. 26 | 27 | -------------------------------------------------------------------------------- /examples/internal-load-balancer/main.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # LAUNCH AN INTERNAL LOAD BALANCER WITH REGIONAL INSTANCE GROUP 3 | # 4 | # This is an example of how to use the internal-load-balancer module to deploy an Internal TCP/UDP load balancer 5 | # sending traffic to an instance group. 6 | # 7 | # As the internal load balancer is not accessible from the public internet, we'll create a "proxy" server in the 8 | # public subnet that can relay the calls to the load balancer. 9 | # --------------------------------------------------------------------------------------------------------------------- 10 | 11 | terraform { 12 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 13 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 14 | # forwards compatible with 1.0.x code. 15 | required_version = ">= 0.12.26" 16 | 17 | required_providers { 18 | google = { 19 | source = "hashicorp/google" 20 | version = "~> 3.43.0" 21 | } 22 | google-beta = { 23 | source = "hashicorp/google-beta" 24 | version = "~> 3.43.0" 25 | } 26 | } 27 | } 28 | 29 | # ------------------------------------------------------------------------------ 30 | # CONFIGURE OUR GCP CONNECTION 31 | # ------------------------------------------------------------------------------ 32 | 33 | provider "google" { 34 | region = var.region 35 | project = var.project 36 | } 37 | 38 | provider "google-beta" { 39 | region = var.region 40 | project = var.project 41 | } 42 | 43 | # ------------------------------------------------------------------------------ 44 | # CREATE THE INTERNAL TCP LOAD BALANCER 45 | # ------------------------------------------------------------------------------ 46 | 47 | module "lb" { 48 | # When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you 49 | # to a specific version of the modules, such as the following example: 50 | # source = "github.com/gruntwork-io/terraform-google-load-balancer.git//modules/internal-load-balancer?ref=v0.2.0" 51 | source = "../../modules/internal-load-balancer" 52 | 53 | name = var.name 54 | region = var.region 55 | project = var.project 56 | 57 | backends = [ 58 | { 59 | description = "Instance group for internal-load-balancer test" 60 | group = google_compute_instance_group.api.self_link 61 | }, 62 | ] 63 | 64 | # This setting will enable internal DNS for the load balancer 65 | service_label = var.name 66 | 67 | network = module.vpc_network.network 68 | subnetwork = module.vpc_network.public_subnetwork 69 | 70 | health_check_port = 5000 71 | http_health_check = false 72 | target_tags = [var.name] 73 | source_tags = [var.name] 74 | ports = ["5000"] 75 | 76 | custom_labels = var.custom_labels 77 | } 78 | 79 | # --------------------------------------------------------------------------------------------------------------------- 80 | # CREATE A NETWORK TO DEPLOY THE RESOURCES TO 81 | # --------------------------------------------------------------------------------------------------------------------- 82 | 83 | module "vpc_network" { 84 | source = "github.com/gruntwork-io/terraform-google-network.git//modules/vpc-network?ref=v0.8.2" 85 | 86 | name_prefix = var.name 87 | project = var.project 88 | region = var.region 89 | 90 | cidr_block = "10.1.0.0/16" 91 | secondary_cidr_block = "10.2.0.0/16" 92 | } 93 | 94 | # ------------------------------------------------------------------------------ 95 | # CREATE THE INSTANCE GROUP WITH A SINGLE INSTANCE 96 | # ------------------------------------------------------------------------------ 97 | 98 | resource "google_compute_instance_group" "api" { 99 | project = var.project 100 | name = "${var.name}-instance-group" 101 | zone = var.zone 102 | instances = [google_compute_instance.api.self_link] 103 | 104 | lifecycle { 105 | create_before_destroy = true 106 | } 107 | } 108 | 109 | resource "google_compute_instance" "api" { 110 | project = var.project 111 | name = "${var.name}-api-instance" 112 | machine_type = "f1-micro" 113 | zone = var.zone 114 | 115 | # We're tagging the instance with the tag specified in the firewall rule 116 | tags = [ 117 | var.name, 118 | module.vpc_network.private, 119 | ] 120 | 121 | boot_disk { 122 | initialize_params { 123 | image = "debian-cloud/debian-9" 124 | } 125 | } 126 | 127 | # Make sure we have the api flask application running 128 | metadata_startup_script = file("${path.module}/../shared/startup_script.sh") 129 | 130 | # Launch the instance in the public subnetwork 131 | # For details, see https://github.com/gruntwork-io/terraform-google-network/tree/master/modules/vpc-network#access-tier 132 | network_interface { 133 | network = module.vpc_network.network 134 | subnetwork = module.vpc_network.public_subnetwork 135 | } 136 | } 137 | 138 | # ------------------------------------------------------------------------------ 139 | # CREATE THE PROXY INSTANCE 140 | # ------------------------------------------------------------------------------ 141 | 142 | resource "google_compute_instance" "proxy" { 143 | project = var.project 144 | name = "${var.name}-proxy-instance" 145 | machine_type = "f1-micro" 146 | zone = var.zone 147 | 148 | # We're tagging the instance with the tag specified in the firewall rule 149 | tags = [ 150 | var.name, 151 | module.vpc_network.public, 152 | ] 153 | 154 | boot_disk { 155 | initialize_params { 156 | image = "debian-cloud/debian-9" 157 | } 158 | } 159 | 160 | # Make sure we have the proxy flask application running 161 | metadata_startup_script = data.template_file.proxy_startup_script.rendered 162 | 163 | # Launch the instance in the public subnetwork 164 | # For details, see https://github.com/gruntwork-io/terraform-google-network/tree/master/modules/vpc-network#access-tier 165 | network_interface { 166 | network = module.vpc_network.network 167 | subnetwork = module.vpc_network.public_subnetwork 168 | 169 | access_config { 170 | // Ephemeral IP 171 | } 172 | } 173 | } 174 | 175 | data "template_file" "proxy_startup_script" { 176 | template = file("${path.module}/startup_script.sh") 177 | 178 | # Pass in the internal DNS name and private IP address of the LB 179 | vars = { 180 | ilb_address = module.lb.load_balancer_domain_name 181 | ilb_ip = module.lb.load_balancer_ip_address 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /examples/internal-load-balancer/outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "load_balancer_ip_address" { 6 | description = "Internal IP address of the load balancer" 7 | value = module.lb.load_balancer_ip_address 8 | } 9 | 10 | output "load_balancer_domain_name" { 11 | description = "Internal domain name of the load balancer" 12 | value = module.lb.load_balancer_domain_name 13 | } 14 | 15 | output "proxy_public_ip_address" { 16 | description = "Public IP address of the proxy instance" 17 | value = google_compute_instance.proxy.network_interface[0].access_config[0].nat_ip 18 | } 19 | 20 | -------------------------------------------------------------------------------- /examples/internal-load-balancer/startup_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | 3 | apt-get update 4 | apt-get install -yq build-essential python-pip rsync 5 | pip install flask requests 6 | 7 | mkdir /app 8 | 9 | cat > /app/app.py <<'EOF' 10 | import requests 11 | from flask import Flask 12 | app = Flask(__name__) 13 | 14 | @app.route('/nameproxy') 15 | def hello_nameproxy(): 16 | return requests.get('http://${ilb_address}:5000/api').content 17 | 18 | @app.route('/ipproxy') 19 | def hello_ipproxy(): 20 | return requests.get('http://${ilb_ip}:5000/api').content 21 | 22 | app.run(host='0.0.0.0') 23 | EOF 24 | 25 | python /app/app.py & 26 | 27 | -------------------------------------------------------------------------------- /examples/internal-load-balancer/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "region" { 12 | description = "The region to create the resources in." 13 | type = string 14 | } 15 | 16 | variable "zone" { 17 | description = "The availability zone to create the sample compute instances in. Must within the region specified in 'var.region'" 18 | type = string 19 | } 20 | 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | # OPTIONAL PARAMETERS 23 | # These variables have defaults, but may be overridden by the operator. 24 | # --------------------------------------------------------------------------------------------------------------------- 25 | 26 | variable "name" { 27 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 28 | type = string 29 | default = "ilb-example" 30 | } 31 | 32 | variable "custom_labels" { 33 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 34 | type = map(string) 35 | default = {} 36 | } 37 | 38 | -------------------------------------------------------------------------------- /examples/network-load-balancer/README.md: -------------------------------------------------------------------------------- 1 | # Network Load Balancer Example 2 | 3 | 6 | 7 | This folder shows an example of how to use the [Network Load Balancer Module](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/network-load-balancer) to create a [GCP Network Load Balancer](https://cloud.google.com/load-balancing/docs/network/) routing requests to a Compute Instance running a simple [Flask](http://flask.pocoo.org/) application on port 5000. 8 | 9 | ## How do you run this example? 10 | 11 | To run this example, you need to: 12 | 13 | 1. Install [Terraform](https://www.terraform.io/). 14 | 1. Open up `variables.tf` and fill in variables that don't have defaults. 15 | 1. `terraform init`. 16 | 1. `terraform plan`. 17 | 1. If the plan looks good, run `terraform apply`. 18 | 19 | When the templates are applied, Terraform will output the public IP address of the Network Load Balancer. 20 | 21 | -------------------------------------------------------------------------------- /examples/network-load-balancer/main.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # LAUNCH A NETWORK LOAD BALANCER 3 | # --------------------------------------------------------------------------------------------------------------------- 4 | 5 | terraform { 6 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 7 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 8 | # forwards compatible with 1.0.x code. 9 | required_version = ">= 0.12.26" 10 | 11 | required_providers { 12 | google-beta = { 13 | source = "hashicorp/google-beta" 14 | version = "~> 3.43.0" 15 | } 16 | } 17 | } 18 | 19 | # ------------------------------------------------------------------------------ 20 | # CONFIGURE OUR GCP CONNECTION 21 | # ------------------------------------------------------------------------------ 22 | 23 | provider "google-beta" { 24 | region = var.region 25 | project = var.project 26 | } 27 | 28 | # ------------------------------------------------------------------------------ 29 | # CREATE THE INTERNAL TCP LOAD BALANCER 30 | # ------------------------------------------------------------------------------ 31 | 32 | module "lb" { 33 | # When using these modules in your own templates, you will need to use a Git URL with a ref attribute that pins you 34 | # to a specific version of the modules, such as the following example: 35 | # source = "github.com/gruntwork-io/terraform-google-load-balancer.git//modules/network-load-balancer?ref=v0.2.0" 36 | source = "../../modules/network-load-balancer" 37 | 38 | name = var.name 39 | region = var.region 40 | project = var.project 41 | 42 | enable_health_check = true 43 | health_check_port = "5000" 44 | health_check_path = "/api" 45 | 46 | firewall_target_tags = [var.name] 47 | 48 | instances = [google_compute_instance.api.self_link] 49 | 50 | custom_labels = var.custom_labels 51 | } 52 | 53 | # --------------------------------------------------------------------------------------------------------------------- 54 | # CREATE A COMPUTE INSTANCE TO ROUTE TRAFFIC TO 55 | # --------------------------------------------------------------------------------------------------------------------- 56 | 57 | resource "google_compute_instance" "api" { 58 | project = var.project 59 | name = "${var.name}-api-instance" 60 | machine_type = "f1-micro" 61 | zone = var.zone 62 | 63 | # We're tagging the instance with the tag specified in the firewall rule 64 | tags = [ 65 | var.name, 66 | ] 67 | 68 | boot_disk { 69 | initialize_params { 70 | image = "debian-cloud/debian-9" 71 | } 72 | } 73 | 74 | # Make sure we have the api flask application running 75 | metadata_startup_script = file("${path.module}/../shared/startup_script.sh") 76 | 77 | network_interface { 78 | network = "default" 79 | 80 | # Assign public ip 81 | access_config {} 82 | } 83 | 84 | 85 | } 86 | 87 | # --------------------------------------------------------------------------------------------------------------------- 88 | # CREATE A FIREWALL RULE TO ALLOW TRAFFIC FROM ALL ADDRESSES 89 | # --------------------------------------------------------------------------------------------------------------------- 90 | 91 | resource "google_compute_firewall" "firewall" { 92 | project = var.project 93 | name = "${var.name}-fw" 94 | network = "default" 95 | 96 | allow { 97 | protocol = "tcp" 98 | ports = ["5000"] 99 | } 100 | 101 | # These IP ranges are required for health checks 102 | source_ranges = ["0.0.0.0/0"] 103 | 104 | # Target tags define the instances to which the rule applies 105 | target_tags = [var.name] 106 | } 107 | -------------------------------------------------------------------------------- /examples/network-load-balancer/outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "load_balancer_ip_address" { 6 | description = "Internal IP address of the load balancer" 7 | value = module.lb.load_balancer_ip_address 8 | } 9 | -------------------------------------------------------------------------------- /examples/network-load-balancer/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "region" { 12 | description = "The region to create the resources in." 13 | type = string 14 | } 15 | 16 | variable "zone" { 17 | description = "The GCP zone to create the sample compute instances in. Must within the region specified in 'var.region'" 18 | type = string 19 | } 20 | 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | # OPTIONAL PARAMETERS 23 | # These variables have defaults, but may be overridden by the operator. 24 | # --------------------------------------------------------------------------------------------------------------------- 25 | 26 | variable "name" { 27 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 28 | type = string 29 | default = "ilb-example" 30 | } 31 | 32 | variable "custom_labels" { 33 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 34 | type = map(string) 35 | default = {} 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/shared/startup_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | 3 | apt-get update 4 | apt-get install -yq build-essential python-pip rsync 5 | pip install flask 6 | 7 | mkdir /app 8 | 9 | cat > /app/app.py <<'EOF' 10 | from flask import Flask 11 | app = Flask(__name__) 12 | 13 | @app.route('/api') 14 | def hello_api(): 15 | return 'Hello, api!' 16 | 17 | app.run(host='0.0.0.0') 18 | EOF 19 | 20 | python /app/app.py & 21 | 22 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # LAUNCH A LOAD BALANCER WITH INSTANCE GROUP AND STORAGE BUCKET BACKEND 3 | # 4 | # This is an example of how to use the http-load-balancer module to deploy a HTTP load balancer 5 | # with multiple backends and optionally ssl and custom domain. 6 | # --------------------------------------------------------------------------------------------------------------------- 7 | 8 | terraform { 9 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 10 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 11 | # forwards compatible with 1.0.x code. 12 | required_version = ">= 0.12.26" 13 | 14 | required_providers { 15 | google = { 16 | source = "hashicorp/google" 17 | version = "~> 3.43.0" 18 | } 19 | google-beta = { 20 | source = "hashicorp/google-beta" 21 | version = "~> 3.43.0" 22 | } 23 | } 24 | } 25 | 26 | # ------------------------------------------------------------------------------ 27 | # CONFIGURE OUR GCP CONNECTION 28 | # ------------------------------------------------------------------------------ 29 | 30 | provider "google" { 31 | region = var.region 32 | project = var.project 33 | } 34 | 35 | provider "google-beta" { 36 | region = var.region 37 | project = var.project 38 | } 39 | 40 | # ------------------------------------------------------------------------------ 41 | # CREATE THE LOAD BALANCER 42 | # ------------------------------------------------------------------------------ 43 | 44 | module "lb" { 45 | source = "./modules/http-load-balancer" 46 | name = var.name 47 | project = var.project 48 | url_map = google_compute_url_map.urlmap.self_link 49 | dns_managed_zone_name = var.dns_managed_zone_name 50 | custom_domain_names = [var.custom_domain_name] 51 | create_dns_entries = var.create_dns_entry 52 | dns_record_ttl = var.dns_record_ttl 53 | enable_http = var.enable_http 54 | enable_ssl = var.enable_ssl 55 | ssl_certificates = google_compute_ssl_certificate.certificate.*.self_link 56 | 57 | custom_labels = var.custom_labels 58 | } 59 | 60 | # ------------------------------------------------------------------------------ 61 | # CREATE THE URL MAP TO MAP PATHS TO BACKENDS 62 | # ------------------------------------------------------------------------------ 63 | 64 | resource "google_compute_url_map" "urlmap" { 65 | project = var.project 66 | 67 | name = "${var.name}-url-map" 68 | description = "URL map for ${var.name}" 69 | 70 | default_service = google_compute_backend_bucket.static.self_link 71 | 72 | host_rule { 73 | hosts = ["*"] 74 | path_matcher = "all" 75 | } 76 | 77 | path_matcher { 78 | name = "all" 79 | default_service = google_compute_backend_bucket.static.self_link 80 | 81 | path_rule { 82 | paths = ["/api", "/api/*"] 83 | service = google_compute_backend_service.api.self_link 84 | } 85 | } 86 | } 87 | 88 | # ------------------------------------------------------------------------------ 89 | # CREATE THE BACKEND SERVICE CONFIGURATION FOR THE INSTANCE GROUP 90 | # ------------------------------------------------------------------------------ 91 | 92 | resource "google_compute_backend_service" "api" { 93 | project = var.project 94 | 95 | name = "${var.name}-api" 96 | description = "API Backend for ${var.name}" 97 | port_name = "http" 98 | protocol = "HTTP" 99 | timeout_sec = 10 100 | enable_cdn = false 101 | 102 | backend { 103 | group = google_compute_instance_group.api.self_link 104 | } 105 | 106 | health_checks = [google_compute_health_check.default.self_link] 107 | 108 | depends_on = [google_compute_instance_group.api] 109 | } 110 | 111 | # ------------------------------------------------------------------------------ 112 | # CONFIGURE HEALTH CHECK FOR THE API BACKEND 113 | # ------------------------------------------------------------------------------ 114 | 115 | resource "google_compute_health_check" "default" { 116 | project = var.project 117 | name = "${var.name}-hc" 118 | 119 | http_health_check { 120 | port = 5000 121 | request_path = "/api" 122 | } 123 | 124 | check_interval_sec = 5 125 | timeout_sec = 5 126 | } 127 | 128 | # ------------------------------------------------------------------------------ 129 | # CREATE THE STORAGE BUCKET FOR THE STATIC CONTENT 130 | # ------------------------------------------------------------------------------ 131 | 132 | resource "google_storage_bucket" "static" { 133 | project = var.project 134 | 135 | name = "${var.name}-bucket" 136 | location = var.static_content_bucket_location 137 | storage_class = "MULTI_REGIONAL" 138 | 139 | website { 140 | main_page_suffix = "index.html" 141 | not_found_page = "404.html" 142 | } 143 | 144 | # For the example, we want to clean up all resources. In production, you should set this to false to prevent 145 | # accidental loss of data 146 | force_destroy = true 147 | 148 | labels = var.custom_labels 149 | } 150 | 151 | # ------------------------------------------------------------------------------ 152 | # CREATE THE BACKEND FOR THE STORAGE BUCKET 153 | # ------------------------------------------------------------------------------ 154 | 155 | resource "google_compute_backend_bucket" "static" { 156 | project = var.project 157 | 158 | name = "${var.name}-backend-bucket" 159 | bucket_name = google_storage_bucket.static.name 160 | } 161 | 162 | # ------------------------------------------------------------------------------ 163 | # UPLOAD SAMPLE CONTENT WITH PUBLIC READ ACCESS 164 | # ------------------------------------------------------------------------------ 165 | 166 | resource "google_storage_default_object_acl" "website_acl" { 167 | bucket = google_storage_bucket.static.name 168 | role_entity = ["READER:allUsers"] 169 | } 170 | 171 | resource "google_storage_bucket_object" "index" { 172 | name = "index.html" 173 | content = "Hello, World!" 174 | bucket = google_storage_bucket.static.name 175 | 176 | # We have to depend on the ACL because otherwise the ACL could get created after the object 177 | depends_on = [google_storage_default_object_acl.website_acl] 178 | } 179 | 180 | resource "google_storage_bucket_object" "not_found" { 181 | name = "404.html" 182 | content = "Uh oh" 183 | bucket = google_storage_bucket.static.name 184 | 185 | # We have to depend on the ACL because otherwise the ACL could get created after the object 186 | depends_on = [google_storage_default_object_acl.website_acl] 187 | } 188 | 189 | # ------------------------------------------------------------------------------ 190 | # IF SSL IS ENABLED, CREATE A SELF-SIGNED CERTIFICATE 191 | # ------------------------------------------------------------------------------ 192 | 193 | resource "tls_self_signed_cert" "cert" { 194 | # Only create if SSL is enabled 195 | count = var.enable_ssl ? 1 : 0 196 | 197 | key_algorithm = "RSA" 198 | private_key_pem = join("", tls_private_key.private_key.*.private_key_pem) 199 | 200 | subject { 201 | common_name = var.custom_domain_name 202 | organization = "Examples, Inc" 203 | } 204 | 205 | validity_period_hours = 12 206 | 207 | allowed_uses = [ 208 | "key_encipherment", 209 | "digital_signature", 210 | "server_auth", 211 | ] 212 | } 213 | 214 | resource "tls_private_key" "private_key" { 215 | count = var.enable_ssl ? 1 : 0 216 | algorithm = "RSA" 217 | ecdsa_curve = "P256" 218 | } 219 | 220 | # ------------------------------------------------------------------------------ 221 | # CREATE A CORRESPONDING GOOGLE CERTIFICATE THAT WE CAN ATTACH TO THE LOAD BALANCER 222 | # ------------------------------------------------------------------------------ 223 | 224 | resource "google_compute_ssl_certificate" "certificate" { 225 | project = var.project 226 | 227 | count = var.enable_ssl ? 1 : 0 228 | 229 | name_prefix = var.name 230 | description = "SSL Certificate" 231 | private_key = join("", tls_private_key.private_key.*.private_key_pem) 232 | certificate = join("", tls_self_signed_cert.cert.*.cert_pem) 233 | 234 | lifecycle { 235 | create_before_destroy = true 236 | } 237 | } 238 | 239 | # ------------------------------------------------------------------------------ 240 | # CREATE THE INSTANCE GROUP WITH A SINGLE INSTANCE AND THE BACKEND SERVICE CONFIGURATION 241 | # 242 | # We use the instance group only to highlight the ability to specify multiple types 243 | # of backends for the load balancer 244 | # ------------------------------------------------------------------------------ 245 | 246 | resource "google_compute_instance_group" "api" { 247 | project = var.project 248 | name = "${var.name}-instance-group" 249 | zone = var.zone 250 | instances = [google_compute_instance.api.self_link] 251 | 252 | lifecycle { 253 | create_before_destroy = true 254 | } 255 | 256 | named_port { 257 | name = "http" 258 | port = 5000 259 | } 260 | } 261 | 262 | resource "google_compute_instance" "api" { 263 | project = var.project 264 | name = "${var.name}-instance" 265 | machine_type = "f1-micro" 266 | zone = var.zone 267 | 268 | # We're tagging the instance with the tag specified in the firewall rule 269 | tags = ["private-app"] 270 | 271 | boot_disk { 272 | initialize_params { 273 | image = "debian-cloud/debian-9" 274 | } 275 | } 276 | 277 | # Make sure we have the flask application running 278 | metadata_startup_script = file("${path.module}/examples/shared/startup_script.sh") 279 | 280 | # Launch the instance in the default subnetwork 281 | network_interface { 282 | subnetwork = "default" 283 | 284 | # This gives the instance a public IP address for internet connectivity. Normally, you would have a Cloud NAT, 285 | # but for the sake of simplicity, we're assigning a public IP to get internet connectivity 286 | # to be able to run startup scripts 287 | access_config { 288 | } 289 | } 290 | } 291 | 292 | # ------------------------------------------------------------------------------ 293 | # CREATE A FIREWALL TO ALLOW ACCESS FROM THE LB TO THE INSTANCE 294 | # ------------------------------------------------------------------------------ 295 | 296 | resource "google_compute_firewall" "firewall" { 297 | project = var.project 298 | name = "${var.name}-fw" 299 | network = "default" 300 | 301 | # Allow load balancer access to the API instances 302 | # https://cloud.google.com/load-balancing/docs/https/#firewall_rules 303 | source_ranges = ["130.211.0.0/22", "35.191.0.0/16"] 304 | 305 | target_tags = ["private-app"] 306 | source_tags = ["private-app"] 307 | 308 | allow { 309 | protocol = "tcp" 310 | ports = ["5000"] 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /modules/http-load-balancer/README.md: -------------------------------------------------------------------------------- 1 | # HTTP(S) Load Balancer Module 2 | 3 | [![Maintained by Gruntwork.io](https://img.shields.io/badge/maintained%20by-gruntwork.io-%235849a6.svg)](https://gruntwork.io/?ref=repo_google_load_balancer) 4 | [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gruntwork-io/terraform-google-load-balancer.svg?label=latest)](https://github.com/gruntwork-io/terraform-google-load-balancer/releases/latest) 5 | ![Terraform Version](https://img.shields.io/badge/tf-%3E%3D1.0.x-blue.svg) 6 | 7 | This Terraform Module creates an [HTTP(S) Cloud Load Balancer](https://cloud.google.com/load-balancing/docs/https/) using [global forwarding rules](https://cloud.google.com/load-balancing/docs/https/global-forwarding-rules). 8 | 9 | HTTP(S) load balancing can balance HTTP and HTTPS traffic across multiple backend instances, across multiple regions. Your entire app is available via a single global IP address, resulting in a simplified DNS setup. HTTP(S) load balancing is scalable, fault-tolerant, requires no pre-warming, and enables content-based load balancing. 10 | 11 | ## Learn 12 | 13 | This repo is a part of [the Gruntwork Infrastructure as Code Library](https://gruntwork.io/infrastructure-as-code-library/), a collection of reusable, battle-tested, production ready infrastructure code. If you’ve never used the Infrastructure as Code Library before, make sure to read [How to use the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/)! 14 | 15 | ### Core concepts 16 | 17 | - [What is Cloud Load Balancing](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer/core-concepts.md#what-is-cloud-load-balancing) 18 | - [HTTP(S) Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#https-load-balancer-terminology) 19 | - [Cloud Load Balancing Documentation](https://cloud.google.com/load-balancing/) 20 | 21 | ### Repo organisation 22 | 23 | This repo has the following folder structure: 24 | 25 | * [root](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master): The root folder contains an example of how to deploy a HTTP Load Balancer with multiple backends. See [http-multi-backend example documentation](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples/http-multi-backend) for the documentation. 26 | 27 | * [modules](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules): This folder contains the main implementation code for this Module. 28 | 29 | The primary modules are: 30 | 31 | * [http-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer) is used to create an [HTTP(S) External Load Balancer](https://cloud.google.com/load-balancing/docs/https/). 32 | * [internal-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/internal-load-balancer) is used to create an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/). 33 | * [network-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/network-load-balancer) is used to create an [External TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/network/). 34 | 35 | * [examples](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): This folder contains examples of how to use the submodules. 36 | 37 | * [test](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/test): Automated tests for the submodules and examples. 38 | 39 | ## Deploy 40 | 41 | If you want to try this repo out for experimenting and learning, check out the following resources: 42 | 43 | - [examples folder](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): The `examples` folder contains sample code optimized for learning, experimenting, and testing. 44 | 45 | ## Manage 46 | 47 | ### Day-to-day operations 48 | 49 | - [How to configure a custom domain](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-a-custom-domain) 50 | - [How to configure SSL](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-ssl) 51 | - [How to configure access logging and monitoring](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/http-load-balancer/core-concepts.md#how-do-you-configure-access-logging-and-monitoring) 52 | 53 | ## Support 54 | 55 | If you need help with this repo or anything else related to infrastructure or DevOps, Gruntwork offers [Commercial Support](https://gruntwork.io/support/) via Slack, email, and phone/video. If you’re already a Gruntwork customer, hop on Slack and ask away! If not, [subscribe now](https://www.gruntwork.io/pricing/). If you’re not sure, feel free to email us at [support@gruntwork.io](mailto:support@gruntwork.io). 56 | 57 | ## Contributions 58 | 59 | Contributions to this repo are very welcome and appreciated! If you find a bug or want to add a new feature or even contribute an entirely new module, we are very happy to accept pull requests, provide feedback, and run your changes through our automated test suite. 60 | 61 | Please see [Contributing to the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/#contributing-to-the-gruntwork-infrastructure-as-code-library) for instructions. 62 | 63 | ## License 64 | 65 | Please see [LICENSE](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/LICENSE.txt) for details on how the code in this repo is licensed. 66 | 67 | Copyright © 2019 Gruntwork, Inc. 68 | -------------------------------------------------------------------------------- /modules/http-load-balancer/core-concepts.md: -------------------------------------------------------------------------------- 1 | # HTTP(S) Load Balancer Core Concepts 2 | 3 | ## What is Cloud Load Balancing? 4 | 5 | [Cloud Load Balancing](https://cloud.google.com/load-balancing/) is a fully distributed, software-defined, managed service for all your traffic. It is not an instance or device based solution, so you won’t be locked into physical load balancing infrastructure or face the HA, scale and management challenges inherent in instance based LBs. Cloud Load Balancing features include: 6 | 7 | * **HTTP(S) Load Balancing:** HTTP(S) load balancing can balance HTTP and HTTPS traffic across multiple backend instances, across multiple regions. 8 | * **Network TCP/UDP Load Balancing:** load balance external traffic. 9 | * **Internal TCP/UDP Load Balancing:** load balance internal traffic. 10 | * **Seamless Autoscaling:** Autoscaling helps your applications gracefully handle increases in traffic and reduces cost when the need for resources is lower. 11 | * **Cloud CDN Integration:** Enabling [Cloud CDN](https://cloud.google.com/cdn/) for HTTP(S) Load Balancing for optimizing application delivery for your users. 12 | * **Stackdriver Logging:** Stackdriver Logging for load balancing logs all the load balancing requests sent to your load balancer. 13 | 14 | You can learn more about Cloud Load Balancing in [the official documentation](https://cloud.google.com/load-balancing/docs/). 15 | 16 | ## HTTP(S) Load Balancer Terminology 17 | 18 | GCP uses non-standard vocabulary for load balancing concepts. In case you're unfamiliar with load balancing on GCP, here's a short guide: 19 | 20 | - **[Global forwarding rules](https://cloud.google.com/load-balancing/docs/https/global-forwarding-rules)** route traffic by IP address, port, and protocol to a load balancing configuration consisting of a target proxy, URL map, and one or more backend services. 21 | - **[Target proxies](https://cloud.google.com/load-balancing/docs/target-proxies)** terminate HTTP(S) connections from clients. One or more global forwarding rules direct traffic to the target proxy, and the target proxy consults the URL map to determine how to route traffic to backends. 22 | - **[URL maps](https://cloud.google.com/load-balancing/docs/https/url-map)** define matching patterns for URL-based routing of requests to the appropriate backend services. A default service is defined to handle any requests that do not match a specified host rule or path matching rule. 23 | - **Backends** are resources to which a GCP load balancer distributes traffic. These include [backend services](https://cloud.google.com/load-balancing/docs/backend-service), such as [instance groups](https://cloud.google.com/compute/docs/instance-groups/) or [backend buckets](https://cloud.google.com/load-balancing/docs/backend-bucket). 24 | 25 | ## How Do You Configure a Custom Domain? 26 | 27 | You can optionally configure a custom domain with input variables `create_dns_entries` and `custom_domain_names`. 28 | 29 | This will create an A records for each domain provided in `custom_domain_names` pointing to the Load Balancer's public IP address. Note that you will also have to provide a managed zone name with `dns_managed_zone_name` variable. 30 | 31 | ## How Do You Configure SSL? 32 | 33 | You can enable SSL with the input variable `enable_ssl`. 34 | 35 | To use HTTPS or SSL load balancing, you must associate at least one SSL certificate with the load balancer's target proxy using the `ssl_certificates` input variable. You can configure the target proxy with up to ten SSL certificates. 36 | 37 | For HTTP(S) Proxy Load Balancing, *Google encrypts traffic between the load balancer and backend instances.* SSL certificate resources *are not required* on individual VM instances. 38 | 39 | ### Using Self-managed SSL certificates 40 | 41 | To use self-managed SSL certificates, you must have an existing [SSL certificate resource](https://cloud.google.com/compute/docs/reference/v1/sslCertificates). You can pass the certificate self links using the `ssl_certificates` input variable. 42 | 43 | ## How Do You Configure Access Logging and Monitoring? 44 | 45 | **NOTE:** This is part of Alpha release of GCP HTTP(S) Load Balancing Logging. For full details, see the [official documentation](https://cloud.google.com/load-balancing/docs/https/https-logging-monitoring). 46 | 47 | ### Access logs with a Google Cloud Storage Bucket backend 48 | 49 | If you intend to use the load balancer for serving a static site using a [Google Cloud Storage](https://cloud.google.com/storage/) bucket backend, you can optionally configure [access logging](https://cloud.google.com/storage/docs/access-logs) for your bucket. 50 | 51 | ### How to view logs 52 | 53 | Each HTTP(S) request is logged temporarily via [Stackdriver Logging](https://cloud.google.com/logging/docs/). During the Alpha testing phase, logging is automatic and does not need to be enabled. To view logs, go to the [Logs Viewer](https://console.cloud.google.com/logs). 54 | 55 | ### What is logged 56 | 57 | HTTP(S) Load Balancing log entries contain information useful for monitoring and debugging your HTTP(S) traffic. Log entries contain the following types of information: 58 | 59 | - General information shown in most GCP logs, such as severity, project ID, project number, timestamp, and so on. 60 | - [HttpRequest](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#HttpRequest) log fields. However, `HttpRequest.protocol` and `HttpRequest.latency` are not populated for HTTP(S) Load Balancing Stackdriver logs. 61 | - a `statusDetails` field inside the `structPayload`. This field holds a string that explains why the load balancer returned the HTTP status that it did. 62 | 63 | ### Monitoring 64 | 65 | HTTP(S) Load Balancing exports monitoring data to [Stackdriver](https://cloud.google.com/monitoring/docs/). 66 | 67 | Monitoring metrics can be used for the following purposes: 68 | 69 | - Evaluate a load balancer's configuration, usage, and performance 70 | - Troubleshoot problems 71 | - Improve resource utilization and user experience 72 | 73 | In addition to the predefined dashboards in Stackdriver, you can create custom dashboards, set up alerts, and query the metrics through the [Stackdriver monitoring API](https://cloud.google.com/monitoring/api/). 74 | 75 | -------------------------------------------------------------------------------- /modules/http-load-balancer/main.tf: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # DEPLOY A HTTP LOAD BALANCER 3 | # This module deploys a HTTP(S) Cloud Load Balancer 4 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | terraform { 7 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 8 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 9 | # forwards compatible with 1.0.x code. 10 | required_version = ">= 0.12.26" 11 | } 12 | 13 | # ------------------------------------------------------------------------------ 14 | # CREATE A PUBLIC IP ADDRESS 15 | # ------------------------------------------------------------------------------ 16 | 17 | resource "google_compute_global_address" "default" { 18 | project = var.project 19 | name = "${var.name}-address" 20 | ip_version = "IPV4" 21 | address_type = "EXTERNAL" 22 | } 23 | 24 | # ------------------------------------------------------------------------------ 25 | # IF PLAIN HTTP ENABLED, CREATE FORWARDING RULE AND PROXY 26 | # ------------------------------------------------------------------------------ 27 | 28 | resource "google_compute_target_http_proxy" "http" { 29 | count = var.enable_http ? 1 : 0 30 | project = var.project 31 | name = "${var.name}-http-proxy" 32 | url_map = var.url_map 33 | } 34 | 35 | resource "google_compute_global_forwarding_rule" "http" { 36 | provider = google-beta 37 | count = var.enable_http ? 1 : 0 38 | project = var.project 39 | name = "${var.name}-http-rule" 40 | target = google_compute_target_http_proxy.http[0].self_link 41 | ip_address = google_compute_global_address.default.address 42 | port_range = "80" 43 | 44 | depends_on = [google_compute_global_address.default] 45 | 46 | labels = var.custom_labels 47 | } 48 | 49 | # ------------------------------------------------------------------------------ 50 | # IF SSL ENABLED, CREATE FORWARDING RULE AND PROXY 51 | # ------------------------------------------------------------------------------ 52 | 53 | resource "google_compute_global_forwarding_rule" "https" { 54 | provider = google-beta 55 | project = var.project 56 | count = var.enable_ssl ? 1 : 0 57 | name = "${var.name}-https-rule" 58 | target = google_compute_target_https_proxy.default[0].self_link 59 | ip_address = google_compute_global_address.default.address 60 | port_range = "443" 61 | depends_on = [google_compute_global_address.default] 62 | 63 | labels = var.custom_labels 64 | } 65 | 66 | resource "google_compute_target_https_proxy" "default" { 67 | project = var.project 68 | count = var.enable_ssl ? 1 : 0 69 | name = "${var.name}-https-proxy" 70 | url_map = var.url_map 71 | 72 | ssl_certificates = var.ssl_certificates 73 | } 74 | 75 | # ------------------------------------------------------------------------------ 76 | # IF DNS ENTRY REQUESTED, CREATE A RECORD POINTING TO THE PUBLIC IP OF THE CLB 77 | # ------------------------------------------------------------------------------ 78 | 79 | resource "google_dns_record_set" "dns" { 80 | project = var.project 81 | count = var.create_dns_entries ? length(var.custom_domain_names) : 0 82 | 83 | name = "${element(var.custom_domain_names, count.index)}." 84 | type = "A" 85 | ttl = var.dns_record_ttl 86 | 87 | managed_zone = var.dns_managed_zone_name 88 | 89 | rrdatas = [google_compute_global_address.default.address] 90 | } 91 | -------------------------------------------------------------------------------- /modules/http-load-balancer/outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "load_balancer_ip_address" { 6 | description = "IP address of the Cloud Load Balancer" 7 | value = google_compute_global_address.default.address 8 | } 9 | 10 | -------------------------------------------------------------------------------- /modules/http-load-balancer/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "name" { 12 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 13 | type = string 14 | } 15 | 16 | variable "url_map" { 17 | description = "A reference (self_link) to the url_map resource to use." 18 | type = string 19 | } 20 | 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | # OPTIONAL MODULE PARAMETERS 23 | # These variables have defaults, but may be overridden by the operator. 24 | # --------------------------------------------------------------------------------------------------------------------- 25 | variable "enable_ssl" { 26 | description = "Set to true to enable ssl. If set to 'true', you will also have to provide 'var.ssl_certificates'." 27 | type = bool 28 | default = false 29 | } 30 | 31 | variable "ssl_certificates" { 32 | description = "List of SSL cert self links. Required if 'enable_ssl' is 'true'." 33 | type = list(string) 34 | default = [] 35 | } 36 | 37 | variable "enable_http" { 38 | description = "Set to true to enable plain http. Note that disabling http does not force SSL and/or redirect HTTP traffic. See https://issuetracker.google.com/issues/35904733" 39 | type = bool 40 | default = true 41 | } 42 | 43 | variable "create_dns_entries" { 44 | description = "If set to true, create a DNS A Record in Cloud DNS for each domain specified in 'custom_domain_names'." 45 | type = bool 46 | default = false 47 | } 48 | 49 | variable "custom_domain_names" { 50 | description = "List of custom domain names." 51 | type = list(string) 52 | default = [] 53 | } 54 | 55 | variable "dns_managed_zone_name" { 56 | description = "The name of the Cloud DNS Managed Zone in which to create the DNS A Records specified in 'var.custom_domain_names'. Only used if 'var.create_dns_entries' is true." 57 | type = string 58 | default = "replace-me" 59 | } 60 | 61 | variable "dns_record_ttl" { 62 | description = "The time-to-live for the site A records (seconds)" 63 | type = number 64 | default = 300 65 | } 66 | 67 | variable "custom_labels" { 68 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 69 | type = map(string) 70 | default = {} 71 | } 72 | 73 | -------------------------------------------------------------------------------- /modules/internal-load-balancer/README.md: -------------------------------------------------------------------------------- 1 | # Internal Load Balancer Module 2 | 3 | [![Maintained by Gruntwork.io](https://img.shields.io/badge/maintained%20by-gruntwork.io-%235849a6.svg)](https://gruntwork.io/?ref=repo_google_load_balancer) 4 | [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gruntwork-io/terraform-google-load-balancer.svg?label=latest)](https://github.com/gruntwork-io/terraform-google-load-balancer/releases/latest) 5 | ![Terraform Version](https://img.shields.io/badge/tf-%3E%3D1.0.x-blue.svg) 6 | 7 | This Terraform Module creates an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/) using [internal forwarding rules](https://cloud.google.com/load-balancing/docs/internal/#forwarding_rule). 8 | 9 | Google Cloud Platform (GCP) Internal TCP/UDP Load Balancing distributes traffic among VM instances in the same region in a VPC network using a private, internal (RFC 1918) IP address. 10 | 11 | ## Learn 12 | 13 | This repo is a part of [the Gruntwork Infrastructure as Code Library](https://gruntwork.io/infrastructure-as-code-library/), a collection of reusable, battle-tested, production ready infrastructure code. If you’ve never used the Infrastructure as Code Library before, make sure to read [How to use the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/)! 14 | 15 | ### Core concepts 16 | 17 | - [What is Cloud Load Balancing](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/internal-load-balancer/core-concepts.md#what-is-cloud-load-balancing) 18 | - [Internal Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-tcpudp-load-balancer-terminology) 19 | - [Internal Load Balancing Documentation](https://cloud.google.com/load-balancing/docs/internal/) 20 | 21 | ### Repo organisation 22 | 23 | This repo has the following folder structure: 24 | 25 | * [root](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master): The root folder contains an example of how to deploy a HTTP Load Balancer with multiple backends. See [http-multi-backend example documentation](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples/http-multi-backend) for the documentation. 26 | 27 | * [modules](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules): This folder contains the main implementation code for this Module. 28 | 29 | The primary modules are: 30 | 31 | * [http-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer) is used to create an [HTTP(S) External Load Balancer](https://cloud.google.com/load-balancing/docs/https/). 32 | * [internal-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/internal-load-balancer) is used to create an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/). 33 | * [network-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/network-load-balancer) is used to create an [External TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/network/). 34 | 35 | * [examples](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): This folder contains examples of how to use the submodules. 36 | 37 | * [test](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/test): Automated tests for the submodules and examples. 38 | 39 | ## Deploy 40 | 41 | If you want to try this repo out for experimenting and learning, check out the following resources: 42 | 43 | - [examples folder](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): The `examples` folder contains sample code optimized for learning, experimenting, and testing. 44 | 45 | ## Manage 46 | 47 | ### Day-to-day operations 48 | 49 | - [Internal Load Balancer access logging and monitoring](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-tcpudp-load-balancing-monitoring) 50 | - [Internal Load Balancer DNS names](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-tcpudp-load-balancing-and-dns-names) 51 | 52 | ## Support 53 | 54 | If you need help with this repo or anything else related to infrastructure or DevOps, Gruntwork offers [Commercial Support](https://gruntwork.io/support/) via Slack, email, and phone/video. If you’re already a Gruntwork customer, hop on Slack and ask away! If not, [subscribe now](https://www.gruntwork.io/pricing/). If you’re not sure, feel free to email us at [support@gruntwork.io](mailto:support@gruntwork.io). 55 | 56 | ## Contributions 57 | 58 | Contributions to this repo are very welcome and appreciated! If you find a bug or want to add a new feature or even contribute an entirely new module, we are very happy to accept pull requests, provide feedback, and run your changes through our automated test suite. 59 | 60 | Please see [Contributing to the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/#contributing-to-the-gruntwork-infrastructure-as-code-library) for instructions. 61 | 62 | ## License 63 | 64 | Please see [LICENSE](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/LICENSE.txt) for details on how the code in this repo is licensed. 65 | 66 | Copyright © 2019 Gruntwork, Inc. 67 | -------------------------------------------------------------------------------- /modules/internal-load-balancer/core-concepts.md: -------------------------------------------------------------------------------- 1 | # Internal Load Balancer Core Concepts 2 | 3 | ## What is Cloud Load Balancing? 4 | 5 | [Cloud Load Balancing](https://cloud.google.com/load-balancing/) is a fully distributed, software-defined, managed service for all your traffic. It is not an instance or device based solution, so you won’t be locked into physical load balancing infrastructure or face the HA, scale and management challenges inherent in instance based LBs. Cloud Load Balancing features include: 6 | 7 | * **HTTP(S) Load Balancing:** HTTP(S) load balancing can balance HTTP and HTTPS traffic across multiple backend instances, across multiple regions. 8 | * **Network TCP/UDP Load Balancing:** load balance external traffic. 9 | * **Internal TCP/UDP Load Balancing:** load balance internal traffic. 10 | * **Seamless Autoscaling:** Autoscaling helps your applications gracefully handle increases in traffic and reduces cost when the need for resources is lower. 11 | * **Cloud CDN Integration:** Enabling [Cloud CDN](https://cloud.google.com/cdn/) for HTTP(S) Load Balancing for optimizing application delivery for your users. 12 | * **Stackdriver Logging:** Stackdriver Logging for load balancing logs all the load balancing requests sent to your load balancer. 13 | 14 | You can learn more about Cloud Load Balancing in [the official documentation](https://cloud.google.com/load-balancing/docs/). 15 | 16 | ## Internal TCP/UDP Load Balancer Terminology 17 | 18 | GCP uses non-standard vocabulary for load balancing concepts. In case you're unfamiliar with load balancing on GCP, here's a short guide: 19 | 20 | - **[Internal IP address](https://cloud.google.com/load-balancing/docs/internal/#load_balancing_ip_address)** is the address for the load balancer. The internal IP address must be in the same subnet as the internal forwarding rule. The subnet must be in the same region as the backend service. 21 | - **[Internal forwarding rules](https://cloud.google.com/load-balancing/docs/https/global-forwarding-rules)** in combination with the internal IP address is the frontend of the load balancer. It defines the protocol and port(s) that the load balancer accepts, and it directs traffic to a regional internal backend service. 22 | - **[Regional internal backend service](https://cloud.google.com/load-balancing/docs/internal/#backend_service)** defines the protocol used to communicate with the backends (instance groups), and it specifies a health check. Backends can be unmanaged instance groups, managed zonal instance groups, or managed regional instance groups. 23 | - **[Health check](https://cloud.google.com/load-balancing/docs/internal/#health-checking)** defines the parameters under which GCP considers the backends it manages to be eligible to receive traffic. Only healthy VMs in the backend instance groups will receive traffic sent from client VMs to the IP address of the load balancer. 24 | 25 | ## Internal TCP/UDP Load Balancing monitoring 26 | 27 | Internal TCP/UDP Load Balancing exports monitoring data to [Stackdriver](https://cloud.google.com/monitoring/docs/). 28 | 29 | ## Internal TCP/UDP Load Balancing and DNS Names 30 | 31 | A DNS address record (A record) is used to map a DNS name to an IP address. When you configure an internal TCP/UDP load balancer, you can optionally designate a service label for GCP to create a Compute Engine internal DNS name for the load balancer. This internal DNS name is constructed from your project ID, internal forwarding rule name, and a service label you choose. 32 | 33 | See [DNS record format](https://cloud.google.com/load-balancing/docs/internal/dns-names#a_record_format) for details about the format of the DNS name that GCP creates for your load balancer. 34 | -------------------------------------------------------------------------------- /modules/internal-load-balancer/main.tf: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # DEPLOY AN INTERNAL LOAD BALANCER 3 | # This module deploys an Internal TCP/UDP Load Balancer 4 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | terraform { 7 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 8 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 9 | # forwards compatible with 1.0.x code. 10 | required_version = ">= 0.12.26" 11 | } 12 | 13 | # ------------------------------------------------------------------------------ 14 | # CREATE FORWARDING RULE 15 | # ------------------------------------------------------------------------------ 16 | 17 | resource "google_compute_forwarding_rule" "default" { 18 | provider = google-beta 19 | project = var.project 20 | name = var.name 21 | region = var.region 22 | network = var.network == "" ? "default" : var.network 23 | subnetwork = var.subnetwork 24 | load_balancing_scheme = "INTERNAL" 25 | backend_service = google_compute_region_backend_service.default.self_link 26 | ip_address = var.ip_address 27 | ip_protocol = var.protocol 28 | ports = var.ports 29 | 30 | # If service label is specified, it will be the first label of the fully qualified service name. 31 | # Due to the provider failing with an empty string, we're setting the name as service label default 32 | service_label = var.service_label == "" ? var.name : var.service_label 33 | 34 | # This is a beta feature 35 | labels = var.custom_labels 36 | } 37 | 38 | # ------------------------------------------------------------------------------ 39 | # CREATE BACKEND SERVICE 40 | # ------------------------------------------------------------------------------ 41 | 42 | resource "google_compute_region_backend_service" "default" { 43 | project = var.project 44 | name = var.name 45 | region = var.region 46 | protocol = var.protocol 47 | timeout_sec = 10 48 | session_affinity = var.session_affinity 49 | 50 | dynamic "backend" { 51 | for_each = var.backends 52 | content { 53 | description = lookup(backend.value, "description", null) 54 | group = lookup(backend.value, "group", null) 55 | } 56 | } 57 | 58 | health_checks = [ 59 | compact( 60 | concat( 61 | google_compute_health_check.tcp.*.self_link, 62 | google_compute_health_check.http.*.self_link 63 | ) 64 | )[0]] 65 | } 66 | 67 | # ------------------------------------------------------------------------------ 68 | # CREATE HEALTH CHECK - ONE OF ´http´ OR ´tcp´ 69 | # ------------------------------------------------------------------------------ 70 | 71 | resource "google_compute_health_check" "tcp" { 72 | count = var.http_health_check ? 0 : 1 73 | 74 | project = var.project 75 | name = "${var.name}-hc" 76 | 77 | tcp_health_check { 78 | port = var.health_check_port 79 | } 80 | } 81 | 82 | resource "google_compute_health_check" "http" { 83 | count = var.http_health_check ? 1 : 0 84 | 85 | project = var.project 86 | name = "${var.name}-hc" 87 | 88 | http_health_check { 89 | port = var.health_check_port 90 | } 91 | } 92 | 93 | # ------------------------------------------------------------------------------ 94 | # CREATE FIREWALLS FOR THE LOAD BALANCER AND HEALTH CHECKS 95 | # ------------------------------------------------------------------------------ 96 | 97 | # Load balancer firewall allows ingress traffic from instances tagged with any of the ´var.source_tags´ 98 | resource "google_compute_firewall" "load_balancer" { 99 | project = var.network_project == "" ? var.project : var.network_project 100 | name = "${var.name}-ilb-fw" 101 | network = var.network 102 | 103 | allow { 104 | protocol = lower(var.protocol) 105 | ports = var.ports 106 | } 107 | 108 | # Source tags defines a source of traffic as coming from the primary internal IP address 109 | # of any instance having a matching network tag. 110 | source_tags = var.source_tags 111 | 112 | # Target tags define the instances to which the rule applies 113 | target_tags = var.target_tags 114 | } 115 | 116 | # Health check firewall allows ingress tcp traffic from the health check IP addresses 117 | resource "google_compute_firewall" "health_check" { 118 | project = var.network_project == "" ? var.project : var.network_project 119 | name = "${var.name}-hc" 120 | network = var.network 121 | 122 | allow { 123 | protocol = "tcp" 124 | ports = [var.health_check_port] 125 | } 126 | 127 | # These IP ranges are required for health checks 128 | source_ranges = ["130.211.0.0/22", "35.191.0.0/16"] 129 | 130 | # Target tags define the instances to which the rule applies 131 | target_tags = var.target_tags 132 | } 133 | -------------------------------------------------------------------------------- /modules/internal-load-balancer/outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "forwarding_rule" { 6 | description = "Self link of the forwarding rule (Load Balancer)" 7 | value = google_compute_forwarding_rule.default.self_link 8 | } 9 | 10 | output "load_balancer_ip_address" { 11 | description = "IP address of the Load Balancer" 12 | value = google_compute_forwarding_rule.default.ip_address 13 | } 14 | 15 | output "load_balancer_domain_name" { 16 | description = "The internal fully qualified service name for this Load Balancer" 17 | value = google_compute_forwarding_rule.default.service_name 18 | } 19 | 20 | output "load_balancer_firewall" { 21 | description = "Self link of the load balancer firewall rule." 22 | value = google_compute_firewall.load_balancer.self_link 23 | } 24 | 25 | output "health_check_firewall" { 26 | description = "Self link of the health check firewall rule." 27 | value = google_compute_firewall.health_check.self_link 28 | } 29 | 30 | output "backend_service" { 31 | description = "Self link of the regional backend service." 32 | value = google_compute_region_backend_service.default.self_link 33 | } 34 | 35 | -------------------------------------------------------------------------------- /modules/internal-load-balancer/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "region" { 12 | description = "All resources will be launched in this region." 13 | type = string 14 | } 15 | 16 | variable "name" { 17 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 18 | type = string 19 | } 20 | 21 | variable "ports" { 22 | description = "List of ports (or port ranges) to forward to backend services. Max is 5." 23 | type = list(string) 24 | } 25 | 26 | variable "health_check_port" { 27 | description = "Port to perform health checks on." 28 | type = number 29 | } 30 | 31 | variable "backends" { 32 | description = "List of backends, should be a map of key-value pairs for each backend, must have the 'group' key." 33 | type = list(map(string)) 34 | # Example 35 | # backends = [ 36 | # { 37 | # description = "Sample Instance Group for Internal LB", 38 | # group = "The fully-qualified URL of an Instance Group" 39 | # } 40 | # ] 41 | } 42 | 43 | # --------------------------------------------------------------------------------------------------------------------- 44 | # OPTIONAL MODULE PARAMETERS 45 | # These variables have defaults, but may be overridden by the operator. 46 | # --------------------------------------------------------------------------------------------------------------------- 47 | 48 | variable "network" { 49 | description = "Self link of the VPC network in which to deploy the resources." 50 | type = string 51 | default = "default" 52 | } 53 | 54 | variable "subnetwork" { 55 | description = "Self link of the VPC subnetwork in which to deploy the resources." 56 | type = string 57 | default = "" 58 | } 59 | 60 | variable "protocol" { 61 | description = "The protocol for the backend and frontend forwarding rule. TCP or UDP." 62 | type = string 63 | default = "TCP" 64 | } 65 | 66 | variable "ip_address" { 67 | description = "IP address of the load balancer. If empty, an IP address will be automatically assigned." 68 | type = string 69 | default = null 70 | } 71 | 72 | variable "service_label" { 73 | description = "An optional prefix to the service name for this Forwarding Rule. If specified, will be the first label of the fully qualified service name." 74 | type = string 75 | default = "" 76 | } 77 | 78 | variable "network_project" { 79 | description = "The name of the GCP Project where the network is located. Useful when using networks shared between projects. If empty, var.project will be used." 80 | type = string 81 | default = "" 82 | } 83 | 84 | variable "http_health_check" { 85 | description = "Set to true if health check is type http, otherwise health check is tcp." 86 | type = bool 87 | default = false 88 | } 89 | 90 | variable "session_affinity" { 91 | description = "The session affinity for the backends, e.g.: NONE, CLIENT_IP. Default is `NONE`." 92 | type = string 93 | default = "NONE" 94 | } 95 | 96 | variable "source_tags" { 97 | description = "List of source tags for traffic between the internal load balancer." 98 | type = list(string) 99 | default = [] 100 | } 101 | 102 | variable "target_tags" { 103 | description = "List of target tags for traffic between the internal load balancer." 104 | type = list(string) 105 | default = [] 106 | } 107 | 108 | variable "custom_labels" { 109 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 110 | type = map(string) 111 | default = {} 112 | } 113 | -------------------------------------------------------------------------------- /modules/network-load-balancer/README.md: -------------------------------------------------------------------------------- 1 | # Network Load Balancer Module 2 | 3 | [![Maintained by Gruntwork.io](https://img.shields.io/badge/maintained%20by-gruntwork.io-%235849a6.svg)](https://gruntwork.io/?ref=repo_google_load_balancer) 4 | [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gruntwork-io/terraform-google-load-balancer.svg?label=latest)](https://github.com/gruntwork-io/terraform-google-load-balancer/releases/latest) 5 | ![Terraform Version](https://img.shields.io/badge/tf-%3E%3D1.0.x-blue.svg) 6 | 7 | This Terraform Module creates a [Network Load Balancer](https://cloud.google.com/load-balancing/docs/network/) using [forwarding rules](https://cloud.google.com/load-balancing/docs/network/#forwarding_rules) and [target pools](https://cloud.google.com/load-balancing/docs/network/#target_pools). 8 | 9 | Google Cloud Platform (GCP) Network Load Balancing distributes traffic among VM instances in the same region in a VPC network. 10 | 11 | ## Learn 12 | 13 | This repo is a part of [the Gruntwork Infrastructure as Code Library](https://gruntwork.io/infrastructure-as-code-library/), a collection of reusable, battle-tested, production ready infrastructure code. If you’ve never used the Infrastructure as Code Library before, make sure to read [How to use the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/)! 14 | 15 | ### Core concepts 16 | 17 | - [What is Cloud Load Balancing](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/network-load-balancer/core-concepts.md#what-is-cloud-load-balancing) 18 | - [Network Load Balancer Terminology](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/network-load-balancer/core-concepts.md#network-tcpudp-load-balancer-terminology) 19 | - [Netowrk Load Balancing Documentation](https://cloud.google.com/load-balancing/docs/network/) 20 | 21 | ### Repo organisation 22 | 23 | This repo has the following folder structure: 24 | 25 | * [root](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master): The root folder contains an example of how to deploy a HTTP Load Balancer with multiple backends. See [http-multi-backend example documentation](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples/http-multi-backend) for the documentation. 26 | 27 | * [modules](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules): This folder contains the main implementation code for this Module. 28 | 29 | The primary modules are: 30 | 31 | * [http-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/http-load-balancer) is used to create an [HTTP(S) External Load Balancer](https://cloud.google.com/load-balancing/docs/https/). 32 | * [internal-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/internal-load-balancer) is used to create an [Internal TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/internal/). 33 | * [network-load-balancer](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/modules/network-load-balancer) is used to create an [External TCP/UDP Load Balancer](https://cloud.google.com/load-balancing/docs/network/). 34 | 35 | * [examples](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): This folder contains examples of how to use the submodules. 36 | 37 | * [test](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/test): Automated tests for the submodules and examples. 38 | 39 | ## Deploy 40 | 41 | If you want to try this repo out for experimenting and learning, check out the following resources: 42 | 43 | - [examples folder](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/examples): The `examples` folder contains sample code optimized for learning, experimenting, and testing. 44 | 45 | ## Manage 46 | 47 | ### Day-to-day operations 48 | 49 | - [Internal Load Balancer access logging and monitoring](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-tcpudp-load-balancing-monitoring) 50 | - [Internal Load Balancer DNS names](https://github.com/gruntwork-io/terraform-google-load-balancer/tree/master/modules/internal-load-balancer/core-concepts.md#internal-tcpudp-load-balancing-and-dns-names) 51 | 52 | ## Support 53 | 54 | If you need help with this repo or anything else related to infrastructure or DevOps, Gruntwork offers [Commercial Support](https://gruntwork.io/support/) via Slack, email, and phone/video. If you’re already a Gruntwork customer, hop on Slack and ask away! If not, [subscribe now](https://www.gruntwork.io/pricing/). If you’re not sure, feel free to email us at [support@gruntwork.io](mailto:support@gruntwork.io). 55 | 56 | ## Contributions 57 | 58 | Contributions to this repo are very welcome and appreciated! If you find a bug or want to add a new feature or even contribute an entirely new module, we are very happy to accept pull requests, provide feedback, and run your changes through our automated test suite. 59 | 60 | Please see [Contributing to the Gruntwork Infrastructure as Code Library](https://gruntwork.io/guides/foundations/how-to-use-gruntwork-infrastructure-as-code-library/#contributing-to-the-gruntwork-infrastructure-as-code-library) for instructions. 61 | 62 | ## License 63 | 64 | Please see [LICENSE](https://github.com/gruntwork-io/terraform-google-load-balancer/blob/master/LICENSE.txt) for details on how the code in this repo is licensed. 65 | 66 | Copyright © 2019 Gruntwork, Inc. 67 | -------------------------------------------------------------------------------- /modules/network-load-balancer/core-concepts.md: -------------------------------------------------------------------------------- 1 | # Network Load Balancer Core Concepts 2 | 3 | ## What is Cloud Load Balancing? 4 | 5 | [Cloud Load Balancing](https://cloud.google.com/load-balancing/) is a fully distributed, software-defined, managed service for all your traffic. It is not an instance or device based solution, so you won’t be locked into physical load balancing infrastructure or face the HA, scale and management challenges inherent in instance based LBs. Cloud Load Balancing features include: 6 | 7 | * **HTTP(S) Load Balancing:** HTTP(S) load balancing can balance HTTP and HTTPS traffic across multiple backend instances, across multiple regions. 8 | * **Network TCP/UDP Load Balancing:** load balance external traffic. 9 | * **Internal TCP/UDP Load Balancing:** load balance internal traffic. 10 | * **Seamless Autoscaling:** Autoscaling helps your applications gracefully handle increases in traffic and reduces cost when the need for resources is lower. 11 | * **Cloud CDN Integration:** Enabling [Cloud CDN](https://cloud.google.com/cdn/) for HTTP(S) Load Balancing for optimizing application delivery for your users. 12 | * **Stackdriver Logging:** Stackdriver Logging for load balancing logs all the load balancing requests sent to your load balancer. 13 | 14 | You can learn more about Cloud Load Balancing in [the official documentation](https://cloud.google.com/load-balancing/docs/). 15 | 16 | ## Network Load Balancer Terminology 17 | 18 | GCP uses non-standard vocabulary for load balancing concepts. In case you're unfamiliar with load balancing on GCP, here's a short guide: 19 | 20 | - **[Target pools](https://cloud.google.com/load-balancing/docs/network/#target_pools)** A Target Pool resource defines a group of instances that should receive incoming traffic from forwarding rules. Target pools can only be used with forwarding rules that handle TCP and UDP traffic. 21 | - **[Forwarding rules](https://cloud.google.com/load-balancing/docs/network/#forwarding_rules)** Forwarding rules work in conjunction with target pools and target instances to support load balancing and protocol forwarding features. 22 | - **[Health checks](https://cloud.google.com/load-balancing/docs/network/#health_checking)** ensure that Compute Engine forwards new connections only to instances that are up and ready to receive them. Compute Engine sends health check requests to each instance at the specified frequency. 23 | -------------------------------------------------------------------------------- /modules/network-load-balancer/main.tf: -------------------------------------------------------------------------------- 1 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | # DEPLOY A NETWORK LOAD BALANCER 3 | # This module deploys a GCP Regional Network Load Balancer 4 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | terraform { 7 | # This module is now only being tested with Terraform 1.0.x. However, to make upgrading easier, we are setting 8 | # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 9 | # forwards compatible with 1.0.x code. 10 | required_version = ">= 0.12.26" 11 | } 12 | 13 | # ------------------------------------------------------------------------------ 14 | # CREATE FORWARDING RULE 15 | # ------------------------------------------------------------------------------ 16 | 17 | resource "google_compute_forwarding_rule" "default" { 18 | provider = google-beta 19 | project = var.project 20 | name = var.name 21 | target = google_compute_target_pool.default.self_link 22 | load_balancing_scheme = "EXTERNAL" 23 | port_range = var.port_range 24 | ip_address = var.ip_address 25 | ip_protocol = var.protocol 26 | 27 | labels = var.custom_labels 28 | } 29 | 30 | # ------------------------------------------------------------------------------ 31 | # CREATE TARGET POOL 32 | # ------------------------------------------------------------------------------ 33 | 34 | resource "google_compute_target_pool" "default" { 35 | provider = google-beta 36 | project = var.project 37 | name = "${var.name}-tp" 38 | region = var.region 39 | session_affinity = var.session_affinity 40 | 41 | instances = var.instances 42 | 43 | health_checks = google_compute_http_health_check.default.*.name 44 | } 45 | 46 | # ------------------------------------------------------------------------------ 47 | # CREATE HEALTH CHECK 48 | # ------------------------------------------------------------------------------ 49 | 50 | resource "google_compute_http_health_check" "default" { 51 | count = var.enable_health_check ? 1 : 0 52 | 53 | provider = google-beta 54 | project = var.project 55 | name = "${var.name}-hc" 56 | request_path = var.health_check_path 57 | port = var.health_check_port 58 | check_interval_sec = var.health_check_interval 59 | healthy_threshold = var.health_check_healthy_threshold 60 | unhealthy_threshold = var.health_check_unhealthy_threshold 61 | timeout_sec = var.health_check_timeout 62 | } 63 | 64 | # ------------------------------------------------------------------------------ 65 | # CREATE FIREWALL FOR THE HEALTH CHECKS 66 | # ------------------------------------------------------------------------------ 67 | 68 | # Health check firewall allows ingress tcp traffic from the health check IP addresses 69 | resource "google_compute_firewall" "health_check" { 70 | count = var.enable_health_check ? 1 : 0 71 | 72 | provider = google-beta 73 | project = var.network_project == null ? var.project : var.network_project 74 | name = "${var.name}-hc-fw" 75 | network = var.network 76 | 77 | allow { 78 | protocol = "tcp" 79 | ports = [var.health_check_port] 80 | } 81 | 82 | # These IP ranges are required for health checks 83 | source_ranges = ["209.85.152.0/22", "209.85.204.0/22", "35.191.0.0/16"] 84 | 85 | # Target tags define the instances to which the rule applies 86 | target_tags = var.firewall_target_tags 87 | } 88 | -------------------------------------------------------------------------------- /modules/network-load-balancer/outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "forwarding_rule" { 6 | description = "Self link of the forwarding rule (Load Balancer)" 7 | value = google_compute_forwarding_rule.default.self_link 8 | } 9 | 10 | output "load_balancer_ip_address" { 11 | description = "IP address of the Load Balancer" 12 | value = google_compute_forwarding_rule.default.ip_address 13 | } 14 | 15 | output "target_pool" { 16 | description = "Self link of the target pool" 17 | value = google_compute_target_pool.default.self_link 18 | } 19 | -------------------------------------------------------------------------------- /modules/network-load-balancer/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "region" { 12 | description = "All resources will be launched in this region." 13 | type = string 14 | } 15 | 16 | variable "name" { 17 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 18 | type = string 19 | } 20 | 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | # OPTIONAL MODULE PARAMETERS 23 | # These variables have defaults, but may be overridden by the operator. 24 | # --------------------------------------------------------------------------------------------------------------------- 25 | 26 | variable "network" { 27 | description = "Self link of the VPC network in which to deploy the resources." 28 | type = string 29 | default = "default" 30 | } 31 | 32 | variable "protocol" { 33 | description = "The protocol for the backend and frontend forwarding rule. TCP or UDP." 34 | type = string 35 | default = "TCP" 36 | } 37 | 38 | variable "ip_address" { 39 | description = "IP address of the load balancer. If empty, an IP address will be automatically assigned." 40 | type = string 41 | default = null 42 | } 43 | 44 | variable "port_range" { 45 | description = "Only packets addressed to ports in the specified range will be forwarded to target. If empty, all packets will be forwarded." 46 | type = string 47 | default = null 48 | } 49 | 50 | variable "enable_health_check" { 51 | description = "Flag to indicate if health check is enabled. If set to true, a firewall rule allowing health check probes is also created." 52 | type = bool 53 | default = false 54 | } 55 | 56 | variable "health_check_port" { 57 | description = "The TCP port number for the HTTP health check request." 58 | type = number 59 | default = 80 60 | } 61 | 62 | variable "health_check_healthy_threshold" { 63 | description = "A so-far unhealthy instance will be marked healthy after this many consecutive successes. The default value is 2." 64 | type = number 65 | default = 2 66 | } 67 | 68 | variable "health_check_unhealthy_threshold" { 69 | description = "A so-far healthy instance will be marked unhealthy after this many consecutive failures. The default value is 2." 70 | type = number 71 | default = 2 72 | } 73 | 74 | variable "health_check_interval" { 75 | description = "How often (in seconds) to send a health check. Default is 5." 76 | type = number 77 | default = 5 78 | } 79 | 80 | variable "health_check_timeout" { 81 | description = "How long (in seconds) to wait before claiming failure. The default value is 5 seconds. It is invalid for 'health_check_timeout' to have greater value than 'health_check_interval'" 82 | type = number 83 | default = 5 84 | } 85 | 86 | variable "health_check_path" { 87 | description = "The request path of the HTTP health check request. The default value is '/'." 88 | type = string 89 | default = "/" 90 | } 91 | 92 | variable "firewall_target_tags" { 93 | description = "List of target tags for the health check firewall rule." 94 | type = list(string) 95 | default = [] 96 | } 97 | 98 | variable "network_project" { 99 | description = "The name of the GCP Project where the network is located. Useful when using networks shared between projects. If empty, var.project will be used." 100 | type = string 101 | default = null 102 | } 103 | 104 | variable "session_affinity" { 105 | description = "The session affinity for the backends, e.g.: NONE, CLIENT_IP. Default is `NONE`." 106 | type = string 107 | default = "NONE" 108 | } 109 | 110 | variable "instances" { 111 | description = "List of self links to instances in the pool. Note that the instances need not exist at the time of target pool creation." 112 | type = list(string) 113 | default = [] 114 | } 115 | 116 | variable "custom_labels" { 117 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 118 | type = map(string) 119 | default = {} 120 | } 121 | 122 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # LOAD BALANCER OUTPUTS 3 | # ------------------------------------------------------------------------------ 4 | 5 | output "load_balancer_ip_address" { 6 | description = "IP address of the Cloud Load Balancer" 7 | value = module.lb.load_balancer_ip_address 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | 6 | 7 | This folder contains automated tests for this Module. All of the tests are written in [Go](https://golang.org/). 8 | Most of these are "integration tests" that deploy real infrastructure using Terraform and verify that infrastructure works as expected using a helper library called [Terratest](https://github.com/gruntwork-io/terratest). 9 | 10 | 11 | 12 | ## WARNING WARNING WARNING 13 | 14 | **Note #1**: Many of these tests create real resources in a GCP project and then try to clean those resources up at 15 | the end of a test run. That means these tests may cost you money to run! When adding tests, please be considerate of 16 | the resources you create and take extra care to clean everything up when you're done! 17 | 18 | **Note #2**: Never forcefully shut the tests down (e.g. by hitting `CTRL + C`) or the cleanup tasks won't run! 19 | 20 | **Note #3**: We set `-timeout 60m` on all tests not because they necessarily take that long, but because Go has a 21 | default test timeout of 10 minutes, after which it forcefully kills the tests with a `SIGQUIT`, preventing the cleanup 22 | tasks from running. Therefore, we set an overlying long timeout to make sure all tests have enough time to finish and 23 | clean up. 24 | 25 | 26 | 27 | ## Running the tests 28 | 29 | ### Prerequisites 30 | 31 | - Install the latest version of [Go](https://golang.org/). 32 | - Install [dep](https://github.com/golang/dep) for Go dependency management. 33 | - Install [Terraform](https://www.terraform.io/downloads.html). 34 | - Configure your Google credentials using one of the [options supported by GCP](https://cloud.google.com/docs/authentication/getting-started). 35 | 36 | 37 | ### One-time setup 38 | 39 | Download Go dependencies using dep: 40 | 41 | ``` 42 | cd test 43 | dep ensure 44 | ``` 45 | 46 | 47 | ### Run all the tests 48 | 49 | ```bash 50 | cd test 51 | go test -v -timeout 60m 52 | ``` 53 | 54 | 55 | ### Run a specific test 56 | 57 | To run a specific test called `TestFoo`: 58 | 59 | ```bash 60 | cd test 61 | go test -v -timeout 60m -run TestFoo 62 | ``` -------------------------------------------------------------------------------- /test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gruntwork-io/terraform-google-load-balancer/test 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gruntwork-io/terratest v0.37.5 7 | github.com/stretchr/testify v1.4.0 8 | ) 9 | -------------------------------------------------------------------------------- /test/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 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM= 9 | cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= 10 | cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= 11 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 12 | cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= 13 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 14 | cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= 15 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 16 | cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= 17 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 18 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 19 | github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 20 | github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 21 | github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 22 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 23 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 24 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= 25 | github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= 26 | github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= 27 | github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= 28 | github.com/Azure/go-autorest/autorest v0.11.5/go.mod h1:foo3aIXRQ90zFve3r0QiDsrjGDUwWhKl0ZOQy1CT14k= 29 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 30 | github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= 31 | github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= 32 | github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= 33 | github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= 34 | github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= 35 | github.com/Azure/go-autorest/autorest/azure/auth v0.5.1/go.mod h1:ea90/jvmnAwDrSooLH4sRIehEPtG/EPUXavDh31MnA4= 36 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= 37 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= 38 | github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= 39 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= 40 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 41 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 42 | github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= 43 | github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 44 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 45 | github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= 46 | github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= 47 | github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= 48 | github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= 49 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 50 | github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 51 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 52 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 53 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 54 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 55 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 56 | github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= 57 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 58 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 59 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 60 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 61 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 62 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 63 | github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= 64 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 65 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 66 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 67 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= 68 | github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= 69 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= 70 | github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0= 71 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= 72 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 73 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 74 | github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 75 | github.com/aws/aws-sdk-go v1.27.1 h1:MXnqY6SlWySaZAqNnXThOvjRFdiiOuKtC6i7baFdNdU= 76 | github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 77 | github.com/aws/aws-sdk-go v1.38.28 h1:2ZzgEupSluR18ClxUnHwXKyuADheZpMblXRAsHqF0tI= 78 | github.com/aws/aws-sdk-go v1.38.28/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 79 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 80 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 81 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 82 | github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 83 | github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= 84 | github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= 85 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 86 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 87 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 88 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 89 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 90 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 91 | github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 92 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 93 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 94 | github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 95 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 96 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 97 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 98 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 99 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 100 | github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 101 | github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= 102 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 103 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 104 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 105 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 106 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 107 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 108 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 109 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 110 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 111 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 112 | github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= 113 | github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= 114 | github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 115 | github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3 h1:2EQbDt6zX40xsToT0KdsmAm4wfA+fPJfv9Yk/PwTxZk= 116 | github.com/docker/cli v0.0.0-20200109221225-a4f60165b7a3/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 117 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 118 | github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 119 | github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE= 120 | github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 121 | github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= 122 | github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= 123 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 124 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 125 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 126 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= 127 | github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 128 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 129 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 130 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 131 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 132 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 133 | github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= 134 | github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 135 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= 136 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 137 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 138 | github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 139 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 140 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 141 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 142 | github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 143 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 144 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 145 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 146 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 147 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 148 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 149 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 150 | github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 h1:skJKxRtNmevLqnayafdLe2AsenqRupVmzZSqrvb5caU= 151 | github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 152 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 153 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 154 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 155 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 156 | github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= 157 | github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 158 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 159 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 160 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 161 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 162 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 163 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 164 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 165 | github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 166 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 167 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 168 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 169 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 170 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 171 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 172 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 173 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 174 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 175 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 176 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 177 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 178 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 179 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 180 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 181 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 182 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 183 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= 184 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 185 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 186 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 187 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 188 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 189 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 190 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 191 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 192 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 193 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 194 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 195 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 196 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 197 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 198 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 199 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 200 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 201 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 202 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 203 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 204 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 205 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 206 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 207 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 208 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 209 | github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3 h1:lvfj3UTMJS9ZD1T4mUjvZTe24RqIe9fYUqpeOza+0Hs= 210 | github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk= 211 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 212 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 213 | github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= 214 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 215 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 216 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 217 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 218 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 219 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 220 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 221 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 222 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 223 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 224 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 225 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 226 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 227 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 228 | github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 229 | github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= 230 | github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= 231 | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= 232 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 233 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 234 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 235 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 236 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 237 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 238 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 239 | github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= 240 | github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= 241 | github.com/gruntwork-io/terratest v0.37.5 h1:W93EhxcxDApa5Xyj/TOpDXx+i5IssTopy5LbEUJkPuA= 242 | github.com/gruntwork-io/terratest v0.37.5/go.mod h1:CSHpZNJdqYQ+TUrigM100jcahRUV5X6w7K2kZJ8iylY= 243 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 244 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 245 | github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= 246 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 247 | github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= 248 | github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 249 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 250 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 251 | github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 252 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 253 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 254 | github.com/hashicorp/hcl/v2 v2.8.2 h1:wmFle3D1vu0okesm8BTLVDyJ6/OL9DCLUwn0b2OptiY= 255 | github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= 256 | github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= 257 | github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= 258 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 259 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 260 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 261 | github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= 262 | github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 263 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 264 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= 265 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= 266 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= 267 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 268 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 269 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 270 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 271 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 272 | github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= 273 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 274 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 275 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 276 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 277 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 278 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 279 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 280 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 281 | github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= 282 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 283 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 284 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 285 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 286 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 287 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 288 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 289 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 290 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 291 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 292 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 293 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 294 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 295 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 296 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 297 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= 298 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 299 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 300 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 301 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 302 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 303 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 304 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 305 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 306 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 307 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 308 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 309 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 310 | github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 311 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= 312 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 313 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 314 | github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= 315 | github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 316 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 317 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 318 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 319 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= 320 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 321 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 322 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 323 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 324 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 325 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 326 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 327 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 328 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 329 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 330 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 331 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 332 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 333 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 334 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 335 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 336 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 337 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 338 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 339 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 340 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 341 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 342 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 343 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 344 | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 345 | github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 346 | github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= 347 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 348 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 349 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 350 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 351 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 352 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 353 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 354 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 355 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 356 | github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= 357 | github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= 358 | github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= 359 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 360 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 361 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 362 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 363 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 364 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 365 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 366 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 367 | github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= 368 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 369 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= 370 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 371 | github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= 372 | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 373 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 374 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 375 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 376 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 377 | github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= 378 | github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= 379 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 380 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 381 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 382 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 383 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 384 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 385 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 386 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 387 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 388 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 389 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 390 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 391 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 392 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 393 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 394 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 395 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 396 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 397 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 398 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 399 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 400 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 401 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 402 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 403 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 404 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 405 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 406 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 407 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 408 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 409 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 410 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 411 | github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= 412 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 413 | github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo= 414 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 415 | github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= 416 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 417 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 418 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 419 | github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= 420 | github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= 421 | github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= 422 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 423 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 424 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 425 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 426 | go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= 427 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 428 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 429 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 430 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 431 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 432 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 433 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 434 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 435 | golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 436 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 437 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 438 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 439 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 440 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 441 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 442 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 443 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 444 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 445 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 446 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 447 | golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 448 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 449 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 450 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg= 451 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 452 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 453 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 454 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 455 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 456 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 457 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 458 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 459 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 460 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= 461 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 462 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 463 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 464 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 465 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 466 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 467 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 468 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 469 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 470 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 471 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 472 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 473 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 474 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 475 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 476 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 477 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 478 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 479 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 480 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 481 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 482 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 483 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 484 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 485 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 486 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 487 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 488 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 489 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 490 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 491 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 492 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= 493 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 494 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 495 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= 496 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 497 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 498 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 499 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 500 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 501 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= 502 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 503 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 504 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 505 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 506 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 507 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 508 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= 509 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 510 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= 511 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 512 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 513 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 514 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 515 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 516 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 517 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 518 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 519 | golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 520 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 521 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 522 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 537 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 538 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 539 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 540 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= 541 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 542 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 543 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 544 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 545 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 546 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 547 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 548 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 549 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 550 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 551 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 552 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 553 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 554 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 555 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 556 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 557 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 558 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 559 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 560 | golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 561 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 562 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 563 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 564 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 565 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 566 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 567 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 568 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 569 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 570 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 571 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 572 | golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= 573 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 574 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 575 | golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 576 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 577 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 578 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 579 | golang.org/x/tools v0.0.0-20191205215504-7b8c8591a921/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 580 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 581 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 582 | golang.org/x/tools v0.0.0-20201110201400-7099162a900a h1:5E6TPwSBG74zT8xSrVc8W59K4ch4NFobVTnh2BYzHyU= 583 | golang.org/x/tools v0.0.0-20201110201400-7099162a900a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 584 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 585 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 586 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 587 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 588 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 589 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 590 | gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= 591 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= 592 | gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= 593 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 594 | google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= 595 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 596 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 597 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 598 | google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA= 599 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 600 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 601 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 602 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 603 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 604 | google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= 605 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 606 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 607 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 608 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 609 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 610 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 611 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 612 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 613 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 614 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 615 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 616 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 617 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 618 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 619 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 620 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 621 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 622 | google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= 623 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 624 | google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= 625 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 626 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 627 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 628 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 629 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 630 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 631 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 632 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 633 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 634 | google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= 635 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 636 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 637 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 638 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 639 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 640 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 641 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 642 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 643 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 644 | gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 645 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 646 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 647 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 648 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 649 | gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 650 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 651 | gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 652 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 653 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 654 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 655 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 656 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 657 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 658 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 659 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 660 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 661 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 662 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 663 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 664 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 665 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 666 | k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= 667 | k8s.io/api v0.19.3 h1:GN6ntFnv44Vptj/b+OnMW7FmzkpDoIDLZRvKX3XH9aU= 668 | k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= 669 | k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= 670 | k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc= 671 | k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= 672 | k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg= 673 | k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= 674 | k8s.io/client-go v0.19.3 h1:ctqR1nQ52NUs6LpI0w+a5U+xjYwflFwA13OJKcicMxg= 675 | k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= 676 | k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE= 677 | k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= 678 | k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= 679 | k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls= 680 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 681 | k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 682 | k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 683 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 684 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 685 | k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= 686 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 687 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 688 | k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= 689 | k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= 690 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= 691 | k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= 692 | k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8= 693 | k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 694 | k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= 695 | k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 696 | modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= 697 | modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= 698 | modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= 699 | modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= 700 | modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= 701 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 702 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 703 | sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= 704 | sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= 705 | sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= 706 | sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 707 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 708 | sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= 709 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 710 | -------------------------------------------------------------------------------- /test/http_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/gruntwork-io/terratest/modules/gcp" 9 | "github.com/gruntwork-io/terratest/modules/logger" 10 | "github.com/gruntwork-io/terratest/modules/random" 11 | "github.com/gruntwork-io/terratest/modules/terraform" 12 | test_structure "github.com/gruntwork-io/terratest/modules/test-structure" 13 | ) 14 | 15 | const OUTPUT_HTTP_LB_IP = "load_balancer_ip_address" 16 | 17 | func TestHttpLoadBalancerMultiBackend(t *testing.T) { 18 | t.Parallel() 19 | 20 | var testcases = []struct { 21 | testName string 22 | createDomain bool 23 | enableSsl bool 24 | enableHttp bool 25 | }{ 26 | { 27 | "TestHttpAndIpOnly", 28 | false, 29 | false, 30 | true, 31 | }, 32 | { 33 | "TestBothProtocolsWithDomain", 34 | true, 35 | true, 36 | true, 37 | }, 38 | } 39 | 40 | for _, testCase := range testcases { 41 | // The following is necessary to make sure testCase's values don't 42 | // get updated due to concurrency within the scope of t.Run(..) below 43 | testCase := testCase 44 | 45 | t.Run(testCase.testName, func(t *testing.T) { 46 | t.Parallel() 47 | 48 | //os.Setenv("SKIP_bootstrap", "true") 49 | //os.Setenv("SKIP_deploy", "true") 50 | //os.Setenv("SKIP_http_tests", "true") 51 | //os.Setenv("SKIP_teardown", "true") 52 | 53 | // The example is the root example 54 | exampleDir := test_structure.CopyTerraformFolderToTemp(t, "../", ".") 55 | 56 | test_structure.RunTestStage(t, "bootstrap", func() { 57 | logger.Logf(t, "Bootstrapping variables") 58 | 59 | projectId := gcp.GetGoogleProjectIDFromEnvVar(t) 60 | region := getRandomRegion(t, projectId) 61 | zone := gcp.GetRandomZoneForRegion(t, projectId, region) 62 | 63 | randomId := strings.ToLower(random.UniqueId()) 64 | // Since some of the resources require the resource name to begin with a lower-case letter, we're prepending an 'a' 65 | randomId = fmt.Sprintf("a%s", randomId) 66 | 67 | domainName := fmt.Sprintf("%s.%s", randomId, ROOT_DOMAIN_NAME_FOR_TEST) 68 | 69 | test_structure.SaveString(t, exampleDir, KEY_DOMAIN_NAME, domainName) 70 | test_structure.SaveString(t, exampleDir, KEY_PROJECT, projectId) 71 | test_structure.SaveString(t, exampleDir, KEY_REGION, region) 72 | test_structure.SaveString(t, exampleDir, KEY_ZONE, zone) 73 | test_structure.SaveString(t, exampleDir, KEY_NAME, randomId) 74 | }) 75 | 76 | // At the end of the test, run `terraform destroy` to clean up any resources that were created 77 | defer test_structure.RunTestStage(t, "teardown", func() { 78 | logger.Logf(t, "Tear down infrastructure") 79 | 80 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 81 | terraform.Destroy(t, terraformOptions) 82 | }) 83 | 84 | test_structure.RunTestStage(t, "deploy", func() { 85 | logger.Logf(t, "Deploying the solution") 86 | 87 | projectId := test_structure.LoadString(t, exampleDir, KEY_PROJECT) 88 | domainName := test_structure.LoadString(t, exampleDir, KEY_DOMAIN_NAME) 89 | region := test_structure.LoadString(t, exampleDir, KEY_REGION) 90 | zone := test_structure.LoadString(t, exampleDir, KEY_ZONE) 91 | name := test_structure.LoadString(t, exampleDir, KEY_NAME) 92 | terraformOptions := createTerratestOptionsForHttpLoadBalancer(exampleDir, projectId, region, zone, name, domainName, MANAGED_ZONE_NAME_FOR_TEST, testCase.createDomain, testCase.enableSsl, testCase.enableHttp) 93 | test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions) 94 | 95 | terraform.InitAndApply(t, terraformOptions) 96 | }) 97 | 98 | test_structure.RunTestStage(t, "http_tests", func() { 99 | 100 | logger.Logf(t, "Running web tests by calling the created website") 101 | 102 | domainName := test_structure.LoadString(t, exampleDir, KEY_DOMAIN_NAME) 103 | 104 | if !testCase.createDomain { 105 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 106 | domainName = terraform.Output(t, terraformOptions, OUTPUT_HTTP_LB_IP) 107 | } 108 | 109 | expectedIndexBody := "Hello, World!" 110 | expectedApiBody := "Hello, api!" 111 | expectedNotFoundBody := "Uh oh" 112 | 113 | if testCase.enableHttp { 114 | VerifyResponse(t, "http", domainName, "", 200, expectedIndexBody) 115 | VerifyResponse(t, "http", domainName, "/api", 200, expectedApiBody) 116 | VerifyResponse(t, "http", domainName, "/bogus", 404, expectedNotFoundBody) 117 | } 118 | 119 | if testCase.enableSsl { 120 | VerifyResponse(t, "https", domainName, "", 200, expectedIndexBody) 121 | VerifyResponse(t, "https", domainName, "/api", 200, expectedApiBody) 122 | VerifyResponse(t, "https", domainName, "/bogus", 404, expectedNotFoundBody) 123 | } 124 | }) 125 | 126 | }) 127 | } 128 | } 129 | 130 | func createTerratestOptionsForHttpLoadBalancer(exampleDir string, projectId string, region string, zone string, name string, domainName string, dnsZoneName string, createDnsEntry bool, enableSsl bool, enableHttp bool) *terraform.Options { 131 | 132 | terratestOptions := &terraform.Options{ 133 | // The path to where your Terraform code is located 134 | TerraformDir: exampleDir, 135 | Vars: map[string]interface{}{ 136 | "project": projectId, 137 | "name": name, 138 | "region": region, 139 | "custom_domain_name": domainName, 140 | "create_dns_entry": createDnsEntry, 141 | "dns_managed_zone_name": dnsZoneName, 142 | "enable_ssl": enableSsl, 143 | "enable_http": enableHttp, 144 | "zone": zone, 145 | }, 146 | } 147 | 148 | return terratestOptions 149 | } 150 | -------------------------------------------------------------------------------- /test/ilb_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/gruntwork-io/terratest/modules/gcp" 10 | "github.com/gruntwork-io/terratest/modules/logger" 11 | "github.com/gruntwork-io/terratest/modules/random" 12 | "github.com/gruntwork-io/terratest/modules/terraform" 13 | test_structure "github.com/gruntwork-io/terratest/modules/test-structure" 14 | ) 15 | 16 | const OUTPUT_PROXY_IP = "proxy_public_ip_address" 17 | 18 | const EXAMPLE_NAME_ILB = "internal-load-balancer" 19 | 20 | func TestInternalLoadBalancer(t *testing.T) { 21 | t.Parallel() 22 | 23 | // We're only executing a single test but to make it easier to add further tests in future, 24 | // we're keeping the testcases struct 25 | var testcases = []struct { 26 | testName string 27 | }{ 28 | { 29 | "TestILBWithProxy", 30 | }, 31 | } 32 | 33 | for _, testCase := range testcases { 34 | // The following is necessary to make sure testCase's values don't 35 | // get updated due to concurrency within the scope of t.Run(..) below 36 | testCase := testCase 37 | 38 | t.Run(testCase.testName, func(t *testing.T) { 39 | t.Parallel() 40 | 41 | //os.Setenv("SKIP_bootstrap", "true") 42 | //os.Setenv("SKIP_deploy", "true") 43 | //os.Setenv("SKIP_http_tests", "true") 44 | //os.Setenv("SKIP_teardown", "true") 45 | 46 | _examplesDir := test_structure.CopyTerraformFolderToTemp(t, "../", "examples") 47 | exampleDir := filepath.Join(_examplesDir, EXAMPLE_NAME_ILB) 48 | 49 | test_structure.RunTestStage(t, "bootstrap", func() { 50 | logger.Logf(t, "Bootstrapping variables") 51 | 52 | projectId := gcp.GetGoogleProjectIDFromEnvVar(t) 53 | region := getRandomRegion(t, projectId) 54 | zone := gcp.GetRandomZoneForRegion(t, projectId, region) 55 | randomId := strings.ToLower(random.UniqueId()) 56 | name := fmt.Sprintf("%s-%s", EXAMPLE_NAME_ILB, randomId) 57 | 58 | test_structure.SaveString(t, exampleDir, KEY_PROJECT, projectId) 59 | test_structure.SaveString(t, exampleDir, KEY_REGION, region) 60 | test_structure.SaveString(t, exampleDir, KEY_ZONE, zone) 61 | test_structure.SaveString(t, exampleDir, KEY_NAME, name) 62 | }) 63 | 64 | // At the end of the test, run `terraform destroy` to clean up any resources that were created 65 | defer test_structure.RunTestStage(t, "teardown", func() { 66 | logger.Logf(t, "Tear down infrastructure") 67 | 68 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 69 | terraform.Destroy(t, terraformOptions) 70 | }) 71 | 72 | test_structure.RunTestStage(t, "deploy", func() { 73 | logger.Logf(t, "Deploying the solution") 74 | 75 | projectId := test_structure.LoadString(t, exampleDir, KEY_PROJECT) 76 | region := test_structure.LoadString(t, exampleDir, KEY_REGION) 77 | zone := test_structure.LoadString(t, exampleDir, KEY_ZONE) 78 | name := test_structure.LoadString(t, exampleDir, KEY_NAME) 79 | terraformOptions := createTerratestOptionsForInternalLoadBalancer(exampleDir, projectId, region, zone, name) 80 | test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions) 81 | 82 | terraform.InitAndApply(t, terraformOptions) 83 | }) 84 | 85 | test_structure.RunTestStage(t, "http_tests", func() { 86 | 87 | logger.Logf(t, "Running http tests by calling the proxy") 88 | 89 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 90 | proxyIp := terraform.Output(t, terraformOptions, OUTPUT_PROXY_IP) 91 | proxyIp = fmt.Sprintf("%s:%s", proxyIp, "5000") 92 | 93 | expectedBody := "Hello, api!" 94 | 95 | VerifyResponse(t, "http", proxyIp, "/nameproxy", 200, expectedBody) 96 | VerifyResponse(t, "http", proxyIp, "/ipproxy", 200, expectedBody) 97 | }) 98 | }) 99 | } 100 | } 101 | 102 | func createTerratestOptionsForInternalLoadBalancer(exampleDir string, projectId string, region string, zone string, name string) *terraform.Options { 103 | 104 | terratestOptions := &terraform.Options{ 105 | // The path to where your Terraform code is located 106 | TerraformDir: exampleDir, 107 | Vars: map[string]interface{}{ 108 | "project": projectId, 109 | "name": name, 110 | "region": region, 111 | "zone": zone, 112 | }, 113 | } 114 | 115 | return terratestOptions 116 | } 117 | -------------------------------------------------------------------------------- /test/nlb_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/gruntwork-io/terratest/modules/gcp" 10 | "github.com/gruntwork-io/terratest/modules/logger" 11 | "github.com/gruntwork-io/terratest/modules/random" 12 | "github.com/gruntwork-io/terratest/modules/terraform" 13 | test_structure "github.com/gruntwork-io/terratest/modules/test-structure" 14 | ) 15 | 16 | const OUTPUT_NLB_IP = "load_balancer_ip_address" 17 | 18 | const EXAMPLE_NAME_NLB = "network-load-balancer" 19 | 20 | func TestNetworkLoadBalancer(t *testing.T) { 21 | t.Parallel() 22 | 23 | // We're only executing a single test but to make it easier to add further tests in future, 24 | // we're keeping the testcases struct 25 | var testcases = []struct { 26 | testName string 27 | }{ 28 | { 29 | "TestNLB", 30 | }, 31 | } 32 | 33 | for _, testCase := range testcases { 34 | // The following is necessary to make sure testCase's values don't 35 | // get updated due to concurrency within the scope of t.Run(..) below 36 | testCase := testCase 37 | 38 | t.Run(testCase.testName, func(t *testing.T) { 39 | t.Parallel() 40 | 41 | //os.Setenv("SKIP_bootstrap", "true") 42 | //os.Setenv("SKIP_deploy", "true") 43 | //os.Setenv("SKIP_http_tests", "true") 44 | //os.Setenv("SKIP_teardown", "true") 45 | 46 | _examplesDir := test_structure.CopyTerraformFolderToTemp(t, "../", "examples") 47 | exampleDir := filepath.Join(_examplesDir, EXAMPLE_NAME_NLB) 48 | 49 | test_structure.RunTestStage(t, "bootstrap", func() { 50 | logger.Logf(t, "Bootstrapping variables") 51 | 52 | projectId := gcp.GetGoogleProjectIDFromEnvVar(t) 53 | region := getRandomRegion(t, projectId) 54 | zone := gcp.GetRandomZoneForRegion(t, projectId, region) 55 | randomId := strings.ToLower(random.UniqueId()) 56 | name := fmt.Sprintf("%s-%s", EXAMPLE_NAME_NLB, randomId) 57 | 58 | terraformOptions := createTerratestOptionsForNetworkLoadBalancer(exampleDir, projectId, region, zone, name) 59 | test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions) 60 | }) 61 | 62 | // At the end of the test, run `terraform destroy` to clean up any resources that were created 63 | defer test_structure.RunTestStage(t, "teardown", func() { 64 | logger.Logf(t, "Tear down infrastructure") 65 | 66 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 67 | terraform.Destroy(t, terraformOptions) 68 | }) 69 | 70 | test_structure.RunTestStage(t, "deploy", func() { 71 | logger.Logf(t, "Deploying the solution") 72 | 73 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 74 | terraform.InitAndApply(t, terraformOptions) 75 | }) 76 | 77 | test_structure.RunTestStage(t, "http_tests", func() { 78 | 79 | logger.Logf(t, "Running http tests by calling the proxy") 80 | 81 | terraformOptions := test_structure.LoadTerraformOptions(t, exampleDir) 82 | nlbIp := terraform.Output(t, terraformOptions, OUTPUT_NLB_IP) 83 | nlbIp = fmt.Sprintf("%s:%s", nlbIp, "5000") 84 | 85 | expectedBody := "Hello, api!" 86 | 87 | VerifyResponse(t, "http", nlbIp, "/api", 200, expectedBody) 88 | }) 89 | }) 90 | } 91 | } 92 | 93 | func createTerratestOptionsForNetworkLoadBalancer(exampleDir string, projectId string, region string, zone string, name string) *terraform.Options { 94 | 95 | terratestOptions := &terraform.Options{ 96 | // The path to where your Terraform code is located 97 | TerraformDir: exampleDir, 98 | Vars: map[string]interface{}{ 99 | "project": projectId, 100 | "name": name, 101 | "region": region, 102 | "zone": zone, 103 | }, 104 | } 105 | 106 | return terratestOptions 107 | } 108 | -------------------------------------------------------------------------------- /test/test_util.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | "testing" 10 | "time" 11 | 12 | "github.com/gruntwork-io/terratest/modules/gcp" 13 | "github.com/gruntwork-io/terratest/modules/logger" 14 | "github.com/gruntwork-io/terratest/modules/retry" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | const ROOT_DOMAIN_NAME_FOR_TEST = "gcloud-test.com" 19 | const MANAGED_ZONE_NAME_FOR_TEST = "gcloudtest" 20 | 21 | //const ROOT_DOMAIN_NAME_FOR_TEST = "gcloud-dev.com" 22 | //const MANAGED_ZONE_NAME_FOR_TEST = "gclouddev" 23 | 24 | const KEY_PROJECT = "project" 25 | const KEY_DOMAIN_NAME = "domain-name" 26 | const KEY_NAME = "name" 27 | const KEY_REGION = "region" 28 | const KEY_ZONE = "zone" 29 | 30 | func getRandomRegion(t *testing.T, projectID string) string { 31 | approvedRegions := []string{"europe-north1", "europe-west1", "europe-west2", "europe-west3", "us-central1", "us-east1", "us-west1"} 32 | //approvedRegions := []string{"europe-north1"} 33 | return gcp.GetRandomRegion(t, projectID, approvedRegions, []string{}) 34 | } 35 | 36 | // A lot of this is repetition from terratest http_helper, but to allow the custom TLS Config, we're 37 | // implementing the methods here, instead. 38 | // TODO: Look into possibility of incorporating the TLS flag into terratest 39 | 40 | func HttpGetE(t *testing.T, url string) (int, string, error) { 41 | logger.Logf(t, "Making a GET call to URL %s", url) 42 | 43 | transCfg := &http.Transport{ 44 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // ignore expired SSL certificates 45 | } 46 | 47 | client := &http.Client{ 48 | Transport: transCfg, 49 | // By default, Go does not impose a timeout, so an HTTP connection attempt can hang for a LONG time. 50 | Timeout: 10 * time.Second, 51 | } 52 | 53 | resp, err := client.Get(url) 54 | if err != nil { 55 | return -1, "", err 56 | } 57 | 58 | defer resp.Body.Close() 59 | body, err := ioutil.ReadAll(resp.Body) 60 | 61 | if err != nil { 62 | return -1, "", err 63 | } 64 | 65 | return resp.StatusCode, strings.TrimSpace(string(body)), nil 66 | } 67 | 68 | func HttpGetWithRetryE(t *testing.T, url string, expectedStatus int, expectedBody string, retries int, sleepBetweenRetries time.Duration) error { 69 | _, err := retry.DoWithRetryE(t, fmt.Sprintf("HTTP GET to URL %s", url), retries, sleepBetweenRetries, func() (string, error) { 70 | return "", HttpGetWithValidationE(t, url, expectedStatus, expectedBody) 71 | }) 72 | 73 | return err 74 | } 75 | 76 | // HttpGetWithValidationE performs an HTTP GET on the given URL and verify that you get back the expected status code and body. If either 77 | // doesn't match, return an error. 78 | func HttpGetWithValidationE(t *testing.T, url string, expectedStatusCode int, expectedBody string) error { 79 | return HttpGetWithCustomValidationE(t, url, func(statusCode int, body string) bool { 80 | return statusCode == expectedStatusCode && body == expectedBody 81 | }) 82 | } 83 | 84 | // HttpGetWithCustomValidationE performs an HTTP GET on the given URL and validate the returned status code and body using the given function. 85 | func HttpGetWithCustomValidationE(t *testing.T, url string, validateResponse func(int, string) bool) error { 86 | statusCode, body, err := HttpGetE(t, url) 87 | 88 | if err != nil { 89 | return err 90 | } 91 | 92 | if !validateResponse(statusCode, body) { 93 | return ValidationFunctionFailed{Url: url, Status: statusCode, Body: body} 94 | } 95 | 96 | return nil 97 | } 98 | 99 | // ValidationFunctionFailed is an error that occurs if a validation function fails. 100 | type ValidationFunctionFailed struct { 101 | Url string 102 | Status int 103 | Body string 104 | } 105 | 106 | func (err ValidationFunctionFailed) Error() string { 107 | return fmt.Sprintf("Validation failed for URL %s. Response status: %d. Response body:\n%s", err.Url, err.Status, err.Body) 108 | } 109 | 110 | func VerifyResponse(t *testing.T, protocol string, url string, path string, expectedStatus int, expectedBody string) { 111 | finalUrl := fmt.Sprintf("%s://%s%s", protocol, url, path) 112 | // Go seems to cache the DNS results quite heavily, so we'll add 113 | // a lot of time to survive that 114 | err := HttpGetWithRetryE(t, finalUrl, expectedStatus, expectedBody, 30, 30*time.Second) 115 | assert.NoError(t, err, "Failed to call URL %s", url) 116 | } 117 | -------------------------------------------------------------------------------- /test/validation/validate_all_modules_and_examples_test.go: -------------------------------------------------------------------------------- 1 | package testvalidate 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | test_structure "github.com/gruntwork-io/terratest/modules/test-structure" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | // TestValidateAllTerraformModulesAndExamples recursively finds all modules and examples (by default) subdirectories in 14 | // the repo and runs Terraform InitAndValidate on them to flush out missing variables, typos, unused vars, etc 15 | func TestValidateAllTerraformModulesAndExamples(t *testing.T) { 16 | t.Parallel() 17 | 18 | cwd, err := os.Getwd() 19 | require.NoError(t, err) 20 | 21 | opts, optsErr := test_structure.NewValidationOptions(filepath.Join(cwd, "../.."), []string{}, []string{}) 22 | require.NoError(t, optsErr) 23 | 24 | test_structure.ValidateAllTerraformModules(t, opts) 25 | } 26 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # These variables are expected to be passed in by the operator 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "project" { 7 | description = "The project ID to create the resources in." 8 | type = string 9 | } 10 | 11 | variable "region" { 12 | description = "The region to create the resources in." 13 | type = string 14 | } 15 | 16 | variable "zone" { 17 | description = "The availability zone to create the sample compute instances in. Must within the region specified in 'var.region'" 18 | type = string 19 | } 20 | 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | # OPTIONAL PARAMETERS 23 | # These variables have defaults, but may be overridden by the operator. 24 | # --------------------------------------------------------------------------------------------------------------------- 25 | 26 | variable "name" { 27 | description = "Name for the load balancer forwarding rule and prefix for supporting resources." 28 | type = string 29 | default = "http-multi-backend" 30 | } 31 | 32 | variable "enable_ssl" { 33 | description = "Set to true to enable ssl. If set to 'true', you will also have to provide 'var.custom_domain_name'." 34 | type = bool 35 | default = false 36 | } 37 | 38 | variable "enable_http" { 39 | description = "Set to true to enable plain http. Note that disabling http does not force SSL and/or redirect HTTP traffic. See https://issuetracker.google.com/issues/35904733" 40 | type = bool 41 | default = true 42 | } 43 | 44 | variable "static_content_bucket_location" { 45 | description = "Location of the bucket that will store the static content. Once a bucket has been created, its location can't be changed. See https://cloud.google.com/storage/docs/bucket-locations" 46 | type = string 47 | default = "US" 48 | } 49 | 50 | variable "create_dns_entry" { 51 | description = "If set to true, create a DNS A Record in Cloud DNS for the domain specified in 'custom_domain_name'." 52 | type = bool 53 | default = false 54 | } 55 | 56 | variable "custom_domain_name" { 57 | description = "Custom domain name." 58 | type = string 59 | default = "" 60 | } 61 | 62 | variable "dns_managed_zone_name" { 63 | description = "The name of the Cloud DNS Managed Zone in which to create the DNS A Record specified in var.custom_domain_name. Only used if var.create_dns_entry is true." 64 | type = string 65 | default = "replace-me" 66 | } 67 | 68 | variable "dns_record_ttl" { 69 | description = "The time-to-live for the load balancer A record (seconds)" 70 | type = string 71 | default = 60 72 | } 73 | 74 | variable "custom_labels" { 75 | description = "A map of custom labels to apply to the resources. The key is the label name and the value is the label value." 76 | type = map(string) 77 | 78 | default = {} 79 | } 80 | --------------------------------------------------------------------------------