diff --git a/.github/workflows/terraform-mixin.yml b/.github/workflows/terraform-mixin.yml
new file mode 100644
index 0000000..3ec0c4a
--- /dev/null
+++ b/.github/workflows/terraform-mixin.yml
@@ -0,0 +1,32 @@
+#name: porter/terraform-mixin
+#on:
+# push:
+# branches:
+# - main
+# - v*
+# pull_request:
+# branches:
+# - main
+#jobs:
+# build:
+# runs-on: ubuntu-latest
+# steps:
+# - name: checkout
+# uses: actions/checkout@v4
+# with:
+# fetch-depth: 0
+# - uses: actions/setup-go@v5
+# with:
+# go-version-file: go.mod
+# cache: true
+# - name: Configure Agent
+# run: go run mage.go ConfigureAgent
+# - name: Test
+# run: mage Test
+# - name: Cross Compile
+# run: mage XBuildAll
+# - name: Publish
+# if: success() && github.event_name != 'PullRequest'
+# env:
+# GITHUB_TOKEN: "${{ secrets.PUBLISH_TOKEN }}"
+# run: mage Publish
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8e8a139
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/.idea
+/bin
+/terraform
+*-packr.go
+/build/git_askpass.sh
+examples/**/Dockerfile
+examples/**/.cnab
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4a4bb02
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,75 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to project admins via email to Carolyn Van Slyck (`me@carolynvanslyck.com`)
+or via direct message in [Slack] to Carolyn Van Slyck (`@carolynvs`).
+All complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+[slack]: https://porter.sh/community/#slack
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..cf533b2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing Guide
+
+This is part of the [Porter][porter] project. If you are a new contributor,
+check out our [New Contributor Guide][new-contrib]. The Porter [Contributing
+Guide][contrib] also has lots of information about how to interact with the
+project.
+
+[porter]: https://github.com/getporter/porter
+[new-contrib]: https://porter.sh/contribute
+[contrib]: https://porter.sh/src/CONTRIBUTING.md
+
+---
+
+* [Initial setup](#initial-setup)
+* [Magefile explained](#magefile-explained)
+
+---
+
+# Initial setup
+
+You need to have [porter installed](https://porter.sh/install) first. Then run
+`mage build install`. This will build and install the mixin into your porter
+home directory.
+
+## Magefile explained
+
+Here are the most common [Magefile](https://magefile.org) tasks:
+
+* `build` builds both the runtime and client.
+* `install` installs the mixin into **~/.porter/mixins**.
+* `testUnit` runs the unit tests.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1502a99
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020 Porter Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
index 4973d41..1f23fb6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,305 @@
-# tofu-mixin
-An OpenTofu Mixin for Porter
+# Terraform Mixin for Porter
+
+This is a Terraform mixin for [Porter](https://porter.sh).
+
+[![porter/terraform-mixin](https://github.com/getporter/terraform-mixin/actions/workflows/terraform-mixin.yml/badge.svg?branch=main)](https://github.com/getporter/terraform-mixin/actions/workflows/terraform-mixin.yml)
+
+
+
+## Install via Porter
+
+This will install the latest mixin release via the Porter CLI.
+
+```
+porter mixin install terraform
+```
+
+## Build from source
+
+Following commands build the terraform mixin.
+```bash
+git clone https://github.com/getporter/terraform-mixin.git
+cd terraform-mixin
+# Learn about Mage in our CONTRIBUTING.md
+go run mage.go EnsureMage
+mage build
+```
+
+Then, to install the resulting mixin into PORTER_HOME, execute
+`mage install`
+
+## Mixin Configuration
+
+```yaml
+mixins:
+- terraform:
+ clientVersion: 1.0.3
+ workingDir: myinfra
+ initFile: providers.tf
+```
+
+### clientVersion
+The Terraform client version can be specified via the `clientVersion` configuration when declaring this mixin.
+
+### workingDir
+The `workingDir` configuration setting is the relative path to your terraform files. Defaults to "terraform".
+
+### initFile
+Terraform providers are installed into the bundle during porter build.
+We recommend that you put your provider declarations into a single file, e.g. "terraform/providers.tf".
+Then use `initFile` to specify the relative path to this file within workingDir.
+This will dramatically improve Docker image layer caching and performance when building, publishing and installing the bundle.
+> 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.
+
+### User Agent Opt Out
+
+When you declare the mixin, you can disable the mixin from customizing the azure user agent string
+
+```yaml
+mixins:
+- terraform:
+ userAgentOptOut: true
+```
+
+By default, the terraform mixin adds the porter and mixin version to the user agent string used by the azure provider.
+We use this to understand which version of porter and the mixin are being used by a bundle, and assist with troubleshooting.
+Below is an example of what the user agent string looks like:
+
+```
+AZURE_HTTP_USER_AGENT="getporter/porter/v1.0.0 getporter/terraform/v1.2.3"
+```
+
+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.
+
+[template Dockerfile]: https://getporter.org/bundle/custom-dockerfile/
+
+## Terraform state
+
+### Let Porter do the heavy lifting
+
+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.
+
+Here is an example setup that works with Porter v0.38:
+
+```yaml
+parameters:
+ - name: tfstate
+ type: file
+ # This designates the path within the installer to place the parameter value
+ path: /cnab/app/terraform/terraform.tfstate
+ # Here we tell Porter that the value for this parameter should come from the 'tfstate' output
+ source:
+ output: tfstate
+
+outputs:
+ - name: tfstate
+ type: file
+ # This designates the path within the installer to read the output from
+ path: /cnab/app/terraform/terraform.tfstate
+```
+
+If you are working with the Porter v1 prerelease, use the new state section:
+
+```yaml
+state:
+ - name: tfstate
+ path: terraform/terraform.tfstate
+ - name: tfvars
+ path: terraform/terraform.tfvars.json
+```
+
+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.
+
+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).
+
+### Remote Backends
+
+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.
+
+
+## Terraform variables file
+
+By default the mixin will create a default
+[`terraform.tfvars.json`](https://www.terraform.io/docs/language/values/variables.html#variable-definitions-tfvars-files)
+file from the `vars` block during during the install step.
+
+To use this file, a `tfvars` file parameter and output must be added to persist it for subsequent steps.
+
+This can be disabled by setting `disableVarFile` to `true` during install.
+
+Here is an example setup using the tfvar file:
+
+```yaml
+parameters:
+ - name: tfvars
+ type: file
+ # This designates the path within the installer to place the parameter value
+ path: /cnab/app/terraform/terraform.tfvars.json
+ # Here we tell Porter that the value for this parameter should come from the 'tfvars' output
+ source:
+ output: tfvars
+ - name: foo
+ type: string
+ applyTo:
+ - install
+ - name: baz
+ type: string
+ default: blaz
+ applyTo:
+ - install
+
+outputs:
+ - name: tfvars
+ type: file
+ # This designates the path within the installer to read the output from
+ path: /cnab/app/terraform/terraform.tfvars.json
+
+install:
+ - terraform:
+ description: "Install Azure Key Vault"
+ vars:
+ foo: bar
+ baz: biz
+ outputs:
+ - name: vault_uri
+upgrade: # No var block required
+ - terraform:
+ description: "Install Azure Key Vault"
+ outputs:
+ - name: vault_uri
+uninstall: # No var block required
+ - terraform:
+ description: "Install Azure Key Vault"
+ outputs:
+ - name: vault_uri
+```
+
+and with var file disabled
+
+```yaml
+parameters:
+ - name: foo
+ type: string
+ applyTo:
+ - install
+ - name: baz
+ type: string
+ default: blaz
+ applyTo:
+ - install
+
+install:
+ - terraform:
+ description: "Install Azure Key Vault"
+ disableVarFile: true
+ vars:
+ foo: bar
+ baz: biz
+ outputs:
+ - name: vault_uri
+uninstall: # Var block required
+ - terraform:
+ description: "Install Azure Key Vault"
+ vars:
+ foo: bar
+ baz: biz
+```
+
+## Examples
+
+### Install
+
+```yaml
+install:
+ - terraform:
+ description: "Install Azure Key Vault"
+ backendConfig:
+ key: "mybundle.tfstate"
+ storage_account_name: "mystorageacct"
+ container_name: "mycontainer"
+ access_key: "myaccesskey"
+ outputs:
+ - name: vault_uri
+```
+
+### Upgrade
+
+```yaml
+upgrade:
+ - terraform:
+ description: "Upgrade Azure Key Vault"
+ backendConfig:
+ key: "mybundle.tfstate"
+ storage_account_name: "mystorageacct"
+ container_name: "mycontainer"
+ access_key: "myaccesskey"
+ outputs:
+ - name: vault_uri
+```
+
+### Invoke
+
+An invoke step is used for any custom action (not one of `install`, `upgrade` or `uninstall`).
+
+By default, the command given to `terraform` will be the step name. Here it is `show`,
+resulting in `terraform show` with the provided configuration.
+
+```yaml
+show:
+ - terraform:
+ description: "Invoke 'terraform show'"
+ backendConfig:
+ key: "mybundle.tfstate"
+ storage_account_name: "mystorageacct"
+ container_name: "mycontainer"
+ access_key: "myaccesskey"
+```
+
+Or, if the step name does not match the intended terraform command, the command
+can be supplied via the `arguments:` section, like so:
+
+```yaml
+printVersion:
+ - terraform:
+ description: "Invoke 'terraform version'"
+ arguments:
+ - version
+```
+
+### Uninstall
+
+```yaml
+uninstall:
+ - terraform:
+ description: "Uninstall Azure Key Vault"
+ backendConfig:
+ key: "mybundle.tfstate"
+ storage_account_name: "mystorageacct"
+ container_name: "mycontainer"
+ access_key: "myaccesskey"
+```
+
+See further examples in the [Examples](examples) directory
+
+## Step Outputs
+
+As seen above, outputs can be declared for a step. All that is needed is the name of the output.
+
+For each output listed, `terraform output ` is invoked to fetch the output value
+from the state file for use by Porter. Outputs can be saved to the filesystem so that subsequent
+steps can use the file by specifying the `destinationFile` field. This is particularly useful
+when your terraform module creates a Kubernetes cluster. In the example below, the module
+creates a cluster, and then writes the kubeconfig to /root/.kube/config so that the rest of the
+bundle can immediately use the cluster.
+
+```yaml
+install:
+ - terraform:
+ description: "Create a Kubernetes cluster"
+ outputs:
+ - name: kubeconfig
+ destinationFile: /root/.kube/config
+```
+
+See the Porter [Outputs documentation](https://porter.sh/wiring/#outputs) on how to wire up
+outputs for use in a bundle.
diff --git a/REVIEWING.md b/REVIEWING.md
new file mode 100644
index 0000000..84866f7
--- /dev/null
+++ b/REVIEWING.md
@@ -0,0 +1,17 @@
+# Reviewing Guide
+
+This is part of the [Porter][porter] project and follows the Porter [Reviewing
+Guide][review].
+
+[porter]: https://github.com/getporter/porter
+[review]: https://porter.sh/src/REVIEWING.md
+
+## Cut a release
+
+🧀💨
+
+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:
+
+https://porter.sh/src/docs/content/mixins
+
+[release]: https://porter.sh/src/REVIEWING.md#cut-a-release
diff --git a/build/atom-template.xml b/build/atom-template.xml
new file mode 100644
index 0000000..03bbeb9
--- /dev/null
+++ b/build/atom-template.xml
@@ -0,0 +1,25 @@
+
+ https://porter.sh
+ Porter Mixins
+ {{Updated}}
+
+
+ Porter Authors
+ https://porter.sh/mixins
+
+ {{#Mixins}}
+
+ {{/Mixins}}
+ {{#Entries}}
+
+ https://cdn.porter.sh/mixins/{{Mixin}}/{{Version}}
+ {{Mixin}} @ {{Version}}
+ {{Updated}}
+
+ {{Version}}
+ {{#Files}}
+
+ {{/Files}}
+
+ {{/Entries}}
+
diff --git a/build/testdata/bundles/terraform/.gitignore b/build/testdata/bundles/terraform/.gitignore
new file mode 100644
index 0000000..ea5d8c0
--- /dev/null
+++ b/build/testdata/bundles/terraform/.gitignore
@@ -0,0 +1,2 @@
+Dockerfile
+.cnab
diff --git a/cmd/terraform/build.go b/cmd/terraform/build.go
new file mode 100644
index 0000000..c83f11c
--- /dev/null
+++ b/cmd/terraform/build.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "github.com/spf13/cobra"
+)
+
+func buildBuildCommand(m *terraform.Mixin) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "build",
+ Short: "Generate Dockerfile lines for the bundle invocation image",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return m.Build(cmd.Context())
+ },
+ }
+ return cmd
+}
diff --git a/cmd/terraform/install.go b/cmd/terraform/install.go
new file mode 100644
index 0000000..482ac39
--- /dev/null
+++ b/cmd/terraform/install.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "github.com/spf13/cobra"
+)
+
+var (
+ commandFile string
+)
+
+func buildInstallCommand(m *terraform.Mixin) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "install",
+ Short: "Execute the install functionality of this mixin",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return m.Install(cmd.Context())
+ },
+ }
+ return cmd
+}
diff --git a/cmd/terraform/invoke.go b/cmd/terraform/invoke.go
new file mode 100644
index 0000000..eac9d5a
--- /dev/null
+++ b/cmd/terraform/invoke.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "github.com/spf13/cobra"
+
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+)
+
+func buildInvokeCommand(mixin *terraform.Mixin) *cobra.Command {
+ opts := terraform.InvokeOptions{}
+
+ cmd := &cobra.Command{
+ Use: "invoke",
+ Short: "Execute the invoke functionality of this mixin",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return mixin.Invoke(cmd.Context(), opts)
+ },
+ }
+ flags := cmd.Flags()
+ flags.StringVar(&opts.Action, "action", "", "Custom action name to invoke.")
+
+ return cmd
+}
diff --git a/cmd/terraform/main.go b/cmd/terraform/main.go
new file mode 100644
index 0000000..254470b
--- /dev/null
+++ b/cmd/terraform/main.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "runtime/debug"
+
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "get.porter.sh/porter/pkg/cli"
+ "github.com/spf13/cobra"
+ "go.opentelemetry.io/otel/attribute"
+)
+
+func main() {
+ run := func() int {
+ ctx := context.Background()
+ m := terraform.New()
+ ctx, err := m.RuntimeConfig.ConfigureLogging(ctx)
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(cli.ExitCodeErr)
+ }
+ cmd := buildRootCommand(m, os.Stdin)
+
+ // We don't have tracing working inside a bundle working currently.
+ // We are using StartRootSpan anyway because it creates a TraceLogger and sets it
+ // on the context, so we can grab it later
+ ctx, log := m.RuntimeConfig.StartRootSpan(ctx, "exec")
+ defer func() {
+ // Capture panics and trace them
+ if panicErr := recover(); panicErr != nil {
+ log.Error(fmt.Errorf("%s", panicErr),
+ attribute.Bool("panic", true),
+ attribute.String("stackTrace", string(debug.Stack())))
+ log.EndSpan()
+ m.Close()
+ os.Exit(cli.ExitCodeErr)
+ } else {
+ log.Close()
+ m.Close()
+ }
+ }()
+
+ if err := cmd.ExecuteContext(ctx); err != nil {
+ return cli.ExitCodeErr
+ }
+ return cli.ExitCodeSuccess
+ }
+ os.Exit(run())
+}
+
+func buildRootCommand(m *terraform.Mixin, in io.Reader) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "terraform",
+ Long: "A terraform mixin for porter 👩🏽✈️",
+ PersistentPreRun: func(cmd *cobra.Command, args []string) {
+ // Enable swapping out stdout/stderr for testing
+ m.In = in
+ m.Out = cmd.OutOrStdout()
+ m.Err = cmd.OutOrStderr()
+ },
+ SilenceUsage: true,
+ }
+
+ cmd.PersistentFlags().BoolVar(&m.DebugMode, "debug", false, "Enable debug logging")
+
+ cmd.AddCommand(buildVersionCommand(m))
+ cmd.AddCommand(buildSchemaCommand(m))
+ cmd.AddCommand(buildBuildCommand(m))
+ cmd.AddCommand(buildInstallCommand(m))
+ cmd.AddCommand(buildInvokeCommand(m))
+ cmd.AddCommand(buildUninstallCommand(m))
+ cmd.AddCommand(buildUpgradeCommand(m))
+
+ return cmd
+}
diff --git a/cmd/terraform/schema.go b/cmd/terraform/schema.go
new file mode 100644
index 0000000..5d4fd2c
--- /dev/null
+++ b/cmd/terraform/schema.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "github.com/spf13/cobra"
+)
+
+func buildSchemaCommand(m *terraform.Mixin) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "schema",
+ Short: "Print the json schema for the mixin",
+ Run: func(cmd *cobra.Command, args []string) {
+ m.PrintSchema()
+ },
+ }
+ return cmd
+}
diff --git a/cmd/terraform/uninstall.go b/cmd/terraform/uninstall.go
new file mode 100644
index 0000000..0247ea4
--- /dev/null
+++ b/cmd/terraform/uninstall.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "github.com/spf13/cobra"
+)
+
+func buildUninstallCommand(m *terraform.Mixin) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "uninstall",
+ Short: "Execute the uninstall functionality of this mixin",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return m.Uninstall(cmd.Context())
+ },
+ }
+ return cmd
+}
diff --git a/cmd/terraform/upgrade.go b/cmd/terraform/upgrade.go
new file mode 100644
index 0000000..ecf8a0c
--- /dev/null
+++ b/cmd/terraform/upgrade.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "github.com/spf13/cobra"
+)
+
+func buildUpgradeCommand(m *terraform.Mixin) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "upgrade",
+ Short: "Execute the upgrade functionality of this mixin",
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return m.Upgrade(cmd.Context())
+ },
+ }
+ return cmd
+}
diff --git a/cmd/terraform/version.go b/cmd/terraform/version.go
new file mode 100644
index 0000000..a1fc675
--- /dev/null
+++ b/cmd/terraform/version.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+ "get.porter.sh/mixin/terraform/pkg/terraform"
+ "get.porter.sh/porter/pkg/porter/version"
+ "github.com/spf13/cobra"
+)
+
+func buildVersionCommand(m *terraform.Mixin) *cobra.Command {
+ opts := version.Options{}
+
+ cmd := &cobra.Command{
+ Use: "version",
+ Short: "Print the mixin version",
+ PreRunE: func(cmd *cobra.Command, args []string) error {
+ return opts.Validate()
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return m.PrintVersion(opts)
+ },
+ }
+
+ f := cmd.Flags()
+ f.StringVarP(&opts.RawFormat, "output", "o", string(version.DefaultVersionFormat),
+ "Specify an output format. Allowed values: json, plaintext")
+
+ return cmd
+}
diff --git a/examples/azure-aks/porter.yaml b/examples/azure-aks/porter.yaml
new file mode 100644
index 0000000..7242af6
--- /dev/null
+++ b/examples/azure-aks/porter.yaml
@@ -0,0 +1,105 @@
+schemaVersion: 1.0.0
+name: terraform-aks
+version: 0.2.0
+registry: ghcr.io/getporter
+
+credentials:
+ - name: subscription_id
+ env: TF_VAR_subscription_id
+
+ - name: tenant_id
+ env: TF_VAR_tenant_id
+
+ - name: client_id
+ env: TF_VAR_client_id
+
+ - name: client_secret
+ env: TF_VAR_client_secret
+
+ - name: ssh_public_key
+ env: TF_VAR_ssh_public_key
+
+ - name: backend_storage_access_key
+ env: TF_VAR_backend_storage_access_key
+
+ - name: backend_storage_account
+ env: TF_VAR_backend_storage_account
+
+ - name: backend_storage_container
+ env: TF_VAR_backend_storage_container
+
+parameters:
+ - name: location
+ type: string
+ default: "East US"
+ env: TF_VAR_location
+
+ - name: kubernetes_version
+ type: string
+ default: "1.21.2"
+ env: TF_VAR_kubernetes_version
+
+ - name: agent_count
+ type: integer
+ default: 1
+ env: TF_VAR_agent_count
+
+ - name: dns_prefix
+ type: string
+ default: "porteraks"
+ env: TF_VAR_dns_prefix
+
+ - name: cluster_name
+ type: string
+ default: "porteraks"
+ env: TF_VAR_cluster_name
+
+ - name: resource_group_name
+ type: string
+ default: "porteraks"
+ env: TF_VAR_resource_group_name
+
+mixins:
+ - terraform:
+ initFile: providers.tf
+
+customActions:
+ show:
+ description: "Invoke 'terraform show'"
+ modifies: false
+
+install:
+ - terraform:
+ description: "Install Azure Kubernetes Service"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+
+upgrade:
+ - terraform:
+ description: "Upgrade Azure Kubernetes Service"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+
+show:
+ - terraform:
+ description: "Invoke 'terraform show'"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+
+uninstall:
+ - terraform:
+ description: "Uninstall Azure Kubernetes Service"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
diff --git a/examples/azure-aks/terraform/aks.tf b/examples/azure-aks/terraform/aks.tf
new file mode 100644
index 0000000..81cea02
--- /dev/null
+++ b/examples/azure-aks/terraform/aks.tf
@@ -0,0 +1,36 @@
+resource "azurerm_resource_group" "k8s" {
+ name = var.resource_group_name
+ location = var.location
+}
+
+resource "azurerm_kubernetes_cluster" "k8s" {
+ name = var.cluster_name
+ location = azurerm_resource_group.k8s.location
+ resource_group_name = azurerm_resource_group.k8s.name
+ dns_prefix = var.dns_prefix
+ kubernetes_version = var.kubernetes_version
+
+ linux_profile {
+ admin_username = "ubuntu"
+
+ ssh_key {
+ key_data = var.ssh_public_key
+ }
+ }
+
+ default_node_pool {
+ name = "default"
+ node_count = var.agent_count
+ vm_size = "Standard_DS2_v2"
+ os_disk_size_gb = 30
+ }
+
+ service_principal {
+ client_id = var.client_id
+ client_secret = var.client_secret
+ }
+
+ tags = {
+ Environment = "Development"
+ }
+}
\ No newline at end of file
diff --git a/examples/azure-aks/terraform/output.tf b/examples/azure-aks/terraform/output.tf
new file mode 100644
index 0000000..7eecbc4
--- /dev/null
+++ b/examples/azure-aks/terraform/output.tf
@@ -0,0 +1,32 @@
+output "client_key" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.client_key
+ sensitive = true
+}
+
+output "client_certificate" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.client_certificate
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.cluster_ca_certificate
+ sensitive = true
+}
+
+output "cluster_username" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.username
+}
+
+output "cluster_password" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.password
+ sensitive = true
+}
+
+output "kube_config" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config_raw
+ sensitive = true
+}
+
+output "host" {
+ value = azurerm_kubernetes_cluster.k8s.kube_config.0.host
+}
\ No newline at end of file
diff --git a/examples/azure-aks/terraform/params.tf b/examples/azure-aks/terraform/params.tf
new file mode 100644
index 0000000..8c657cd
--- /dev/null
+++ b/examples/azure-aks/terraform/params.tf
@@ -0,0 +1,29 @@
+variable "client_id" {}
+variable "client_secret" {}
+variable "tenant_id" {}
+variable "subscription_id" {}
+variable "kubernetes_version" {}
+
+variable "agent_count" {
+ default = 1
+}
+
+variable "ssh_public_key" {
+ default = "~/.ssh/id_rsa.pub"
+}
+
+variable "dns_prefix" {
+ default = "akstest"
+}
+
+variable "cluster_name" {
+ default = "akstest"
+}
+
+variable "resource_group_name" {
+ default = "azure-akstest"
+}
+
+variable location {
+ default = "East US"
+}
\ No newline at end of file
diff --git a/examples/azure-aks/terraform/providers.tf b/examples/azure-aks/terraform/providers.tf
new file mode 100644
index 0000000..34857e8
--- /dev/null
+++ b/examples/azure-aks/terraform/providers.tf
@@ -0,0 +1,18 @@
+provider "azurerm" {
+ features {}
+ subscription_id = var.subscription_id
+ client_id = var.client_id
+ client_secret = var.client_secret
+ tenant_id = var.tenant_id
+}
+
+terraform {
+ required_version = "1.2.9"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "=3.22.0"
+ }
+ }
+ backend "azurerm" {}
+}
diff --git a/examples/azure-keyvault/porter.yaml b/examples/azure-keyvault/porter.yaml
new file mode 100644
index 0000000..973f6ba
--- /dev/null
+++ b/examples/azure-keyvault/porter.yaml
@@ -0,0 +1,95 @@
+
+schemaVersion: 1.0.0
+name: terraform-keyvault
+version: 0.2.0
+registry: ghcr.io/getporter
+
+credentials:
+ - name: subscription_id
+ env: TF_VAR_subscription_id
+
+ - name: tenant_id
+ env: TF_VAR_tenant_id
+
+ - name: client_id
+ env: TF_VAR_client_id
+
+ - name: client_secret
+ env: TF_VAR_client_secret
+
+ - name: backend_storage_access_key
+ env: TF_VAR_backend_storage_access_key
+
+ - name: backend_storage_account
+ env: TF_VAR_backend_storage_account
+
+ - name: backend_storage_container
+ env: TF_VAR_backend_storage_container
+
+parameters:
+ - name: keyvault_name
+ type: string
+ default: "porterkvtest"
+ env: TF_VAR_keyvault_name
+
+ - name: location
+ type: string
+ default: "East US"
+ env: TF_VAR_location
+
+ - name: resource_group_name
+ type: string
+ default: "porterkvtest"
+ env: TF_VAR_resource_group_name
+
+mixins:
+ - exec
+ - terraform:
+ initFile: providers.tf
+
+customActions:
+ show:
+ description: "Invoke 'terraform show'"
+ modifies: false
+
+install:
+ - terraform:
+ description: "Install Azure Key Vault"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+ outputs:
+ - name: vault_uri
+
+upgrade:
+ - terraform:
+ description: "Upgrade Azure Key Vault"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+ outputs:
+ - name: vault_uri
+
+show:
+ - terraform:
+ description: "Invoke 'terraform show'"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
+ outputs:
+ - name: vault_uri
+
+uninstall:
+ - terraform:
+ description: "Uninstall Azure Key Vault"
+ backendConfig:
+ key: ${ bundle.name }.tfstate
+ storage_account_name: ${ bundle.credentials.backend_storage_account }
+ container_name: ${ bundle.credentials.backend_storage_container }
+ access_key: ${ bundle.credentials.backend_storage_access_key }
diff --git a/examples/azure-keyvault/terraform/keyvault.tf b/examples/azure-keyvault/terraform/keyvault.tf
new file mode 100644
index 0000000..48ddbb5
--- /dev/null
+++ b/examples/azure-keyvault/terraform/keyvault.tf
@@ -0,0 +1,41 @@
+resource "azurerm_resource_group" "test" {
+ name = var.resource_group_name
+ location = var.location
+}
+
+resource "azurerm_key_vault" "test" {
+ name = var.keyvault_name
+ location = azurerm_resource_group.test.location
+ resource_group_name = azurerm_resource_group.test.name
+
+ enabled_for_disk_encryption = true
+ tenant_id = var.tenant_id
+
+ sku_name = "standard"
+
+ access_policy {
+ tenant_id = var.tenant_id
+ object_id = var.client_id
+
+ key_permissions = [
+ "get",
+ ]
+
+ secret_permissions = [
+ "get",
+ ]
+
+ storage_permissions = [
+ "get",
+ ]
+ }
+
+ network_acls {
+ default_action = "Deny"
+ bypass = "AzureServices"
+ }
+
+ tags = {
+ environment = "Production"
+ }
+}
diff --git a/examples/azure-keyvault/terraform/output.tf b/examples/azure-keyvault/terraform/output.tf
new file mode 100644
index 0000000..34969ea
--- /dev/null
+++ b/examples/azure-keyvault/terraform/output.tf
@@ -0,0 +1,7 @@
+output "id" {
+ value = azurerm_key_vault.test.id
+}
+
+output "vault_uri" {
+ value = azurerm_key_vault.test.vault_uri
+}
\ No newline at end of file
diff --git a/examples/azure-keyvault/terraform/params.tf b/examples/azure-keyvault/terraform/params.tf
new file mode 100644
index 0000000..d627b29
--- /dev/null
+++ b/examples/azure-keyvault/terraform/params.tf
@@ -0,0 +1,14 @@
+variable "client_id" {}
+variable "client_secret" {}
+variable "tenant_id" {}
+variable "subscription_id" {}
+
+variable "resource_group_name" {
+ default = "azure-kvtest"
+}
+
+variable location {
+ default = "East US"
+}
+
+variable "keyvault_name" {}
\ No newline at end of file
diff --git a/examples/azure-keyvault/terraform/providers.tf b/examples/azure-keyvault/terraform/providers.tf
new file mode 100644
index 0000000..34857e8
--- /dev/null
+++ b/examples/azure-keyvault/terraform/providers.tf
@@ -0,0 +1,18 @@
+provider "azurerm" {
+ features {}
+ subscription_id = var.subscription_id
+ client_id = var.client_id
+ client_secret = var.client_secret
+ tenant_id = var.tenant_id
+}
+
+terraform {
+ required_version = "1.2.9"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "=3.22.0"
+ }
+ }
+ backend "azurerm" {}
+}
diff --git a/examples/basic-tf-example/.gitignore b/examples/basic-tf-example/.gitignore
new file mode 100644
index 0000000..ea5d8c0
--- /dev/null
+++ b/examples/basic-tf-example/.gitignore
@@ -0,0 +1,2 @@
+Dockerfile
+.cnab
diff --git a/examples/basic-tf-example/README.md b/examples/basic-tf-example/README.md
new file mode 100644
index 0000000..64b5bc6
--- /dev/null
+++ b/examples/basic-tf-example/README.md
@@ -0,0 +1,13 @@
+# Basic Terraform Example Bundle
+
+This example demonstrates how to define and use variables and outputs of different data types in a bundle.
+
+## Try it out
+
+```
+cd examples/basic-tf-example
+porter build
+porter install
+porter upgrade
+porter uninstall
+```
diff --git a/examples/basic-tf-example/porter.yaml b/examples/basic-tf-example/porter.yaml
new file mode 100644
index 0000000..4b1b4d1
--- /dev/null
+++ b/examples/basic-tf-example/porter.yaml
@@ -0,0 +1,163 @@
+schemaVersion: 1.0.0
+name: basic-tf-example
+version: 0.3.0
+registry: ghcr.io/getporter
+
+parameters:
+ - name: file_contents
+ type: string
+ applyTo:
+ - install
+ - upgrade
+ default: "foo!"
+ - name: map_var
+ type: object
+ applyTo:
+ - install
+ - upgrade
+ default: { "foo": "bar" }
+ - name: array_var
+ type: array
+ applyTo:
+ - install
+ - upgrade
+ default:
+ [
+ "mylist",
+ "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz",
+ ]
+ - name: boolean_var
+ type: boolean
+ applyTo:
+ - install
+ - upgrade
+ default: true
+ - name: number_var
+ type: number
+ applyTo:
+ - install
+ - upgrade
+ default: 1
+ - name: json_encoded_html_string_var
+ type: string
+ applyTo:
+ - install
+ - upgrade
+ default: "testing?connection&string=<>"
+ - name: complex_object_var
+ type: object
+ applyTo:
+ - install
+ - upgrade
+ default:
+ {
+ "nested_object":
+ { "internal_value": "https://my.connection.com?test&test=$hello" },
+ "top_value": "https://my.service?test=$id<>",
+ }
+
+state:
+ - name: tfstate
+ path: terraform/terraform.tfstate
+ - name: tfvars
+ path: terraform/terraform.tfvars.json
+
+mixins:
+ - terraform:
+ clientVersion: 1.0.0
+
+install:
+ - terraform:
+ description: "Install Terraform assets"
+ vars:
+ file_contents: ${bundle.parameters.file_contents}
+ map_var: ${bundle.parameters.map_var}
+ array_var: ${bundle.parameters.array_var}
+ boolean_var: ${bundle.parameters.boolean_var}
+ number_var: ${bundle.parameters.number_var}
+ json_encoded_html_string_var: ${bundle.parameters.json_encoded_html_string_var}
+ complex_object_var: ${bundle.parameters.complex_object_var}
+ outputs:
+ - name: file_contents
+ - name: map_var
+ - name: array_var
+ - name: boolean_var
+ - name: number_var
+ - name: json_encoded_html_string_var
+ - name: complex_object_var
+
+upgrade:
+ - terraform:
+ description: "Upgrade Terraform assets"
+ vars:
+ file_contents: ${bundle.parameters.file_contents}
+ map_var: ${bundle.parameters.map_var}
+ array_var: ${bundle.parameters.array_var}
+ boolean_var: ${bundle.parameters.boolean_var}
+ number_var: ${bundle.parameters.number_var}
+ json_encoded_html_string_var: ${bundle.parameters.json_encoded_html_string_var}
+ complex_object_var: ${bundle.parameters.complex_object_var}
+ outputs:
+ - name: file_contents
+ - name: map_var
+ - name: array_var
+ - name: boolean_var
+ - name: number_var
+ - name: json_encoded_html_string_var
+ - name: complex_object_var
+
+show:
+ - terraform:
+ description: "Invoke 'terraform show'"
+
+plan:
+ - terraform:
+ description: "Invoke 'terraform plan'"
+# Note: this can't be 'version:' as this would conflict with top-level field
+# Hence the need for the 'arguments:' override
+printVersion:
+ - terraform:
+ description: "Invoke 'terraform version'"
+ arguments:
+ - "version"
+
+uninstall:
+ - terraform:
+ description: "Uninstall Terraform assets"
+
+outputs:
+ - name: file_contents
+ type: string
+ applyTo:
+ - install
+ - upgrade
+ - name: map_var
+ type: object
+ applyTo:
+ - install
+ - upgrade
+ - name: array_var
+ type: array
+ applyTo:
+ - install
+ - upgrade
+ - name: boolean_var
+ type: boolean
+ applyTo:
+ - install
+ - upgrade
+ - name: number_var
+ type: number
+ applyTo:
+ - install
+ - upgrade
+ - name: json_encoded_html_string_var
+ type: string
+ applyTo:
+ - install
+ - upgrade
+ - name: complex_object_var
+ type: object
+ applyTo:
+ - install
+ - upgrade
diff --git a/examples/basic-tf-example/terraform/main.tf b/examples/basic-tf-example/terraform/main.tf
new file mode 100644
index 0000000..172159b
--- /dev/null
+++ b/examples/basic-tf-example/terraform/main.tf
@@ -0,0 +1,4 @@
+resource "local_file" "foo" {
+ content = var.file_contents
+ filename = "${path.module}/foo"
+}
diff --git a/examples/basic-tf-example/terraform/outputs.tf b/examples/basic-tf-example/terraform/outputs.tf
new file mode 100644
index 0000000..a000b02
--- /dev/null
+++ b/examples/basic-tf-example/terraform/outputs.tf
@@ -0,0 +1,28 @@
+output "file_contents" {
+ value = var.file_contents
+ description = "Contents of the file 'foo'"
+}
+
+output "map_var" {
+ value = var.map_var
+}
+
+output "array_var" {
+ value = var.array_var
+}
+
+output "boolean_var" {
+ value = var.boolean_var
+}
+
+output "number_var" {
+ value = var.number_var
+}
+
+output "json_encoded_html_string_var" {
+ value = var.json_encoded_html_string_var
+ }
+
+output "complex_object_var" {
+ value = var.complex_object_var
+}
diff --git a/examples/basic-tf-example/terraform/variables.tf b/examples/basic-tf-example/terraform/variables.tf
new file mode 100644
index 0000000..e4c6d28
--- /dev/null
+++ b/examples/basic-tf-example/terraform/variables.tf
@@ -0,0 +1,50 @@
+variable "file_contents" {
+ description = "Contents of the file 'foo'"
+ default = "bar"
+}
+
+variable "map_var" {
+ description = "Map variable"
+ type = map(string)
+ default = { foo = "bar" }
+}
+
+variable "array_var" {
+ description = "Array Variable"
+ type = list(any)
+ default = ["mylist"]
+}
+
+variable "boolean_var" {
+ description = "Boolean Variable"
+ type = bool
+ default = false
+}
+
+variable "number_var" {
+ description = "Number Variable"
+ type = number
+ default = 0
+}
+
+variable "json_encoded_html_string_var" {
+ description = "String variable with html characters that should not be escaped"
+ type = string
+ default = "hello&world"
+}
+
+variable "complex_object_var" {
+ description = "Object variable"
+ type = object({
+ top_value = string
+ nested_object = object({
+ internal_value = string
+ })
+ })
+ default = {
+ top_value = "top_value"
+ nested_object = {
+ internal_value = "internal"
+ }
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..7eae635
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,150 @@
+module get.porter.sh/mixin/terraform
+
+go 1.21
+
+toolchain go1.21.3
+
+replace (
+ // These are replace directives copied from porter
+ // When you use a newer version of Porter, if you run into trouble with go mod tidy
+ // Copy any additional replace directives from Porter's go.mod file
+ // They must match the replaces used by porter everything to compile
+ // See https://github.com/hashicorp/go-plugin/pull/127 and
+ // https://github.com/hashicorp/go-plugin/pull/163
+ // Also includes branches we haven't PR'd yet: capture-yamux-logs, context-cancellation
+ // Tagged from v1.4.4, the porter branch
+ github.com/hashicorp/go-plugin => github.com/getporter/go-plugin v1.4.4-porter.1
+ // Fixes https://github.com/spf13/viper/issues/761
+ github.com/spf13/viper => github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363
+)
+
+require (
+ get.porter.sh/magefiles v0.6.7
+ get.porter.sh/porter v1.0.17
+ github.com/PaesslerAG/jsonpath v0.1.1
+ github.com/carolynvs/magex v0.9.0
+ github.com/ghodss/yaml v1.0.0
+ github.com/hashicorp/go-multierror v1.1.1
+ github.com/pkg/errors v0.9.1
+ github.com/spf13/cobra v1.7.0
+ github.com/stretchr/testify v1.8.4
+ github.com/tidwall/gjson v1.15.0
+ github.com/xeipuuv/gojsonschema v1.2.0
+ go.opentelemetry.io/otel v1.19.0
+ gopkg.in/yaml.v2 v2.4.0
+)
+
+require (
+ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
+ github.com/Masterminds/semver v1.5.0 // indirect
+ github.com/Masterminds/semver/v3 v3.2.1 // indirect
+ github.com/PaesslerAG/gval v1.2.2 // indirect
+ github.com/PuerkitoBio/goquery v1.8.1 // indirect
+ github.com/andybalholm/brotli v1.1.0 // indirect
+ github.com/andybalholm/cascadia v1.3.2 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/carolynvs/aferox v0.3.0 // indirect
+ github.com/cbroglie/mustache v1.4.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+ github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cnabio/cnab-go v0.25.1 // indirect
+ github.com/cnabio/cnab-to-oci v0.4.0 // indirect
+ github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
+ github.com/cyberphone/json-canonicalization v0.0.0-20230701045847-91eb5f1b7744 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/docker/cli v24.0.5+incompatible // indirect
+ github.com/docker/distribution v2.8.2+incompatible // indirect
+ github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible // indirect
+ github.com/docker/docker-credential-helpers v0.7.0 // indirect
+ github.com/docker/go-connections v0.4.0 // indirect
+ github.com/docker/go-metrics v0.0.1 // indirect
+ github.com/docker/go-units v0.5.0 // indirect
+ github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
+ github.com/fatih/color v1.15.0 // indirect
+ github.com/fsnotify/fsnotify v1.6.0 // indirect
+ github.com/go-logr/logr v1.3.0 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/goccy/go-yaml v1.11.0 // indirect
+ github.com/golang/protobuf v1.5.3 // indirect
+ github.com/golang/snappy v0.0.4 // indirect
+ github.com/google/go-containerregistry v0.19.0 // indirect
+ github.com/gorilla/mux v1.8.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
+ github.com/hashicorp/go-hclog v1.5.0 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/jeremywohl/flatten v1.0.1 // indirect
+ github.com/klauspost/compress v1.17.7 // indirect
+ github.com/klauspost/pgzip v1.2.6 // indirect
+ github.com/magefile/mage v1.15.0 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+ github.com/mholt/archiver/v3 v3.5.1 // indirect
+ github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 // indirect
+ github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/mmcdole/gofeed v1.2.1 // indirect
+ github.com/mmcdole/goxpp v1.1.0 // indirect
+ github.com/moby/term v0.5.0 // indirect
+ github.com/morikuni/aec v1.0.0 // indirect
+ github.com/nwaples/rardecode v1.1.3 // indirect
+ github.com/oklog/ulid v1.3.1 // indirect
+ github.com/olekukonko/tablewriter v0.0.5 // indirect
+ github.com/opencontainers/go-digest v1.0.0 // indirect
+ github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
+ github.com/osteele/liquid v1.3.0 // indirect
+ github.com/osteele/tuesday v1.0.3 // indirect
+ github.com/pelletier/go-toml v1.9.5 // indirect
+ github.com/pierrec/lz4/v4 v4.1.21 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.16.0 // indirect
+ github.com/prometheus/client_model v0.4.0 // indirect
+ github.com/prometheus/common v0.44.0 // indirect
+ github.com/prometheus/procfs v0.11.0 // indirect
+ github.com/qri-io/jsonpointer v0.1.1 // indirect
+ github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e // indirect
+ github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/sergi/go-diff v1.2.0 // indirect
+ github.com/shopspring/decimal v1.3.1 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/spf13/afero v1.9.5 // indirect
+ github.com/spf13/cast v1.5.1 // indirect
+ github.com/spf13/jwalterweatherman v1.1.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/spf13/viper v1.14.0 // indirect
+ github.com/subosito/gotenv v1.4.2 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
+ github.com/ulikunitz/xz v0.5.11 // indirect
+ github.com/vbatts/tar-split v0.11.3 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
+ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect
+ go.opentelemetry.io/otel/metric v1.19.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.19.0 // indirect
+ go.opentelemetry.io/otel/trace v1.19.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.0.0 // indirect
+ go.uber.org/atomic v1.11.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.24.0 // indirect
+ golang.org/x/net v0.19.0 // indirect
+ golang.org/x/sync v0.5.0 // indirect
+ golang.org/x/sys v0.15.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
+ google.golang.org/grpc v1.59.0 // indirect
+ google.golang.org/protobuf v1.31.0 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..d219d90
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,1113 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+get.porter.sh/magefiles v0.6.7 h1:/MtnsUs17yRRSZyh6IRX+eP4dc47IRGwUY0aMgUK06Y=
+get.porter.sh/magefiles v0.6.7/go.mod h1:w37oTKICvvaEKR5KVB9UfN2EX30uYO9Qk0oRoz80DOU=
+get.porter.sh/porter v1.0.17 h1:x827yJ4VUsYLL8glC8GUZq+gycme1gYtvu/viyH1S+g=
+get.porter.sh/porter v1.0.17/go.mod h1:NtSW1T0UhrnwGBf0H5nhgTkHMnt97j338w+XtjQoDCU=
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
+github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
+github.com/PaesslerAG/gval v1.2.2 h1:Y7iBzhgE09IGTt5QgGQ2IdaYYYOU134YGHBThD+wm9E=
+github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac=
+github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
+github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
+github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
+github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
+github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
+github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
+github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/carolynvs/aferox v0.3.0 h1:CMT50zX88amTMbFfFIWSTKRVRaOw6sejUMbbKiCD4zE=
+github.com/carolynvs/aferox v0.3.0/go.mod h1:eb7CHGIO33CCZS//xtnblvPZbuuZMv0p1VbhiSwZnH4=
+github.com/carolynvs/magex v0.9.0 h1:fWe7oshGv6zuei5Z6EI95RSlOKjIifBZ26myB9G+m/I=
+github.com/carolynvs/magex v0.9.0/go.mod h1:H1LW6RYJ/sNbisMmPe9E73aJZa8geKLKK9mBWLWz3ek=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cbroglie/mustache v1.4.0 h1:Azg0dVhxTml5me+7PsZ7WPrQq1Gkf3WApcHMjMprYoU=
+github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cnabio/cnab-go v0.25.1 h1:dsaJi2bM17unFUECJbyW3cV9MRmzajjhv9K9rihRhGU=
+github.com/cnabio/cnab-go v0.25.1/go.mod h1:aM87s3eXGX8K1UuJOC7G5RrCtqkXx5O1rMDqgyMd70E=
+github.com/cnabio/cnab-to-oci v0.4.0 h1:ofQQwkN4lRpiccCyI0AUEuSgq8nuxpTsCOScrSBMThI=
+github.com/cnabio/cnab-to-oci v0.4.0/go.mod h1:uUBN42cR/ExiESX9yk4ZO60CHxx9zdqyt1pmiIUJW7Q=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
+github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
+github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/cyberphone/json-canonicalization v0.0.0-20230701045847-91eb5f1b7744 h1:MqMnhqqfDsYF2bjxndKIqvISTIRBb1KCzrIwVzKJHe0=
+github.com/cyberphone/json-canonicalization v0.0.0-20230701045847-91eb5f1b7744/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
+github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
+github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible h1:sdGvA1bxu/1J51gAs1XU0bZC+2WxncYnI210as3c6g8=
+github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
+github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
+github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
+github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
+github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
+github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363 h1:OI6oF+tvVpVTDbIAbKYwDQlznmWJJNFgMSFxCbgxsME=
+github.com/getporter/viper v1.7.1-porter.2.0.20210514172839-3ea827168363/go.mod h1:TMyCLryAYE7EgeSfzTbjQLLiLkOjZeJuiFVGK5FYwog=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
+github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=
+github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
+github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic=
+github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
+github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs=
+github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
+github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
+github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
+github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
+github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 h1:lPmsut5Sk7eK2BmDXuvNEvMbT7MkAJBu64Yxr7iJ6nk=
+github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37/go.mod h1:dYWq+UWoFCDY1TndvFUQuhBbIYmZpjreC8adEAx93zE=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s=
+github.com/mmcdole/gofeed v1.2.1/go.mod h1:2wVInNpgmC85q16QTTuwbuKxtKkHLCDDtf0dCmnrNr4=
+github.com/mmcdole/goxpp v1.1.0 h1:WwslZNF7KNAXTFuzRtn/OKZxFLJAAyOA9w82mDz2ZGI=
+github.com/mmcdole/goxpp v1.1.0/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
+github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
+github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
+github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
+github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/osteele/liquid v1.3.0 h1:TwZNI5Y0K+v0MF6JDSoEeRGeHugV8OTi7GIXfdA91fY=
+github.com/osteele/liquid v1.3.0/go.mod h1:VmzQQHa5v4E0GvGzqccfAfLgMwRk2V+s1QbxYx9dGak=
+github.com/osteele/tuesday v1.0.3 h1:SrCmo6sWwSgnvs1bivmXLvD7Ko9+aJvvkmDjB5G4FTU=
+github.com/osteele/tuesday v1.0.3/go.mod h1:pREKpE+L03UFuR+hiznj3q7j3qB1rUZ4XfKejwWFF2M=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
+github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
+github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
+github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
+github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
+github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
+github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
+github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
+github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
+github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
+github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e h1:gqHzseevuZPr3oOLES1nrPO3exQfeTKUiPcJub5axVs=
+github.com/qri-io/jsonschema v0.2.2-0.20210831022256-780655b2ba0e/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
+github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
+github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
+github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
+github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
+github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/tidwall/gjson v1.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw=
+github.com/tidwall/gjson v1.15.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
+github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
+github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
+github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
+go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
+go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
+go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
+go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
+go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
+go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
+go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
+go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
+go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
+go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
+go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
+go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
+google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k=
+google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
+google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
+gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/mage.go b/mage.go
new file mode 100644
index 0000000..f502b06
--- /dev/null
+++ b/mage.go
@@ -0,0 +1,14 @@
+// +build ignore
+
+package main
+
+import (
+ "os"
+
+ "github.com/magefile/mage/mage"
+)
+
+// This file allows someone to run mage commands without mage installed
+// by running `go run mage.go TARGET`.
+// See https://magefile.org/zeroinstall/
+func main() { os.Exit(mage.Main()) }
diff --git a/magefile.go b/magefile.go
new file mode 100644
index 0000000..f3a951e
--- /dev/null
+++ b/magefile.go
@@ -0,0 +1,84 @@
+//go:build mage
+
+package main
+
+import (
+ "get.porter.sh/magefiles/git"
+ "get.porter.sh/magefiles/mixins"
+ "get.porter.sh/magefiles/porter"
+ "github.com/carolynvs/magex/shx"
+)
+
+const (
+ mixinName = "terraform"
+ mixinPackage = "get.porter.sh/mixin/terraform"
+ mixinBin = "bin/mixins/" + mixinName
+)
+
+var (
+ magefile = mixins.NewMagefile(mixinPackage, mixinName, mixinBin)
+ must = shx.CommandBuilder{StopOnError: true}
+)
+
+func ConfigureAgent() {
+ magefile.ConfigureAgent()
+}
+
+// Build the mixin
+func Build() {
+ magefile.Build()
+}
+
+// Cross-compile the mixin before a release
+func XBuildAll() {
+ magefile.XBuildAll()
+}
+
+// Run unit tests
+func TestUnit() {
+ magefile.TestUnit()
+}
+
+func Test() {
+ magefile.Test()
+ Build()
+ TestIntegration()
+}
+
+// Publish the mixin to github
+func Publish() {
+ magefile.Publish()
+}
+
+// TestPublish tries out publish locally, with your github forks
+// Assumes that you forked and kept the repository name unchanged.
+func TestPublish(username string) {
+ magefile.TestPublish(username)
+}
+
+// Install the mixin
+func Install() {
+ magefile.Install()
+}
+
+// Remove generated build files
+func Clean() {
+ magefile.Clean()
+}
+
+// Install porter locally
+func EnsureLocalPorter() {
+ porter.UseBinForPorterHome()
+ porter.EnsurePorter()
+}
+
+func TestIntegration() {
+ EnsureLocalPorter()
+ must.Command("./scripts/test/test-cli.sh").RunV()
+}
+
+// SetupDCO configures your git repository to automatically sign your commits
+// to comply with our DCO
+func SetupDCO() error {
+ return git.SetupDCO()
+}
diff --git a/pkg/terraform/action.go b/pkg/terraform/action.go
new file mode 100644
index 0000000..3a59603
--- /dev/null
+++ b/pkg/terraform/action.go
@@ -0,0 +1,156 @@
+package terraform
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+ "github.com/tidwall/gjson"
+ "gopkg.in/yaml.v2"
+)
+
+func (m *Mixin) loadAction(ctx context.Context) (*Action, error) {
+ var action Action
+ err := builder.LoadAction(ctx, m.RuntimeConfig, "", func(contents []byte) (interface{}, error) {
+ err := yaml.Unmarshal(contents, &action)
+ return &action, err
+ })
+ return &action, err
+}
+
+var _ builder.ExecutableAction = Action{}
+var _ builder.BuildableAction = Action{}
+
+type Action struct {
+ Name string
+ Steps []Step // using UnmarshalYAML so that we don't need a custom type per action
+}
+
+// MakeSteps builds a slice of Steps for data to be unmarshaled into.
+func (a Action) MakeSteps() interface{} {
+ return &[]Step{}
+}
+
+// UnmarshalYAML takes any yaml in this form
+// ACTION:
+// - terraform: ...
+// and puts the steps into the Action.Steps field
+func (a *Action) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ results, err := builder.UnmarshalAction(unmarshal, a)
+ if err != nil {
+ return err
+ }
+
+ for actionName, action := range results {
+ a.Name = actionName
+ for _, result := range action {
+ step := result.(*[]Step)
+ a.Steps = append(a.Steps, *step...)
+ }
+ break // There is only 1 action
+ }
+ return nil
+}
+
+func (a Action) GetSteps() []builder.ExecutableStep {
+ // Go doesn't have generics, nothing to see here...
+ steps := make([]builder.ExecutableStep, len(a.Steps))
+ for i := range a.Steps {
+ steps[i] = a.Steps[i]
+ }
+
+ return steps
+}
+
+type Step struct {
+ Instruction `yaml:"terraform"`
+}
+
+var _ builder.ExecutableStep = Step{}
+var _ builder.HasCustomDashes = Step{}
+
+func (s Step) GetCommand() string {
+ return "terraform"
+}
+
+func (s Step) GetWorkingDir() string {
+ return "."
+}
+
+func (s Step) GetArguments() []string {
+ return s.Arguments
+}
+
+func (s Step) GetFlags() builder.Flags {
+ return s.Flags
+}
+
+func (s Step) GetDashes() builder.Dashes {
+ // All flags in the terraform cli use a single dash
+ return builder.Dashes{
+ Long: "-",
+ Short: "-",
+ }
+}
+
+func (s Step) GetOutputs() []builder.Output {
+ // Go doesn't have generics, nothing to see here...
+ outputs := make([]builder.Output, len(s.Outputs))
+ for i := range s.Outputs {
+ outputs[i] = s.Outputs[i]
+ }
+ return outputs
+}
+
+// applyVarsToStepFlags converts the Terraform vars specified in YAML into a list of -var flags
+// with the variable value set in a format that terraform expects.
+func applyVarsToStepFlags(step *Step) error {
+ if len(step.Vars) == 0 {
+ // return early because otherwise parseVars.ForEach below will print `-var =` even when the result is empty
+ return nil
+ }
+
+ vars, err := json.Marshal(step.Vars)
+ if err != nil {
+ return fmt.Errorf("error marshaling terraform variables to json")
+ }
+
+ parsedVars := gjson.Parse(string(vars))
+ parsedVars.ForEach(func(key, value gjson.Result) bool {
+ // ensure that the flag value is set using a format that terraform expects
+ // primitive data types should print the value directly, e.g. astring, 1, true, 2.4
+ // complex data types should be json, e.g. [1,2,3] or {"color":"blue}
+ step.Flags = append(step.Flags, builder.NewFlag("var", fmt.Sprintf("'%s=%s'", key.String(), value.String())))
+ return true
+ })
+
+ return nil
+}
+
+type Instruction struct {
+ Name string `yaml:"name"`
+ Description string `yaml:"description"`
+ Arguments []string `yaml:"arguments,omitempty"`
+ Flags builder.Flags `yaml:"flags,omitempty"`
+ Outputs []Output `yaml:"outputs,omitempty"`
+ TerraformFields `yaml:",inline"`
+}
+
+// TerraformFields represent fields specific to Terraform
+type TerraformFields struct {
+ Vars map[string]interface{} `yaml:"vars,omitempty"`
+ DisableVarFile bool `yaml:"disableVarFile,omitempty"`
+ LogLevel string `yaml:"logLevel,omitempty"`
+ BackendConfig map[string]interface{} `yaml:"backendConfig,omitempty"`
+}
+
+type Output struct {
+ Name string `yaml:"name"`
+ // Write the output to the specified file
+ DestinationFile string `yaml:"destinationFile,omitempty"`
+}
+
+func (o Output) GetName() string {
+ return o.Name
+}
diff --git a/pkg/terraform/action_test.go b/pkg/terraform/action_test.go
new file mode 100644
index 0000000..93ebcff
--- /dev/null
+++ b/pkg/terraform/action_test.go
@@ -0,0 +1,77 @@
+package terraform
+
+import (
+ "io/ioutil"
+ "sort"
+ "testing"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v2"
+)
+
+func TestMixin_UnmarshalStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/step-input.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+
+ step := action.Steps[0]
+ assert.Equal(t, "Custom Action", step.Description)
+ assert.NotEmpty(t, step.Outputs)
+ assert.Equal(t, Output{Name: "myoutput"}, step.Outputs[0])
+
+ require.Len(t, step.Arguments, 1)
+ assert.Equal(t, "custom", step.Arguments[0])
+
+ sort.Sort(step.Flags)
+ require.Len(t, step.Flags, 3)
+ assert.Equal(t, builder.NewFlag("backendConfig", "key=my.tfstate"), step.Flags[0])
+ assert.Equal(t, builder.NewFlag("logLevel", "TRACE"), step.Flags[1])
+ assert.Equal(t, builder.NewFlag("vars", "myvar=foo"), step.Flags[2])
+}
+
+func TestApplyVarsToStepFlags(t *testing.T) {
+ t.Run("parse all var data types", func(t *testing.T) {
+ s := Step{}
+ s.Vars = map[string]interface{}{
+ "string": "mystring",
+ "bool": true,
+ "int": 22,
+ "number": 1.5,
+ "list": []string{"a", "b", "c"},
+ "doc": map[string]interface{}{
+ "logLevel": "warn",
+ "debug": true,
+ "exclude": []int{1, 2, 3},
+ "stuff": map[string]interface{}{"things": true}},
+ }
+
+ applyVarsToStepFlags(&s)
+
+ gotFlags := s.Flags.ToSlice(s.GetDashes())
+ wantFlags := []string{
+ "-var", `'bool=true'`,
+ "-var", `'doc={"debug":true,"exclude":[1,2,3],"logLevel":"warn","stuff":{"things":true}}'`,
+ "-var", `'int=22'`,
+ "-var", `'list=["a","b","c"]'`,
+ "-var", `'number=1.5'`,
+ "-var", `'string=mystring'`,
+ }
+ assert.Equal(t, wantFlags, gotFlags)
+ })
+
+ t.Run("empty vars", func(t *testing.T) {
+ s := Step{}
+
+ applyVarsToStepFlags(&s)
+
+ gotFlags := s.Flags.ToSlice(s.GetDashes())
+ assert.Empty(t, gotFlags)
+ })
+}
diff --git a/pkg/terraform/build.go b/pkg/terraform/build.go
new file mode 100644
index 0000000..88b7387
--- /dev/null
+++ b/pkg/terraform/build.go
@@ -0,0 +1,77 @@
+package terraform
+
+import (
+ "context"
+ "text/template"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+ "github.com/ghodss/yaml"
+ "github.com/pkg/errors"
+)
+
+const dockerfileLines = `
+ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT="{{ .UserAgentOptOut}}"
+ENV AZURE_HTTP_USER_AGENT="{{ .AzureUserAgent }}"
+RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
+ apt-get update && apt-get install -y wget unzip && \
+ wget https://releases.hashicorp.com/terraform/{{.ClientVersion}}/terraform_{{.ClientVersion}}_linux_amd64.zip --progress=dot:giga && \
+ unzip terraform_{{.ClientVersion}}_linux_amd64.zip -d /usr/bin && \
+ rm terraform_{{.ClientVersion}}_linux_amd64.zip
+COPY {{.WorkingDir}}/{{.InitFile}} $BUNDLE_DIR/{{.WorkingDir}}/
+RUN cd $BUNDLE_DIR/{{.WorkingDir}} && \
+ terraform init -backend=false && \
+ rm -fr .terraform/providers && \
+ terraform providers mirror /usr/local/share/terraform/plugins
+`
+
+// BuildInput represents stdin passed to the mixin for the build command.
+type BuildInput struct {
+ Config *MixinConfig
+}
+
+// MixinConfig represents configuration that can be set on the terraform mixin in porter.yaml
+// mixins:
+// - terraform:
+// version: 0.12.17
+type MixinConfig struct {
+ // ClientVersion is the version of the terraform CLI to install
+ ClientVersion string `yaml:"clientVersion,omitempty"`
+
+ // UserAgentOptOut allows a bundle author to opt out from adding porter and the mixin's version to the terraform user agent string.
+ UserAgentOptOut bool `yaml:"userAgentOptOut,omitempty"`
+
+ InitFile string `yaml:"initFile,omitempty"`
+ WorkingDir string `yaml:"workingDir,omitempty"`
+}
+
+type buildConfig struct {
+ MixinConfig
+
+ // AzureUserAgent is the contents of the azure user agent environment variable
+ AzureUserAgent string
+}
+
+func (m *Mixin) Build(ctx context.Context) error {
+ input := BuildInput{
+ Config: &m.config, // Apply config directly to the mixin
+ }
+ err := builder.LoadAction(ctx, m.RuntimeConfig, "", func(contents []byte) (interface{}, error) {
+ err := yaml.Unmarshal(contents, &input)
+ return &input, err
+ })
+ if err != nil {
+ return err
+ }
+
+ tmpl, err := template.New("Dockerfile").Parse(dockerfileLines)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing terraform mixin Dockerfile template")
+ }
+
+ cfg := buildConfig{MixinConfig: *input.Config}
+ if !input.Config.UserAgentOptOut {
+ cfg.AzureUserAgent = m.userAgent
+ }
+
+ return tmpl.Execute(m.Out, cfg)
+}
diff --git a/pkg/terraform/build_test.go b/pkg/terraform/build_test.go
new file mode 100644
index 0000000..3e9a31d
--- /dev/null
+++ b/pkg/terraform/build_test.go
@@ -0,0 +1,49 @@
+package terraform
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "testing"
+
+ "get.porter.sh/mixin/terraform/pkg"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMixin_Build(t *testing.T) {
+ testcases := []struct {
+ name string
+ inputFile string
+ expectedVersion string
+ expectedUserAgent string
+ }{
+ {name: "build with custom config", inputFile: "testdata/build-input-with-config.yaml", expectedVersion: "https://releases.hashicorp.com/terraform/0.13.0-rc1/terraform_0.13.0-rc1_linux_amd64.zip", expectedUserAgent: "ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=\"true\"\nENV AZURE_HTTP_USER_AGENT=\"\""},
+ {name: "build with the default Terraform config", expectedVersion: "https://releases.hashicorp.com/terraform/1.2.9/terraform_1.2.9_linux_amd64.zip", expectedUserAgent: "ENV PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=\"false\"\nENV AZURE_HTTP_USER_AGENT=\"getporter/porter getporter/terraform/v1.2.3"},
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Set a fake version of the mixin and porter for our user agent
+ pkg.Version = "v1.2.3"
+
+ var data []byte
+ var err error
+ if tc.inputFile != "" {
+ data, err = ioutil.ReadFile(tc.inputFile)
+ require.NoError(t, err)
+ }
+
+ m := NewTestMixin(t)
+ m.In = bytes.NewReader(data)
+
+ err = m.Build(context.Background())
+ require.NoError(t, err, "build failed")
+
+ gotOutput := m.TestContext.GetOutput()
+ assert.Contains(t, gotOutput, tc.expectedVersion)
+ assert.Contains(t, gotOutput, tc.expectedUserAgent)
+ assert.NotContains(t, "{{.", gotOutput, "Not all of the template values were consumed")
+ })
+ }
+}
diff --git a/pkg/terraform/config.go b/pkg/terraform/config.go
new file mode 100644
index 0000000..ad04014
--- /dev/null
+++ b/pkg/terraform/config.go
@@ -0,0 +1,63 @@
+package terraform
+
+import (
+ "strconv"
+ "strings"
+
+ "get.porter.sh/porter/pkg"
+)
+
+const (
+ // AzureUserAgentEnvVar is the environment variable used by the azure provider to set
+ // the user agent string sent to Azure.
+ AzureUserAgentEnvVar = "AZURE_HTTP_USER_AGENT"
+
+ // UserAgentOptOutEnvVar is the name of the environment variable that disables
+ // user agent reporting.
+ UserAgentOptOutEnvVar = "PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT"
+)
+
+// SetUserAgent sets the AZURE_HTTP_USER_AGENT environment variable with
+// the full user agent string, which includes both a portion for porter and the
+// mixin.
+func (m *Mixin) SetUserAgent() {
+ // Check if PORTER_TERRAFORM_MIXIN_USER_AGENT_OPT_OUT=true, which disables editing the user agent string
+ if optOut, _ := strconv.ParseBool(m.Getenv(UserAgentOptOutEnvVar)); optOut {
+ return
+ }
+
+ // Check if we have already set the user agent
+ if m.userAgent != "" {
+ return
+ }
+
+ porterUserAgent := pkg.UserAgent()
+ mixinUserAgent := m.GetMixinUserAgent()
+ userAgent := []string{porterUserAgent, mixinUserAgent}
+ // Append porter and the mixin's version to the user agent string. Some clouds and
+ // environments will have set the environment variable already and we don't want
+ // to clobber it.
+ value := strings.Join(userAgent, " ")
+ if agentStr, ok := m.LookupEnv(AzureUserAgentEnvVar); ok {
+
+ // Check if we have already set the user agent
+ if strings.Contains(agentStr, value) {
+ value = agentStr
+ } else {
+ userAgent = append(userAgent, agentStr)
+ value = strings.Join(userAgent, " ")
+ }
+ }
+
+ m.userAgent = value
+
+ // Set the environment variable so that when we call the
+ // azure provider, it's automatically passed too.
+ m.Setenv(AzureUserAgentEnvVar, m.userAgent)
+}
+
+// GetMixinUserAgent returns the portion of the user agent string for the mixin.
+func (m *Mixin) GetMixinUserAgent() string {
+ v := m.Version()
+ return "getporter/" + v.Name + "/" + v.Version
+}
diff --git a/pkg/terraform/config_test.go b/pkg/terraform/config_test.go
new file mode 100644
index 0000000..a36cef9
--- /dev/null
+++ b/pkg/terraform/config_test.go
@@ -0,0 +1,88 @@
+package terraform
+
+import (
+ "os"
+ "testing"
+
+ "get.porter.sh/mixin/terraform/pkg"
+ "get.porter.sh/porter/pkg/runtime"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMixinSetsUserAgentEnvVar(t *testing.T) {
+ // CI sets this value and we need to clear it out to make the test reproducible
+ os.Unsetenv(AzureUserAgentEnvVar)
+
+ t.Run("sets env var", func(t *testing.T) {
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+ m := New()
+ expected := "getporter/porter getporter/terraform/" + pkg.Version
+ require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar))
+ require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later")
+ })
+ t.Run("edits env var", func(t *testing.T) {
+ os.Unsetenv(AzureUserAgentEnvVar)
+ // Validate that if the user customizations of the env var are preserved
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+ cfg := runtime.NewConfig()
+ customUserAgent := "mycustom/v1.2.3"
+ cfg.Setenv(AzureUserAgentEnvVar, customUserAgent)
+ m := NewFor(cfg)
+ expected := "getporter/porter getporter/terraform/v1.2.3 mycustom/v1.2.3"
+ require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar))
+ require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later")
+ })
+
+ t.Run("env var already set", func(t *testing.T) {
+ // Validate that calling multiple times doesn't make a messed up env var
+ os.Unsetenv(AzureUserAgentEnvVar)
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+ cfg := runtime.NewConfig()
+ customUserAgent := "getporter/porter getporter/terraform/v1.2.3"
+ cfg.Setenv(AzureUserAgentEnvVar, customUserAgent)
+ m := New()
+ m.SetUserAgent()
+ expected := "getporter/porter getporter/terraform/v1.2.3"
+ require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar))
+ require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later")
+ })
+ t.Run("call multiple times", func(t *testing.T) {
+ // Validate that calling multiple times doesn't make a messed up env var
+ os.Unsetenv(AzureUserAgentEnvVar)
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+ m := New()
+ m.SetUserAgent()
+ m.SetUserAgent()
+ expected := "getporter/porter getporter/terraform/v1.2.3"
+ require.Equal(t, expected, m.Getenv(AzureUserAgentEnvVar))
+ require.Equal(t, expected, m.userAgent, "validate we remember the user agent string for later")
+ })
+}
+
+func TestMixinSetsUserAgentEnvVar_OptOut(t *testing.T) {
+ // CI sets this value and we need to clear it out to make the test reproducible
+ os.Unsetenv(AzureUserAgentEnvVar)
+
+ t.Run("opt-out", func(t *testing.T) {
+ // 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
+ cfg := runtime.NewConfig()
+ cfg.Setenv(UserAgentOptOutEnvVar, "true")
+ m := NewFor(cfg)
+ _, hasEnv := m.LookupEnv(AzureUserAgentEnvVar)
+ require.False(t, hasEnv, "expected the opt out to skip setting the AZURE_HTTP_USER_AGENT environment variable")
+ })
+ t.Run("opt-out preserves original value", func(t *testing.T) {
+ // 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
+ cfg := runtime.NewConfig()
+ cfg.Setenv(UserAgentOptOutEnvVar, "true")
+ customUserAgent := "mycustom/v1.2.3"
+ cfg.Setenv(AzureUserAgentEnvVar, customUserAgent)
+ m := NewFor(cfg)
+ require.Equal(t, customUserAgent, m.Getenv(AzureUserAgentEnvVar), "expected opting out to not prevent the user from setting a custom user agent")
+ require.Empty(t, m.userAgent, "validate we remember that we opted out")
+ })
+}
diff --git a/pkg/terraform/helpers.go b/pkg/terraform/helpers.go
new file mode 100644
index 0000000..2aea9e1
--- /dev/null
+++ b/pkg/terraform/helpers.go
@@ -0,0 +1,34 @@
+package terraform
+
+import (
+ "sort"
+ "testing"
+
+ "get.porter.sh/porter/pkg/portercontext"
+)
+
+type TestMixin struct {
+ *Mixin
+ TestContext *portercontext.TestContext
+}
+
+// NewTestMixin initializes a terraform mixin, with the output buffered, and an in-memory file system.
+func NewTestMixin(t *testing.T) *TestMixin {
+ c := portercontext.NewTestContext(t)
+ m := New()
+ m.Context = c.Context
+ return &TestMixin{
+ Mixin: m,
+ TestContext: c,
+ }
+}
+
+func sortKeys(m map[string]interface{}) []string {
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ return keys
+}
diff --git a/pkg/terraform/helpers_test.go b/pkg/terraform/helpers_test.go
new file mode 100644
index 0000000..e125bd4
--- /dev/null
+++ b/pkg/terraform/helpers_test.go
@@ -0,0 +1,23 @@
+package terraform
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSortKeys(t *testing.T) {
+ m := map[string]interface{}{
+ "delicious": "true",
+ "apples": "green",
+ "are": "needed",
+ }
+
+ expected := []string{
+ "apples",
+ "are",
+ "delicious",
+ }
+
+ assert.Equal(t, expected, sortKeys(m))
+}
diff --git a/pkg/terraform/init.go b/pkg/terraform/init.go
new file mode 100644
index 0000000..6e2de64
--- /dev/null
+++ b/pkg/terraform/init.go
@@ -0,0 +1,42 @@
+package terraform
+
+import (
+ "context"
+ "fmt"
+ "strings"
+)
+
+// Init runs terraform init with the provided backendConfig, if supplied
+func (m *Mixin) Init(ctx context.Context, backendConfig map[string]interface{}) error {
+ cmd := m.NewCommand(ctx, "terraform", "init")
+
+ if len(backendConfig) > 0 {
+ cmd.Args = append(cmd.Args, "-backend=true")
+
+ for _, k := range sortKeys(backendConfig) {
+ cmd.Args = append(cmd.Args, fmt.Sprintf("-backend-config=%s=%s", k, backendConfig[k]))
+ }
+
+ cmd.Args = append(cmd.Args, "-reconfigure")
+ }
+
+ cmd.Stdout = m.Out
+ cmd.Stderr = m.Err
+
+ prettyCmd := fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " "))
+ if m.DebugMode {
+ fmt.Fprintln(m.Out, prettyCmd)
+ }
+
+ err := cmd.Start()
+ if err != nil {
+ return fmt.Errorf("could not execute command, %s: %s", prettyCmd, err)
+ }
+
+ err = cmd.Wait()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/terraform/init_test.go b/pkg/terraform/init_test.go
new file mode 100644
index 0000000..8102e89
--- /dev/null
+++ b/pkg/terraform/init_test.go
@@ -0,0 +1,38 @@
+package terraform
+
+import (
+ "context"
+ "os"
+ "testing"
+
+ "get.porter.sh/porter/pkg/test"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMixin_Init(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ os.Setenv(test.ExpectedCommandEnv, "terraform init")
+
+ h := NewTestMixin(t)
+
+ err := h.Init(context.Background(), nil)
+
+ require.NoError(t, err)
+}
+
+func TestMixin_InitBackend(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ os.Setenv(test.ExpectedCommandEnv,
+ "terraform init -backend=true -backend-config=donuts=definitely -backend-config=drink=dubonnet -reconfigure")
+
+ h := NewTestMixin(t)
+
+ backendConfig := map[string]interface{}{
+ "drink": "dubonnet",
+ "donuts": "definitely",
+ }
+
+ err := h.Init(context.Background(), backendConfig)
+
+ require.NoError(t, err)
+}
diff --git a/pkg/terraform/install.go b/pkg/terraform/install.go
new file mode 100644
index 0000000..e0da305
--- /dev/null
+++ b/pkg/terraform/install.go
@@ -0,0 +1,70 @@
+package terraform
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+)
+
+// defaultTerraformVarFilename is the default name for terrafrom tfvars json file
+const defaultTerraformVarsFilename = "terraform.tfvars.json"
+
+// Install runs a terraform apply
+func (m *Mixin) Install(ctx context.Context) error {
+ action, err := m.loadAction(ctx)
+ if err != nil {
+ return err
+ }
+ step := action.Steps[0]
+
+ err = m.commandPreRun(ctx, &step)
+ if err != nil {
+ return err
+ }
+
+ // Update step fields that exec/builder works with
+ step.Arguments = []string{"apply"}
+ // Always run in non-interactive mode
+ step.Flags = append(step.Flags, builder.NewFlag("auto-approve"))
+ step.Flags = append(step.Flags, builder.NewFlag("input=false"))
+
+ vbs, err := json.Marshal(step.Vars)
+ if err != nil {
+ return err
+ }
+ // Only create a tf var file for install
+ if !step.DisableVarFile && action.Name == "install" {
+ vf, err := m.FileSystem.Create(defaultTerraformVarsFilename)
+ if err != nil {
+ return err
+ }
+ defer vf.Close()
+
+ // If the vars block is empty, set vbs to an empty JSON object
+ // to prevent terraform from erroring out
+ if len(step.Vars) == 0 {
+ vbs = []byte("{}")
+ }
+
+ _, err = vf.Write(vbs)
+ if err != nil {
+ return err
+ }
+
+ if m.DebugMode {
+ fmt.Fprintf(m.Err, "DEBUG: TF var file created:\n%s\n", string(vbs))
+ }
+ }
+
+ applyVarsToStepFlags(&step)
+
+ action.Steps[0] = step
+ _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action)
+ if err != nil {
+ return err
+ }
+
+ return m.handleOutputs(ctx, step.Outputs)
+}
diff --git a/pkg/terraform/install_test.go b/pkg/terraform/install_test.go
new file mode 100644
index 0000000..810439a
--- /dev/null
+++ b/pkg/terraform/install_test.go
@@ -0,0 +1,141 @@
+package terraform
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+ "testing"
+
+ "get.porter.sh/porter/pkg/test"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v2"
+)
+
+// sad hack: not sure how to make a common test main for all my subpackages
+func TestMain(m *testing.M) {
+ test.TestMainWithMockedCommandHandlers(m)
+}
+
+func TestMixin_UnmarshalInstallStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/install-input.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+ step := action.Steps[0]
+
+ assert.Equal(t, "Install MySQL", step.Description)
+ assert.Equal(t, "TRACE", step.LogLevel)
+ assert.Equal(t, false, step.DisableVarFile)
+}
+
+func TestMixin_Install(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform apply -auto-approve -input=false -var myvar=foo",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/install-input.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ err = h.Install(context.Background())
+ require.NoError(t, err)
+
+ assert.Equal(t, "TRACE", os.Getenv("TF_LOG"))
+
+ wd := h.Getwd()
+ require.NoError(t, err)
+ assert.Equal(t, wd, h.config.WorkingDir)
+ fc, err := h.FileSystem.ReadFile(path.Join(wd, "terraform.tfvars.json"))
+ require.NoError(t, err)
+ assert.Equal(t, fc, []byte("{\"myvar\":\"foo\"}"))
+}
+
+func TestMixin_UnmarshalInstallSaveVarStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/install-input-disable-save-vars.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+ step := action.Steps[0]
+
+ assert.Equal(t, "Install MySQL", step.Description)
+ assert.Equal(t, "TRACE", step.LogLevel)
+ assert.Equal(t, true, step.DisableVarFile)
+}
+
+func TestMixin_InstallDisableSaveVars(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform apply -auto-approve -input=false -var myvar=foo",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/install-input-disable-save-vars.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ err = h.Install(context.Background())
+ require.NoError(t, err)
+
+ assert.Equal(t, "TRACE", os.Getenv("TF_LOG"))
+
+ wd := h.Getwd()
+ assert.Equal(t, wd, h.config.WorkingDir)
+ _, err = h.FileSystem.Stat(path.Join(wd, "terraform.tfvars.json"))
+ require.Error(t, err)
+}
+
+func TestMixin_InstallNoVarsBlock(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform apply -auto-approve -input=false",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/install-input-no-vars-block.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ err = h.Install(context.Background())
+ require.NoError(t, err)
+
+ assert.Equal(t, "TRACE", os.Getenv("TF_LOG"))
+
+ wd := h.Getwd()
+ assert.Equal(t, wd, h.config.WorkingDir)
+ fc, err := h.FileSystem.ReadFile(path.Join(wd, "terraform.tfvars.json"))
+ require.NoError(t, err)
+ assert.Equal(t, fc, []byte("{}"))
+}
diff --git a/pkg/terraform/invoke.go b/pkg/terraform/invoke.go
new file mode 100644
index 0000000..42e0e1a
--- /dev/null
+++ b/pkg/terraform/invoke.go
@@ -0,0 +1,42 @@
+package terraform
+
+import (
+ "context"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+)
+
+type InvokeOptions struct {
+ Action string
+}
+
+// Invoke runs a custom terraform action
+func (m *Mixin) Invoke(ctx context.Context, opts InvokeOptions) error {
+ action, err := m.loadAction(ctx)
+ if err != nil {
+ return err
+ }
+ step := action.Steps[0]
+
+ err = m.commandPreRun(ctx, &step)
+ if err != nil {
+ return err
+ }
+
+ // Update step fields that exec/builder works with
+ commands := []string{opts.Action}
+ if len(step.Arguments) > 0 {
+ commands = step.GetArguments()
+ }
+ step.Arguments = commands
+
+ applyVarsToStepFlags(&step)
+
+ action.Steps[0] = step
+ _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action)
+ if err != nil {
+ return err
+ }
+
+ return m.handleOutputs(ctx, step.Outputs)
+}
diff --git a/pkg/terraform/invoke_test.go b/pkg/terraform/invoke_test.go
new file mode 100644
index 0000000..a721d51
--- /dev/null
+++ b/pkg/terraform/invoke_test.go
@@ -0,0 +1,55 @@
+package terraform
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "get.porter.sh/porter/pkg/test"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ yaml "gopkg.in/yaml.v2"
+)
+
+func TestMixin_UnmarshalInvokeStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/invoke-input.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+ step := action.Steps[0]
+
+ assert.Equal(t, "Custom Action", step.Description)
+}
+
+func TestMixin_Invoke(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform custom -var myvar=foo",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/invoke-input.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ opts := InvokeOptions{}
+ err = h.Invoke(context.Background(), opts)
+ require.NoError(t, err)
+
+ wd := h.Getwd()
+ require.NoError(t, err)
+ assert.Equal(t, wd, h.config.WorkingDir)
+}
diff --git a/pkg/terraform/schema.go b/pkg/terraform/schema.go
new file mode 100644
index 0000000..3bf422e
--- /dev/null
+++ b/pkg/terraform/schema.go
@@ -0,0 +1,13 @@
+package terraform
+
+import (
+ _ "embed"
+ "fmt"
+)
+
+//go:embed schema/schema.json
+var schema string
+
+func (m *Mixin) PrintSchema() {
+ fmt.Fprintf(m.Out, schema)
+}
diff --git a/pkg/terraform/schema/schema.json b/pkg/terraform/schema/schema.json
new file mode 100644
index 0000000..be8bf32
--- /dev/null
+++ b/pkg/terraform/schema/schema.json
@@ -0,0 +1,243 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "definitions": {
+ "declaration": {
+ "oneOf": [
+ {
+ "description": "Declare the terraform mixin without configuration",
+ "type": "string",
+ "enum": ["terraform"]
+ },
+ {"$ref": "#/definitions/config"}
+ ]
+ },
+ "config": {
+ "description": "Declare the terraform mixin with additional configuration",
+ "type": "object",
+ "properties": {
+ "terraform": {
+ "description": "terraform mixin configuration",
+ "type": "object",
+ "properties": {
+ "clientVersion": {
+ "description": "Version of terraform to install in the bundle",
+ "type": "string"
+ },
+ "initFile": {
+ "description": "Relative path from the workingDir to a file defining all providers, used when running terraform init.",
+ "type": "string"
+ },
+ "workingDir": {
+ "description": "Relative path to your terraform files, defaults to 'terraform'",
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false,
+ "required": ["terraform"]
+ },
+ "installStep": {
+ "type": "object",
+ "properties": {
+ "terraform": {
+ "$ref": "#/definitions/terraform"
+ }
+ },
+ "required": [
+ "terraform"
+ ],
+ "additionalProperties": false
+ },
+ "invokeStep": {
+ "type": "object",
+ "properties": {
+ "terraform": {
+ "$ref": "#/definitions/terraform"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "terraform"
+ ]
+ },
+ "terraform": {
+ "type": "object",
+ "properties": {
+ "arguments": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "backendConfig": {
+ "type": "object"
+ },
+ "description": {
+ "$ref": "#/definitions/stepDescription"
+ },
+ "flags": {
+ "type": "object",
+ "additionalProperties": {
+ "type": [
+ "null",
+ "boolean",
+ "number",
+ "string"
+ ]
+ }
+ },
+ "logLevel": {
+ "type": "string"
+ },
+ "outputs": {
+ "$ref": "#/definitions/outputs"
+ },
+ "vars": {
+ "type": "object"
+ },
+ "disableVarFile": {
+ "type": "boolean"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "description"
+ ]
+ },
+ "upgradeStep": {
+ "type": "object",
+ "properties": {
+ "terraform": {
+ "type": "object",
+ "properties": {
+ "arguments": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "backendConfig": {
+ "type": "object"
+ },
+ "description": {
+ "$ref": "#/definitions/stepDescription"
+ },
+ "flags": {
+ "type": "object",
+ "additionalProperties": {
+ "type": [
+ "null",
+ "boolean",
+ "number",
+ "string"
+ ]
+ }
+ },
+ "logLevel": {
+ "type": "string"
+ },
+ "outputs": {
+ "$ref": "#/definitions/outputs"
+ },
+ "vars": {
+ "type": "object"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "description"
+ ]
+ }
+ },
+ "required": [
+ "terraform"
+ ],
+ "additionalProperties": false
+ },
+ "uninstallStep": {
+ "type": "object",
+ "properties": {
+ "terraform": {
+ "type": "object",
+ "properties": {
+ "backendConfig": {
+ "type": "object"
+ },
+ "description": {
+ "$ref": "#/definitions/stepDescription"
+ },
+ "logLevel": {
+ "type": "string"
+ },
+ "outputs": {
+ "$ref": "#/definitions/outputs"
+ },
+ "vars": {
+ "type": "object"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "description"
+ ]
+ }
+ },
+ "required": [
+ "terraform"
+ ],
+ "additionalProperties": false
+ },
+ "stepDescription": {
+ "type": "string",
+ "minLength": 1
+ },
+ "outputs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "name"
+ ]
+ }
+ }
+ },
+ "type": "object",
+ "properties": {
+ "install": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/installStep"
+ }
+ },
+ "upgrade": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/upgradeStep"
+ }
+ },
+ "uninstall": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/uninstallStep"
+ }
+ },
+ "mixins": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/declaration" }
+ }
+ },
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/invokeStep"
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkg/terraform/schema_test.go b/pkg/terraform/schema_test.go
new file mode 100644
index 0000000..c2fd2e9
--- /dev/null
+++ b/pkg/terraform/schema_test.go
@@ -0,0 +1,141 @@
+package terraform
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "strings"
+ "testing"
+
+ "github.com/PaesslerAG/jsonpath"
+ "github.com/ghodss/yaml"
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/xeipuuv/gojsonschema"
+)
+
+func TestMixin_PrintSchema(t *testing.T) {
+ m := NewTestMixin(t)
+
+ m.PrintSchema()
+
+ gotSchema := m.TestContext.GetOutput()
+
+ assert.Equal(t, schema, gotSchema)
+}
+
+func TestMixin_ValidatePayload(t *testing.T) {
+ testcases := []struct {
+ name string
+ step string
+ pass bool
+ error string
+ }{
+ {"install", "testdata/install-input.yaml", true, ""},
+ {"install.disable-save-var-file", "testdata/install-input-disable-save-vars.yaml", true, ""},
+ {"invoke", "testdata/invoke-input.yaml", true, ""},
+ {"upgrade", "testdata/upgrade-input.yaml", true, ""},
+ {"uninstall", "testdata/uninstall-input.yaml", true, ""},
+ {"install.missing-desc", "testdata/bad-install-input.missing-desc.yaml", false, "install.0.terraform: Invalid type. Expected: object, given: null"},
+ {"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"},
+ {"upgrade.disable-var-file", "testdata/bad-upgrade-disable-save-var.yaml", false, "upgrade.0.terraform: Additional property disableVarFile is not allowed"},
+ {"uninstall.input-not-valid", "testdata/bad-uninstall-input.input-not-valid.yaml", false, "uninstall.0.terraform: Additional property input is not allowed"},
+ {"uninstall.disable-var-file", "testdata/bad-uninstall-disable-save-var.yaml", false, "uninstall.0.terraform: Additional property disableVarFile is not allowed"},
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ m := NewTestMixin(t)
+ b, err := ioutil.ReadFile(tc.step)
+ require.NoError(t, err)
+
+ err = m.validatePayload(b)
+ if tc.pass {
+ require.NoError(t, err)
+ } else {
+ require.EqualError(t, err, tc.error)
+ }
+ })
+ }
+}
+
+func (m *Mixin) validatePayload(b []byte) error {
+ // Load the step as a go dump
+ s := make(map[string]interface{})
+ err := yaml.Unmarshal(b, &s)
+ if err != nil {
+ return errors.Wrap(err, "could not marshal payload as yaml")
+ }
+ manifestLoader := gojsonschema.NewGoLoader(s)
+
+ // Load the step schema
+ schemaLoader := gojsonschema.NewStringLoader(schema)
+
+ validator, err := gojsonschema.NewSchema(schemaLoader)
+ if err != nil {
+ return errors.Wrap(err, "unable to compile the mixin step schema")
+ }
+
+ // Validate the manifest against the schema
+ result, err := validator.Validate(manifestLoader)
+ if err != nil {
+ return errors.Wrap(err, "unable to validate the mixin step schema")
+ }
+ if !result.Valid() {
+ errs := make([]string, 0, len(result.Errors()))
+ for _, resultErr := range result.Errors() {
+ doAppend := true
+ for _, err := range errs {
+ // no need to append if already exists
+ if err == resultErr.String() {
+ doAppend = false
+ }
+ }
+ if doAppend {
+ errs = append(errs, resultErr.String())
+ }
+ }
+ return errors.New(strings.Join(errs, "\n\t* "))
+ }
+
+ return nil
+}
+
+func TestMixin_CheckSchema(t *testing.T) {
+ // 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
+ // check that certain characteristics of the schema that Porter expects are present
+ // Once we have a mixin library, that would be a good place to package up this type of helper function
+ var schemaMap map[string]interface{}
+ err := json.Unmarshal([]byte(schema), &schemaMap)
+ require.NoError(t, err, "could not unmarshal the schema into a map")
+
+ t.Run("mixin configuration", func(t *testing.T) {
+ // Check that mixin config is defined, and has all the supported fields
+ configSchema, err := jsonpath.Get("$.definitions.config", schemaMap)
+ require.NoError(t, err, "could not find the mixin config schema declaration")
+ _, err = jsonpath.Get("$.properties.terraform.properties.clientVersion", configSchema)
+ require.NoError(t, err, "clientVersion was not included in the mixin config schema")
+ _, err = jsonpath.Get("$.properties.terraform.properties.initFile", configSchema)
+ require.NoError(t, err, "initFile was not included in the mixin config schema")
+ _, err = jsonpath.Get("$.properties.terraform.properties.workingDir", configSchema)
+ require.NoError(t, err, "workingDir was not included in the mixin config schema")
+ })
+
+ // Check that schema are defined for each action
+ actions := []string{"install", "upgrade", "invoke", "uninstall"}
+ for _, action := range actions {
+ t.Run("supports "+action, func(t *testing.T) {
+ actionPath := fmt.Sprintf("$.definitions.%sStep", action)
+ _, err := jsonpath.Get(actionPath, schemaMap)
+ require.NoErrorf(t, err, "could not find the %sStep declaration", action)
+ })
+ }
+
+ // Check that the invoke action is registered
+ additionalSchema, err := jsonpath.Get("$.additionalProperties.items", schemaMap)
+ require.NoError(t, err, "the invoke action was not registered in the schema")
+ require.Contains(t, additionalSchema, "$ref")
+ invokeRef := additionalSchema.(map[string]interface{})["$ref"]
+ require.Equal(t, "#/definitions/invokeStep", invokeRef, "the invoke action was not registered correctly")
+}
diff --git a/pkg/terraform/terraform.go b/pkg/terraform/terraform.go
new file mode 100644
index 0000000..d21aa34
--- /dev/null
+++ b/pkg/terraform/terraform.go
@@ -0,0 +1,159 @@
+package terraform
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "get.porter.sh/porter/pkg/runtime"
+ "github.com/hashicorp/go-multierror"
+ "github.com/pkg/errors"
+)
+
+const (
+ // DefaultWorkingDir is the default working directory for Terraform.
+ DefaultWorkingDir = "terraform"
+
+ // DefaultClientVersion is the default version of the terraform cli.
+ DefaultClientVersion = "1.2.9"
+
+ // DefaultInitFile is the default file used to initialize terraform providers during build.
+ DefaultInitFile = ""
+)
+
+// Mixin is the logic behind the terraform mixin
+type Mixin struct {
+ runtime.RuntimeConfig
+ config MixinConfig
+
+ userAgent string
+}
+
+// New terraform mixin client, initialized with useful defaults.
+func New() *Mixin {
+ return NewFor(runtime.NewConfig())
+}
+
+func NewFor(cfg runtime.RuntimeConfig) *Mixin {
+
+ m := &Mixin{
+ RuntimeConfig: cfg,
+ config: MixinConfig{
+ WorkingDir: DefaultWorkingDir,
+ ClientVersion: DefaultClientVersion,
+ InitFile: DefaultInitFile,
+ },
+ }
+
+ m.SetUserAgent()
+ return m
+}
+
+func (m *Mixin) getPayloadData() ([]byte, error) {
+ reader := bufio.NewReader(m.In)
+ data, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not read the payload from STDIN")
+ }
+ return data, nil
+}
+
+func (m *Mixin) getOutput(ctx context.Context, outputName string) ([]byte, error) {
+ // Using -json instead of -raw because terraform only allows for string, bool,
+ // and number output types when using -raw. This means that the outputs will
+ // need to be unencoded to raw string because -json does json compliant html
+ // special character encoding.
+ cmd := m.NewCommand(ctx, "terraform", "output", "-json", outputName)
+ cmd.Stderr = m.Err
+
+ // Terraform appears to auto-append a newline character when printing outputs
+ // Trim this character before returning the output
+ out, err := cmd.Output()
+ if err != nil {
+ prettyCmd := fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " "))
+ return nil, errors.Wrap(err, fmt.Sprintf("couldn't run command %s", prettyCmd))
+ }
+
+ // Implement a custom JSON encoder that doesn't do HTML escaping. This allows
+ // for recrusive decoding of complex JSON objects using the unmarshal and then
+ // re-encoding it but skipping the html escaping. This allows for complex types
+ // like maps to be represented as a byte slice without having go types be
+ // part of that byte slice, eg: without the re-encoding, a JSON byte slice with
+ // '{"foo": "bar"}' would become map[foo:bar].
+ var outDat interface{}
+ err = json.Unmarshal(out, &outDat)
+ if err != nil {
+ return []byte{}, err
+ }
+ // If the output is a string then don't re-encode it, just return the decoded
+ // json string. Re-encoding the string will wrap it in quotes which breaks
+ // the compabiltity with -raw
+ if outStr, ok := outDat.(string); ok {
+ return []byte(outStr), nil
+ }
+ buffer := &bytes.Buffer{}
+ encoder := json.NewEncoder(buffer)
+ encoder.SetEscapeHTML(false)
+ err = encoder.Encode(outDat)
+ if err != nil {
+ return []byte{}, err
+ }
+ return bytes.TrimRight(buffer.Bytes(), "\n"), nil
+}
+
+func (m *Mixin) handleOutputs(ctx context.Context, outputs []Output) error {
+ var bigErr *multierror.Error
+
+ for _, output := range outputs {
+ bytes, err := m.getOutput(ctx, output.Name)
+ if err != nil {
+ bigErr = multierror.Append(bigErr, err)
+ continue
+ }
+
+ err = m.Context.WriteMixinOutputToFile(output.Name, bytes)
+ if err != nil {
+ bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to persist output '%s'", output.Name))
+ }
+
+ if output.DestinationFile != "" {
+ err = m.Context.FileSystem.MkdirAll(filepath.Dir(output.DestinationFile), 0700)
+ if err != nil {
+ bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to create destination directory for output '%s'", output.Name))
+ }
+
+ err = m.Context.FileSystem.WriteFile(output.DestinationFile, bytes, 0700)
+ if err != nil {
+ bigErr = multierror.Append(bigErr, errors.Wrapf(err, "unable to copy output '%s' to '%s'", output.Name, output.DestinationFile))
+ }
+ }
+ }
+ return bigErr.ErrorOrNil()
+}
+
+// commandPreRun runs setup tasks applicable for every action
+func (m *Mixin) commandPreRun(ctx context.Context, step *Step) error {
+ if step.LogLevel != "" {
+ os.Setenv("TF_LOG", step.LogLevel)
+ }
+
+ // First, change to specified working dir
+ m.Chdir(m.config.WorkingDir)
+ if m.DebugMode {
+ fmt.Fprintln(m.Err, "Terraform working directory is", m.Getwd())
+ }
+
+ // Initialize Terraform
+ fmt.Println("Initializing Terraform...")
+ err := m.Init(ctx, step.BackendConfig)
+ if err != nil {
+ return fmt.Errorf("could not init terraform, %s", err)
+ }
+ return nil
+}
diff --git a/pkg/terraform/testdata/bad-install-input.desc-empty.yaml b/pkg/terraform/testdata/bad-install-input.desc-empty.yaml
new file mode 100644
index 0000000..288f024
--- /dev/null
+++ b/pkg/terraform/testdata/bad-install-input.desc-empty.yaml
@@ -0,0 +1,3 @@
+install:
+- terraform:
+ description: ""
\ No newline at end of file
diff --git a/pkg/terraform/testdata/bad-install-input.missing-desc.yaml b/pkg/terraform/testdata/bad-install-input.missing-desc.yaml
new file mode 100644
index 0000000..3bdeaee
--- /dev/null
+++ b/pkg/terraform/testdata/bad-install-input.missing-desc.yaml
@@ -0,0 +1,2 @@
+install:
+- terraform:
diff --git a/pkg/terraform/testdata/bad-uninstall-disable-save-var.yaml b/pkg/terraform/testdata/bad-uninstall-disable-save-var.yaml
new file mode 100644
index 0000000..10c3e8c
--- /dev/null
+++ b/pkg/terraform/testdata/bad-uninstall-disable-save-var.yaml
@@ -0,0 +1,9 @@
+uninstall:
+ - terraform:
+ description: "Uninstall MySQL"
+ logLevel: TRACE
+ disableVarFile: true
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
diff --git a/pkg/terraform/testdata/bad-uninstall-input.input-not-valid.yaml b/pkg/terraform/testdata/bad-uninstall-input.input-not-valid.yaml
new file mode 100644
index 0000000..80ed328
--- /dev/null
+++ b/pkg/terraform/testdata/bad-uninstall-input.input-not-valid.yaml
@@ -0,0 +1,4 @@
+uninstall:
+- terraform:
+ description: "Invalid Uninstall"
+ input: true
\ No newline at end of file
diff --git a/pkg/terraform/testdata/bad-upgrade-disable-save-var.yaml b/pkg/terraform/testdata/bad-upgrade-disable-save-var.yaml
new file mode 100644
index 0000000..58f0012
--- /dev/null
+++ b/pkg/terraform/testdata/bad-upgrade-disable-save-var.yaml
@@ -0,0 +1,9 @@
+upgrade:
+ - terraform:
+ description: "Upgrade MySQL"
+ logLevel: TRACE
+ disableVarFile: true
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
diff --git a/pkg/terraform/testdata/build-input-with-config.yaml b/pkg/terraform/testdata/build-input-with-config.yaml
new file mode 100644
index 0000000..f83bc59
--- /dev/null
+++ b/pkg/terraform/testdata/build-input-with-config.yaml
@@ -0,0 +1,6 @@
+config:
+ clientVersion: 0.13.0-rc1
+ userAgentOptOut: true
+ install:
+ - terraform:
+ description: "noop"
diff --git a/pkg/terraform/testdata/install-input-disable-save-vars.yaml b/pkg/terraform/testdata/install-input-disable-save-vars.yaml
new file mode 100644
index 0000000..328be81
--- /dev/null
+++ b/pkg/terraform/testdata/install-input-disable-save-vars.yaml
@@ -0,0 +1,9 @@
+install:
+ - terraform:
+ description: "Install MySQL"
+ logLevel: TRACE
+ disableVarFile: true
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
diff --git a/pkg/terraform/testdata/install-input-no-vars-block.yaml b/pkg/terraform/testdata/install-input-no-vars-block.yaml
new file mode 100644
index 0000000..9394605
--- /dev/null
+++ b/pkg/terraform/testdata/install-input-no-vars-block.yaml
@@ -0,0 +1,7 @@
+install:
+- terraform:
+ description: "Install MySQL"
+ input: false
+ logLevel: TRACE
+ backendConfig:
+ key: "my.tfstate"
diff --git a/pkg/terraform/testdata/install-input.yaml b/pkg/terraform/testdata/install-input.yaml
new file mode 100644
index 0000000..13679eb
--- /dev/null
+++ b/pkg/terraform/testdata/install-input.yaml
@@ -0,0 +1,8 @@
+install:
+- terraform:
+ description: "Install MySQL"
+ logLevel: TRACE
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
diff --git a/pkg/terraform/testdata/invoke-input.yaml b/pkg/terraform/testdata/invoke-input.yaml
new file mode 100644
index 0000000..0241084
--- /dev/null
+++ b/pkg/terraform/testdata/invoke-input.yaml
@@ -0,0 +1,10 @@
+custom:
+- terraform:
+ description: "Custom Action"
+ arguments:
+ - "custom"
+ logLevel: TRACE
+ backendConfig:
+ key: my.tfstate
+ vars:
+ myvar: foo
diff --git a/pkg/terraform/testdata/step-input.yaml b/pkg/terraform/testdata/step-input.yaml
new file mode 100644
index 0000000..98ac0fd
--- /dev/null
+++ b/pkg/terraform/testdata/step-input.yaml
@@ -0,0 +1,13 @@
+custom:
+- terraform:
+ description: "Custom Action"
+ arguments:
+ - "custom"
+ flags:
+ logLevel: TRACE
+ backendConfig:
+ - "key=my.tfstate"
+ vars:
+ - "myvar=foo"
+ outputs:
+ - name: myoutput
diff --git a/pkg/terraform/testdata/uninstall-input.yaml b/pkg/terraform/testdata/uninstall-input.yaml
new file mode 100644
index 0000000..4ba68d4
--- /dev/null
+++ b/pkg/terraform/testdata/uninstall-input.yaml
@@ -0,0 +1,8 @@
+uninstall:
+- terraform:
+ description: "Uninstall MySQL"
+ logLevel: TRACE
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
\ No newline at end of file
diff --git a/pkg/terraform/testdata/upgrade-input.yaml b/pkg/terraform/testdata/upgrade-input.yaml
new file mode 100644
index 0000000..24686b4
--- /dev/null
+++ b/pkg/terraform/testdata/upgrade-input.yaml
@@ -0,0 +1,8 @@
+upgrade:
+- terraform:
+ description: "Upgrade MySQL"
+ logLevel: TRACE
+ backendConfig:
+ key: "my.tfstate"
+ vars:
+ myvar: "foo"
\ No newline at end of file
diff --git a/pkg/terraform/uninstall.go b/pkg/terraform/uninstall.go
new file mode 100644
index 0000000..7d43efc
--- /dev/null
+++ b/pkg/terraform/uninstall.go
@@ -0,0 +1,37 @@
+package terraform
+
+import (
+ "context"
+
+ "get.porter.sh/porter/pkg/exec/builder"
+)
+
+// Uninstall runs a terraform destroy
+func (m *Mixin) Uninstall(ctx context.Context) error {
+ action, err := m.loadAction(ctx)
+ if err != nil {
+ return err
+ }
+ step := action.Steps[0]
+
+ err = m.commandPreRun(ctx, &step)
+ if err != nil {
+ return err
+ }
+
+ // Update step fields that exec/builder works with
+ step.Arguments = []string{"destroy"}
+ // Always run in non-interactive mode
+ step.Flags = append(step.Flags, builder.NewFlag("auto-approve"))
+ step.Flags = append(step.Flags, builder.NewFlag("input=false"))
+
+ applyVarsToStepFlags(&step)
+
+ action.Steps[0] = step
+ _, err = builder.ExecuteSingleStepAction(ctx, m.RuntimeConfig, action)
+ if err != nil {
+ return err
+ }
+
+ return m.handleOutputs(ctx, step.Outputs)
+}
diff --git a/pkg/terraform/uninstall_test.go b/pkg/terraform/uninstall_test.go
new file mode 100644
index 0000000..5390e1e
--- /dev/null
+++ b/pkg/terraform/uninstall_test.go
@@ -0,0 +1,54 @@
+package terraform
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "get.porter.sh/porter/pkg/test"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ yaml "gopkg.in/yaml.v2"
+)
+
+func TestMixin_UnmarshalUninstallStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/uninstall-input.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+ step := action.Steps[0]
+
+ assert.Equal(t, "Uninstall MySQL", step.Description)
+}
+
+func TestMixin_Uninstall(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform destroy -auto-approve -input=false -var myvar=foo",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/uninstall-input.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ err = h.Uninstall(context.Background())
+ require.NoError(t, err)
+
+ wd := h.Getwd()
+ require.NoError(t, err)
+ assert.Equal(t, wd, h.config.WorkingDir)
+}
diff --git a/pkg/terraform/upgrade.go b/pkg/terraform/upgrade.go
new file mode 100644
index 0000000..1609306
--- /dev/null
+++ b/pkg/terraform/upgrade.go
@@ -0,0 +1,8 @@
+package terraform
+
+import "context"
+
+// Upgrade runs a terraform apply, just like Install()
+func (m *Mixin) Upgrade(ctx context.Context) error {
+ return m.Install(ctx)
+}
diff --git a/pkg/terraform/upgrade_test.go b/pkg/terraform/upgrade_test.go
new file mode 100644
index 0000000..d28952b
--- /dev/null
+++ b/pkg/terraform/upgrade_test.go
@@ -0,0 +1,54 @@
+package terraform
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "get.porter.sh/porter/pkg/test"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v2"
+)
+
+func TestMixin_UnmarshalUpgradeStep(t *testing.T) {
+ b, err := ioutil.ReadFile("testdata/upgrade-input.yaml")
+ require.NoError(t, err)
+
+ var action Action
+ err = yaml.Unmarshal(b, &action)
+ require.NoError(t, err)
+ require.Len(t, action.Steps, 1)
+ step := action.Steps[0]
+
+ assert.Equal(t, "Upgrade MySQL", step.Description)
+}
+
+func TestMixin_Upgrade(t *testing.T) {
+ defer os.Unsetenv(test.ExpectedCommandEnv)
+ expectedCommand := strings.Join([]string{
+ "terraform init -backend=true -backend-config=key=my.tfstate -reconfigure",
+ "terraform apply -auto-approve -input=false -var myvar=foo",
+ }, "\n")
+ os.Setenv(test.ExpectedCommandEnv, expectedCommand)
+
+ b, err := ioutil.ReadFile("testdata/upgrade-input.yaml")
+ require.NoError(t, err)
+
+ h := NewTestMixin(t)
+ h.In = bytes.NewReader(b)
+
+ // Set up working dir as current dir
+ h.config.WorkingDir = h.Getwd()
+ require.NoError(t, err)
+
+ err = h.Upgrade(context.Background())
+ require.NoError(t, err)
+
+ wd := h.Getwd()
+ require.NoError(t, err)
+ assert.Equal(t, wd, h.config.WorkingDir)
+}
diff --git a/pkg/terraform/version.go b/pkg/terraform/version.go
new file mode 100644
index 0000000..7d73d11
--- /dev/null
+++ b/pkg/terraform/version.go
@@ -0,0 +1,23 @@
+package terraform
+
+import (
+ "get.porter.sh/mixin/terraform/pkg"
+ "get.porter.sh/porter/pkg/mixin"
+ "get.porter.sh/porter/pkg/pkgmgmt"
+ "get.porter.sh/porter/pkg/porter/version"
+)
+
+func (m *Mixin) PrintVersion(opts version.Options) error {
+ return version.PrintVersion(m.Context, opts, m.Version())
+}
+
+func (m *Mixin) Version() mixin.Metadata {
+ return mixin.Metadata{
+ Name: "terraform",
+ VersionInfo: pkgmgmt.VersionInfo{
+ Version: pkg.Version,
+ Commit: pkg.Commit,
+ Author: "Porter Authors",
+ },
+ }
+}
diff --git a/pkg/terraform/version_test.go b/pkg/terraform/version_test.go
new file mode 100644
index 0000000..aa48b4e
--- /dev/null
+++ b/pkg/terraform/version_test.go
@@ -0,0 +1,55 @@
+package terraform
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "get.porter.sh/mixin/terraform/pkg"
+ "get.porter.sh/porter/pkg/porter/version"
+ "get.porter.sh/porter/pkg/printer"
+)
+
+func TestPrintVersion(t *testing.T) {
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+
+ m := NewTestMixin(t)
+
+ opts := version.Options{}
+ err := opts.Validate()
+ require.NoError(t, err)
+ m.PrintVersion(opts)
+
+ gotOutput := m.TestContext.GetOutput()
+ wantOutput := "terraform v1.2.3 (abc123) by Porter Authors"
+ if !strings.Contains(gotOutput, wantOutput) {
+ t.Fatalf("invalid output:\nWANT:\t%q\nGOT:\t%q\n", wantOutput, gotOutput)
+ }
+}
+
+func TestPrintJsonVersion(t *testing.T) {
+ pkg.Commit = "abc123"
+ pkg.Version = "v1.2.3"
+
+ m := NewTestMixin(t)
+
+ opts := version.Options{}
+ opts.RawFormat = string(printer.FormatJson)
+ err := opts.Validate()
+ require.NoError(t, err)
+ m.PrintVersion(opts)
+
+ gotOutput := m.TestContext.GetOutput()
+ wantOutput := `{
+ "name": "terraform",
+ "version": "v1.2.3",
+ "commit": "abc123",
+ "author": "Porter Authors"
+}
+`
+ if !strings.Contains(gotOutput, wantOutput) {
+ t.Fatalf("invalid output:\nWANT:\t%q\nGOT:\t%q\n", wantOutput, gotOutput)
+ }
+}
diff --git a/pkg/version.go b/pkg/version.go
new file mode 100644
index 0000000..fbea841
--- /dev/null
+++ b/pkg/version.go
@@ -0,0 +1,7 @@
+package pkg
+
+// These are build-time values, set during an official release
+var (
+ Commit string
+ Version string
+)
diff --git a/scripts/test/test-cli.sh b/scripts/test/test-cli.sh
new file mode 100755
index 0000000..e0fa2df
--- /dev/null
+++ b/scripts/test/test-cli.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+set -xeuo pipefail
+export REGISTRY=${REGISTRY:-$USER}
+export REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"
+export PORTER_HOME=${PORTER_HOME:-$REPO_DIR/bin}
+# Run tests in a temp directory
+export TEST_DIR=/tmp/porter/terraform
+mkdir -p ${TEST_DIR}
+pushd ${TEST_DIR}
+trap popd EXIT
+
+function verify-output() {
+ # Verify the output matches the expected value
+ output=`${PORTER_HOME}/porter installation output show $1`
+ if [[ "${output}" != "$2" ]]; then
+ echo "Output '$1' value: '${output}' does not match expected"
+ return 1
+ fi
+
+ # Verify the output has no extra newline (mixin should trim newline added by terraform cli)
+ if [[ "$(${PORTER_HOME}/porter installation output show $1 | wc -l)" > 1 ]]; then
+ echo "Output '$1' has an extra newline character"
+ return 1
+ fi
+}
+
+
+# Copy terraform assets
+cp -r ${REPO_DIR}/examples/basic-tf-example/terraform .
+
+# Copy in the terraform porter manifest
+cp ${REPO_DIR}/examples/basic-tf-example/porter.yaml .
+
+${PORTER_HOME}/porter build
+${PORTER_HOME}/porter install --verbosity=debug \
+ --param file_contents='foo!' \
+ --param map_var='{"foo": "bar"}' \
+ --param array_var='["mylist", "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' \
+ --param boolean_var=true \
+ --param number_var=1 \
+ --param json_encoded_html_string_var='testing?connection&string=<>' \
+ --param complex_object_var='{"top_value": "https://my.service?test=$id<>", "nested_object": {"internal_value": "https://my.connection.com?test&test=$hello"}}' \
+ --force
+
+echo "Verifying installation output after install"
+verify-output "file_contents" 'foo!'
+verify-output "map_var" '{"foo":"bar"}'
+verify-output "array_var" '["mylist","https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]'
+verify-output "boolean_var" 'true'
+verify-output "number_var" '1'
+verify-output "complex_object_var" '{"nested_object":{"internal_value":"https://my.connection.com?test&test=$hello"},"top_value":"https://my.service?test=$id<>"}'
+verify-output "json_encoded_html_string_var" 'testing?connection&string=<>'
+
+${PORTER_HOME}/porter invoke --verbosity=debug --action=plan --debug
+
+${PORTER_HOME}/porter upgrade --verbosity=debug \
+ --param file_contents='bar!' \
+ --param map_var='{"bar": "baz"}' \
+ --param array_var='["mylist", "https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]' \
+ --param boolean_var=false \
+ --param number_var=2 \
+ --param json_encoded_html_string_var='?new#conn&string$characters~!' \
+ --param complex_object_var='{"top_value": "https://my.updated.service?test=$id<>", "nested_object": {"internal_value": "https://new.connection.com?test&test=$hello"}}'
+
+echo "Verifying installation output after upgrade"
+verify-output "file_contents" 'bar!'
+verify-output "map_var" '{"bar":"baz"}'
+verify-output "array_var" '["mylist","https://ml.azure.com/?wsid=/subscriptions/zzzz/resourceGroups/some-rsg/providers/Microsoft.MachineLearningServices/workspaces/myworkspace&tid=zzzzz"]'
+verify-output "boolean_var" 'false'
+verify-output "number_var" '2'
+verify-output "json_encoded_html_string_var" '?new#conn&string$characters~!'
+verify-output "complex_object_var" '{"nested_object":{"internal_value":"https://new.connection.com?test&test=$hello"},"top_value":"https://my.updated.service?test=$id<>"}'
+
+${PORTER_HOME}/porter uninstall --debug