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