├── .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 | [](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 |
--------------------------------------------------------------------------------