├── .github ├── dependabot.yml └── workflows │ └── terraform-mixin.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── REVIEWING.md ├── build ├── atom-template.xml └── testdata │ └── bundles │ └── terraform │ └── .gitignore ├── cmd └── terraform │ ├── build.go │ ├── install.go │ ├── invoke.go │ ├── main.go │ ├── schema.go │ ├── uninstall.go │ ├── upgrade.go │ └── version.go ├── examples ├── azure-aks │ ├── porter.yaml │ └── terraform │ │ ├── aks.tf │ │ ├── output.tf │ │ ├── params.tf │ │ └── providers.tf ├── azure-keyvault │ ├── porter.yaml │ └── terraform │ │ ├── keyvault.tf │ │ ├── output.tf │ │ ├── params.tf │ │ └── providers.tf └── basic-tf-example │ ├── .gitignore │ ├── README.md │ ├── porter.yaml │ └── terraform │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── go.mod ├── go.sum ├── mage.go ├── magefile.go ├── pkg ├── terraform │ ├── action.go │ ├── action_test.go │ ├── build.go │ ├── build_test.go │ ├── config.go │ ├── config_test.go │ ├── helpers.go │ ├── helpers_test.go │ ├── init.go │ ├── init_test.go │ ├── install.go │ ├── install_test.go │ ├── invoke.go │ ├── invoke_test.go │ ├── schema.go │ ├── schema │ │ └── schema.json │ ├── schema_test.go │ ├── terraform.go │ ├── testdata │ │ ├── bad-install-input.desc-empty.yaml │ │ ├── bad-install-input.missing-desc.yaml │ │ ├── bad-uninstall-disable-save-var.yaml │ │ ├── bad-uninstall-input.input-not-valid.yaml │ │ ├── bad-upgrade-disable-save-var.yaml │ │ ├── build-input-in-airgapped-env.yaml │ │ ├── build-input-with-config.yaml │ │ ├── install-input-disable-save-vars.yaml │ │ ├── install-input-no-vars-block.yaml │ │ ├── install-input.yaml │ │ ├── invoke-input.yaml │ │ ├── step-input.yaml │ │ ├── uninstall-input.yaml │ │ └── upgrade-input.yaml │ ├── uninstall.go │ ├── uninstall_test.go │ ├── upgrade.go │ ├── upgrade_test.go │ ├── version.go │ └── version_test.go └── version.go └── scripts └── test └── test-cli.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: gomod 9 | directory: / 10 | schedule: 11 | interval: weekly 12 | day: sunday 13 | labels: 14 | - "dependabot 🤖" 15 | -------------------------------------------------------------------------------- /.github/workflows/terraform-mixin.yml: -------------------------------------------------------------------------------- 1 | name: porter/terraform-mixin 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - v* 7 | tags: 8 | - v* 9 | - "!canary*" 10 | - "!latest*" 11 | pull_request: 12 | branches: 13 | - main 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - uses: actions/setup-go@v5 23 | with: 24 | go-version-file: go.mod 25 | cache: true 26 | - name: Configure Agent 27 | run: go run mage.go ConfigureAgent 28 | - name: Test 29 | run: mage Test 30 | - name: Cross Compile 31 | run: mage XBuildAll 32 | - name: Publish 33 | if: success() && github.event_name != 'PullRequest' 34 | env: 35 | GITHUB_TOKEN: "${{ secrets.PUBLISH_TOKEN }}" 36 | run: mage Publish -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /bin 3 | /terraform 4 | *-packr.go 5 | /build/git_askpass.sh 6 | examples/**/Dockerfile 7 | examples/**/.cnab -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported to project admins via email to Carolyn Van Slyck (`me@carolynvanslyck.com`) 59 | or via direct message in [Slack] to Carolyn Van Slyck (`@carolynvs`). 60 | All complaints will be reviewed and investigated and will result in a response that 61 | is deemed necessary and appropriate to the circumstances. The project team is 62 | obligated to maintain confidentiality with regard to the reporter of an incident. 63 | Further details of specific enforcement policies may be posted separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | [slack]: https://porter.sh/community/#slack -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | This is part of the [Porter][porter] project. If you are a new contributor, 4 | check out our [New Contributor Guide][new-contrib]. The Porter [Contributing 5 | Guide][contrib] also has lots of information about how to interact with the 6 | project. 7 | 8 | [porter]: https://github.com/getporter/porter 9 | [new-contrib]: https://porter.sh/contribute 10 | [contrib]: https://porter.sh/src/CONTRIBUTING.md 11 | 12 | --- 13 | 14 | * [Initial setup](#initial-setup) 15 | * [Magefile explained](#magefile-explained) 16 | 17 | --- 18 | 19 | # Initial setup 20 | 21 | You need to have [porter installed](https://porter.sh/install) first. Then run 22 | `mage build install`. This will build and install the mixin into your porter 23 | home directory. 24 | 25 | ## Magefile explained 26 | 27 | Here are the most common [Magefile](https://magefile.org) tasks: 28 | 29 | * `build` builds both the runtime and client. 30 | * `install` installs the mixin into **~/.porter/mixins**. 31 | * `testUnit` runs the unit tests. 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Porter Authors 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Mixin for Porter 2 | 3 | This is a Terraform mixin for [Porter](https://porter.sh). 4 | 5 | [![porter/terraform-mixin](https://github.com/getporter/terraform-mixin/actions/workflows/terraform-mixin.yml/badge.svg?branch=main)](https://github.com/getporter/terraform-mixin/actions/workflows/terraform-mixin.yml) 6 | 7 | 8 | 9 | ## Install via Porter 10 | 11 | This will install the latest mixin release via the Porter CLI. 12 | 13 | ``` 14 | porter mixin install terraform 15 | ``` 16 | 17 | ## Build from source 18 | 19 | Following commands build the terraform mixin. 20 | 21 | ```bash 22 | git clone https://github.com/getporter/terraform-mixin.git 23 | cd terraform-mixin 24 | # Learn about Mage in our CONTRIBUTING.md 25 | go run mage.go EnsureMage 26 | mage build 27 | ``` 28 | 29 | Then, to install the resulting mixin into PORTER_HOME, execute 30 | `mage install` 31 | 32 | ## Mixin Configuration 33 | 34 | ```yaml 35 | mixins: 36 | - terraform: 37 | clientVersion: 1.0.3 38 | workingDir: myinfra 39 | initFile: providers.tf 40 | installHost: install.example.com 41 | providerHost: providers.example.com 42 | ``` 43 | 44 | ### clientVersion 45 | 46 | The Terraform client version can be specified via the `clientVersion` configuration when declaring this mixin. 47 | 48 | ### workingDir 49 | 50 | The `workingDir` configuration setting is the relative path to your terraform files. Defaults to "terraform". 51 | 52 | ### initFile 53 | 54 | Terraform providers are installed into the bundle during porter build. 55 | We recommend that you put your provider declarations into a single file, e.g. "terraform/providers.tf". 56 | Then use `initFile` to specify the relative path to this file within workingDir. 57 | This will dramatically improve Docker image layer caching and performance when building, publishing and installing the bundle. 58 | > Note: this approach isn't suitable when using terraform modules as those need to be "initilized" as well but aren't specified in the `initFile`. You shouldn't specifiy an `initFile` in this situation. 59 | 60 | ### installHost 61 | 62 | Optional host that mirrors the official terraform installation at 63 | `https://releases.hashicorp.com/*` in order to install in an air-gapped 64 | environment. 65 | 66 | ### providerHost 67 | 68 | Optional host to use as a network mirror when installing terraform providers. 69 | This needs to conform to the [Terraform registry 70 | protocol](https://www.terraform.io/docs/internals/provider-registry-protocol.html). 71 | 72 | ### User Agent Opt Out 73 | 74 | When you declare the mixin, you can disable the mixin from customizing the azure user agent string 75 | 76 | ```yaml 77 | mixins: 78 | - terraform: 79 | userAgentOptOut: true 80 | ``` 81 | 82 | By default, the terraform mixin adds the porter and mixin version to the user agent string used by the azure provider. 83 | We use this to understand which version of porter and the mixin are being used by a bundle, and assist with troubleshooting. 84 | Below is an example of what the user agent string looks like: 85 | 86 | ``` 87 | AZURE_HTTP_USER_AGENT="getporter/porter/v1.0.0 getporter/terraform/v1.2.3" 88 | ``` 89 | 90 | You can add your own custom strings to the user agent string by editing your [template Dockerfile] and setting the AZURE_HTTP_USER_AGENT environment variable. 91 | 92 | [template Dockerfile]: https://getporter.org/bundle/custom-dockerfile/ 93 | 94 | ## Terraform state 95 | 96 | ### Let Porter do the heavy lifting 97 | 98 | The simplest way to use this mixin with Porter is to let Porter track the Terraform [state](https://www.terraform.io/docs/state/index.html) as actions are executed. This can be done via a parameter of type `file` that has a source of a corresponding output (of the same `file` type). Each time the bundle is executed, the output will capture the updated state file and inject it into the next action via its parameter correlate. 99 | 100 | Here is an example setup that works with Porter v0.38: 101 | 102 | ```yaml 103 | parameters: 104 | - name: tfstate 105 | type: file 106 | # This designates the path within the installer to place the parameter value 107 | path: /cnab/app/terraform/terraform.tfstate 108 | # Here we tell Porter that the value for this parameter should come from the 'tfstate' output 109 | source: 110 | output: tfstate 111 | 112 | outputs: 113 | - name: tfstate 114 | type: file 115 | # This designates the path within the installer to read the output from 116 | path: /cnab/app/terraform/terraform.tfstate 117 | ``` 118 | 119 | If you are working with the Porter v1 prerelease, use the new state section: 120 | 121 | ```yaml 122 | state: 123 | - name: tfstate 124 | path: terraform/terraform.tfstate 125 | - name: tfvars 126 | path: terraform/terraform.tfvars.json 127 | ``` 128 | 129 | The [TabbyCats Tracker bundle](https://github.com/carolynvs/tabbycat-demo) is a good example of how to use the terraform mixin with the Porter v1 prerelease. 130 | 131 | The specified path inside the installer (`/cnab/app/terraform/terraform.tfstate`) should be where Terraform will be looking to read/write its state. For a full example bundle using this approach, see the [basic-tf-example](examples/basic-tf-example). 132 | 133 | ### Remote Backends 134 | 135 | Alternatively, state can be managed by a remote backend. When doing so, each action step needs to supply the remote backend config via `backendConfig`. In the step examples below, the configuration has key/value pairs according to the [Azurerm](https://www.terraform.io/docs/backends/types/azurerm.html) backend. 136 | 137 | ## Terraform variables file 138 | 139 | By default the mixin will create a default 140 | [`terraform.tfvars.json`](https://www.terraform.io/docs/language/values/variables.html#variable-definitions-tfvars-files) 141 | file from the `vars` block during during the install step. 142 | 143 | To use this file, a `tfvars` file parameter and output must be added to persist it for subsequent steps. 144 | 145 | This can be disabled by setting `disableVarFile` to `true` during install. 146 | 147 | Here is an example setup using the tfvar file: 148 | 149 | ```yaml 150 | parameters: 151 | - name: tfvars 152 | type: file 153 | # This designates the path within the installer to place the parameter value 154 | path: /cnab/app/terraform/terraform.tfvars.json 155 | # Here we tell Porter that the value for this parameter should come from the 'tfvars' output 156 | source: 157 | output: tfvars 158 | - name: foo 159 | type: string 160 | applyTo: 161 | - install 162 | - name: baz 163 | type: string 164 | default: blaz 165 | applyTo: 166 | - install 167 | 168 | outputs: 169 | - name: tfvars 170 | type: file 171 | # This designates the path within the installer to read the output from 172 | path: /cnab/app/terraform/terraform.tfvars.json 173 | 174 | install: 175 | - terraform: 176 | description: "Install Azure Key Vault" 177 | vars: 178 | foo: bar 179 | baz: biz 180 | outputs: 181 | - name: vault_uri 182 | upgrade: # No var block required 183 | - terraform: 184 | description: "Install Azure Key Vault" 185 | outputs: 186 | - name: vault_uri 187 | uninstall: # No var block required 188 | - terraform: 189 | description: "Install Azure Key Vault" 190 | outputs: 191 | - name: vault_uri 192 | ``` 193 | 194 | and with var file disabled 195 | 196 | ```yaml 197 | parameters: 198 | - name: foo 199 | type: string 200 | applyTo: 201 | - install 202 | - name: baz 203 | type: string 204 | default: blaz 205 | applyTo: 206 | - install 207 | 208 | install: 209 | - terraform: 210 | description: "Install Azure Key Vault" 211 | disableVarFile: true 212 | vars: 213 | foo: bar 214 | baz: biz 215 | outputs: 216 | - name: vault_uri 217 | uninstall: # Var block required 218 | - terraform: 219 | description: "Install Azure Key Vault" 220 | vars: 221 | foo: bar 222 | baz: biz 223 | ``` 224 | 225 | ## Examples 226 | 227 | ### Install 228 | 229 | ```yaml 230 | install: 231 | - terraform: 232 | description: "Install Azure Key Vault" 233 | backendConfig: 234 | key: "mybundle.tfstate" 235 | storage_account_name: "mystorageacct" 236 | container_name: "mycontainer" 237 | access_key: "myaccesskey" 238 | outputs: 239 | - name: vault_uri 240 | ``` 241 | 242 | ### Upgrade 243 | 244 | ```yaml 245 | upgrade: 246 | - terraform: 247 | description: "Upgrade Azure Key Vault" 248 | backendConfig: 249 | key: "mybundle.tfstate" 250 | storage_account_name: "mystorageacct" 251 | container_name: "mycontainer" 252 | access_key: "myaccesskey" 253 | outputs: 254 | - name: vault_uri 255 | ``` 256 | 257 | ### Invoke 258 | 259 | An invoke step is used for any custom action (not one of `install`, `upgrade` or `uninstall`). 260 | 261 | By default, the command given to `terraform` will be the step name. Here it is `show`, 262 | resulting in `terraform show` with the provided configuration. 263 | 264 | ```yaml 265 | show: 266 | - terraform: 267 | description: "Invoke 'terraform show'" 268 | backendConfig: 269 | key: "mybundle.tfstate" 270 | storage_account_name: "mystorageacct" 271 | container_name: "mycontainer" 272 | access_key: "myaccesskey" 273 | ``` 274 | 275 | Or, if the step name does not match the intended terraform command, the command 276 | can be supplied via the `arguments:` section, like so: 277 | 278 | ```yaml 279 | printVersion: 280 | - terraform: 281 | description: "Invoke 'terraform version'" 282 | arguments: 283 | - version 284 | ``` 285 | 286 | ### Uninstall 287 | 288 | ```yaml 289 | uninstall: 290 | - terraform: 291 | description: "Uninstall Azure Key Vault" 292 | backendConfig: 293 | key: "mybundle.tfstate" 294 | storage_account_name: "mystorageacct" 295 | container_name: "mycontainer" 296 | access_key: "myaccesskey" 297 | ``` 298 | 299 | See further examples in the [Examples](examples) directory 300 | 301 | ## Step Outputs 302 | 303 | As seen above, outputs can be declared for a step. All that is needed is the name of the output. 304 | 305 | For each output listed, `terraform output ` is invoked to fetch the output value 306 | from the state file for use by Porter. Outputs can be saved to the filesystem so that subsequent 307 | steps can use the file by specifying the `destinationFile` field. This is particularly useful 308 | when your terraform module creates a Kubernetes cluster. In the example below, the module 309 | creates a cluster, and then writes the kubeconfig to /root/.kube/config so that the rest of the 310 | bundle can immediately use the cluster. 311 | 312 | ```yaml 313 | install: 314 | - terraform: 315 | description: "Create a Kubernetes cluster" 316 | outputs: 317 | - name: kubeconfig 318 | destinationFile: /root/.kube/config 319 | ``` 320 | 321 | See the Porter [Outputs documentation](https://porter.sh/wiring/#outputs) on how to wire up 322 | outputs for use in a bundle. 323 | -------------------------------------------------------------------------------- /REVIEWING.md: -------------------------------------------------------------------------------- 1 | # Reviewing Guide 2 | 3 | This is part of the [Porter][porter] project and follows the Porter [Reviewing 4 | Guide][review]. 5 | 6 | [porter]: https://github.com/getporter/porter 7 | [review]: https://porter.sh/src/REVIEWING.md 8 | 9 | ## Cut a release 10 | 11 | 🧀💨 12 | 13 | All mixins follow the same process for [cutting a release][release]. There is an additional step after tagging the release. When any documentation on the readme is changed, update the matching documentation page for the mixin on the porter website: 14 | 15 | https://porter.sh/src/docs/content/mixins 16 | 17 | [release]: https://porter.sh/src/REVIEWING.md#cut-a-release 18 | -------------------------------------------------------------------------------- /build/atom-template.xml: -------------------------------------------------------------------------------- 1 | 2 | https://porter.sh 3 | Porter Mixins 4 | {{Updated}} 5 | 6 | 7 | Porter Authors 8 | https://porter.sh/mixins 9 | 10 | {{#Mixins}} 11 | 12 | {{/Mixins}} 13 | {{#Entries}} 14 | 15 | https://cdn.porter.sh/mixins/{{Mixin}}/{{Version}} 16 | {{Mixin}} @ {{Version}} 17 | {{Updated}} 18 | 19 | {{Version}} 20 | {{#Files}} 21 | 22 | {{/Files}} 23 | 24 | {{/Entries}} 25 | 26 | -------------------------------------------------------------------------------- /build/testdata/bundles/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .cnab 3 | -------------------------------------------------------------------------------- /cmd/terraform/build.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func buildBuildCommand(m *terraform.Mixin) *cobra.Command { 9 | cmd := &cobra.Command{ 10 | Use: "build", 11 | Short: "Generate Dockerfile lines for the bundle invocation image", 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return m.Build(cmd.Context()) 14 | }, 15 | } 16 | return cmd 17 | } 18 | -------------------------------------------------------------------------------- /cmd/terraform/install.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var ( 9 | commandFile string 10 | ) 11 | 12 | func buildInstallCommand(m *terraform.Mixin) *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "install", 15 | Short: "Execute the install functionality of this mixin", 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | return m.Install(cmd.Context()) 18 | }, 19 | } 20 | return cmd 21 | } 22 | -------------------------------------------------------------------------------- /cmd/terraform/invoke.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "get.porter.sh/mixin/terraform/pkg/terraform" 7 | ) 8 | 9 | func buildInvokeCommand(mixin *terraform.Mixin) *cobra.Command { 10 | opts := terraform.InvokeOptions{} 11 | 12 | cmd := &cobra.Command{ 13 | Use: "invoke", 14 | Short: "Execute the invoke functionality of this mixin", 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | return mixin.Invoke(cmd.Context(), opts) 17 | }, 18 | } 19 | flags := cmd.Flags() 20 | flags.StringVar(&opts.Action, "action", "", "Custom action name to invoke.") 21 | 22 | return cmd 23 | } 24 | -------------------------------------------------------------------------------- /cmd/terraform/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "os" 8 | "runtime/debug" 9 | 10 | "get.porter.sh/mixin/terraform/pkg/terraform" 11 | "get.porter.sh/porter/pkg/cli" 12 | "github.com/spf13/cobra" 13 | "go.opentelemetry.io/otel/attribute" 14 | ) 15 | 16 | func main() { 17 | run := func() int { 18 | ctx := context.Background() 19 | m := terraform.New() 20 | ctx, err := m.RuntimeConfig.ConfigureLogging(ctx) 21 | if err != nil { 22 | fmt.Println(err) 23 | os.Exit(cli.ExitCodeErr) 24 | } 25 | cmd := buildRootCommand(m, os.Stdin) 26 | 27 | // We don't have tracing working inside a bundle working currently. 28 | // We are using StartRootSpan anyway because it creates a TraceLogger and sets it 29 | // on the context, so we can grab it later 30 | ctx, log := m.RuntimeConfig.StartRootSpan(ctx, "exec") 31 | defer func() { 32 | // Capture panics and trace them 33 | if panicErr := recover(); panicErr != nil { 34 | log.Error(fmt.Errorf("%s", panicErr), 35 | attribute.Bool("panic", true), 36 | attribute.String("stackTrace", string(debug.Stack()))) 37 | log.EndSpan() 38 | m.Close() 39 | os.Exit(cli.ExitCodeErr) 40 | } else { 41 | log.Close() 42 | m.Close() 43 | } 44 | }() 45 | 46 | if err := cmd.ExecuteContext(ctx); err != nil { 47 | return cli.ExitCodeErr 48 | } 49 | return cli.ExitCodeSuccess 50 | } 51 | os.Exit(run()) 52 | } 53 | 54 | func buildRootCommand(m *terraform.Mixin, in io.Reader) *cobra.Command { 55 | cmd := &cobra.Command{ 56 | Use: "terraform", 57 | Long: "A terraform mixin for porter 👩🏽‍✈️", 58 | PersistentPreRun: func(cmd *cobra.Command, args []string) { 59 | // Enable swapping out stdout/stderr for testing 60 | m.In = in 61 | m.Out = cmd.OutOrStdout() 62 | m.Err = cmd.OutOrStderr() 63 | }, 64 | SilenceUsage: true, 65 | } 66 | 67 | cmd.PersistentFlags().BoolVar(&m.DebugMode, "debug", false, "Enable debug logging") 68 | 69 | cmd.AddCommand(buildVersionCommand(m)) 70 | cmd.AddCommand(buildSchemaCommand(m)) 71 | cmd.AddCommand(buildBuildCommand(m)) 72 | cmd.AddCommand(buildInstallCommand(m)) 73 | cmd.AddCommand(buildInvokeCommand(m)) 74 | cmd.AddCommand(buildUninstallCommand(m)) 75 | cmd.AddCommand(buildUpgradeCommand(m)) 76 | 77 | return cmd 78 | } 79 | -------------------------------------------------------------------------------- /cmd/terraform/schema.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func buildSchemaCommand(m *terraform.Mixin) *cobra.Command { 9 | cmd := &cobra.Command{ 10 | Use: "schema", 11 | Short: "Print the json schema for the mixin", 12 | Run: func(cmd *cobra.Command, args []string) { 13 | m.PrintSchema() 14 | }, 15 | } 16 | return cmd 17 | } 18 | -------------------------------------------------------------------------------- /cmd/terraform/uninstall.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func buildUninstallCommand(m *terraform.Mixin) *cobra.Command { 9 | cmd := &cobra.Command{ 10 | Use: "uninstall", 11 | Short: "Execute the uninstall functionality of this mixin", 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return m.Uninstall(cmd.Context()) 14 | }, 15 | } 16 | return cmd 17 | } 18 | -------------------------------------------------------------------------------- /cmd/terraform/upgrade.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func buildUpgradeCommand(m *terraform.Mixin) *cobra.Command { 9 | cmd := &cobra.Command{ 10 | Use: "upgrade", 11 | Short: "Execute the upgrade functionality of this mixin", 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return m.Upgrade(cmd.Context()) 14 | }, 15 | } 16 | return cmd 17 | } 18 | -------------------------------------------------------------------------------- /cmd/terraform/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg/terraform" 5 | "get.porter.sh/porter/pkg/porter/version" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func buildVersionCommand(m *terraform.Mixin) *cobra.Command { 10 | opts := version.Options{} 11 | 12 | cmd := &cobra.Command{ 13 | Use: "version", 14 | Short: "Print the mixin version", 15 | PreRunE: func(cmd *cobra.Command, args []string) error { 16 | return opts.Validate() 17 | }, 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | return m.PrintVersion(opts) 20 | }, 21 | } 22 | 23 | f := cmd.Flags() 24 | f.StringVarP(&opts.RawFormat, "output", "o", string(version.DefaultVersionFormat), 25 | "Specify an output format. Allowed values: json, plaintext") 26 | 27 | return cmd 28 | } 29 | -------------------------------------------------------------------------------- /examples/azure-aks/porter.yaml: -------------------------------------------------------------------------------- 1 | schemaVersion: 1.0.0 2 | name: terraform-aks 3 | version: 0.2.0 4 | registry: ghcr.io/getporter 5 | 6 | credentials: 7 | - name: subscription_id 8 | env: TF_VAR_subscription_id 9 | 10 | - name: tenant_id 11 | env: TF_VAR_tenant_id 12 | 13 | - name: client_id 14 | env: TF_VAR_client_id 15 | 16 | - name: client_secret 17 | env: TF_VAR_client_secret 18 | 19 | - name: ssh_public_key 20 | env: TF_VAR_ssh_public_key 21 | 22 | - name: backend_storage_access_key 23 | env: TF_VAR_backend_storage_access_key 24 | 25 | - name: backend_storage_account 26 | env: TF_VAR_backend_storage_account 27 | 28 | - name: backend_storage_container 29 | env: TF_VAR_backend_storage_container 30 | 31 | parameters: 32 | - name: location 33 | type: string 34 | default: "East US" 35 | env: TF_VAR_location 36 | 37 | - name: kubernetes_version 38 | type: string 39 | default: "1.21.2" 40 | env: TF_VAR_kubernetes_version 41 | 42 | - name: agent_count 43 | type: integer 44 | default: 1 45 | env: TF_VAR_agent_count 46 | 47 | - name: dns_prefix 48 | type: string 49 | default: "porteraks" 50 | env: TF_VAR_dns_prefix 51 | 52 | - name: cluster_name 53 | type: string 54 | default: "porteraks" 55 | env: TF_VAR_cluster_name 56 | 57 | - name: resource_group_name 58 | type: string 59 | default: "porteraks" 60 | env: TF_VAR_resource_group_name 61 | 62 | mixins: 63 | - terraform: 64 | initFile: providers.tf 65 | 66 | customActions: 67 | show: 68 | description: "Invoke 'terraform show'" 69 | modifies: false 70 | 71 | install: 72 | - terraform: 73 | description: "Install Azure Kubernetes Service" 74 | backendConfig: 75 | key: ${ bundle.name }.tfstate 76 | storage_account_name: ${ bundle.credentials.backend_storage_account } 77 | container_name: ${ bundle.credentials.backend_storage_container } 78 | access_key: ${ bundle.credentials.backend_storage_access_key } 79 | 80 | upgrade: 81 | - terraform: 82 | description: "Upgrade Azure Kubernetes Service" 83 | backendConfig: 84 | key: ${ bundle.name }.tfstate 85 | storage_account_name: ${ bundle.credentials.backend_storage_account } 86 | container_name: ${ bundle.credentials.backend_storage_container } 87 | access_key: ${ bundle.credentials.backend_storage_access_key } 88 | 89 | show: 90 | - terraform: 91 | description: "Invoke 'terraform show'" 92 | backendConfig: 93 | key: ${ bundle.name }.tfstate 94 | storage_account_name: ${ bundle.credentials.backend_storage_account } 95 | container_name: ${ bundle.credentials.backend_storage_container } 96 | access_key: ${ bundle.credentials.backend_storage_access_key } 97 | 98 | uninstall: 99 | - terraform: 100 | description: "Uninstall Azure Kubernetes Service" 101 | backendConfig: 102 | key: ${ bundle.name }.tfstate 103 | storage_account_name: ${ bundle.credentials.backend_storage_account } 104 | container_name: ${ bundle.credentials.backend_storage_container } 105 | access_key: ${ bundle.credentials.backend_storage_access_key } 106 | -------------------------------------------------------------------------------- /examples/azure-aks/terraform/aks.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "k8s" { 2 | name = var.resource_group_name 3 | location = var.location 4 | } 5 | 6 | resource "azurerm_kubernetes_cluster" "k8s" { 7 | name = var.cluster_name 8 | location = azurerm_resource_group.k8s.location 9 | resource_group_name = azurerm_resource_group.k8s.name 10 | dns_prefix = var.dns_prefix 11 | kubernetes_version = var.kubernetes_version 12 | 13 | linux_profile { 14 | admin_username = "ubuntu" 15 | 16 | ssh_key { 17 | key_data = var.ssh_public_key 18 | } 19 | } 20 | 21 | default_node_pool { 22 | name = "default" 23 | node_count = var.agent_count 24 | vm_size = "Standard_DS2_v2" 25 | os_disk_size_gb = 30 26 | } 27 | 28 | service_principal { 29 | client_id = var.client_id 30 | client_secret = var.client_secret 31 | } 32 | 33 | tags = { 34 | Environment = "Development" 35 | } 36 | } -------------------------------------------------------------------------------- /examples/azure-aks/terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "client_key" { 2 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.client_key 3 | sensitive = true 4 | } 5 | 6 | output "client_certificate" { 7 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.client_certificate 8 | sensitive = true 9 | } 10 | 11 | output "cluster_ca_certificate" { 12 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.cluster_ca_certificate 13 | sensitive = true 14 | } 15 | 16 | output "cluster_username" { 17 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.username 18 | } 19 | 20 | output "cluster_password" { 21 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.password 22 | sensitive = true 23 | } 24 | 25 | output "kube_config" { 26 | value = azurerm_kubernetes_cluster.k8s.kube_config_raw 27 | sensitive = true 28 | } 29 | 30 | output "host" { 31 | value = azurerm_kubernetes_cluster.k8s.kube_config.0.host 32 | } -------------------------------------------------------------------------------- /examples/azure-aks/terraform/params.tf: -------------------------------------------------------------------------------- 1 | variable "client_id" {} 2 | variable "client_secret" {} 3 | variable "tenant_id" {} 4 | variable "subscription_id" {} 5 | variable "kubernetes_version" {} 6 | 7 | variable "agent_count" { 8 | default = 1 9 | } 10 | 11 | variable "ssh_public_key" { 12 | default = "~/.ssh/id_rsa.pub" 13 | } 14 | 15 | variable "dns_prefix" { 16 | default = "akstest" 17 | } 18 | 19 | variable "cluster_name" { 20 | default = "akstest" 21 | } 22 | 23 | variable "resource_group_name" { 24 | default = "azure-akstest" 25 | } 26 | 27 | variable location { 28 | default = "East US" 29 | } -------------------------------------------------------------------------------- /examples/azure-aks/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | subscription_id = var.subscription_id 4 | client_id = var.client_id 5 | client_secret = var.client_secret 6 | tenant_id = var.tenant_id 7 | } 8 | 9 | terraform { 10 | required_version = "1.2.9" 11 | required_providers { 12 | azurerm = { 13 | source = "hashicorp/azurerm" 14 | version = "=3.22.0" 15 | } 16 | } 17 | backend "azurerm" {} 18 | } 19 | -------------------------------------------------------------------------------- /examples/azure-keyvault/porter.yaml: -------------------------------------------------------------------------------- 1 | 2 | schemaVersion: 1.0.0 3 | name: terraform-keyvault 4 | version: 0.2.0 5 | registry: ghcr.io/getporter 6 | 7 | credentials: 8 | - name: subscription_id 9 | env: TF_VAR_subscription_id 10 | 11 | - name: tenant_id 12 | env: TF_VAR_tenant_id 13 | 14 | - name: client_id 15 | env: TF_VAR_client_id 16 | 17 | - name: client_secret 18 | env: TF_VAR_client_secret 19 | 20 | - name: backend_storage_access_key 21 | env: TF_VAR_backend_storage_access_key 22 | 23 | - name: backend_storage_account 24 | env: TF_VAR_backend_storage_account 25 | 26 | - name: backend_storage_container 27 | env: TF_VAR_backend_storage_container 28 | 29 | parameters: 30 | - name: keyvault_name 31 | type: string 32 | default: "porterkvtest" 33 | env: TF_VAR_keyvault_name 34 | 35 | - name: location 36 | type: string 37 | default: "East US" 38 | env: TF_VAR_location 39 | 40 | - name: resource_group_name 41 | type: string 42 | default: "porterkvtest" 43 | env: TF_VAR_resource_group_name 44 | 45 | mixins: 46 | - exec 47 | - terraform: 48 | initFile: providers.tf 49 | 50 | customActions: 51 | show: 52 | description: "Invoke 'terraform show'" 53 | modifies: false 54 | 55 | install: 56 | - terraform: 57 | description: "Install Azure Key Vault" 58 | backendConfig: 59 | key: ${ bundle.name }.tfstate 60 | storage_account_name: ${ bundle.credentials.backend_storage_account } 61 | container_name: ${ bundle.credentials.backend_storage_container } 62 | access_key: ${ bundle.credentials.backend_storage_access_key } 63 | outputs: 64 | - name: vault_uri 65 | 66 | upgrade: 67 | - terraform: 68 | description: "Upgrade Azure Key Vault" 69 | backendConfig: 70 | key: ${ bundle.name }.tfstate 71 | storage_account_name: ${ bundle.credentials.backend_storage_account } 72 | container_name: ${ bundle.credentials.backend_storage_container } 73 | access_key: ${ bundle.credentials.backend_storage_access_key } 74 | outputs: 75 | - name: vault_uri 76 | 77 | show: 78 | - terraform: 79 | description: "Invoke 'terraform show'" 80 | backendConfig: 81 | key: ${ bundle.name }.tfstate 82 | storage_account_name: ${ bundle.credentials.backend_storage_account } 83 | container_name: ${ bundle.credentials.backend_storage_container } 84 | access_key: ${ bundle.credentials.backend_storage_access_key } 85 | outputs: 86 | - name: vault_uri 87 | 88 | uninstall: 89 | - terraform: 90 | description: "Uninstall Azure Key Vault" 91 | backendConfig: 92 | key: ${ bundle.name }.tfstate 93 | storage_account_name: ${ bundle.credentials.backend_storage_account } 94 | container_name: ${ bundle.credentials.backend_storage_container } 95 | access_key: ${ bundle.credentials.backend_storage_access_key } 96 | -------------------------------------------------------------------------------- /examples/azure-keyvault/terraform/keyvault.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "test" { 2 | name = var.resource_group_name 3 | location = var.location 4 | } 5 | 6 | resource "azurerm_key_vault" "test" { 7 | name = var.keyvault_name 8 | location = azurerm_resource_group.test.location 9 | resource_group_name = azurerm_resource_group.test.name 10 | 11 | enabled_for_disk_encryption = true 12 | tenant_id = var.tenant_id 13 | 14 | sku_name = "standard" 15 | 16 | access_policy { 17 | tenant_id = var.tenant_id 18 | object_id = var.client_id 19 | 20 | key_permissions = [ 21 | "get", 22 | ] 23 | 24 | secret_permissions = [ 25 | "get", 26 | ] 27 | 28 | storage_permissions = [ 29 | "get", 30 | ] 31 | } 32 | 33 | network_acls { 34 | default_action = "Deny" 35 | bypass = "AzureServices" 36 | } 37 | 38 | tags = { 39 | environment = "Production" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/azure-keyvault/terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azurerm_key_vault.test.id 3 | } 4 | 5 | output "vault_uri" { 6 | value = azurerm_key_vault.test.vault_uri 7 | } -------------------------------------------------------------------------------- /examples/azure-keyvault/terraform/params.tf: -------------------------------------------------------------------------------- 1 | variable "client_id" {} 2 | variable "client_secret" {} 3 | variable "tenant_id" {} 4 | variable "subscription_id" {} 5 | 6 | variable "resource_group_name" { 7 | default = "azure-kvtest" 8 | } 9 | 10 | variable location { 11 | default = "East US" 12 | } 13 | 14 | variable "keyvault_name" {} -------------------------------------------------------------------------------- /examples/azure-keyvault/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | subscription_id = var.subscription_id 4 | client_id = var.client_id 5 | client_secret = var.client_secret 6 | tenant_id = var.tenant_id 7 | } 8 | 9 | terraform { 10 | required_version = "1.2.9" 11 | required_providers { 12 | azurerm = { 13 | source = "hashicorp/azurerm" 14 | version = "=3.22.0" 15 | } 16 | } 17 | backend "azurerm" {} 18 | } 19 | -------------------------------------------------------------------------------- /examples/basic-tf-example/.gitignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .cnab 3 | -------------------------------------------------------------------------------- /examples/basic-tf-example/README.md: -------------------------------------------------------------------------------- 1 | # Basic Terraform Example Bundle 2 | 3 | This example demonstrates how to define and use variables and outputs of different data types in a bundle. 4 | 5 | ## Try it out 6 | 7 | ``` 8 | cd examples/basic-tf-example 9 | porter build 10 | porter install 11 | porter upgrade 12 | porter uninstall 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/basic-tf-example/porter.yaml: -------------------------------------------------------------------------------- 1 | schemaVersion: 1.0.0 2 | name: basic-tf-example 3 | version: 0.3.0 4 | registry: ghcr.io/getporter 5 | 6 | parameters: 7 | - name: file_contents 8 | type: string 9 | applyTo: 10 | - install 11 | - upgrade 12 | default: "foo!" 13 | - name: map_var 14 | type: object 15 | applyTo: 16 | - install 17 | - upgrade 18 | default: { "foo": "bar" } 19 | - name: array_var 20 | type: array 21 | applyTo: 22 | - install 23 | - upgrade 24 | default: 25 | [ 26 | "mylist", 27 | "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz", 28 | ] 29 | - name: boolean_var 30 | type: boolean 31 | applyTo: 32 | - install 33 | - upgrade 34 | default: true 35 | - name: number_var 36 | type: number 37 | applyTo: 38 | - install 39 | - upgrade 40 | default: 1 41 | - name: json_encoded_html_string_var 42 | type: string 43 | applyTo: 44 | - install 45 | - upgrade 46 | default: "testing?connection&string=<>" 47 | - name: complex_object_var 48 | type: object 49 | applyTo: 50 | - install 51 | - upgrade 52 | default: 53 | { 54 | "nested_object": 55 | { "internal_value": "https://my.connection.com?test&test=$hello" }, 56 | "top_value": "https://my.service?test=$id<>", 57 | } 58 | 59 | state: 60 | - name: tfstate 61 | path: terraform/terraform.tfstate 62 | - name: tfvars 63 | path: terraform/terraform.tfvars.json 64 | 65 | mixins: 66 | - terraform: 67 | clientVersion: 1.0.0 68 | 69 | install: 70 | - terraform: 71 | description: "Install Terraform assets" 72 | vars: 73 | file_contents: ${bundle.parameters.file_contents} 74 | map_var: ${bundle.parameters.map_var} 75 | array_var: ${bundle.parameters.array_var} 76 | boolean_var: ${bundle.parameters.boolean_var} 77 | number_var: ${bundle.parameters.number_var} 78 | json_encoded_html_string_var: ${bundle.parameters.json_encoded_html_string_var} 79 | complex_object_var: ${bundle.parameters.complex_object_var} 80 | outputs: 81 | - name: file_contents 82 | - name: map_var 83 | - name: array_var 84 | - name: boolean_var 85 | - name: number_var 86 | - name: json_encoded_html_string_var 87 | - name: complex_object_var 88 | 89 | upgrade: 90 | - terraform: 91 | description: "Upgrade Terraform assets" 92 | vars: 93 | file_contents: ${bundle.parameters.file_contents} 94 | map_var: ${bundle.parameters.map_var} 95 | array_var: ${bundle.parameters.array_var} 96 | boolean_var: ${bundle.parameters.boolean_var} 97 | number_var: ${bundle.parameters.number_var} 98 | json_encoded_html_string_var: ${bundle.parameters.json_encoded_html_string_var} 99 | complex_object_var: ${bundle.parameters.complex_object_var} 100 | outputs: 101 | - name: file_contents 102 | - name: map_var 103 | - name: array_var 104 | - name: boolean_var 105 | - name: number_var 106 | - name: json_encoded_html_string_var 107 | - name: complex_object_var 108 | 109 | show: 110 | - terraform: 111 | description: "Invoke 'terraform show'" 112 | 113 | plan: 114 | - terraform: 115 | description: "Invoke 'terraform plan'" 116 | # Note: this can't be 'version:' as this would conflict with top-level field 117 | # Hence the need for the 'arguments:' override 118 | printVersion: 119 | - terraform: 120 | description: "Invoke 'terraform version'" 121 | arguments: 122 | - "version" 123 | 124 | uninstall: 125 | - terraform: 126 | description: "Uninstall Terraform assets" 127 | 128 | outputs: 129 | - name: file_contents 130 | type: string 131 | applyTo: 132 | - install 133 | - upgrade 134 | - name: map_var 135 | type: object 136 | applyTo: 137 | - install 138 | - upgrade 139 | - name: array_var 140 | type: array 141 | applyTo: 142 | - install 143 | - upgrade 144 | - name: boolean_var 145 | type: boolean 146 | applyTo: 147 | - install 148 | - upgrade 149 | - name: number_var 150 | type: number 151 | applyTo: 152 | - install 153 | - upgrade 154 | - name: json_encoded_html_string_var 155 | type: string 156 | applyTo: 157 | - install 158 | - upgrade 159 | - name: complex_object_var 160 | type: object 161 | applyTo: 162 | - install 163 | - upgrade 164 | -------------------------------------------------------------------------------- /examples/basic-tf-example/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "local_file" "foo" { 2 | content = var.file_contents 3 | filename = "${path.module}/foo" 4 | } 5 | -------------------------------------------------------------------------------- /examples/basic-tf-example/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "file_contents" { 2 | value = var.file_contents 3 | description = "Contents of the file 'foo'" 4 | } 5 | 6 | output "map_var" { 7 | value = var.map_var 8 | } 9 | 10 | output "array_var" { 11 | value = var.array_var 12 | } 13 | 14 | output "boolean_var" { 15 | value = var.boolean_var 16 | } 17 | 18 | output "number_var" { 19 | value = var.number_var 20 | } 21 | 22 | output "json_encoded_html_string_var" { 23 | value = var.json_encoded_html_string_var 24 | } 25 | 26 | output "complex_object_var" { 27 | value = var.complex_object_var 28 | } 29 | -------------------------------------------------------------------------------- /examples/basic-tf-example/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "file_contents" { 2 | description = "Contents of the file 'foo'" 3 | default = "bar" 4 | } 5 | 6 | variable "map_var" { 7 | description = "Map variable" 8 | type = map(string) 9 | default = { foo = "bar" } 10 | } 11 | 12 | variable "array_var" { 13 | description = "Array Variable" 14 | type = list(any) 15 | default = ["mylist"] 16 | } 17 | 18 | variable "boolean_var" { 19 | description = "Boolean Variable" 20 | type = bool 21 | default = false 22 | } 23 | 24 | variable "number_var" { 25 | description = "Number Variable" 26 | type = number 27 | default = 0 28 | } 29 | 30 | variable "json_encoded_html_string_var" { 31 | description = "String variable with html characters that should not be escaped" 32 | type = string 33 | default = "hello&world" 34 | } 35 | 36 | variable "complex_object_var" { 37 | description = "Object variable" 38 | type = object({ 39 | top_value = string 40 | nested_object = object({ 41 | internal_value = string 42 | }) 43 | }) 44 | default = { 45 | top_value = "top_value" 46 | nested_object = { 47 | internal_value = "internal" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module get.porter.sh/mixin/terraform 2 | 3 | go 1.23.9 4 | 5 | replace ( 6 | // These are replace directives copied from porter 7 | // When you use a newer version of Porter, if you run into trouble with go mod tidy 8 | // Copy any additional replace directives from Porter's go.mod file 9 | // They must match the replaces used by porter everything to compile 10 | // See https://github.com/hashicorp/go-plugin/pull/127 and 11 | // https://github.com/hashicorp/go-plugin/pull/163 12 | // Also includes branches we haven't PR'd yet: capture-yamux-logs, context-cancellation 13 | // Tagged from v1.4.4, the porter branch 14 | github.com/hashicorp/go-plugin => github.com/getporter/go-plugin v1.4.4-porter.1 15 | // Fixes https://github.com/spf13/viper/issues/761 16 | github.com/spf13/viper => github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363 17 | ) 18 | 19 | require ( 20 | get.porter.sh/magefiles v0.6.11 21 | get.porter.sh/porter v1.2.1 22 | github.com/PaesslerAG/jsonpath v0.1.1 23 | github.com/carolynvs/magex v0.9.0 24 | github.com/ghodss/yaml v1.0.0 25 | github.com/hashicorp/go-multierror v1.1.1 26 | github.com/pkg/errors v0.9.1 27 | github.com/spf13/cobra v1.9.1 28 | github.com/stretchr/testify v1.10.0 29 | github.com/tidwall/gjson v1.18.0 30 | github.com/xeipuuv/gojsonschema v1.2.0 31 | go.opentelemetry.io/otel v1.36.0 32 | gopkg.in/yaml.v2 v2.4.0 33 | ) 34 | 35 | require ( 36 | github.com/Masterminds/semver v1.5.0 // indirect 37 | github.com/Masterminds/semver/v3 v3.3.1 // indirect 38 | github.com/PaesslerAG/gval v1.2.3 // indirect 39 | github.com/PuerkitoBio/goquery v1.10.0 // indirect 40 | github.com/andybalholm/cascadia v1.3.2 // indirect 41 | github.com/beorn7/perks v1.0.1 // indirect 42 | github.com/carolynvs/aferox v0.3.0 // indirect 43 | github.com/cbroglie/mustache v1.4.0 // indirect 44 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 45 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 46 | github.com/cnabio/cnab-go v0.25.2 // indirect 47 | github.com/cnabio/cnab-to-oci v0.4.1 // indirect 48 | github.com/containerd/log v0.1.0 // indirect 49 | github.com/containerd/stargz-snapshotter/estargz v0.16.1 // indirect 50 | github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect 51 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 52 | github.com/distribution/reference v0.6.0 // indirect 53 | github.com/docker/cli v27.4.1+incompatible // indirect 54 | github.com/docker/distribution v2.8.3+incompatible // indirect 55 | github.com/docker/docker v27.4.1+incompatible // indirect 56 | github.com/docker/docker-credential-helpers v0.8.2 // indirect 57 | github.com/docker/go-connections v0.5.0 // indirect 58 | github.com/docker/go-metrics v0.0.1 // indirect 59 | github.com/dustin/go-humanize v1.0.1 // indirect 60 | github.com/fatih/color v1.18.0 // indirect 61 | github.com/fsnotify/fsnotify v1.8.0 // indirect 62 | github.com/go-logr/logr v1.4.2 // indirect 63 | github.com/go-logr/stdr v1.2.2 // indirect 64 | github.com/goccy/go-yaml v1.14.0 // indirect 65 | github.com/google/go-containerregistry v0.20.2 // indirect 66 | github.com/google/uuid v1.6.0 // indirect 67 | github.com/gorilla/mux v1.8.1 // indirect 68 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect 69 | github.com/hashicorp/errwrap v1.1.0 // indirect 70 | github.com/hashicorp/go-hclog v1.6.3 // indirect 71 | github.com/hashicorp/hcl v1.0.0 // indirect 72 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 73 | github.com/jeremywohl/flatten v1.0.1 // indirect 74 | github.com/klauspost/compress v1.17.11 // indirect 75 | github.com/magefile/mage v1.15.0 // indirect 76 | github.com/magiconair/properties v1.8.7 // indirect 77 | github.com/mattn/go-colorable v0.1.13 // indirect 78 | github.com/mattn/go-isatty v0.0.20 // indirect 79 | github.com/mattn/go-runewidth v0.0.16 // indirect 80 | github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 // indirect 81 | github.com/mitchellh/go-homedir v1.1.0 // indirect 82 | github.com/mitchellh/mapstructure v1.5.0 // indirect 83 | github.com/mmcdole/gofeed v1.3.0 // indirect 84 | github.com/mmcdole/goxpp v1.1.1 // indirect 85 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 86 | github.com/oklog/ulid v1.3.1 // indirect 87 | github.com/olekukonko/tablewriter v0.0.5 // indirect 88 | github.com/opencontainers/go-digest v1.0.0 // indirect 89 | github.com/opencontainers/image-spec v1.1.0 // indirect 90 | github.com/osteele/liquid v1.6.0 // indirect 91 | github.com/osteele/tuesday v1.0.3 // indirect 92 | github.com/pelletier/go-toml v1.9.5 // indirect 93 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 94 | github.com/prometheus/client_golang v1.20.5 // indirect 95 | github.com/prometheus/client_model v0.6.1 // indirect 96 | github.com/prometheus/common v0.60.1 // indirect 97 | github.com/prometheus/procfs v0.15.1 // indirect 98 | github.com/qri-io/jsonpointer v0.1.1 // indirect 99 | github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e // indirect 100 | github.com/rivo/uniseg v0.4.7 // indirect 101 | github.com/sergi/go-diff v1.2.0 // indirect 102 | github.com/shopspring/decimal v1.4.0 // indirect 103 | github.com/sirupsen/logrus v1.9.3 // indirect 104 | github.com/spf13/afero v1.11.0 // indirect 105 | github.com/spf13/cast v1.7.0 // indirect 106 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 107 | github.com/spf13/pflag v1.0.6 // indirect 108 | github.com/spf13/viper v1.19.0 // indirect 109 | github.com/subosito/gotenv v1.6.0 // indirect 110 | github.com/tidwall/match v1.1.1 // indirect 111 | github.com/tidwall/pretty v1.2.1 // indirect 112 | github.com/uwu-tools/magex v0.10.1 // indirect 113 | github.com/vbatts/tar-split v0.11.6 // indirect 114 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect 115 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 116 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 117 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect 118 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect 119 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect 120 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 // indirect 121 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 122 | go.opentelemetry.io/otel/sdk v1.33.0 // indirect 123 | go.opentelemetry.io/otel/trace v1.36.0 // indirect 124 | go.opentelemetry.io/proto/otlp v1.4.0 // indirect 125 | go.uber.org/multierr v1.11.0 // indirect 126 | go.uber.org/zap v1.27.0 // indirect 127 | golang.org/x/net v0.38.0 // indirect 128 | golang.org/x/sync v0.13.0 // indirect 129 | golang.org/x/sys v0.31.0 // indirect 130 | golang.org/x/text v0.23.0 // indirect 131 | google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect 132 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect 133 | google.golang.org/grpc v1.69.2 // indirect 134 | google.golang.org/protobuf v1.36.0 // indirect 135 | gopkg.in/ini.v1 v1.67.0 // indirect 136 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect 137 | gopkg.in/yaml.v3 v3.0.1 // indirect 138 | ) 139 | -------------------------------------------------------------------------------- /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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | get.porter.sh/magefiles v0.6.11 h1:SrRjMVZgbr0l4Usxb0PSQrqgJTU6U8+kbwelbAvw8UQ= 15 | get.porter.sh/magefiles v0.6.11/go.mod h1:cOhVvwcNpBEkQdq7HNHaPXDf1WjudM0/Tist+xINtw0= 16 | get.porter.sh/porter v1.2.1 h1:c4JffFkibxNXbUA6WsBcwZMehz5D3GwJHlRCre2VcGM= 17 | get.porter.sh/porter v1.2.1/go.mod h1:bRVeCerT2rg7FVB9pySSbWCJoyYr2rQrw2EU1w90yb0= 18 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 19 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 20 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 21 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 22 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 23 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 24 | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= 25 | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 26 | github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= 27 | github.com/PaesslerAG/gval v1.2.3 h1:Z3B/zLyWvqxjUtkIOEkFauqLnQn8Q37F1Q+uAjLXgMw= 28 | github.com/PaesslerAG/gval v1.2.3/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= 29 | github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= 30 | github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk= 31 | github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= 32 | github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= 33 | github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= 34 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 35 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 36 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 37 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 38 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 39 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 40 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 41 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 42 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 43 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 44 | github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= 45 | github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 46 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 47 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 48 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 49 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 50 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 51 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 52 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 53 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 54 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 55 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 56 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 57 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 58 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 59 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 60 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 61 | github.com/carolynvs/aferox v0.3.0 h1:CMT50zX88amTMbFfFIWSTKRVRaOw6sejUMbbKiCD4zE= 62 | github.com/carolynvs/aferox v0.3.0/go.mod h1:eb7CHGIO33CCZS//xtnblvPZbuuZMv0p1VbhiSwZnH4= 63 | github.com/carolynvs/magex v0.9.0 h1:fWe7oshGv6zuei5Z6EI95RSlOKjIifBZ26myB9G+m/I= 64 | github.com/carolynvs/magex v0.9.0/go.mod h1:H1LW6RYJ/sNbisMmPe9E73aJZa8geKLKK9mBWLWz3ek= 65 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 66 | github.com/cbroglie/mustache v1.4.0 h1:Azg0dVhxTml5me+7PsZ7WPrQq1Gkf3WApcHMjMprYoU= 67 | github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM= 68 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 69 | github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 70 | github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 71 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 72 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 73 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 74 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 75 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 76 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 77 | github.com/cnabio/cnab-go v0.25.2 h1:TuWZu6jfFbuNhigdnWPD/5I8UNQvpExCT3n8uNhPz6s= 78 | github.com/cnabio/cnab-go v0.25.2/go.mod h1:nB1VbiyH9kIQv+5zAC/Y6dpsrsvgNzq2CH55fpkWHbE= 79 | github.com/cnabio/cnab-to-oci v0.4.1 h1:5gVa57n41XiQ63ftdXpYbePxVccmvVbf2t/LzIM4upQ= 80 | github.com/cnabio/cnab-to-oci v0.4.1/go.mod h1:iJPkrzXWWjGqZKlrI35DCgomT94/K/BLYTg7Exnw7OI= 81 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 82 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 83 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 84 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 85 | github.com/containerd/stargz-snapshotter/estargz v0.16.1 h1:7YswwU6746cJBN3p3l65JRk3+NZL7bap9Y6E3YeYowk= 86 | github.com/containerd/stargz-snapshotter/estargz v0.16.1/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= 87 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 88 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 89 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 90 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 91 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 92 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 93 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 94 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 95 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 96 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 97 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 98 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 99 | github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f h1:eHnXnuK47UlSTOQexbzxAZfekVz6i+LKRdj1CU5DPaM= 100 | github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= 101 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 102 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 103 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 104 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 105 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 106 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 107 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 108 | github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI= 109 | github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 110 | github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= 111 | github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 112 | github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= 113 | github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 114 | github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= 115 | github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= 116 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 117 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 118 | github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= 119 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 120 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= 121 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 122 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 123 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 124 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 125 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 126 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 127 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 128 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 129 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 130 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 131 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 132 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 133 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 134 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 135 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 136 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 137 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 138 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 139 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 140 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 141 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 142 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 143 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 144 | github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= 145 | github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 146 | github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363 h1:OI6oF+tvVpVTDbIAbKYwDQlznmWJJNFgMSFxCbgxsME= 147 | github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363/go.mod h1:TMyCLryAYE7EgeSfzTbjQLLiLkOjZeJuiFVGK5FYwog= 148 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 149 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 150 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 151 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 152 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 153 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 154 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 155 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 156 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 157 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 158 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 159 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 160 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 161 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 162 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 163 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 164 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 165 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 166 | github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y= 167 | github.com/goccy/go-yaml v1.14.0 h1:G/NDXJvf1CX0FshjxKn2AOL0MnrxsSJNpY9FpvMRblw= 168 | github.com/goccy/go-yaml v1.14.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= 169 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 170 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 171 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 172 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 173 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 174 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 175 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 176 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 177 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 178 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 179 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 180 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 181 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 182 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 183 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 184 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 185 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 186 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 187 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 188 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 189 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 190 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 191 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 192 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 193 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 194 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 195 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 196 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 197 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 198 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 199 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 200 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 201 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 202 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 203 | github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= 204 | github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= 205 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 206 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 207 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 208 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 209 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 210 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 211 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 212 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 213 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 214 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 215 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 216 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 217 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 218 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 219 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 220 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 221 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 222 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 223 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 224 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 225 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= 226 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= 227 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 228 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 229 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 230 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 231 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 232 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 233 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 234 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 235 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 236 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 237 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 238 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 239 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 240 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 241 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 242 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 243 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 244 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 245 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 246 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 247 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 248 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 252 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 253 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 254 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 255 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 256 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 257 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 258 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 259 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 260 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 261 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 262 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 263 | github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= 264 | github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= 265 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 266 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 267 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 268 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 269 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 270 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 271 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 272 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 273 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 274 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 275 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 276 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 277 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 278 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 279 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 280 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 281 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 282 | github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= 283 | github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= 284 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 285 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 286 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 287 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 288 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 292 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 293 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 294 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 295 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 296 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 297 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 298 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 299 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 300 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 301 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 302 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 303 | github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 304 | github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= 305 | github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 306 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 307 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 308 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 309 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 310 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 311 | github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 312 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 313 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 314 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 315 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 316 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 317 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 318 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 319 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 320 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 321 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 322 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 323 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 324 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 325 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 326 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 327 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 328 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 329 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 330 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 331 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 332 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 333 | github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 h1:lPmsut5Sk7eK2BmDXuvNEvMbT7MkAJBu64Yxr7iJ6nk= 334 | github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37/go.mod h1:dYWq+UWoFCDY1TndvFUQuhBbIYmZpjreC8adEAx93zE= 335 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 336 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 337 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 338 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 339 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 340 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 341 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 342 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 343 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 344 | github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 345 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 346 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 347 | github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4= 348 | github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE= 349 | github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8= 350 | github.com/mmcdole/goxpp v1.1.1/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= 351 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 352 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 353 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 354 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 355 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 356 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 357 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 358 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 359 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 360 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 361 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 362 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 363 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 364 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 365 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 366 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 367 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 368 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 369 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 370 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 371 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 372 | github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= 373 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 374 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 375 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 376 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 377 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 378 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 379 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 380 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 381 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 382 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 383 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 384 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 385 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 386 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 387 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 388 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 389 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 390 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 391 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 392 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 393 | github.com/osteele/liquid v1.6.0 h1:bTsbZjPIr7F+pU+K6o//Y5//W4McMzvUlMXWGOVvpc0= 394 | github.com/osteele/liquid v1.6.0/go.mod h1:xU0Z2dn2hOQIEFEWNmeltOmCtfhtoW/2fCyiNQeNG+U= 395 | github.com/osteele/tuesday v1.0.3 h1:SrCmo6sWwSgnvs1bivmXLvD7Ko9+aJvvkmDjB5G4FTU= 396 | github.com/osteele/tuesday v1.0.3/go.mod h1:pREKpE+L03UFuR+hiznj3q7j3qB1rUZ4XfKejwWFF2M= 397 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 398 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 399 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 400 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 401 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 402 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 403 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 404 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 405 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 406 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 407 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 408 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 409 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 410 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 411 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 412 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 413 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 414 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 415 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 416 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 417 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 418 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 419 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 420 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 421 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 422 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 423 | github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= 424 | github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= 425 | github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= 426 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 427 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 428 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 429 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 430 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 431 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 432 | github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= 433 | github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= 434 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 435 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 436 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 437 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 438 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 439 | github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 440 | github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= 441 | github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 442 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 443 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 444 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 445 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 446 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 447 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 448 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 449 | github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 450 | github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 451 | github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= 452 | github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= 453 | github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e h1:gqHzseevuZPr3oOLES1nrPO3exQfeTKUiPcJub5axVs= 454 | github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI= 455 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 456 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 457 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 458 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 459 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 460 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 461 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 462 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 463 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 464 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 465 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 466 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 467 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 468 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 469 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 470 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 471 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 472 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 473 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 474 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 475 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 476 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 477 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 478 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 479 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 480 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 481 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 482 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 483 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 484 | github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= 485 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 486 | github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 487 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 488 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 489 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 490 | github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= 491 | github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 492 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 493 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 494 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 495 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 496 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 497 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 498 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 499 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 500 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 501 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 502 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 503 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 504 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 505 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 506 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 507 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 508 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 509 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 510 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 511 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 512 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 513 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 514 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 515 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 516 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 517 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 518 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 519 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 520 | github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= 521 | github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 522 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 523 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 524 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 525 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 526 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 527 | github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= 528 | github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= 529 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 530 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 531 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 532 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 533 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 534 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 535 | github.com/uwu-tools/magex v0.10.1 h1:qEJtkM+5nGKt/3BaRgj+X7pf+pNZ4SDyEEPMzEeUjkw= 536 | github.com/uwu-tools/magex v0.10.1/go.mod h1:5uQvmocqEueCbgK4Dm67mIfhjq80o408F17J6867go8= 537 | github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= 538 | github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= 539 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 540 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= 541 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 542 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 543 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 544 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 545 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 546 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 547 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 548 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 549 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 550 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 551 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 552 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 553 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 554 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 555 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 556 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 557 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 558 | go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= 559 | go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= 560 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= 561 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= 562 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= 563 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= 564 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= 565 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= 566 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 h1:W5AWUn/IVe8RFb5pZx1Uh9Laf/4+Qmm4kJL5zPuvR+0= 567 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0/go.mod h1:mzKxJywMNBdEX8TSJais3NnsVZUaJ+bAy6UxPTng2vk= 568 | go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= 569 | go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= 570 | go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= 571 | go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= 572 | go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= 573 | go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= 574 | go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= 575 | go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= 576 | go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= 577 | go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= 578 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 579 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 580 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 581 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 582 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 583 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 584 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 585 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 586 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 587 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 588 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 589 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 590 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 591 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 592 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 593 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 594 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 595 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 596 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 597 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 598 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 599 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 600 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 601 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 602 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 603 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 604 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 605 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 606 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 607 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 608 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 609 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 610 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 611 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 612 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 613 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 614 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 615 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 616 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 617 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 618 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 619 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 620 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 621 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 622 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 623 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 624 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 625 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 626 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 627 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 628 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 629 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 630 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 631 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 632 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 633 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 634 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 635 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 636 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 637 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 638 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 639 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 640 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 641 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 642 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 643 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 644 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 645 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 646 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 647 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 648 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 649 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 650 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 651 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 652 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 653 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 654 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 655 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 656 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 657 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 658 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 659 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 660 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 661 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 662 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 663 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 664 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 665 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 666 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 667 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 668 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 669 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 670 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 671 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 672 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 673 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 674 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 675 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 676 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 677 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 678 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 679 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 680 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 681 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 682 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 683 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 684 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 685 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 686 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 687 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 688 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 689 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 690 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 691 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 692 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 693 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 694 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 695 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 696 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 697 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 698 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 699 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 700 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 701 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 702 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 703 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 704 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 705 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 706 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 707 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 708 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 709 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 710 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 711 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 712 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 713 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 714 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 715 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 716 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 717 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 718 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 719 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 720 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 721 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 722 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 723 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 724 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 725 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 726 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 727 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 728 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 729 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 730 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 731 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 732 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 733 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 734 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 735 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 736 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 737 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 738 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 739 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 740 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 741 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 742 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 743 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 744 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 745 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 746 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 747 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 748 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 749 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 750 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 751 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 752 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 753 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 754 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 755 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 756 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 757 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 758 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 759 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 760 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 761 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 762 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 763 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 764 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 765 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 766 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 767 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 768 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 769 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 770 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 771 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 772 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 773 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 774 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 775 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 776 | google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= 777 | google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= 778 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= 779 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= 780 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 781 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 782 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 783 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 784 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 785 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 786 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 787 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 788 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 789 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 790 | google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= 791 | google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= 792 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 793 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 794 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 795 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 796 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 797 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 798 | google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= 799 | google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 800 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 801 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 802 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 803 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 804 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 805 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 806 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 807 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 808 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 809 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 810 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 811 | gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 812 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 813 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 814 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 815 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= 816 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= 817 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 818 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 819 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 820 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 821 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 822 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 823 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 824 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 825 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 826 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 827 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 828 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 829 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 830 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 831 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 832 | gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= 833 | gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 834 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 835 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 836 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 837 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 838 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 839 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 840 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 841 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 842 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 843 | -------------------------------------------------------------------------------- /mage.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/magefile/mage/mage" 9 | ) 10 | 11 | // This file allows someone to run mage commands without mage installed 12 | // by running `go run mage.go TARGET`. 13 | // See https://magefile.org/zeroinstall/ 14 | func main() { os.Exit(mage.Main()) } 15 | -------------------------------------------------------------------------------- /magefile.go: -------------------------------------------------------------------------------- 1 | //go:build mage 2 | 3 | package main 4 | 5 | import ( 6 | "get.porter.sh/magefiles/git" 7 | "get.porter.sh/magefiles/mixins" 8 | "get.porter.sh/magefiles/porter" 9 | "github.com/carolynvs/magex/shx" 10 | ) 11 | 12 | const ( 13 | mixinName = "terraform" 14 | mixinPackage = "get.porter.sh/mixin/terraform" 15 | mixinBin = "bin/mixins/" + mixinName 16 | ) 17 | 18 | var ( 19 | magefile = mixins.NewMagefile(mixinPackage, mixinName, mixinBin) 20 | must = shx.CommandBuilder{StopOnError: true} 21 | ) 22 | 23 | func ConfigureAgent() { 24 | magefile.ConfigureAgent() 25 | } 26 | 27 | // Build the mixin 28 | func Build() { 29 | magefile.Build() 30 | } 31 | 32 | // Cross-compile the mixin before a release 33 | func XBuildAll() { 34 | magefile.XBuildAll() 35 | } 36 | 37 | // Run unit tests 38 | func TestUnit() { 39 | magefile.TestUnit() 40 | } 41 | 42 | func Test() { 43 | magefile.Test() 44 | Build() 45 | TestIntegration() 46 | } 47 | 48 | // Publish the mixin to github 49 | func Publish() { 50 | magefile.Publish() 51 | } 52 | 53 | // TestPublish tries out publish locally, with your github forks 54 | // Assumes that you forked and kept the repository name unchanged. 55 | func TestPublish(username string) { 56 | magefile.TestPublish(username) 57 | } 58 | 59 | // Install the mixin 60 | func Install() { 61 | magefile.Install() 62 | } 63 | 64 | // Remove generated build files 65 | func Clean() { 66 | magefile.Clean() 67 | } 68 | 69 | // Install porter locally 70 | func EnsureLocalPorter() { 71 | porter.UseBinForPorterHome() 72 | porter.EnsurePorter() 73 | } 74 | 75 | func TestIntegration() { 76 | EnsureLocalPorter() 77 | must.Command("./scripts/test/test-cli.sh").RunV() 78 | } 79 | 80 | // SetupDCO configures your git repository to automatically sign your commits 81 | // to comply with our DCO 82 | func SetupDCO() error { 83 | return git.SetupDCO() 84 | } 85 | -------------------------------------------------------------------------------- /pkg/terraform/action.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "get.porter.sh/porter/pkg/exec/builder" 9 | "github.com/tidwall/gjson" 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | func (m *Mixin) loadAction(ctx context.Context) (*Action, error) { 14 | var action Action 15 | err := builder.LoadAction(ctx, m.RuntimeConfig, "", func(contents []byte) (interface{}, error) { 16 | err := yaml.Unmarshal(contents, &action) 17 | return &action, err 18 | }) 19 | return &action, err 20 | } 21 | 22 | var _ builder.ExecutableAction = Action{} 23 | var _ builder.BuildableAction = Action{} 24 | 25 | type Action struct { 26 | Name string 27 | Steps []Step // using UnmarshalYAML so that we don't need a custom type per action 28 | } 29 | 30 | // MakeSteps builds a slice of Steps for data to be unmarshaled into. 31 | func (a Action) MakeSteps() interface{} { 32 | return &[]Step{} 33 | } 34 | 35 | // UnmarshalYAML takes any yaml in this form 36 | // ACTION: 37 | // - terraform: ... 38 | // and puts the steps into the Action.Steps field 39 | func (a *Action) UnmarshalYAML(unmarshal func(interface{}) error) error { 40 | results, err := builder.UnmarshalAction(unmarshal, a) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | for actionName, action := range results { 46 | a.Name = actionName 47 | for _, result := range action { 48 | step := result.(*[]Step) 49 | a.Steps = append(a.Steps, *step...) 50 | } 51 | break // There is only 1 action 52 | } 53 | return nil 54 | } 55 | 56 | func (a Action) GetSteps() []builder.ExecutableStep { 57 | // Go doesn't have generics, nothing to see here... 58 | steps := make([]builder.ExecutableStep, len(a.Steps)) 59 | for i := range a.Steps { 60 | steps[i] = a.Steps[i] 61 | } 62 | 63 | return steps 64 | } 65 | 66 | type Step struct { 67 | Instruction `yaml:"terraform"` 68 | } 69 | 70 | var _ builder.ExecutableStep = Step{} 71 | var _ builder.HasCustomDashes = Step{} 72 | 73 | func (s Step) GetCommand() string { 74 | return "terraform" 75 | } 76 | 77 | func (s Step) GetWorkingDir() string { 78 | return "." 79 | } 80 | 81 | func (s Step) GetArguments() []string { 82 | return s.Arguments 83 | } 84 | 85 | func (s Step) GetFlags() builder.Flags { 86 | return s.Flags 87 | } 88 | 89 | func (s Step) GetDashes() builder.Dashes { 90 | // All flags in the terraform cli use a single dash 91 | return builder.Dashes{ 92 | Long: "-", 93 | Short: "-", 94 | } 95 | } 96 | 97 | func (s Step) GetOutputs() []builder.Output { 98 | // Go doesn't have generics, nothing to see here... 99 | outputs := make([]builder.Output, len(s.Outputs)) 100 | for i := range s.Outputs { 101 | outputs[i] = s.Outputs[i] 102 | } 103 | return outputs 104 | } 105 | 106 | // applyVarsToStepFlags converts the Terraform vars specified in YAML into a list of -var flags 107 | // with the variable value set in a format that terraform expects. 108 | func applyVarsToStepFlags(step *Step) error { 109 | if len(step.Vars) == 0 { 110 | // return early because otherwise parseVars.ForEach below will print `-var =` even when the result is empty 111 | return nil 112 | } 113 | 114 | vars, err := json.Marshal(step.Vars) 115 | if err != nil { 116 | return fmt.Errorf("error marshaling terraform variables to json") 117 | } 118 | 119 | parsedVars := gjson.Parse(string(vars)) 120 | parsedVars.ForEach(func(key, value gjson.Result) bool { 121 | // ensure that the flag value is set using a format that terraform expects 122 | // primitive data types should print the value directly, e.g. astring, 1, true, 2.4 123 | // complex data types should be json, e.g. [1,2,3] or {"color":"blue} 124 | step.Flags = append(step.Flags, builder.NewFlag("var", fmt.Sprintf("'%s=%s'", key.String(), value.String()))) 125 | return true 126 | }) 127 | 128 | return nil 129 | } 130 | 131 | type Instruction struct { 132 | Name string `yaml:"name"` 133 | Description string `yaml:"description"` 134 | Arguments []string `yaml:"arguments,omitempty"` 135 | Flags builder.Flags `yaml:"flags,omitempty"` 136 | Outputs []Output `yaml:"outputs,omitempty"` 137 | TerraformFields `yaml:",inline"` 138 | } 139 | 140 | // TerraformFields represent fields specific to Terraform 141 | type TerraformFields struct { 142 | Vars map[string]interface{} `yaml:"vars,omitempty"` 143 | DisableVarFile bool `yaml:"disableVarFile,omitempty"` 144 | LogLevel string `yaml:"logLevel,omitempty"` 145 | BackendConfig map[string]interface{} `yaml:"backendConfig,omitempty"` 146 | } 147 | 148 | type Output struct { 149 | Name string `yaml:"name"` 150 | // Write the output to the specified file 151 | DestinationFile string `yaml:"destinationFile,omitempty"` 152 | } 153 | 154 | func (o Output) GetName() string { 155 | return o.Name 156 | } 157 | -------------------------------------------------------------------------------- /pkg/terraform/action_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "io/ioutil" 5 | "sort" 6 | "testing" 7 | 8 | "get.porter.sh/porter/pkg/exec/builder" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | "gopkg.in/yaml.v2" 13 | ) 14 | 15 | func TestMixin_UnmarshalStep(t *testing.T) { 16 | b, err := ioutil.ReadFile("testdata/step-input.yaml") 17 | require.NoError(t, err) 18 | 19 | var action Action 20 | err = yaml.Unmarshal(b, &action) 21 | require.NoError(t, err) 22 | require.Len(t, action.Steps, 1) 23 | 24 | step := action.Steps[0] 25 | assert.Equal(t, "Custom Action", step.Description) 26 | assert.NotEmpty(t, step.Outputs) 27 | assert.Equal(t, Output{Name: "myoutput"}, step.Outputs[0]) 28 | 29 | require.Len(t, step.Arguments, 1) 30 | assert.Equal(t, "custom", step.Arguments[0]) 31 | 32 | sort.Sort(step.Flags) 33 | require.Len(t, step.Flags, 3) 34 | assert.Equal(t, builder.NewFlag("backendConfig", "key=my.tfstate"), step.Flags[0]) 35 | assert.Equal(t, builder.NewFlag("logLevel", "TRACE"), step.Flags[1]) 36 | assert.Equal(t, builder.NewFlag("vars", "myvar=foo"), step.Flags[2]) 37 | } 38 | 39 | func TestApplyVarsToStepFlags(t *testing.T) { 40 | t.Run("parse all var data types", func(t *testing.T) { 41 | s := Step{} 42 | s.Vars = map[string]interface{}{ 43 | "string": "mystring", 44 | "bool": true, 45 | "int": 22, 46 | "number": 1.5, 47 | "list": []string{"a", "b", "c"}, 48 | "doc": map[string]interface{}{ 49 | "logLevel": "warn", 50 | "debug": true, 51 | "exclude": []int{1, 2, 3}, 52 | "stuff": map[string]interface{}{"things": true}}, 53 | } 54 | 55 | applyVarsToStepFlags(&s) 56 | 57 | gotFlags := s.Flags.ToSlice(s.GetDashes()) 58 | wantFlags := []string{ 59 | "-var", `'bool=true'`, 60 | "-var", `'doc={"debug":true,"exclude":[1,2,3],"logLevel":"warn","stuff":{"things":true}}'`, 61 | "-var", `'int=22'`, 62 | "-var", `'list=["a","b","c"]'`, 63 | "-var", `'number=1.5'`, 64 | "-var", `'string=mystring'`, 65 | } 66 | assert.Equal(t, wantFlags, gotFlags) 67 | }) 68 | 69 | t.Run("empty vars", func(t *testing.T) { 70 | s := Step{} 71 | 72 | applyVarsToStepFlags(&s) 73 | 74 | gotFlags := s.Flags.ToSlice(s.GetDashes()) 75 | assert.Empty(t, gotFlags) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /pkg/terraform/build.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | "text/template" 6 | 7 | "get.porter.sh/porter/pkg/exec/builder" 8 | "github.com/ghodss/yaml" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | const dockerfileLines = ` 13 | ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT="{{ .UserAgentOptOut}}" 14 | ENV AZURE_HTTP_USER_AGENT="{{ .AzureUserAgent }}" 15 | RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \ 16 | apt-get update && apt-get install -y wget unzip && \ 17 | wget {{or .InstallHost "https://releases.hashicorp.com"}}/terraform/{{.ClientVersion}}/terraform_{{.ClientVersion}}_linux_amd64.zip --progress=dot:giga && \ 18 | unzip terraform_{{.ClientVersion}}_linux_amd64.zip -d /usr/bin && \ 19 | rm terraform_{{.ClientVersion}}_linux_amd64.zip 20 | COPY {{.WorkingDir}}/{{.InitFile}} $BUNDLE_DIR/{{.WorkingDir}}/ 21 | RUN cd $BUNDLE_DIR/{{.WorkingDir}} && mkdir -p /usr/local/share/terraform/plugins/ 22 | {{if .ProviderHost }} 23 | ENV TF_CLI_CONFIG_FILE=$BUNDLE_DIR/{{.WorkingDir}}/provider_mirror.tfrc 24 | RUN tee < provider_mirror.tfrc && terraform init -backend=false && cp --recursive .terraform/providers /usr/local/share/terraform/plugins/ 25 | provider_installation { 26 | direct { 27 | exclude = ["registry.terraform.io/*/*"] 28 | } 29 | network_mirror { 30 | url = "{{ .ProviderHost }}" 31 | } 32 | } 33 | EOF 34 | {{ else }} 35 | RUN terraform init -backend=false && \ 36 | rm -fr .terraform/providers && \ 37 | terraform providers mirror /usr/local/share/terraform/plugins 38 | {{ end }} 39 | ` 40 | 41 | // BuildInput represents stdin passed to the mixin for the build command. 42 | type BuildInput struct { 43 | Config *MixinConfig 44 | } 45 | 46 | // MixinConfig represents configuration that can be set on the terraform mixin in porter.yaml 47 | // mixins: 48 | // - terraform: 49 | // version: 0.12.17 50 | type MixinConfig struct { 51 | // ClientVersion is the version of the terraform CLI to install 52 | ClientVersion string `yaml:"clientVersion,omitempty"` 53 | 54 | // UserAgentOptOut allows a bundle author to opt out from adding porter and the mixin's version to the terraform user agent string. 55 | UserAgentOptOut bool `yaml:"userAgentOptOut,omitempty"` 56 | 57 | InitFile string `yaml:"initFile,omitempty"` 58 | WorkingDir string `yaml:"workingDir,omitempty"` 59 | 60 | // Host from which to install `terraform`. 61 | InstallHost string `yaml:"installHost,omitempty"` 62 | 63 | // Host from which to download providers, i.e. a provider registry. See 64 | // terraform provider registry documentation: 65 | // https://developer.hashicorp.com/terraform/internals/provider-registry-protocol 66 | ProviderHost string `yaml:"providerHost,omitempty"` 67 | } 68 | 69 | type buildConfig struct { 70 | MixinConfig 71 | 72 | // AzureUserAgent is the contents of the azure user agent environment variable 73 | AzureUserAgent string 74 | } 75 | 76 | func (m *Mixin) Build(ctx context.Context) error { 77 | input := BuildInput{ 78 | Config: &m.config, // Apply config directly to the mixin 79 | } 80 | err := builder.LoadAction(ctx, m.RuntimeConfig, "", func(contents []byte) (interface{}, error) { 81 | err := yaml.Unmarshal(contents, &input) 82 | return &input, err 83 | }) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | tmpl, err := template.New("Dockerfile").Parse(dockerfileLines) 89 | if err != nil { 90 | return errors.Wrapf(err, "error parsing terraform mixin Dockerfile template") 91 | } 92 | 93 | cfg := buildConfig{MixinConfig: *input.Config} 94 | if !input.Config.UserAgentOptOut { 95 | cfg.AzureUserAgent = m.userAgent 96 | } 97 | 98 | return tmpl.Execute(m.Out, cfg) 99 | } 100 | -------------------------------------------------------------------------------- /pkg/terraform/build_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io/ioutil" 7 | "testing" 8 | 9 | "get.porter.sh/mixin/terraform/pkg" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestMixin_Build(t *testing.T) { 15 | testcases := []struct { 16 | name string 17 | inputFile string 18 | expectedVersion string 19 | expectedUserAgent string 20 | expectedProviderHost string 21 | }{ 22 | { 23 | name: "build with custom config", 24 | inputFile: "testdata/build-input-with-config.yaml", 25 | expectedVersion: "https://releases.hashicorp.com/terraform/0.13.0-rc1/terraform_0.13.0-rc1_linux_amd64.zip", 26 | expectedUserAgent: "ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=\"true\"\nENV AZURE_HTTP_USER_AGENT=\"\"", 27 | }, 28 | { 29 | name: "build with the default Terraform config", 30 | expectedVersion: "https://releases.hashicorp.com/terraform/1.2.9/terraform_1.2.9_linux_amd64.zip", 31 | expectedUserAgent: "ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=\"false\"\nENV AZURE_HTTP_USER_AGENT=\"getporter/porter getporter/terraform/v1.2.3", 32 | }, 33 | { 34 | name: "build in airgrapped environment", 35 | inputFile: "testdata/build-input-in-airgapped-env.yaml", 36 | expectedVersion: "https://install.example.com/terraform/1.2.3/terraform_1.2.3_linux_amd64.zip", 37 | expectedProviderHost: "https://providers.example.com", 38 | }, 39 | } 40 | 41 | for _, tc := range testcases { 42 | t.Run(tc.name, func(t *testing.T) { 43 | // Set a fake version of the mixin and porter for our user agent 44 | pkg.Version = "v1.2.3" 45 | 46 | var data []byte 47 | var err error 48 | if tc.inputFile != "" { 49 | data, err = ioutil.ReadFile(tc.inputFile) 50 | require.NoError(t, err) 51 | } 52 | 53 | m := NewTestMixin(t) 54 | m.In = bytes.NewReader(data) 55 | 56 | err = m.Build(context.Background()) 57 | require.NoError(t, err, "build failed") 58 | 59 | gotOutput := m.TestContext.GetOutput() 60 | assert.Contains(t, gotOutput, tc.expectedVersion) 61 | assert.Contains(t, gotOutput, tc.expectedUserAgent) 62 | 63 | if tc.expectedProviderHost != "" { 64 | assert.Contains(t, gotOutput, tc.expectedProviderHost) 65 | assert.Contains(t, gotOutput, "ENV TF_CLI_CONFIG_FILE=") 66 | } else { 67 | assert.NotContains(t, gotOutput, "network_mirror") 68 | assert.NotContains(t, gotOutput, "TF_CLI_CONFIG_FILE") 69 | } 70 | 71 | assert.NotContains(t, "{{.", gotOutput, "Not all of the template values were consumed") 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/terraform/config.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "get.porter.sh/porter/pkg" 8 | ) 9 | 10 | const ( 11 | // AzureUserAgentEnvVar is the environment variable used by the azure provider to set 12 | // the user agent string sent to Azure. 13 | AzureUserAgentEnvVar = "AZURE_HTTP_USER_AGENT" 14 | 15 | // UserAgentOptOutEnvVar is the name of the environment variable that disables 16 | // user agent reporting. 17 | UserAgentOptOutEnvVar = "PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT" 18 | ) 19 | 20 | // SetUserAgent sets the AZURE_HTTP_USER_AGENT environment variable with 21 | // the full user agent string, which includes both a portion for porter and the 22 | // mixin. 23 | func (m *Mixin) SetUserAgent() { 24 | // Check if PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=true, which disables editing the user agent string 25 | if optOut, _ := strconv.ParseBool(m.Getenv(UserAgentOptOutEnvVar)); optOut { 26 | return 27 | } 28 | 29 | // Check if we have already set the user agent 30 | if m.userAgent != "" { 31 | return 32 | } 33 | 34 | porterUserAgent := pkg.UserAgent() 35 | mixinUserAgent := m.GetMixinUserAgent() 36 | userAgent := []string{porterUserAgent, mixinUserAgent} 37 | // Append porter and the mixin's version to the user agent string. Some clouds and 38 | // environments will have set the environment variable already and we don't want 39 | // to clobber it. 40 | value := strings.Join(userAgent, " ") 41 | if agentStr, ok := m.LookupEnv(AzureUserAgentEnvVar); ok { 42 | 43 | // Check if we have already set the user agent 44 | if strings.Contains(agentStr, value) { 45 | value = agentStr 46 | } else { 47 | userAgent = append(userAgent, agentStr) 48 | value = strings.Join(userAgent, " ") 49 | } 50 | } 51 | 52 | m.userAgent = value 53 | 54 | // Set the environment variable so that when we call the 55 | // azure provider, it's automatically passed too. 56 | m.Setenv(AzureUserAgentEnvVar, m.userAgent) 57 | } 58 | 59 | // GetMixinUserAgent returns the portion of the user agent string for the mixin. 60 | func (m *Mixin) GetMixinUserAgent() string { 61 | v := m.Version() 62 | return "getporter/" + v.Name + "/" + v.Version 63 | } 64 | -------------------------------------------------------------------------------- /pkg/terraform/config_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "get.porter.sh/mixin/terraform/pkg" 8 | "get.porter.sh/porter/pkg/runtime" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestMixinSetsUserAgentEnvVar(t *testing.T) { 13 | // CI sets this value and we need to clear it out to make the test reproducible 14 | os.Unsetenv(AzureUserAgentEnvVar) 15 | 16 | t.Run("sets env var", func(t *testing.T) { 17 | pkg.Commit = "abc123" 18 | pkg.Version = "v1.2.3" 19 | m := New() 20 | expected := "getporter/porter getporter/terraform/" + pkg.Version 21 | require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar)) 22 | require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later") 23 | }) 24 | t.Run("edits env var", func(t *testing.T) { 25 | os.Unsetenv(AzureUserAgentEnvVar) 26 | // Validate that if the user customizations of the env var are preserved 27 | pkg.Commit = "abc123" 28 | pkg.Version = "v1.2.3" 29 | cfg := runtime.NewConfig() 30 | customUserAgent := "mycustom/v1.2.3" 31 | cfg.Setenv(AzureUserAgentEnvVar, customUserAgent) 32 | m := NewFor(cfg) 33 | expected := "getporter/porter getporter/terraform/v1.2.3 mycustom/v1.2.3" 34 | require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar)) 35 | require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later") 36 | }) 37 | 38 | t.Run("env var already set", func(t *testing.T) { 39 | // Validate that calling multiple times doesn't make a messed up env var 40 | os.Unsetenv(AzureUserAgentEnvVar) 41 | pkg.Commit = "abc123" 42 | pkg.Version = "v1.2.3" 43 | cfg := runtime.NewConfig() 44 | customUserAgent := "getporter/porter getporter/terraform/v1.2.3" 45 | cfg.Setenv(AzureUserAgentEnvVar, customUserAgent) 46 | m := New() 47 | m.SetUserAgent() 48 | expected := "getporter/porter getporter/terraform/v1.2.3" 49 | require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar)) 50 | require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later") 51 | }) 52 | t.Run("call multiple times", func(t *testing.T) { 53 | // Validate that calling multiple times doesn't make a messed up env var 54 | os.Unsetenv(AzureUserAgentEnvVar) 55 | pkg.Commit = "abc123" 56 | pkg.Version = "v1.2.3" 57 | m := New() 58 | m.SetUserAgent() 59 | m.SetUserAgent() 60 | expected := "getporter/porter getporter/terraform/v1.2.3" 61 | require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar)) 62 | require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later") 63 | }) 64 | } 65 | 66 | func TestMixinSetsUserAgentEnvVar_OptOut(t *testing.T) { 67 | // CI sets this value and we need to clear it out to make the test reproducible 68 | os.Unsetenv(AzureUserAgentEnvVar) 69 | 70 | t.Run("opt-out", func(t *testing.T) { 71 | // Validate that at runtime when we are calling the az cli, that if the bundle author opted-out, we don't set the user agent string 72 | cfg := runtime.NewConfig() 73 | cfg.Setenv(UserAgentOptOutEnvVar, "true") 74 | m := NewFor(cfg) 75 | _, hasEnv := m.LookupEnv(AzureUserAgentEnvVar) 76 | require.False(t, hasEnv, "expected the opt out to skip setting the AZURE_HTTP_USER_AGENT environment variable") 77 | }) 78 | t.Run("opt-out preserves original value", func(t *testing.T) { 79 | // Validate that at runtime when we are calling the az cli, that if the bundle author opted-out, we don't set the user agent string 80 | cfg := runtime.NewConfig() 81 | cfg.Setenv(UserAgentOptOutEnvVar, "true") 82 | customUserAgent := "mycustom/v1.2.3" 83 | cfg.Setenv(AzureUserAgentEnvVar, customUserAgent) 84 | m := NewFor(cfg) 85 | require.Equal(t, customUserAgent, m.Getenv(AzureUserAgentEnvVar), "expected opting out to not prevent the user from setting a custom user agent") 86 | require.Empty(t, m.userAgent, "validate we remember that we opted out") 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /pkg/terraform/helpers.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | 7 | "get.porter.sh/porter/pkg/portercontext" 8 | ) 9 | 10 | type TestMixin struct { 11 | *Mixin 12 | TestContext *portercontext.TestContext 13 | } 14 | 15 | // NewTestMixin initializes a terraform mixin, with the output buffered, and an in-memory file system. 16 | func NewTestMixin(t *testing.T) *TestMixin { 17 | c := portercontext.NewTestContext(t) 18 | m := New() 19 | m.Context = c.Context 20 | return &TestMixin{ 21 | Mixin: m, 22 | TestContext: c, 23 | } 24 | } 25 | 26 | func sortKeys(m map[string]interface{}) []string { 27 | keys := make([]string, 0, len(m)) 28 | for k := range m { 29 | keys = append(keys, k) 30 | } 31 | sort.Strings(keys) 32 | 33 | return keys 34 | } 35 | -------------------------------------------------------------------------------- /pkg/terraform/helpers_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSortKeys(t *testing.T) { 10 | m := map[string]interface{}{ 11 | "delicious": "true", 12 | "apples": "green", 13 | "are": "needed", 14 | } 15 | 16 | expected := []string{ 17 | "apples", 18 | "are", 19 | "delicious", 20 | } 21 | 22 | assert.Equal(t, expected, sortKeys(m)) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/terraform/init.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // Init runs terraform init with the provided backendConfig, if supplied 10 | func (m *Mixin) Init(ctx context.Context, backendConfig map[string]interface{}) error { 11 | cmd := m.NewCommand(ctx, "terraform", "init") 12 | 13 | if len(backendConfig) > 0 { 14 | cmd.Args = append(cmd.Args, "-backend=true") 15 | 16 | for _, k := range sortKeys(backendConfig) { 17 | cmd.Args = append(cmd.Args, fmt.Sprintf("-backend-config=%s=%s", k, backendConfig[k])) 18 | } 19 | 20 | cmd.Args = append(cmd.Args, "-reconfigure") 21 | } 22 | 23 | cmd.Stdout = m.Out 24 | cmd.Stderr = m.Err 25 | 26 | prettyCmd := fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " ")) 27 | if m.DebugMode { 28 | fmt.Fprintln(m.Out, prettyCmd) 29 | } 30 | 31 | err := cmd.Start() 32 | if err != nil { 33 | return fmt.Errorf("could not execute command, %s: %s", prettyCmd, err) 34 | } 35 | 36 | err = cmd.Wait() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/terraform/init_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "get.porter.sh/porter/pkg/test" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestMixin_Init(t *testing.T) { 13 | defer os.Unsetenv(test.ExpectedCommandEnv) 14 | os.Setenv(test.ExpectedCommandEnv, "terraform init") 15 | 16 | h := NewTestMixin(t) 17 | 18 | err := h.Init(context.Background(), nil) 19 | 20 | require.NoError(t, err) 21 | } 22 | 23 | func TestMixin_InitBackend(t *testing.T) { 24 | defer os.Unsetenv(test.ExpectedCommandEnv) 25 | os.Setenv(test.ExpectedCommandEnv, 26 | "terraform init -backend=true -backend-config=donuts=definitely -backend-config=drink=dubonnet -reconfigure") 27 | 28 | h := NewTestMixin(t) 29 | 30 | backendConfig := map[string]interface{}{ 31 | "drink": "dubonnet", 32 | "donuts": "definitely", 33 | } 34 | 35 | err := h.Init(context.Background(), backendConfig) 36 | 37 | require.NoError(t, err) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/terraform/install.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "get.porter.sh/porter/pkg/exec/builder" 9 | ) 10 | 11 | // defaultTerraformVarFilename is the default name for terrafrom tfvars json file 12 | const defaultTerraformVarsFilename = "terraform.tfvars.json" 13 | 14 | // Install runs a terraform apply 15 | func (m *Mixin) Install(ctx context.Context) error { 16 | action, err := m.loadAction(ctx) 17 | if err != nil { 18 | return err 19 | } 20 | step := action.Steps[0] 21 | 22 | err = m.commandPreRun(ctx, &step) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | // Update step fields that exec/builder works with 28 | step.Arguments = []string{"apply"} 29 | // Always run in non-interactive mode 30 | step.Flags = append(step.Flags, builder.NewFlag("auto-approve")) 31 | step.Flags = append(step.Flags, builder.NewFlag("input=false")) 32 | 33 | vbs, err := json.Marshal(step.Vars) 34 | if err != nil { 35 | return err 36 | } 37 | // Only create a tf var file for install 38 | if !step.DisableVarFile && action.Name == "install" { 39 | vf, err := m.FileSystem.Create(defaultTerraformVarsFilename) 40 | if err != nil { 41 | return err 42 | } 43 | defer vf.Close() 44 | 45 | // If the vars block is empty, set vbs to an empty JSON object 46 | // to prevent terraform from erroring out 47 | if len(step.Vars) == 0 { 48 | vbs = []byte("{}") 49 | } 50 | 51 | _, err = vf.Write(vbs) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | if m.DebugMode { 57 | fmt.Fprintf(m.Err, "DEBUG: TF var file created:\n%s\n", string(vbs)) 58 | } 59 | } 60 | 61 | applyVarsToStepFlags(&step) 62 | 63 | action.Steps[0] = step 64 | _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | return m.handleOutputs(ctx, step.Outputs) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/terraform/install_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "strings" 10 | "testing" 11 | 12 | "get.porter.sh/porter/pkg/test" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | "gopkg.in/yaml.v2" 16 | ) 17 | 18 | // sad hack: not sure how to make a common test main for all my subpackages 19 | func TestMain(m *testing.M) { 20 | test.TestMainWithMockedCommandHandlers(m) 21 | } 22 | 23 | func TestMixin_UnmarshalInstallStep(t *testing.T) { 24 | b, err := ioutil.ReadFile("testdata/install-input.yaml") 25 | require.NoError(t, err) 26 | 27 | var action Action 28 | err = yaml.Unmarshal(b, &action) 29 | require.NoError(t, err) 30 | require.Len(t, action.Steps, 1) 31 | step := action.Steps[0] 32 | 33 | assert.Equal(t, "Install MySQL", step.Description) 34 | assert.Equal(t, "TRACE", step.LogLevel) 35 | assert.Equal(t, false, step.DisableVarFile) 36 | } 37 | 38 | func TestMixin_Install(t *testing.T) { 39 | defer os.Unsetenv(test.ExpectedCommandEnv) 40 | expectedCommand := strings.Join([]string{ 41 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 42 | "terraform apply -auto-approve -input=false -var myvar=foo", 43 | }, "\n") 44 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 45 | 46 | b, err := ioutil.ReadFile("testdata/install-input.yaml") 47 | require.NoError(t, err) 48 | 49 | h := NewTestMixin(t) 50 | h.In = bytes.NewReader(b) 51 | 52 | // Set up working dir as current dir 53 | h.config.WorkingDir = h.Getwd() 54 | require.NoError(t, err) 55 | 56 | err = h.Install(context.Background()) 57 | require.NoError(t, err) 58 | 59 | assert.Equal(t, "TRACE", os.Getenv("TF_LOG")) 60 | 61 | wd := h.Getwd() 62 | require.NoError(t, err) 63 | assert.Equal(t, wd, h.config.WorkingDir) 64 | fc, err := h.FileSystem.ReadFile(path.Join(wd, "terraform.tfvars.json")) 65 | require.NoError(t, err) 66 | assert.Equal(t, fc, []byte("{\"myvar\":\"foo\"}")) 67 | } 68 | 69 | func TestMixin_UnmarshalInstallSaveVarStep(t *testing.T) { 70 | b, err := ioutil.ReadFile("testdata/install-input-disable-save-vars.yaml") 71 | require.NoError(t, err) 72 | 73 | var action Action 74 | err = yaml.Unmarshal(b, &action) 75 | require.NoError(t, err) 76 | require.Len(t, action.Steps, 1) 77 | step := action.Steps[0] 78 | 79 | assert.Equal(t, "Install MySQL", step.Description) 80 | assert.Equal(t, "TRACE", step.LogLevel) 81 | assert.Equal(t, true, step.DisableVarFile) 82 | } 83 | 84 | func TestMixin_InstallDisableSaveVars(t *testing.T) { 85 | defer os.Unsetenv(test.ExpectedCommandEnv) 86 | expectedCommand := strings.Join([]string{ 87 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 88 | "terraform apply -auto-approve -input=false -var myvar=foo", 89 | }, "\n") 90 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 91 | 92 | b, err := ioutil.ReadFile("testdata/install-input-disable-save-vars.yaml") 93 | require.NoError(t, err) 94 | 95 | h := NewTestMixin(t) 96 | h.In = bytes.NewReader(b) 97 | 98 | // Set up working dir as current dir 99 | h.config.WorkingDir = h.Getwd() 100 | require.NoError(t, err) 101 | 102 | err = h.Install(context.Background()) 103 | require.NoError(t, err) 104 | 105 | assert.Equal(t, "TRACE", os.Getenv("TF_LOG")) 106 | 107 | wd := h.Getwd() 108 | assert.Equal(t, wd, h.config.WorkingDir) 109 | _, err = h.FileSystem.Stat(path.Join(wd, "terraform.tfvars.json")) 110 | require.Error(t, err) 111 | } 112 | 113 | func TestMixin_InstallNoVarsBlock(t *testing.T) { 114 | defer os.Unsetenv(test.ExpectedCommandEnv) 115 | expectedCommand := strings.Join([]string{ 116 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 117 | "terraform apply -auto-approve -input=false", 118 | }, "\n") 119 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 120 | 121 | b, err := ioutil.ReadFile("testdata/install-input-no-vars-block.yaml") 122 | require.NoError(t, err) 123 | 124 | h := NewTestMixin(t) 125 | h.In = bytes.NewReader(b) 126 | 127 | // Set up working dir as current dir 128 | h.config.WorkingDir = h.Getwd() 129 | require.NoError(t, err) 130 | 131 | err = h.Install(context.Background()) 132 | require.NoError(t, err) 133 | 134 | assert.Equal(t, "TRACE", os.Getenv("TF_LOG")) 135 | 136 | wd := h.Getwd() 137 | assert.Equal(t, wd, h.config.WorkingDir) 138 | fc, err := h.FileSystem.ReadFile(path.Join(wd, "terraform.tfvars.json")) 139 | require.NoError(t, err) 140 | assert.Equal(t, fc, []byte("{}")) 141 | } 142 | -------------------------------------------------------------------------------- /pkg/terraform/invoke.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | 6 | "get.porter.sh/porter/pkg/exec/builder" 7 | ) 8 | 9 | type InvokeOptions struct { 10 | Action string 11 | } 12 | 13 | // Invoke runs a custom terraform action 14 | func (m *Mixin) Invoke(ctx context.Context, opts InvokeOptions) error { 15 | action, err := m.loadAction(ctx) 16 | if err != nil { 17 | return err 18 | } 19 | step := action.Steps[0] 20 | 21 | err = m.commandPreRun(ctx, &step) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | // Update step fields that exec/builder works with 27 | commands := []string{opts.Action} 28 | if len(step.Arguments) > 0 { 29 | commands = step.GetArguments() 30 | } 31 | step.Arguments = commands 32 | 33 | applyVarsToStepFlags(&step) 34 | 35 | action.Steps[0] = step 36 | _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return m.handleOutputs(ctx, step.Outputs) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/terraform/invoke_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io/ioutil" 7 | "os" 8 | "strings" 9 | "testing" 10 | 11 | "get.porter.sh/porter/pkg/test" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | yaml "gopkg.in/yaml.v2" 15 | ) 16 | 17 | func TestMixin_UnmarshalInvokeStep(t *testing.T) { 18 | b, err := ioutil.ReadFile("testdata/invoke-input.yaml") 19 | require.NoError(t, err) 20 | 21 | var action Action 22 | err = yaml.Unmarshal(b, &action) 23 | require.NoError(t, err) 24 | require.Len(t, action.Steps, 1) 25 | step := action.Steps[0] 26 | 27 | assert.Equal(t, "Custom Action", step.Description) 28 | } 29 | 30 | func TestMixin_Invoke(t *testing.T) { 31 | defer os.Unsetenv(test.ExpectedCommandEnv) 32 | expectedCommand := strings.Join([]string{ 33 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 34 | "terraform custom -var myvar=foo", 35 | }, "\n") 36 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 37 | 38 | b, err := ioutil.ReadFile("testdata/invoke-input.yaml") 39 | require.NoError(t, err) 40 | 41 | h := NewTestMixin(t) 42 | h.In = bytes.NewReader(b) 43 | 44 | // Set up working dir as current dir 45 | h.config.WorkingDir = h.Getwd() 46 | require.NoError(t, err) 47 | 48 | opts := InvokeOptions{} 49 | err = h.Invoke(context.Background(), opts) 50 | require.NoError(t, err) 51 | 52 | wd := h.Getwd() 53 | require.NoError(t, err) 54 | assert.Equal(t, wd, h.config.WorkingDir) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/terraform/schema.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | ) 7 | 8 | //go:embed schema/schema.json 9 | var schema string 10 | 11 | func (m *Mixin) PrintSchema() { 12 | fmt.Fprintf(m.Out, schema) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/terraform/schema/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "definitions": { 4 | "declaration": { 5 | "oneOf": [ 6 | { 7 | "description": "Declare the terraform mixin without configuration", 8 | "type": "string", 9 | "enum": ["terraform"] 10 | }, 11 | {"$ref": "#/definitions/config"} 12 | ] 13 | }, 14 | "config": { 15 | "description": "Declare the terraform mixin with additional configuration", 16 | "type": "object", 17 | "properties": { 18 | "terraform": { 19 | "description": "terraform mixin configuration", 20 | "type": "object", 21 | "properties": { 22 | "clientVersion": { 23 | "description": "Version of terraform to install in the bundle", 24 | "type": "string" 25 | }, 26 | "initFile": { 27 | "description": "Relative path from the workingDir to a file defining all providers, used when running terraform init.", 28 | "type": "string" 29 | }, 30 | "workingDir": { 31 | "description": "Relative path to your terraform files, defaults to 'terraform'", 32 | "type": "string" 33 | } 34 | }, 35 | "additionalProperties": false 36 | } 37 | }, 38 | "additionalProperties": false, 39 | "required": ["terraform"] 40 | }, 41 | "installStep": { 42 | "type": "object", 43 | "properties": { 44 | "terraform": { 45 | "$ref": "#/definitions/terraform" 46 | } 47 | }, 48 | "required": [ 49 | "terraform" 50 | ], 51 | "additionalProperties": false 52 | }, 53 | "invokeStep": { 54 | "type": "object", 55 | "properties": { 56 | "terraform": { 57 | "$ref": "#/definitions/terraform" 58 | } 59 | }, 60 | "additionalProperties": false, 61 | "required": [ 62 | "terraform" 63 | ] 64 | }, 65 | "terraform": { 66 | "type": "object", 67 | "properties": { 68 | "arguments": { 69 | "type": "array", 70 | "items": { 71 | "type": "string" 72 | } 73 | }, 74 | "backendConfig": { 75 | "type": "object" 76 | }, 77 | "description": { 78 | "$ref": "#/definitions/stepDescription" 79 | }, 80 | "flags": { 81 | "type": "object", 82 | "additionalProperties": { 83 | "type": [ 84 | "null", 85 | "boolean", 86 | "number", 87 | "string" 88 | ] 89 | } 90 | }, 91 | "logLevel": { 92 | "type": "string" 93 | }, 94 | "outputs": { 95 | "$ref": "#/definitions/outputs" 96 | }, 97 | "vars": { 98 | "type": "object" 99 | }, 100 | "disableVarFile": { 101 | "type": "boolean" 102 | } 103 | }, 104 | "additionalProperties": false, 105 | "required": [ 106 | "description" 107 | ] 108 | }, 109 | "upgradeStep": { 110 | "type": "object", 111 | "properties": { 112 | "terraform": { 113 | "type": "object", 114 | "properties": { 115 | "arguments": { 116 | "type": "array", 117 | "items": { 118 | "type": "string" 119 | } 120 | }, 121 | "backendConfig": { 122 | "type": "object" 123 | }, 124 | "description": { 125 | "$ref": "#/definitions/stepDescription" 126 | }, 127 | "flags": { 128 | "type": "object", 129 | "additionalProperties": { 130 | "type": [ 131 | "null", 132 | "boolean", 133 | "number", 134 | "string" 135 | ] 136 | } 137 | }, 138 | "logLevel": { 139 | "type": "string" 140 | }, 141 | "outputs": { 142 | "$ref": "#/definitions/outputs" 143 | }, 144 | "vars": { 145 | "type": "object" 146 | } 147 | }, 148 | "additionalProperties": false, 149 | "required": [ 150 | "description" 151 | ] 152 | } 153 | }, 154 | "required": [ 155 | "terraform" 156 | ], 157 | "additionalProperties": false 158 | }, 159 | "uninstallStep": { 160 | "type": "object", 161 | "properties": { 162 | "terraform": { 163 | "type": "object", 164 | "properties": { 165 | "backendConfig": { 166 | "type": "object" 167 | }, 168 | "description": { 169 | "$ref": "#/definitions/stepDescription" 170 | }, 171 | "logLevel": { 172 | "type": "string" 173 | }, 174 | "outputs": { 175 | "$ref": "#/definitions/outputs" 176 | }, 177 | "vars": { 178 | "type": "object" 179 | } 180 | }, 181 | "additionalProperties": false, 182 | "required": [ 183 | "description" 184 | ] 185 | } 186 | }, 187 | "required": [ 188 | "terraform" 189 | ], 190 | "additionalProperties": false 191 | }, 192 | "stepDescription": { 193 | "type": "string", 194 | "minLength": 1 195 | }, 196 | "outputs": { 197 | "type": "array", 198 | "items": { 199 | "type": "object", 200 | "properties": { 201 | "name": { 202 | "type": "string" 203 | } 204 | }, 205 | "additionalProperties": false, 206 | "required": [ 207 | "name" 208 | ] 209 | } 210 | } 211 | }, 212 | "type": "object", 213 | "properties": { 214 | "install": { 215 | "type": "array", 216 | "items": { 217 | "$ref": "#/definitions/installStep" 218 | } 219 | }, 220 | "upgrade": { 221 | "type": "array", 222 | "items": { 223 | "$ref": "#/definitions/upgradeStep" 224 | } 225 | }, 226 | "uninstall": { 227 | "type": "array", 228 | "items": { 229 | "$ref": "#/definitions/uninstallStep" 230 | } 231 | }, 232 | "mixins": { 233 | "type": "array", 234 | "items": { "$ref": "#/definitions/declaration" } 235 | } 236 | }, 237 | "additionalProperties": { 238 | "type": "array", 239 | "items": { 240 | "$ref": "#/definitions/invokeStep" 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /pkg/terraform/schema_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | "testing" 9 | 10 | "github.com/PaesslerAG/jsonpath" 11 | "github.com/ghodss/yaml" 12 | "github.com/pkg/errors" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | "github.com/xeipuuv/gojsonschema" 16 | ) 17 | 18 | func TestMixin_PrintSchema(t *testing.T) { 19 | m := NewTestMixin(t) 20 | 21 | m.PrintSchema() 22 | 23 | gotSchema := m.TestContext.GetOutput() 24 | 25 | assert.Equal(t, schema, gotSchema) 26 | } 27 | 28 | func TestMixin_ValidatePayload(t *testing.T) { 29 | testcases := []struct { 30 | name string 31 | step string 32 | pass bool 33 | error string 34 | }{ 35 | {"install", "testdata/install-input.yaml", true, ""}, 36 | {"install.disable-save-var-file", "testdata/install-input-disable-save-vars.yaml", true, ""}, 37 | {"invoke", "testdata/invoke-input.yaml", true, ""}, 38 | {"upgrade", "testdata/upgrade-input.yaml", true, ""}, 39 | {"uninstall", "testdata/uninstall-input.yaml", true, ""}, 40 | {"install.missing-desc", "testdata/bad-install-input.missing-desc.yaml", false, "install.0.terraform: Invalid type. Expected: object, given: null"}, 41 | {"install.desc-empty", "testdata/bad-install-input.desc-empty.yaml", false, "install.0.terraform.description: String length must be greater than or equal to 1"}, 42 | {"upgrade.disable-var-file", "testdata/bad-upgrade-disable-save-var.yaml", false, "upgrade.0.terraform: Additional property disableVarFile is not allowed"}, 43 | {"uninstall.input-not-valid", "testdata/bad-uninstall-input.input-not-valid.yaml", false, "uninstall.0.terraform: Additional property input is not allowed"}, 44 | {"uninstall.disable-var-file", "testdata/bad-uninstall-disable-save-var.yaml", false, "uninstall.0.terraform: Additional property disableVarFile is not allowed"}, 45 | } 46 | 47 | for _, tc := range testcases { 48 | t.Run(tc.name, func(t *testing.T) { 49 | m := NewTestMixin(t) 50 | b, err := ioutil.ReadFile(tc.step) 51 | require.NoError(t, err) 52 | 53 | err = m.validatePayload(b) 54 | if tc.pass { 55 | require.NoError(t, err) 56 | } else { 57 | require.EqualError(t, err, tc.error) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | func (m *Mixin) validatePayload(b []byte) error { 64 | // Load the step as a go dump 65 | s := make(map[string]interface{}) 66 | err := yaml.Unmarshal(b, &s) 67 | if err != nil { 68 | return errors.Wrap(err, "could not marshal payload as yaml") 69 | } 70 | manifestLoader := gojsonschema.NewGoLoader(s) 71 | 72 | // Load the step schema 73 | schemaLoader := gojsonschema.NewStringLoader(schema) 74 | 75 | validator, err := gojsonschema.NewSchema(schemaLoader) 76 | if err != nil { 77 | return errors.Wrap(err, "unable to compile the mixin step schema") 78 | } 79 | 80 | // Validate the manifest against the schema 81 | result, err := validator.Validate(manifestLoader) 82 | if err != nil { 83 | return errors.Wrap(err, "unable to validate the mixin step schema") 84 | } 85 | if !result.Valid() { 86 | errs := make([]string, 0, len(result.Errors())) 87 | for _, resultErr := range result.Errors() { 88 | doAppend := true 89 | for _, err := range errs { 90 | // no need to append if already exists 91 | if err == resultErr.String() { 92 | doAppend = false 93 | } 94 | } 95 | if doAppend { 96 | errs = append(errs, resultErr.String()) 97 | } 98 | } 99 | return errors.New(strings.Join(errs, "\n\t* ")) 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func TestMixin_CheckSchema(t *testing.T) { 106 | // Long term it would be great to have a helper function in Porter that a mixin can use to check that it meets certain interfaces 107 | // check that certain characteristics of the schema that Porter expects are present 108 | // Once we have a mixin library, that would be a good place to package up this type of helper function 109 | var schemaMap map[string]interface{} 110 | err := json.Unmarshal([]byte(schema), &schemaMap) 111 | require.NoError(t, err, "could not unmarshal the schema into a map") 112 | 113 | t.Run("mixin configuration", func(t *testing.T) { 114 | // Check that mixin config is defined, and has all the supported fields 115 | configSchema, err := jsonpath.Get("$.definitions.config", schemaMap) 116 | require.NoError(t, err, "could not find the mixin config schema declaration") 117 | _, err = jsonpath.Get("$.properties.terraform.properties.clientVersion", configSchema) 118 | require.NoError(t, err, "clientVersion was not included in the mixin config schema") 119 | _, err = jsonpath.Get("$.properties.terraform.properties.initFile", configSchema) 120 | require.NoError(t, err, "initFile was not included in the mixin config schema") 121 | _, err = jsonpath.Get("$.properties.terraform.properties.workingDir", configSchema) 122 | require.NoError(t, err, "workingDir was not included in the mixin config schema") 123 | }) 124 | 125 | // Check that schema are defined for each action 126 | actions := []string{"install", "upgrade", "invoke", "uninstall"} 127 | for _, action := range actions { 128 | t.Run("supports "+action, func(t *testing.T) { 129 | actionPath := fmt.Sprintf("$.definitions.%sStep", action) 130 | _, err := jsonpath.Get(actionPath, schemaMap) 131 | require.NoErrorf(t, err, "could not find the %sStep declaration", action) 132 | }) 133 | } 134 | 135 | // Check that the invoke action is registered 136 | additionalSchema, err := jsonpath.Get("$.additionalProperties.items", schemaMap) 137 | require.NoError(t, err, "the invoke action was not registered in the schema") 138 | require.Contains(t, additionalSchema, "$ref") 139 | invokeRef := additionalSchema.(map[string]interface{})["$ref"] 140 | require.Equal(t, "#/definitions/invokeStep", invokeRef, "the invoke action was not registered correctly") 141 | } 142 | -------------------------------------------------------------------------------- /pkg/terraform/terraform.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "context" 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | 14 | "get.porter.sh/porter/pkg/runtime" 15 | "github.com/hashicorp/go-multierror" 16 | "github.com/pkg/errors" 17 | ) 18 | 19 | const ( 20 | // DefaultWorkingDir is the default working directory for Terraform. 21 | DefaultWorkingDir = "terraform" 22 | 23 | // DefaultClientVersion is the default version of the terraform cli. 24 | DefaultClientVersion = "1.2.9" 25 | 26 | // DefaultInitFile is the default file used to initialize terraform providers during build. 27 | DefaultInitFile = "" 28 | ) 29 | 30 | // Mixin is the logic behind the terraform mixin 31 | type Mixin struct { 32 | runtime.RuntimeConfig 33 | config MixinConfig 34 | 35 | userAgent string 36 | } 37 | 38 | // New terraform mixin client, initialized with useful defaults. 39 | func New() *Mixin { 40 | return NewFor(runtime.NewConfig()) 41 | } 42 | 43 | func NewFor(cfg runtime.RuntimeConfig) *Mixin { 44 | 45 | m := &Mixin{ 46 | RuntimeConfig: cfg, 47 | config: MixinConfig{ 48 | WorkingDir: DefaultWorkingDir, 49 | ClientVersion: DefaultClientVersion, 50 | InitFile: DefaultInitFile, 51 | }, 52 | } 53 | 54 | m.SetUserAgent() 55 | return m 56 | } 57 | 58 | func (m *Mixin) getPayloadData() ([]byte, error) { 59 | reader := bufio.NewReader(m.In) 60 | data, err := ioutil.ReadAll(reader) 61 | if err != nil { 62 | return nil, errors.Wrap(err, "could not read the payload from STDIN") 63 | } 64 | return data, nil 65 | } 66 | 67 | func (m *Mixin) getOutput(ctx context.Context, outputName string) ([]byte, error) { 68 | // Using -json instead of -raw because terraform only allows for string, bool, 69 | // and number output types when using -raw. This means that the outputs will 70 | // need to be unencoded to raw string because -json does json compliant html 71 | // special character encoding. 72 | cmd := m.NewCommand(ctx, "terraform", "output", "-json", outputName) 73 | cmd.Stderr = m.Err 74 | 75 | // Terraform appears to auto-append a newline character when printing outputs 76 | // Trim this character before returning the output 77 | out, err := cmd.Output() 78 | if err != nil { 79 | prettyCmd := fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " ")) 80 | return nil, errors.Wrap(err, fmt.Sprintf("couldn't run command %s", prettyCmd)) 81 | } 82 | 83 | // Implement a custom JSON encoder that doesn't do HTML escaping. This allows 84 | // for recrusive decoding of complex JSON objects using the unmarshal and then 85 | // re-encoding it but skipping the html escaping. This allows for complex types 86 | // like maps to be represented as a byte slice without having go types be 87 | // part of that byte slice, eg: without the re-encoding, a JSON byte slice with 88 | // '{"foo": "bar"}' would become map[foo:bar]. 89 | var outDat interface{} 90 | err = json.Unmarshal(out, &outDat) 91 | if err != nil { 92 | return []byte{}, err 93 | } 94 | // If the output is a string then don't re-encode it, just return the decoded 95 | // json string. Re-encoding the string will wrap it in quotes which breaks 96 | // the compabiltity with -raw 97 | if outStr, ok := outDat.(string); ok { 98 | return []byte(outStr), nil 99 | } 100 | buffer := &bytes.Buffer{} 101 | encoder := json.NewEncoder(buffer) 102 | encoder.SetEscapeHTML(false) 103 | err = encoder.Encode(outDat) 104 | if err != nil { 105 | return []byte{}, err 106 | } 107 | return bytes.TrimRight(buffer.Bytes(), "\n"), nil 108 | } 109 | 110 | func (m *Mixin) handleOutputs(ctx context.Context, outputs []Output) error { 111 | var bigErr *multierror.Error 112 | 113 | for _, output := range outputs { 114 | bytes, err := m.getOutput(ctx, output.Name) 115 | if err != nil { 116 | bigErr = multierror.Append(bigErr, err) 117 | continue 118 | } 119 | 120 | err = m.Context.WriteMixinOutputToFile(output.Name, bytes) 121 | if err != nil { 122 | bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to persist output '%s'", output.Name)) 123 | } 124 | 125 | if output.DestinationFile != "" { 126 | err = m.Context.FileSystem.MkdirAll(filepath.Dir(output.DestinationFile), 0700) 127 | if err != nil { 128 | bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to create destination directory for output '%s'", output.Name)) 129 | } 130 | 131 | err = m.Context.FileSystem.WriteFile(output.DestinationFile, bytes, 0700) 132 | if err != nil { 133 | bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to copy output '%s' to '%s'", output.Name, output.DestinationFile)) 134 | } 135 | } 136 | } 137 | return bigErr.ErrorOrNil() 138 | } 139 | 140 | // commandPreRun runs setup tasks applicable for every action 141 | func (m *Mixin) commandPreRun(ctx context.Context, step *Step) error { 142 | if step.LogLevel != "" { 143 | os.Setenv("TF_LOG", step.LogLevel) 144 | } 145 | 146 | // First, change to specified working dir 147 | m.Chdir(m.config.WorkingDir) 148 | if m.DebugMode { 149 | fmt.Fprintln(m.Err, "Terraform working directory is", m.Getwd()) 150 | } 151 | 152 | // Initialize Terraform 153 | fmt.Println("Initializing Terraform...") 154 | err := m.Init(ctx, step.BackendConfig) 155 | if err != nil { 156 | return fmt.Errorf("could not init terraform, %s", err) 157 | } 158 | return nil 159 | } 160 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/bad-install-input.desc-empty.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | - terraform: 3 | description: "" -------------------------------------------------------------------------------- /pkg/terraform/testdata/bad-install-input.missing-desc.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | - terraform: 3 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/bad-uninstall-disable-save-var.yaml: -------------------------------------------------------------------------------- 1 | uninstall: 2 | - terraform: 3 | description: "Uninstall MySQL" 4 | logLevel: TRACE 5 | disableVarFile: true 6 | backendConfig: 7 | key: "my.tfstate" 8 | vars: 9 | myvar: "foo" 10 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/bad-uninstall-input.input-not-valid.yaml: -------------------------------------------------------------------------------- 1 | uninstall: 2 | - terraform: 3 | description: "Invalid Uninstall" 4 | input: true -------------------------------------------------------------------------------- /pkg/terraform/testdata/bad-upgrade-disable-save-var.yaml: -------------------------------------------------------------------------------- 1 | upgrade: 2 | - terraform: 3 | description: "Upgrade MySQL" 4 | logLevel: TRACE 5 | disableVarFile: true 6 | backendConfig: 7 | key: "my.tfstate" 8 | vars: 9 | myvar: "foo" 10 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/build-input-in-airgapped-env.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | clientVersion: 1.2.3 3 | installHost: https://install.example.com 4 | providerHost: https://providers.example.com/ 5 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/build-input-with-config.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | clientVersion: 0.13.0-rc1 3 | userAgentOptOut: true 4 | install: 5 | - terraform: 6 | description: "noop" 7 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/install-input-disable-save-vars.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | - terraform: 3 | description: "Install MySQL" 4 | logLevel: TRACE 5 | disableVarFile: true 6 | backendConfig: 7 | key: "my.tfstate" 8 | vars: 9 | myvar: "foo" 10 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/install-input-no-vars-block.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | - terraform: 3 | description: "Install MySQL" 4 | input: false 5 | logLevel: TRACE 6 | backendConfig: 7 | key: "my.tfstate" 8 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/install-input.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | - terraform: 3 | description: "Install MySQL" 4 | logLevel: TRACE 5 | backendConfig: 6 | key: "my.tfstate" 7 | vars: 8 | myvar: "foo" 9 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/invoke-input.yaml: -------------------------------------------------------------------------------- 1 | custom: 2 | - terraform: 3 | description: "Custom Action" 4 | arguments: 5 | - "custom" 6 | logLevel: TRACE 7 | backendConfig: 8 | key: my.tfstate 9 | vars: 10 | myvar: foo 11 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/step-input.yaml: -------------------------------------------------------------------------------- 1 | custom: 2 | - terraform: 3 | description: "Custom Action" 4 | arguments: 5 | - "custom" 6 | flags: 7 | logLevel: TRACE 8 | backendConfig: 9 | - "key=my.tfstate" 10 | vars: 11 | - "myvar=foo" 12 | outputs: 13 | - name: myoutput 14 | -------------------------------------------------------------------------------- /pkg/terraform/testdata/uninstall-input.yaml: -------------------------------------------------------------------------------- 1 | uninstall: 2 | - terraform: 3 | description: "Uninstall MySQL" 4 | logLevel: TRACE 5 | backendConfig: 6 | key: "my.tfstate" 7 | vars: 8 | myvar: "foo" -------------------------------------------------------------------------------- /pkg/terraform/testdata/upgrade-input.yaml: -------------------------------------------------------------------------------- 1 | upgrade: 2 | - terraform: 3 | description: "Upgrade MySQL" 4 | logLevel: TRACE 5 | backendConfig: 6 | key: "my.tfstate" 7 | vars: 8 | myvar: "foo" -------------------------------------------------------------------------------- /pkg/terraform/uninstall.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "context" 5 | 6 | "get.porter.sh/porter/pkg/exec/builder" 7 | ) 8 | 9 | // Uninstall runs a terraform destroy 10 | func (m *Mixin) Uninstall(ctx context.Context) error { 11 | action, err := m.loadAction(ctx) 12 | if err != nil { 13 | return err 14 | } 15 | step := action.Steps[0] 16 | 17 | err = m.commandPreRun(ctx, &step) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | // Update step fields that exec/builder works with 23 | step.Arguments = []string{"destroy"} 24 | // Always run in non-interactive mode 25 | step.Flags = append(step.Flags, builder.NewFlag("auto-approve")) 26 | step.Flags = append(step.Flags, builder.NewFlag("input=false")) 27 | 28 | applyVarsToStepFlags(&step) 29 | 30 | action.Steps[0] = step 31 | _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | return m.handleOutputs(ctx, step.Outputs) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/terraform/uninstall_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io/ioutil" 7 | "os" 8 | "strings" 9 | "testing" 10 | 11 | "get.porter.sh/porter/pkg/test" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | yaml "gopkg.in/yaml.v2" 15 | ) 16 | 17 | func TestMixin_UnmarshalUninstallStep(t *testing.T) { 18 | b, err := ioutil.ReadFile("testdata/uninstall-input.yaml") 19 | require.NoError(t, err) 20 | 21 | var action Action 22 | err = yaml.Unmarshal(b, &action) 23 | require.NoError(t, err) 24 | require.Len(t, action.Steps, 1) 25 | step := action.Steps[0] 26 | 27 | assert.Equal(t, "Uninstall MySQL", step.Description) 28 | } 29 | 30 | func TestMixin_Uninstall(t *testing.T) { 31 | defer os.Unsetenv(test.ExpectedCommandEnv) 32 | expectedCommand := strings.Join([]string{ 33 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 34 | "terraform destroy -auto-approve -input=false -var myvar=foo", 35 | }, "\n") 36 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 37 | 38 | b, err := ioutil.ReadFile("testdata/uninstall-input.yaml") 39 | require.NoError(t, err) 40 | 41 | h := NewTestMixin(t) 42 | h.In = bytes.NewReader(b) 43 | 44 | // Set up working dir as current dir 45 | h.config.WorkingDir = h.Getwd() 46 | require.NoError(t, err) 47 | 48 | err = h.Uninstall(context.Background()) 49 | require.NoError(t, err) 50 | 51 | wd := h.Getwd() 52 | require.NoError(t, err) 53 | assert.Equal(t, wd, h.config.WorkingDir) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/terraform/upgrade.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import "context" 4 | 5 | // Upgrade runs a terraform apply, just like Install() 6 | func (m *Mixin) Upgrade(ctx context.Context) error { 7 | return m.Install(ctx) 8 | } 9 | -------------------------------------------------------------------------------- /pkg/terraform/upgrade_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io/ioutil" 7 | "os" 8 | "strings" 9 | "testing" 10 | 11 | "get.porter.sh/porter/pkg/test" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | "gopkg.in/yaml.v2" 15 | ) 16 | 17 | func TestMixin_UnmarshalUpgradeStep(t *testing.T) { 18 | b, err := ioutil.ReadFile("testdata/upgrade-input.yaml") 19 | require.NoError(t, err) 20 | 21 | var action Action 22 | err = yaml.Unmarshal(b, &action) 23 | require.NoError(t, err) 24 | require.Len(t, action.Steps, 1) 25 | step := action.Steps[0] 26 | 27 | assert.Equal(t, "Upgrade MySQL", step.Description) 28 | } 29 | 30 | func TestMixin_Upgrade(t *testing.T) { 31 | defer os.Unsetenv(test.ExpectedCommandEnv) 32 | expectedCommand := strings.Join([]string{ 33 | "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure", 34 | "terraform apply -auto-approve -input=false -var myvar=foo", 35 | }, "\n") 36 | os.Setenv(test.ExpectedCommandEnv, expectedCommand) 37 | 38 | b, err := ioutil.ReadFile("testdata/upgrade-input.yaml") 39 | require.NoError(t, err) 40 | 41 | h := NewTestMixin(t) 42 | h.In = bytes.NewReader(b) 43 | 44 | // Set up working dir as current dir 45 | h.config.WorkingDir = h.Getwd() 46 | require.NoError(t, err) 47 | 48 | err = h.Upgrade(context.Background()) 49 | require.NoError(t, err) 50 | 51 | wd := h.Getwd() 52 | require.NoError(t, err) 53 | assert.Equal(t, wd, h.config.WorkingDir) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/terraform/version.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "get.porter.sh/mixin/terraform/pkg" 5 | "get.porter.sh/porter/pkg/mixin" 6 | "get.porter.sh/porter/pkg/pkgmgmt" 7 | "get.porter.sh/porter/pkg/porter/version" 8 | ) 9 | 10 | func (m *Mixin) PrintVersion(opts version.Options) error { 11 | return version.PrintVersion(m.Context, opts, m.Version()) 12 | } 13 | 14 | func (m *Mixin) Version() mixin.Metadata { 15 | return mixin.Metadata{ 16 | Name: "terraform", 17 | VersionInfo: pkgmgmt.VersionInfo{ 18 | Version: pkg.Version, 19 | Commit: pkg.Commit, 20 | Author: "Porter Authors", 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/terraform/version_test.go: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "get.porter.sh/mixin/terraform/pkg" 10 | "get.porter.sh/porter/pkg/porter/version" 11 | "get.porter.sh/porter/pkg/printer" 12 | ) 13 | 14 | func TestPrintVersion(t *testing.T) { 15 | pkg.Commit = "abc123" 16 | pkg.Version = "v1.2.3" 17 | 18 | m := NewTestMixin(t) 19 | 20 | opts := version.Options{} 21 | err := opts.Validate() 22 | require.NoError(t, err) 23 | m.PrintVersion(opts) 24 | 25 | gotOutput := m.TestContext.GetOutput() 26 | wantOutput := "terraform v1.2.3 (abc123) by Porter Authors" 27 | if !strings.Contains(gotOutput, wantOutput) { 28 | t.Fatalf("invalid output:\nWANT:\t%q\nGOT:\t%q\n", wantOutput, gotOutput) 29 | } 30 | } 31 | 32 | func TestPrintJsonVersion(t *testing.T) { 33 | pkg.Commit = "abc123" 34 | pkg.Version = "v1.2.3" 35 | 36 | m := NewTestMixin(t) 37 | 38 | opts := version.Options{} 39 | opts.RawFormat = string(printer.FormatJson) 40 | err := opts.Validate() 41 | require.NoError(t, err) 42 | m.PrintVersion(opts) 43 | 44 | gotOutput := m.TestContext.GetOutput() 45 | wantOutput := `{ 46 | "name": "terraform", 47 | "version": "v1.2.3", 48 | "commit": "abc123", 49 | "author": "Porter Authors" 50 | } 51 | ` 52 | if !strings.Contains(gotOutput, wantOutput) { 53 | t.Fatalf("invalid output:\nWANT:\t%q\nGOT:\t%q\n", wantOutput, gotOutput) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pkg/version.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | // These are build-time values, set during an official release 4 | var ( 5 | Commit string 6 | Version string 7 | ) 8 | -------------------------------------------------------------------------------- /scripts/test/test-cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xeuo pipefail 4 | export REGISTRY=${REGISTRY:-$USER} 5 | export REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )" 6 | export PORTER_HOME=${PORTER_HOME:-$REPO_DIR/bin} 7 | # Run tests in a temp directory 8 | export TEST_DIR=/tmp/porter/terraform 9 | mkdir -p ${TEST_DIR} 10 | pushd ${TEST_DIR} 11 | trap popd EXIT 12 | 13 | function verify-output() { 14 | # Verify the output matches the expected value 15 | output=`${PORTER_HOME}/porter installation output show $1` 16 | if [[ "${output}" != "$2" ]]; then 17 | echo "Output '$1' value: '${output}' does not match expected" 18 | return 1 19 | fi 20 | 21 | # Verify the output has no extra newline (mixin should trim newline added by terraform cli) 22 | if [[ "$(${PORTER_HOME}/porter installation output show $1 | wc -l)" > 1 ]]; then 23 | echo "Output '$1' has an extra newline character" 24 | return 1 25 | fi 26 | } 27 | 28 | 29 | # Copy terraform assets 30 | cp -r ${REPO_DIR}/examples/basic-tf-example/terraform . 31 | 32 | # Copy in the terraform porter manifest 33 | cp ${REPO_DIR}/examples/basic-tf-example/porter.yaml . 34 | 35 | ${PORTER_HOME}/porter build 36 | ${PORTER_HOME}/porter install --verbosity=debug \ 37 | --param file_contents='foo!' \ 38 | --param map_var='{"foo": "bar"}' \ 39 | --param array_var='["mylist", "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' \ 40 | --param boolean_var=true \ 41 | --param number_var=1 \ 42 | --param json_encoded_html_string_var='testing?connection&string=<>' \ 43 | --param complex_object_var='{"top_value": "https://my.service?test=$id<>", "nested_object": {"internal_value": "https://my.connection.com?test&test=$hello"}}' \ 44 | --force 45 | 46 | echo "Verifying installation output after install" 47 | verify-output "file_contents" 'foo!' 48 | verify-output "map_var" '{"foo":"bar"}' 49 | verify-output "array_var" '["mylist","https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' 50 | verify-output "boolean_var" 'true' 51 | verify-output "number_var" '1' 52 | verify-output "complex_object_var" '{"nested_object":{"internal_value":"https://my.connection.com?test&test=$hello"},"top_value":"https://my.service?test=$id<>"}' 53 | verify-output "json_encoded_html_string_var" 'testing?connection&string=<>' 54 | 55 | ${PORTER_HOME}/porter invoke --verbosity=debug --action=plan --debug 56 | 57 | ${PORTER_HOME}/porter upgrade --verbosity=debug \ 58 | --param file_contents='bar!' \ 59 | --param map_var='{"bar": "baz"}' \ 60 | --param array_var='["mylist", "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' \ 61 | --param boolean_var=false \ 62 | --param number_var=2 \ 63 | --param json_encoded_html_string_var='?new#conn&string$characters~!' \ 64 | --param complex_object_var='{"top_value": "https://my.updated.service?test=$id<>", "nested_object": {"internal_value": "https://new.connection.com?test&test=$hello"}}' 65 | 66 | echo "Verifying installation output after upgrade" 67 | verify-output "file_contents" 'bar!' 68 | verify-output "map_var" '{"bar":"baz"}' 69 | verify-output "array_var" '["mylist","https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' 70 | verify-output "boolean_var" 'false' 71 | verify-output "number_var" '2' 72 | verify-output "json_encoded_html_string_var" '?new#conn&string$characters~!' 73 | verify-output "complex_object_var" '{"nested_object":{"internal_value":"https://new.connection.com?test&test=$hello"},"top_value":"https://my.updated.service?test=$id<>"}' 74 | 75 | ${PORTER_HOME}/porter uninstall --debug 76 | --------------------------------------------------------------------------------