From 25c95e0545e716dd2dc28a298fcbede324b7bc96 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 22 Aug 2022 17:09:12 +0000 Subject: [PATCH] backport of commit 84f226cb74467898a9ac57315600ad1b35895102 --- .github/ISSUE_TEMPLATE/bug_report.md | 84 ++ .github/ISSUE_TEMPLATE/bug_report.yml | 124 -- .github/ISSUE_TEMPLATE/config.yml | 14 +- .github/ISSUE_TEMPLATE/feature_request.md | 62 + .github/ISSUE_TEMPLATE/feature_request.yml | 87 -- .github/workflows/build-Dockerfile | 4 +- .github/workflows/checks.yml | 8 +- CHANGELOG.md | 169 ++- Makefile | 5 +- README.md | 26 +- commands.go | 2 - docs/destroying.md | 2 +- .../resource-instance-change-lifecycle.png | Bin 199031 -> 249200 bytes docs/planning-behaviors.md | 294 ----- docs/plugin-protocol/tfplugin5.3.proto | 381 ------ docs/plugin-protocol/tfplugin6.3.proto | 362 ------ docs/resource-instance-change-lifecycle.md | 406 ++---- experiments.go | 23 - go.mod | 52 +- go.sum | 235 +++- internal/addrs/instance_key.go | 62 +- internal/addrs/instance_key_test.go | 75 -- internal/addrs/map.go | 128 -- internal/addrs/map_test.go | 83 -- internal/addrs/module_package.go | 47 +- internal/addrs/module_source.go | 173 ++- internal/addrs/module_source_test.go | 82 +- internal/addrs/move_endpoint_module.go | 6 +- internal/addrs/provider.go | 301 ++++- internal/addrs/provider_test.go | 4 +- internal/addrs/set.go | 32 +- internal/addrs/unique_key.go | 4 - internal/backend/init/init.go | 23 +- internal/backend/init/init_test.go | 2 + internal/backend/local/backend_local_test.go | 7 +- internal/backend/local/testing.go | 9 +- .../backend/remote-state/azure/arm_client.go | 49 +- .../backend/remote-state/azure/backend.go | 9 + .../remote-state/azure/backend_test.go | 173 ++- internal/backend/remote-state/azure/client.go | 3 +- .../remote-state/azure/helpers_test.go | 2 + internal/backend/remote-state/cos/backend.go | 17 +- .../backend/remote-state/etcdv2/backend.go | 96 ++ .../remote-state/etcdv2/backend_test.go | 11 + .../backend/remote-state/etcdv2/client.go | 46 + .../remote-state/etcdv2/client_test.go | 45 + .../backend/remote-state/etcdv3/backend.go | 168 +++ .../remote-state/etcdv3/backend_state.go | 110 ++ .../remote-state/etcdv3/backend_test.go | 107 ++ .../backend/remote-state/etcdv3/client.go | 211 +++ .../remote-state/etcdv3/client_test.go | 103 ++ internal/backend/remote/backend_state.go | 23 +- .../providers/terraform/data_source_state.go | 7 +- internal/cloud/backend.go | 12 +- internal/cloud/backend_common.go | 11 - internal/cloud/backend_plan.go | 51 +- internal/cloud/backend_runTasks.go | 2 +- internal/cloud/backend_state.go | 36 +- internal/cloud/backend_state_test.go | 49 +- internal/cloud/state.go | 160 --- internal/cloud/state_test.go | 83 -- internal/cloud/testing.go | 71 +- internal/cloud/tfe_client_mock.go | 51 +- internal/command/apply_test.go | 80 +- internal/command/command_test.go | 91 -- .../command/e2etest/module_archive_test.go | 32 - .../command/e2etest/provider_plugin_test.go | 12 +- .../e2etest/testdata/module-archive/main.tf | 5 - internal/command/fmt.go | 17 +- internal/command/fmt_test.go | 28 +- internal/command/format/diagnostic.go | 20 +- internal/command/format/diagnostic_test.go | 188 --- internal/command/import.go | 18 +- internal/command/import_test.go | 57 + internal/command/init.go | 21 +- internal/command/jsonplan/plan.go | 20 +- internal/command/jsonplan/plan_test.go | 13 - internal/command/jsonstate/state.go | 6 +- internal/command/jsonstate/state_test.go | 5 +- internal/command/meta.go | 21 +- internal/command/meta_backend.go | 7 +- internal/command/meta_backend_migrate_test.go | 3 +- internal/command/meta_config.go | 1 - internal/command/output.go | 16 +- internal/command/plan_test.go | 83 -- internal/command/providers.go | 5 +- internal/command/providers_mirror.go | 5 +- internal/command/show_test.go | 9 +- internal/command/test.go | 2 +- internal/command/testdata/plan/main.tf | 6 +- internal/command/testdata/plan/output.jsonlog | 5 - .../testdata/show-json/unknown-output/main.tf | 19 - .../show-json/unknown-output/output.json | 96 -- internal/command/views/hook_json.go | 17 +- internal/command/views/hook_ui.go | 24 +- internal/command/views/json/diagnostic.go | 67 +- .../command/views/json/diagnostic_test.go | 136 -- internal/command/views/json/function.go | 112 -- internal/command/views/json/function_test.go | 92 -- ...e-when-not-caused-by-sensitive-values.json | 26 - ...ype-when-not-caused-by-unknown-values.json | 26 - internal/configs/configload/loader.go | 13 - internal/configs/experiments.go | 94 +- internal/configs/experiments_test.go | 28 +- internal/configs/module_call_test.go | 4 +- internal/configs/named_values.go | 33 +- internal/configs/parser.go | 20 - internal/configs/parser_config.go | 2 +- internal/configs/provider_validation.go | 4 +- .../object-optional-attrs-experiment.tf | 6 + .../object-optional-attrs-experiment.tf | 35 + internal/earlyconfig/diagnostics.go | 4 - internal/experiments/experiment.go | 3 +- internal/getmodules/getter.go | 5 +- internal/getproviders/didyoumean.go | 2 +- internal/getproviders/errors.go | 1 - internal/getproviders/registry_client.go | 20 +- internal/grpcwrap/provider.go | 4 - internal/grpcwrap/provider6.go | 4 - internal/httpclient/useragent.go | 2 +- internal/initwd/module_install.go | 21 +- internal/lang/funcs/defaults.go | 288 +++++ internal/lang/funcs/defaults_test.go | 648 ++++++++++ internal/lang/funcs/string.go | 52 - internal/lang/functions.go | 5 +- internal/lang/functions_test.go | 94 +- internal/moduletest/provider.go | 9 +- internal/plans/changes.go | 20 +- internal/plans/changes_sync.go | 16 - internal/plugin/grpc_provider.go | 174 +-- internal/plugin6/grpc_provider.go | 170 +-- internal/provider-simple-v6/provider.go | 19 - internal/provider-simple/provider.go | 10 - internal/providercache/errors.go | 14 + internal/providercache/installer_test.go | 5 +- internal/providercache/package_install.go | 24 +- internal/providers/provider.go | 13 - internal/refactoring/move_execute.go | 35 +- internal/refactoring/move_execute_test.go | 211 ++- internal/refactoring/move_statement_test.go | 1 - internal/refactoring/move_validate.go | 90 +- internal/refactoring/move_validate_test.go | 13 +- internal/registry/regsrc/module.go | 2 +- internal/states/remote/state.go | 14 - internal/states/remote/state_test.go | 206 ++- internal/states/state.go | 4 +- internal/states/statemgr/filesystem.go | 14 - internal/states/statemgr/filesystem_test.go | 14 - internal/states/statemgr/lock.go | 4 - internal/states/statemgr/persistent.go | 12 - internal/states/statemgr/statemgr_fake.go | 8 - internal/states/statemgr/testing.go | 4 - internal/terraform/context_apply2_test.go | 398 +----- internal/terraform/context_apply_test.go | 115 +- internal/terraform/context_import.go | 4 +- internal/terraform/context_import_test.go | 37 +- internal/terraform/context_plan.go | 17 +- internal/terraform/context_plan_test.go | 9 - internal/terraform/context_test.go | 8 - internal/terraform/context_validate.go | 3 +- internal/terraform/context_walk.go | 2 +- internal/terraform/diagnostics.go | 42 - internal/terraform/eval_count.go | 8 +- internal/terraform/eval_for_each.go | 3 - internal/terraform/eval_for_each_test.go | 24 +- internal/terraform/eval_variable.go | 5 - internal/terraform/graph_builder_apply.go | 5 +- .../terraform/graph_builder_destroy_plan.go | 115 ++ internal/terraform/graph_builder_import.go | 101 ++ internal/terraform/graph_builder_plan.go | 147 +-- internal/terraform/graph_builder_plan_test.go | 25 +- internal/terraform/graph_builder_validate.go | 40 + internal/terraform/node_output.go | 27 +- internal/terraform/node_output_test.go | 22 +- internal/terraform/node_provider.go | 5 +- internal/terraform/node_resource_abstract.go | 7 - .../node_resource_abstract_instance.go | 87 +- .../terraform/node_resource_abstract_test.go | 7 - .../terraform/node_resource_apply_instance.go | 27 +- internal/terraform/node_resource_plan.go | 11 - internal/terraform/provider_mock.go | 12 - internal/terraform/terraform_test.go | 6 - .../testdata/import-provider/main.tf | 1 + internal/terraform/transform_config.go | 21 - internal/terraform/transform_destroy_edge.go | 22 +- .../terraform/transform_destroy_edge_test.go | 181 +-- internal/terraform/transform_diff.go | 6 +- internal/terraform/transform_diff_test.go | 56 - ...ce_import.go => transform_import_state.go} | 51 + .../terraform/transform_orphan_resource.go | 7 - internal/terraform/transform_output.go | 18 +- internal/terraform/transform_provider_test.go | 61 + internal/terraform/transform_reference.go | 9 - internal/terraform/transform_targets_test.go | 4 +- internal/tfdiags/consolidate_warnings.go | 4 - internal/tfdiags/diagnostic.go | 7 - internal/tfdiags/diagnostic_base.go | 4 - internal/tfdiags/diagnostic_extra.go | 171 --- internal/tfdiags/error.go | 5 - internal/tfdiags/hcl.go | 4 - internal/tfdiags/rpc_friendly.go | 5 - internal/tfdiags/simple_warning.go | 5 - internal/tfplugin5/tfplugin5.pb.go | 1147 ++++++++--------- internal/tfplugin5/tfplugin5.proto | 2 +- internal/tfplugin6/tfplugin6.pb.go | 975 +++++++------- internal/tfplugin6/tfplugin6.proto | 2 +- internal/typeexpr/defaults.go | 157 --- internal/typeexpr/defaults_test.go | 504 -------- internal/typeexpr/get_type.go | 179 +-- internal/typeexpr/get_type_test.go | 669 ---------- internal/typeexpr/public.go | 18 +- internal/typeexpr/type_string_test.go | 100 -- internal/typeexpr/type_type_test.go | 118 -- main.go | 3 - scripts/goimportscheck.sh | 19 - tools/tools.go | 1 + version/version.go | 2 +- website/README.md | 68 +- website/data/language-nav-data.json | 15 +- website/docs/cli/commands/fmt.mdx | 12 +- website/docs/cli/commands/init.mdx | 2 +- .../docs/language/expressions/references.mdx | 17 +- .../language/expressions/type-constraints.mdx | 161 +-- website/docs/language/functions/defaults.mdx | 198 +++ website/docs/language/functions/endswith.mdx | 27 - .../docs/language/functions/startswith.mdx | 27 - .../language/modules/develop/refactoring.mdx | 6 + website/docs/language/resources/syntax.mdx | 2 +- .../language/settings/backends/azurerm.mdx | 20 +- website/layouts/language.erb | 12 +- 230 files changed, 5930 insertions(+), 9467 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 docs/planning-behaviors.md delete mode 100644 docs/plugin-protocol/tfplugin5.3.proto delete mode 100644 docs/plugin-protocol/tfplugin6.3.proto delete mode 100644 experiments.go delete mode 100644 internal/addrs/instance_key_test.go delete mode 100644 internal/addrs/map.go delete mode 100644 internal/addrs/map_test.go create mode 100644 internal/backend/remote-state/etcdv2/backend.go create mode 100644 internal/backend/remote-state/etcdv2/backend_test.go create mode 100644 internal/backend/remote-state/etcdv2/client.go create mode 100644 internal/backend/remote-state/etcdv2/client_test.go create mode 100644 internal/backend/remote-state/etcdv3/backend.go create mode 100644 internal/backend/remote-state/etcdv3/backend_state.go create mode 100644 internal/backend/remote-state/etcdv3/backend_test.go create mode 100644 internal/backend/remote-state/etcdv3/client.go create mode 100644 internal/backend/remote-state/etcdv3/client_test.go delete mode 100644 internal/cloud/state.go delete mode 100644 internal/cloud/state_test.go delete mode 100644 internal/command/e2etest/module_archive_test.go delete mode 100644 internal/command/e2etest/testdata/module-archive/main.tf delete mode 100644 internal/command/testdata/plan/output.jsonlog delete mode 100644 internal/command/testdata/show-json/unknown-output/main.tf delete mode 100644 internal/command/testdata/show-json/unknown-output/output.json delete mode 100644 internal/command/views/json/function.go delete mode 100644 internal/command/views/json/function_test.go delete mode 100644 internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value-when-not-caused-by-sensitive-values.json delete mode 100644 internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type-when-not-caused-by-unknown-values.json create mode 100644 internal/configs/testdata/invalid-modules/object-optional-attrs-experiment/object-optional-attrs-experiment.tf create mode 100644 internal/configs/testdata/warning-files/object-optional-attrs-experiment.tf create mode 100644 internal/lang/funcs/defaults.go create mode 100644 internal/lang/funcs/defaults_test.go create mode 100644 internal/providercache/errors.go delete mode 100644 internal/terraform/diagnostics.go create mode 100644 internal/terraform/graph_builder_destroy_plan.go create mode 100644 internal/terraform/graph_builder_import.go create mode 100644 internal/terraform/graph_builder_validate.go rename internal/terraform/{node_resource_import.go => transform_import_state.go} (83%) delete mode 100644 internal/tfdiags/diagnostic_extra.go delete mode 100644 internal/typeexpr/defaults.go delete mode 100644 internal/typeexpr/defaults_test.go delete mode 100644 internal/typeexpr/get_type_test.go delete mode 100644 internal/typeexpr/type_string_test.go delete mode 100644 internal/typeexpr/type_type_test.go delete mode 100755 scripts/goimportscheck.sh create mode 100644 website/docs/language/functions/defaults.mdx delete mode 100644 website/docs/language/functions/endswith.mdx delete mode 100644 website/docs/language/functions/startswith.mdx diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000000..a4d37fa551b7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,84 @@ +--- +name: Bug report +about: Let us know about an unexpected error, a crash, or an incorrect behavior. +labels: bug, new + +--- + + + +### Terraform Version + + +``` +... +``` + +### Terraform Configuration Files + + +```terraform +... +``` + +### Debug Output + + +### Expected Behavior + + +### Actual Behavior + + +### Steps to Reproduce + + +### Additional Context + + +### References + diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index c4d9de4995b6..000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: Bug Report -description: Let us know about an unexpected error, a crash, or an incorrect behavior. -labels: ["bug", "new"] -body: - - type: markdown - attributes: - value: | - # Thank you for opening an issue. - - The [hashicorp/terraform](https://github.com/hashicorp/terraform) issue tracker is reserved for bug reports relating to the core Terraform CLI application and configuration language. - - For general usage questions, please see: https://www.terraform.io/community.html. - - ## If your issue relates to: - * **Terraform Cloud/Enterprise**: please email tf-cloud@hashicorp.support or [open a new request](https://support.hashicorp.com/hc/en-us/requests/new). - * **AWS Terraform Provider**: Open an issue at [hashicorp/terraform-provider-aws](https://github.com/hashicorp/terraform-provider-aws/issues/new/choose). - * **Azure Terraform Provider**: Open an issue at [hashicorp/terraform-provider-azurerm](https://github.com/hashicorp/terraform-provider-azurerm/issues/new/choose). - * **Other Terraform Providers**: Please open an issue in the provider's own repository, which can be found by searching the [Terraform Registry](https://registry.terraform.io/browse/providers). - - ## Filing a bug report - - To fix problems, we need clear reproduction cases - we need to be able to see it happen locally. A reproduction case is ideally something a Terraform Core engineer can git-clone or copy-paste and run immediately, without inventing any details or context. - - * A short example can be directly copy-pasteable; longer examples should be in separate git repositories, especially if multiple files are needed - * Please include all needed context. For example, if you figured out that an expression can cause a crash, put the expression in a variable definition or a resource - * Set defaults on (or omit) any variables. The person reproducing it should not need to invent variable settings - * If multiple steps are required, such as running terraform twice, consider scripting it in a simple shell script. Providing a script can be easier than explaining what changes to make to the config between runs. - * Omit any unneeded complexity: remove variables, conditional statements, functions, modules, providers, and resources that are not needed to trigger the bug - * When possible, use the [null resource](https://www.terraform.io/docs/providers/null/resource.html) provider rather than a real provider in order to minimize external dependencies. We know this isn't always feasible. The Terraform Core team doesn't have deep domain knowledge in every provider, or access to every cloud platform for reproduction cases. - - - type: textarea - id: tf-version - attributes: - label: Terraform Version - description: Run `terraform version` to show the version, and paste the result below. If you are not running the latest version of Terraform, please try upgrading because your issue may have already been fixed. - render: shell - placeholder: ...output of `terraform version`... - value: - validations: - required: true - - - type: textarea - id: tf-config - attributes: - label: Terraform Configuration Files - description: Paste the relevant parts of your Terraform configuration between the ``` marks below. For Terraform configs larger than a few resources, or that involve multiple files, please make a GitHub repository that we can clone, rather than copy-pasting multiple files in here. - placeholder: - value: | - ```terraform - ...terraform config... - ``` - validations: - required: true - - - type: textarea - id: tf-debug - attributes: - label: Debug Output - description: Full debug output can be obtained by running Terraform with the environment variable `TF_LOG=trace`. Please create a GitHub Gist containing the debug output. Please do _not_ paste the debug output in the issue, since debug output is long. Debug output may contain sensitive information. Please review it before posting publicly. - placeholder: ...link to gist... - value: - validations: - required: true - - type: textarea - id: tf-expected - attributes: - label: Expected Behavior - description: What should have happened? - placeholder: What should have happened? - value: - validations: - required: true - - type: textarea - id: tf-actual - attributes: - label: Actual Behavior - description: What actually happened? - placeholder: What actually happened? - value: - validations: - required: true - - type: textarea - id: tf-repro-steps - attributes: - label: Steps to Reproduce - description: | - Please list the full steps required to reproduce the issue, for example: - 1. `terraform init` - 2. `terraform apply` - placeholder: | - 1. `terraform init` - 2. `terraform apply` - value: - validations: - required: true - - type: textarea - id: tf-add-context - attributes: - label: Additional Context - description: | - Are there anything atypical about your situation that we should know? - For example: is Terraform running in a wrapper script or in a CI system? Are you passing any unusual command line options or environment variables to opt-in to non-default behavior?" - placeholder: Additional context... - value: - validations: - required: false - - type: textarea - id: tf-references - attributes: - label: References - description: | - Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example: - ``` - - #6017 - ``` - placeholder: - value: - validations: - required: false - - - type: markdown - attributes: - value: | - **Note:** If the submit button is disabled and you have filled out all required fields, please check that you did not forget a **Title** for the issue. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2c525cbbf294..d5792ab6f311 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,18 +3,12 @@ contact_links: - name: Terraform Cloud/Enterprise Troubleshooting and Feature Requests url: https://support.hashicorp.com/hc/en-us/requests/new about: For issues and feature requests related to the Terraform Cloud/Enterprise platform, please submit a HashiCorp support request or email tf-cloud@hashicorp.support - - name: AWS Terraform Provider Feedback and Questions - url: https://github.com/hashicorp/terraform-provider-aws - about: The AWS Terraform Provider has its own repository, any provider related issues or questions should be directed there. - - name: Azure Terraform Provider Feedback and Questions - url: https://github.com/hashicorp/terraform-provider-azurerm - about: The Azure Terraform Provider has its own repository, any provider related issues or questions should be directed there. - - name: Other Provider-related Feedback and Questions + - name: Provider-related Feedback and Questions url: https://registry.terraform.io/browse/providers - about: Each provider (e.g. GCP, Oracle, K8S, etc.) has its own repository, any provider related issues or questions should be directed to the appropriate issue tracker linked from the Registry. + about: Each provider (e.g. AWS, Azure, GCP, Oracle, K8S, etc.) has its own repository, any provider related issues or questions should be directed to the appropriate issue tracker linked from the Registry. - name: Provider Development Feedback and Questions url: https://github.com/hashicorp/terraform-plugin-sdk/issues/new/choose about: Plugin SDK has its own repository, any SDK and provider development related issues or questions should be directed there. - - name: Terraform Usage, Language, or Workflow Questions + - name: Terraform Language or Workflow Questions url: https://discuss.hashicorp.com/c/terraform-core - about: Please ask and answer language or workflow related questions through the Terraform Core Community Forum. \ No newline at end of file + about: Please ask and answer language or workflow related questions through the Terraform Core Community Forum. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000000..438d3cca63f7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,62 @@ +--- +name: Feature request +about: Suggest a new feature or other enhancement. +labels: enhancement, new + +--- + + + +### Current Terraform Version + + +``` +... +``` + +### Use-cases + + +### Attempted Solutions + + +### Proposal + + +### References + diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 9549ba81577e..000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Feature Request -description: Suggest a new feature or other enhancement. -labels: ["enhancement", "new"] -body: - - type: markdown - attributes: - value: | - # Thank you for opening a feature request. - - The [hashicorp/terraform](https://github.com/hashicorp/terraform) issue tracker is reserved for feature requests relating to the core Terraform CLI application and configuration language. - - For general usage questions, please see: https://www.terraform.io/community.html. - - ## If your feature request relates to: - * **Terraform Cloud/Enterprise**: please email tf-cloud@hashicorp.support or [open a new request](https://support.hashicorp.com/hc/en-us/requests/new). - * **AWS Terraform Provider**: Open an issue at [hashicorp/terraform-provider-aws](https://github.com/hashicorp/terraform-provider-aws/issues/new/choose). - * **Azure Terraform Provider**: Open an issue at [hashicorp/terraform-provider-azurerm](https://github.com/hashicorp/terraform-provider-azurerm/issues/new/choose). - * **Other Terraform Providers**: Please open an issue in the provider's own repository, which can be found by searching the [Terraform Registry](https://registry.terraform.io/browse/providers). - - - type: textarea - id: tf-version - attributes: - label: Terraform Version - description: Run `terraform version` to show the version, and paste the result below. If you're not using the latest version, please check to see if something related to your request has already been implemented in a later version. - render: shell - placeholder: ...output of `terraform version`... - value: - validations: - required: true - - - type: textarea - id: tf-use-case - attributes: - label: Use Cases - description: | - In order to properly evaluate a feature request, it is necessary to understand the use cases for it. - Please describe below the _end goal_ you are trying to achieve that has led you to request this feature. - Please keep this section focused on the problem and not on the suggested solution. We'll get to that in a moment, below! - placeholder: - value: - validations: - required: true - - - type: textarea - id: tf-attempted-solution - attributes: - label: Attempted Solutions - description: | - If you've already tried to solve the problem within Terraform's existing features and found a limitation that prevented you from succeeding, please describe it below in as much detail as possible. - Ideally, this would include real configuration snippets that you tried, real Terraform command lines you ran, and what results you got in each case. - Please remove any sensitive information such as passwords before sharing configuration snippets and command lines. - placeholder: - value: - validations: - required: true - - - type: textarea - id: tf-proposal - attributes: - label: Proposal - description: | - If you have an idea for a way to address the problem via a change to Terraform features, please describe it below. - In this section, it's helpful to include specific examples of how what you are suggesting might look in configuration files, or on the command line, since that allows us to understand the full picture of what you are proposing. - If you're not sure of some details, don't worry! When we evaluate the feature request we may suggest modifications as necessary to work within the design constraints of Terraform Core. - placeholder: - value: - validations: - required: false - - - type: textarea - id: tf-references - attributes: - label: References - description: | - Are there any other GitHub issues, whether open or closed, that are related to the problem you've described above or to the suggested solution? If so, please create a list below that mentions each of them. For example: - ``` - - #6017 - ``` - placeholder: - value: - validations: - required: false - - - type: markdown - attributes: - value: | - **Note:** If the submit button is disabled and you have filled out all required fields, please check that you did not forget a **Title** for the issue. diff --git a/.github/workflows/build-Dockerfile b/.github/workflows/build-Dockerfile index c0ea5b89d360..6f8f15853a42 100644 --- a/.github/workflows/build-Dockerfile +++ b/.github/workflows/build-Dockerfile @@ -24,10 +24,10 @@ ARG TARGETARCH LABEL maintainer="HashiCorp Terraform Team " # New standard version label. -LABEL version=$PRODUCT_VERSION +LABEL version=$VERSION # Historical Terraform-specific label preserved for backward compatibility. -LABEL "com.hashicorp.terraform.version"="${PRODUCT_VERSION}" +LABEL "com.hashicorp.terraform.version"="${VERSION}" RUN apk add --no-cache git openssh diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 56165d3934dd..dd92ecf7224f 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -141,8 +141,6 @@ jobs: steps: - name: "Fetch source code" uses: actions/checkout@v2 - with: - fetch-depth: 0 # We need to do comparisons against the main branch. - name: Determine Go version id: go @@ -180,13 +178,9 @@ jobs: restore-keys: | protobuf-tools- - - name: Install CI tooling - run: | - go install golang.org/x/tools/cmd/goimports@v0.1.11 - - name: "Code consistency checks" run: | - make fmtcheck importscheck generate staticcheck exhaustive protobuf + make fmtcheck generate staticcheck exhaustive protobuf if [[ -n "$(git status --porcelain)" ]]; then echo >&2 "ERROR: Generated files are inconsistent. Run 'make generate' and 'make protobuf' locally and then commit the updated files." git >&2 status --porcelain diff --git a/CHANGELOG.md b/CHANGELOG.md index 699b86d8a948..92128ee66640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,72 +1,151 @@ -## 1.3.0 (Unreleased) +## 1.2.8 (Unreleased) -NEW FEATURES: +## 1.2.7 (August 10, 2022) + +ENHANCEMENTS: + +* config: Check for direct references to deprecated computed attributes. ([#31576](https://github.com/hashicorp/terraform/issues/31576)) + +BUG FIXES: + +* config: Fix an crash if a submodule contains a resource whose implied provider local name contains invalid characters, by adding additional validation rules to turn it into a real error. ([#31573](https://github.com/hashicorp/terraform/issues/31573)) +* core: Fix some handling of provider schema attributes which use the newer "structural typing" mechanism introduced with protocol version 6, and therefore with the new Terraform Plugin Framework ([#31532](https://github.com/hashicorp/terraform/issues/31532)) +* command: Add missing output text for applyable refresh plans. ([#31469](https://github.com/hashicorp/terraform/issues/31469)) + +## 1.2.6 (July 27, 2022) + +ENHANCEMENTS: + +* Add a warning and guidance when `terraform init` fails to fully populate the `.terraform.lock.hcl` file. ([#31399](https://github.com/hashicorp/terraform/issues/31399)) +* Add a direct link to the relevant documentation when `terraform init` fails on missing checksums. ([#31408](https://github.com/hashicorp/terraform/issues/31408)) + +BUG FIXES: + +* Fix panic on `terraform show` when state file is invalid or unavailable. ([#31444](https://github.com/hashicorp/terraform/issues/31444)) +* Fix `terraform providers lock` command failing on missing checksums. ([#31389](https://github.com/hashicorp/terraform/issues/31389)) +* Some combinations of move block operations would be executed in the wrong order ([#31499](https://github.com/hashicorp/terraform/issues/31499)) +* Don't attribute an error to the provider when a computed attribute is listed in `ignore_changes` ([#31509](https://github.com/hashicorp/terraform/issues/31509)) + +## 1.2.5 (July 13, 2022) + +BUG FIXES: + +* Report correct error message when a prerelease field is included in the `required_version` global constraint. ([#31331](https://github.com/hashicorp/terraform/issues/31331)) +* Fix case when extra blank lines were inserted into the plan for unchanged blocks. ([#31330](https://github.com/hashicorp/terraform/issues/31330)) + +## 1.2.4 (June 29, 2022) + +ENHANCEMENTS: -* **Optional attributes for object type constraints:** When declaring an input variable whose type constraint includes an object type, you can now declare individual attributes as optional, and specify a default value to use if the caller doesn't set it. For example: +* Improved validation of `required_providers` to prevent single providers from being required with multiple names. ([#31218](https://github.com/hashicorp/terraform/issues/31218)) +* Improved plan performance by optimizing `addrs.Module.String` for allocations. ([#31293](https://github.com/hashicorp/terraform/issues/31293)) - ```terraform - variable "with_optional_attribute" { - type = object({ - a = string # a required attribute - b = optional(string) # an optional attribute - c = optional(number, 127) # an optional attribute with a default value - }) - } - ``` +BUG FIXES: - Assigning `{ a = "foo" }` to this variable will result in the value `{ a = "foo", b = null, c = 127 }`. +* backend/http: Fixed bug where the HTTP backend would fail to retry acquiring the state lock and ignored the `-lock-timeout` flag. ([#31256](https://github.com/hashicorp/terraform/issues/31256)) +* Fix crash if a `precondition` or `postcondition` block omitted the required `condition` argument. ([#31290](https://github.com/hashicorp/terraform/issues/31290)) -* Added functions: `startswith` and `endswith` allow you to check whether a given string has a specified prefix or suffix. ([#31220](https://github.com/hashicorp/terraform/issues/31220)) +## 1.2.3 (June 15, 2022) UPGRADE NOTES: -* `terraform show -json`: Output changes now include more detail about the unknown-ness of the planned value. Previously, a planned output would be marked as either fully known or partially unknown, with the `after_unknown` field having value `false` or `true` respectively. Now outputs correctly expose the full structure of unknownness for complex values, allowing consumers of the JSON output format to determine which values in a collection are known only after apply. -* `terraform import`: The `-allow-missing-config` has been removed, and at least an empty configuration block must exist to import a resource. +* The following remote state backends are now marked as deprecated, and are + planned to be removed in a future Terraform release. These backends have + been unmaintained since before Terraform v1.0, and may contain known bugs, + outdated packages, or security vulnerabilities. + - artifactory + - etcd + - etcdv3 + - manta + - swift + +BUG FIXES: + +* Missing check for error diagnostics in GetProviderSchema could result in panic ([#31184](https://github.com/hashicorp/terraform/issues/31184)) +* Module registries returning X-Terraform-Get locations with no URL would error with "no getter available for X-Terraform-Get source protocol" ([#31237](https://github.com/hashicorp/terraform/issues/31237)) +* Fix crash from concurrent operation on shared set of resource instance dependencies ([#31246](https://github.com/hashicorp/terraform/issues/31246)) +* backend/cos: `tencentcloud-terraform-lock` tag was not removed in all cases ([#31223](https://github.com/hashicorp/terraform/issues/31223)) - Consumers of the JSON output format expecting on the `after_unknown` field to be only `false` or `true` should be updated to support [the change representation](https://www.terraform.io/internals/json-format#change-representation) described in the documentation, and as was already used for resource changes. ([#31235](https://github.com/hashicorp/terraform/issues/31235)) +## 1.2.2 (June 01, 2022) ENHANCEMENTS: -* config: Optional attributes for object type constraints, as described under new features above. ([#31154](https://github.com/hashicorp/terraform/issues/31154)) -* `terraform fmt` now accepts multiple target paths, allowing formatting of several individual files at once. ([#28191](https://github.com/hashicorp/terraform/issues/28191)) -* When reporting an error message related to a function call, Terraform will now include contextual information about the signature of the function that was being called, as an aid to understanding why the call might have failed. ([#31299](https://github.com/hashicorp/terraform/issues/31299)) -* When reporting an error or warning message that isn't caused by values being unknown or marked as sensitive, Terraform will no longer mention any values having those characteristics in the contextual information presented alongside the error. Terraform will still return this information for the small subset of error messages that are specifically about unknown values or sensitive values being invalid in certain contexts. ([#31299](https://github.com/hashicorp/terraform/issues/31299)) -* The Terraform CLI now calls `PlanResourceChange` for compatible providers when destroying resource instances. ([#31179](https://github.com/hashicorp/terraform/issues/31179)) -* The AzureRM Backend now only supports MSAL (and Microsoft Graph) and no longer makes use of ADAL (and Azure Active Directory Graph) for authentication ([#31070](https://github.com/hashicorp/terraform/issues/31070)) -* The COS backend now supports global acceleration. ([#31425](https://github.com/hashicorp/terraform/issues/31425)) -* providercache: include host in provider installation error ([#31524](https://github.com/hashicorp/terraform/issues/31524)) -* refactoring: `moved` blocks can now be used to move resources to and from external modules ([#31556](https://github.com/hashicorp/terraform/issues/31556)) -* When showing the progress of a remote operation running in Terraform Cloud, Terraform CLI will include information about pre-plan run tasks ([#31617](https://github.com/hashicorp/terraform/issues/31617)) +* Invalid `-var` arguments with spaces between the name and value now have an improved error message ([#30985](https://github.com/hashicorp/terraform/issues/30985)) BUG FIXES: -* config: Terraform was not previously evaluating preconditions and postconditions during the apply phase for resource instances that didn't have any changes pending, which was incorrect because the outcome of a condition can potentially be affected by changes to _other_ objects in the configuration. Terraform will now always check the conditions for every resource instance included in a plan during the apply phase, even for resource instances that have "no-op" changes. This means that some failures that would previously have been detected only by a subsequent run will now be detected during the same run that caused them, thereby giving the feedback at the appropriate time. ([#31491](https://github.com/hashicorp/terraform/issues/31491)) -* `terraform show -json`: Fixed missing unknown markers in the encoding of partially unknown tuples and sets. ([#31236](https://github.com/hashicorp/terraform/issues/31236)) -* `terraform output` CLI help documentation is now more consistent with web-based documentation. ([#29354](https://github.com/hashicorp/terraform/issues/29354)) -* getproviders: account for occasionally missing Host header in errors ([#31542](https://github.com/hashicorp/terraform/issues/31542)) -* core: Do not create "delete" changes for nonexistent outputs ([#31471](https://github.com/hashicorp/terraform/issues/31471)) -* configload: validate implied provider names in submodules to avoid crash ([#31573](https://github.com/hashicorp/terraform/issues/31573)) -* core: `import` fails when resources or modules are expanded with for each, or input from data sources is required ([#31283](https://github.com/hashicorp/terraform/issues/31283)) +* Terraform now hides invalid input values for sensitive root module variables when generating error diagnostics ([#30552](https://github.com/hashicorp/terraform/issues/30552)) +* Fixed crash on CLI autocomplete ([#31160](https://github.com/hashicorp/terraform/issues/31160)) +* The "Configuration contains unknown values" error message now includes attribute paths ([#31111](https://github.com/hashicorp/terraform/issues/31111)) + +## 1.2.1 (May 23, 2022) -EXPERIMENTS: +BUG FIXES: + +* SSH provisioner connections fail when using signed `ed25519` keys ([#31092](https://github.com/hashicorp/terraform/issues/31092)) +* Crash with invalid module source ([#31060](https://github.com/hashicorp/terraform/issues/31060)) +* Incorrect "Module is incompatible with count, for_each, and depends_on" error when a provider is nested within a module along with a sub-module using `count` or `for_each` ([#31091](https://github.com/hashicorp/terraform/issues/31091)) + + +## 1.2.0 (May 18, 2022) + +UPGRADE NOTES: -* This release concludes the `module_variable_optional_attrs` experiment, which started in Terraform v0.14.0. The final design of the optional attributes feature is similar to the experimental form in the previous releases, but with two major differences: - * The `optional` function-like modifier for declaring an optional attribute now accepts an optional second argument for specifying a default value to use when the attribute isn't set by the caller. If not specified, the default value is a null value of the appropriate type as before. - * The built-in `defaults` function, previously used to meet the use-case of replacing null values with default values, will not graduate to stable and has been removed. Use the second argument of `optional` inline in your type constraint to declare default values instead. +* If you use the [third-party credentials helper plugin terraform-credentials-env](https://github.com/apparentlymart/terraform-credentials-env), you should disable it as part of upgrading to Terraform v1.2 because similar functionality is now built in to Terraform itself. - If you have any experimental modules that were participating in this experiment, you will need to remove the experiment opt-in and adopt the new syntax for declaring default values in order to migrate your existing module to the stablized version of this feature. If you are writing a shared module for others to use, we recommend declaring that your module requires Terraform v1.3.0 or later to give specific feedback when using the new feature on older Terraform versions, in place of the previous declaration to use the experimental form of this feature: + The new behavior supports the same environment variable naming scheme but has a difference in priority order from the credentials helper: `TF_TOKEN_...` environment variables will now take priority over credentials blocks in CLI configuration and credentials stored automatically by terraform login, which is not true for credentials provided by any credentials helper plugin. If you see Terraform using different credentials after upgrading, check to make sure you do not specify credentials for the same host in multiple locations. + + If you use the credentials helper in conjunction with the [hashicorp/tfe](https://registry.terraform.io/providers/hashicorp/tfe) Terraform provider to manage Terraform Cloud or Terraform Enterprise objects with Terraform, you should also upgrade to version 0.31 of that provider, which added the corresponding built-in support for these environment variables. +* The official Linux packages for the v1.2 series now require Linux kernel version 2.6.32 or later. +* When making outgoing HTTPS or other TLS connections as a client, Terraform now requires the server to support TLS v1.2. TLS v1.0 and v1.1 are no longer supported. Any safely up-to-date server should support TLS 1.2, and mainstream web browsers have required it since 2020. +* When making outgoing HTTPS or other TLS connections as a client, Terraform will no longer accept CA certificates signed using the SHA-1 hash function. Publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015. + +(Note: the changes to Terraform's requirements when interacting with TLS servers apply only to requests made by Terraform CLI itself, such as provider/module installation and state storage requests. Terraform provider plugins include their own TLS clients which may have different requirements, and may add new requirements in their own releases, independently of Terraform CLI changes.) + +NEW FEATURES: + +* `precondition` and `postcondition` check blocks for resources, data sources, and module output values: module authors can now document assumptions and assertions about configuration and state values. If these conditions are not met, Terraform will report a custom error message to the user and halt further execution. +* `replace_triggered_by` is a new `lifecycle` argument for managed resources which triggers replacement of an object based on changes to an upstream dependency. +* You can now specify credentials for [Terraform-native services](https://www.terraform.io/internals/remote-service-discovery) using an environment variable named as `TF_TOKEN_` followed by an encoded version of the hostname. For example, Terraform will use variable `TF_TOKEN_app_terraform_io` as a bearer token for requests to "app.terraform.io", for the Terraform Cloud integration and private registry requests. + +ENHANCEMENTS: + +* When showing a plan, Terraform CLI will now only show "Changes outside of Terraform" if they relate to resources and resource attributes that contributed to the changes Terraform is proposing to make. ([#30486](https://github.com/hashicorp/terraform/issues/30486)) +* Error messages for preconditions, postconditions, and custom variable validations are now evaluated as expressions, allowing interpolation of relevant values into the output. ([#30613](https://github.com/hashicorp/terraform/issues/30613)) +* When showing the progress of a remote operation running in Terraform Cloud, Terraform CLI will include information about post-plan [run tasks](https://www.terraform.io/cloud-docs/workspaces/settings/run-tasks). ([#30141](https://github.com/hashicorp/terraform/issues/30141)) +* Terraform will now show a slightly different note in the plan output if a data resource read is deferred to the apply step due to it depending on a managed resource that has changes pending. ([#30971](https://github.com/hashicorp/terraform/issues/30971)) +* The "Invalid for_each argument" error message for unknown maps/sets now includes an additional paragraph to try to help the user notice they can move apply-time values into the map _values_ instead of the map _keys_, and thus avoid the problem without resorting to `-target`. ([#30327](https://github.com/hashicorp/terraform/issues/30327)) +* There are some small improvements to the error and warning messages Terraform will emit in the case of invalid provider configuration passing between modules. There are no changes to which situations will produce errors and warnings, but the messages now include additional information intended to clarify what problem Terraform is describing and how to address it. ([#30639](https://github.com/hashicorp/terraform/issues/30639)) +* The environment variables `TF_CLOUD_ORGANIZATION` and `TF_CLOUD_HOSTNAME` now serve as fallbacks for the arguments of the same name inside a `cloud` block configuring integration with Terraform Cloud. +* The environment variable `TF_WORKSPACE` will now additionally serve as an implicit configuration of a single selected workspace on Terraform Cloud if (and only if) the `cloud` block does not include an explicit workspaces configuration. +* The AzureRM Backend now defaults to using MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication. ([#30891](https://github.com/hashicorp/terraform/issues/30891)) +* The AzureRM Backend now supports authenticating as a service principal using OpenID Connect. ([#30936](https://github.com/hashicorp/terraform/pull/30936)) +* When running on macOS, Terraform will now use platform APIs to validate certificates presented by TLS (HTTPS) servers. This may change exactly which root certificates Terraform will accept as valid. ([#30768](https://github.com/hashicorp/terraform/issues/30768)) +* Show remote host in error message for clarity when installation of provider fails ([#30810](https://github.com/hashicorp/terraform/issues/30810)) +* Terraform now prints a warning when adding an attribute to `ignore_changes` that is managed only by the provider. Specifying non-configurable attributes in `ignore_changes` has no effect because `ignore_changes` tells Terraform to ignore future changes made in the configuration. ([#30517](https://github.com/hashicorp/terraform/issues/30517)) +* `terraform show -json` now includes exact type information for output values. ([#30945](https://github.com/hashicorp/terraform/issues/30945)) +* The `ssh` provisioner connection now supports SSH over HTTP proxy. ([#30274](https://github.com/hashicorp/terraform/pull/30274)) +* * The SSH client for provisioners now supports newer key algorithms, allowing it to connect to servers running more recent versions of OpenSSH. ([#30962](https://github.com/hashicorp/terraform/issues/30962)) + +BUG FIXES: - ```hcl - terraform { - required_version = ">= 1.3.0" - } - ``` +* Terraform now handles type constraints, nullability, and custom variable validation properly for root module variables. Previously there was an order of operations problem where the nullability and custom variable validation were checked too early, prior to dealing with the type constraints, and thus that logic could potentially "see" an incorrectly-typed value in spite of the type constraint, leading to incorrect errors. ([#29959](https://github.com/hashicorp/terraform/issues/29959)) +* When reporting a type mismatch between the true and false results of a conditional expression when both results are of the same structural type kind (object/tuple, or a collection thereof), Terraform will no longer return a confusing message like "the types are object and object, respectively", and will instead attempt to explain how the two structural types differ. ([#30920](https://github.com/hashicorp/terraform/issues/30920)) +* Applying the various type conversion functions like `tostring`, `tonumber`, etc to `null` will now return a null value of the intended type. For example, `tostring(null)` converts from a null value of an unknown type to a null value of string type. Terraform can often handle such conversions automatically when needed, but explicit annotations like this can help Terraform to understand author intent when inferring type conversions for complex-typed values. ([#30879](https://github.com/hashicorp/terraform/issues/30879)) +* Terraform now returns an error when `cidrnetmask()` is called with an IPv6 address, as it was previously documented to do. IPv6 standards do not preserve the "netmask" syntax sometimes used for IPv4 network configuration; use CIDR prefix syntax instead. ([#30703](https://github.com/hashicorp/terraform/issues/30703)) +* When performing advanced state management with the `terraform state` commands, Terraform now checks the `required_version` field in the configuration before proceeding. ([#30511](https://github.com/hashicorp/terraform/pull/30511)) +* When rendering a diff, Terraform now quotes the name of any object attribute whose string representation is not a valid identifier. ([#30766](https://github.com/hashicorp/terraform/issues/30766)) +* Terraform will now prioritize local terraform variables over remote terraform variables in operations such as `import`, `plan`, `refresh` and `apply` for workspaces in local execution mode. This behavior applies to both `remote` backend and the `cloud` integration configuration. ([#29972](https://github.com/hashicorp/terraform/issues/29972)) +* `terraform show -json`: JSON plan output now correctly maps aliased providers to their configurations, and includes the full provider source address alongside the short provider name. ([#30138](https://github.com/hashicorp/terraform/issues/30138)) +* The local token configuration in the `cloud` and `remote` backend now has higher priority than a token specified in a `credentials` block in the CLI configuration. ([#30664](https://github.com/hashicorp/terraform/issues/30664)) +* The `cloud` integration now gracefully exits when `-input=false` and an operation requires some user input. +* Terraform will now reliably detect an interruption (e.g. Ctrl+C) during planning for `terraform apply -auto-approve`. Previously there was a window of time where interruption would cancel the plan step but not prevent Terraform from proceeding to the apply step. ([#30979](https://github.com/hashicorp/terraform/issues/30979)) +* Terraform will no longer crash if a provider fails to return a schema. ([#30987](https://github.com/hashicorp/terraform/issues/30987)) ## Previous Releases For information on prior major and minor releases, see their changelogs: -* [v1.2](https://github.com/hashicorp/terraform/blob/v1.2/CHANGELOG.md) * [v1.1](https://github.com/hashicorp/terraform/blob/v1.1/CHANGELOG.md) * [v1.0](https://github.com/hashicorp/terraform/blob/v1.0/CHANGELOG.md) * [v0.15](https://github.com/hashicorp/terraform/blob/v0.15/CHANGELOG.md) diff --git a/Makefile b/Makefile index f393c1fb3eb6..e14991b6aba4 100644 --- a/Makefile +++ b/Makefile @@ -35,9 +35,6 @@ protobuf: fmtcheck: @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" -importscheck: - @sh -c "'$(CURDIR)/scripts/goimportscheck.sh'" - staticcheck: @sh -c "'$(CURDIR)/scripts/staticcheck.sh'" @@ -66,4 +63,4 @@ website/build-local: # under parallel conditions. .NOTPARALLEL: -.PHONY: fmtcheck importscheck generate protobuf website website-test staticcheck website/local website/build-local +.PHONY: fmtcheck generate protobuf website website-test staticcheck website/local website/build-local diff --git a/README.md b/README.md index e8509e37606a..4f235995f237 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# Terraform +Terraform +========= - Website: https://www.terraform.io - Forums: [HashiCorp Discuss](https://discuss.hashicorp.com/c/terraform-core) @@ -14,35 +15,32 @@ The key features of Terraform are: - **Infrastructure as Code**: Infrastructure is described using a high-level configuration syntax. This allows a blueprint of your datacenter to be versioned and treated as you would any other code. Additionally, infrastructure can be shared and re-used. -- **Execution Plans**: Terraform has a "planning" step where it generates an execution plan. The execution plan shows what Terraform will do when you call apply. This lets you avoid any surprises when Terraform manipulates infrastructure. +- **Execution Plans**: Terraform has a "planning" step where it generates an *execution plan*. The execution plan shows what Terraform will do when you call apply. This lets you avoid any surprises when Terraform manipulates infrastructure. - **Resource Graph**: Terraform builds a graph of all your resources, and parallelizes the creation and modification of any non-dependent resources. Because of this, Terraform builds infrastructure as efficiently as possible, and operators get insight into dependencies in their infrastructure. - **Change Automation**: Complex changesets can be applied to your infrastructure with minimal human interaction. With the previously mentioned execution plan and resource graph, you know exactly what Terraform will change and in what order, avoiding many possible human errors. -For more information, refer to the [What is Terraform?](https://www.terraform.io/intro) page on the Terraform website. - -## Getting Started & Documentation +For more information, see the [introduction section](https://www.terraform.io/intro) of the Terraform website. +Getting Started & Documentation +------------------------------- Documentation is available on the [Terraform website](https://www.terraform.io): - -- [Introduction](https://www.terraform.io/intro) -- [Documentation](https://www.terraform.io/docs) + - [Intro](https://www.terraform.io/intro/index.html) + - [Docs](https://www.terraform.io/docs/index.html) If you're new to Terraform and want to get started creating infrastructure, please check out our [Getting Started guides](https://learn.hashicorp.com/terraform#getting-started) on HashiCorp's learning platform. There are also [additional guides](https://learn.hashicorp.com/terraform#operations-and-development) to continue your learning. Show off your Terraform knowledge by passing a certification exam. Visit the [certification page](https://www.hashicorp.com/certification/) for information about exams and find [study materials](https://learn.hashicorp.com/terraform/certification/terraform-associate) on HashiCorp's learning platform. -## Developing Terraform +Developing Terraform +-------------------- This repository contains only Terraform core, which includes the command line interface and the main graph engine. Providers are implemented as plugins, and Terraform can automatically download providers that are published on [the Terraform Registry](https://registry.terraform.io). HashiCorp develops some providers, and others are developed by other organizations. For more information, see [Extending Terraform](https://www.terraform.io/docs/extend/index.html). -- To learn more about compiling Terraform and contributing suggested changes, refer to [the contributing guide](.github/CONTRIBUTING.md). +To learn more about compiling Terraform and contributing suggested changes, please refer to [the contributing guide](.github/CONTRIBUTING.md). -- To learn more about how we handle bug reports, refer to the [bug triage guide](./BUGPROCESS.md). - -- To learn how to contribute to the Terraform documentation in this repository, refer to the [Terraform Documentation README](/website/README.md). +To learn more about how we handle bug reports, please read the [bug triage guide](./BUGPROCESS.md). ## License - [Mozilla Public License v2.0](https://github.com/hashicorp/terraform/blob/main/LICENSE) diff --git a/commands.go b/commands.go index ea1f3200300e..41c39066dd9f 100644 --- a/commands.go +++ b/commands.go @@ -100,8 +100,6 @@ func initCommands( ProviderSource: providerSrc, ProviderDevOverrides: providerDevOverrides, UnmanagedProviders: unmanagedProviders, - - AllowExperimentalFeatures: ExperimentsAllowed(), } // The command list is included in the terraform -help diff --git a/docs/destroying.md b/docs/destroying.md index 9643e26cbbc8..adf0f4d97f5e 100644 --- a/docs/destroying.md +++ b/docs/destroying.md @@ -1,7 +1,7 @@ # Terraform Core Resource Destruction Notes This document intends to describe some of the details and complications -involved in the destruction of resources. It covers the ordering defined for +involved in the destructions of resources. It covers the ordering defined for related create and destroy operations, as well as changes to the lifecycle ordering imposed by `create_before_destroy`. It is not intended to enumerate all possible combinations of dependency ordering, only to outline the basics diff --git a/docs/images/resource-instance-change-lifecycle.png b/docs/images/resource-instance-change-lifecycle.png index b6cf16e4fa5b894acb44eaf9cd5c9b45d9086a7f..3872be78c7a5e4b135a18ce54efe8112ac12a487 100644 GIT binary patch literal 249200 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

P)JCxMrEa@rmAA7wXAa9~)PXxt*`GhrG+1 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%0D#L!XqI)c*v z|CVJ%0Y(+OVq_u@10W#TCaIWSQHCsZNhc&Eie>l&@ge+7dx{8yrty*Xqab2G2ObcV z9+GGFf2G7)G0K@3;)5+%32uIM^iM5I1Tn1u3kDthQ_}bkl&i0sz3x>k$f!-Ml$peA z^&vjlhf6J?`qYx4axp?er90C))Ut+8JP@4zm*X+u^<8T9QD(rD_}mdh=yi8@oB{E= zqwvNd<9R6&_RrXL$)f(b7$Wyq49Dsq;0v1Xr5CILysLZU@W8dBhnr+v-Fuf(5C&QF z&ww3ey+eGL#5hbKTbAyP8g|a4wXwyf9C@d_@q*cuTj@s?B@W#c|i0I1? z0?!9qnTeIY+!Jz4`_diBe19-q$9c`JPvKoYm&<)l!n2tF5W&3xRgIAgk&7hRLmUCK zf`HB-zI>P|eD-j!eRF*;==N0-#-N-tb7q?)HjaL5^Ih0Ye7qYcCkM?1|FNm9+Yf3j zRX!2}&6p^7&v{%IZ$1|HxTs1Ad2NaVW`uLw(25p5Be7h%RqVj4$Jvt!(V-$raF$4SZXO8(l=jF62Q&^Biqi7 z$c54j6{d4fwBGmxY2aiIWpdaON`#UBX7lzOKe-+^Kdg;ddHAE8Jr<8@$>5aC;c`je z3dUN3gLHqbBPEWjx4d-{`Dj~LahQRCQ$a^2lV6aKK`RuGM&`@;z1fx?N`54{oT(!D zN2}2WECQH|&=ZEKOfA8iyX~B-79nr5*BSiw0gEke66vTCwlVMCF}M{w#h2UJGif)_ z<@H8umdYk<3_jvau*F5`wR(~pv3_F{&NoE65MdAzFHp?<)LdSPa7hIvR++uz4M3W` zeDet%Nedq5KfT+&a|Yqe^R>Yf;0v|zA+i27LW=Sd9;~Fsx;1R%+me-_zD9lVA(D|V zrGa+7fa4QN^CS(y*V7%;gu%4Phr0(>&n3qrv*M*+8M?R7TZSa|W`ee&$qLl#$YdjR z{cf9|Gz2e%tJ$q}n#Jd}szf<=#%#Y<^teb$`u03K9RJ-^$M7lrD-PD)tJ%6Z0p1_h zyzE3N1WbBC?l>f}A+dM;HwLcX>XlVd8f#0W&|GX)t>kH$)$;7C5*ep5X9ZTyZu*G_ za=SR`$G%-2mVEdAhi6<=zJwB*snnePy&+C&JpA~vWiBk9MQY}hAXi`5M+rZ3$cfK@ z7yDrJ6NhvR-7D6`ieU?gg;j7klEoS6hE+Il5OW`W5G}l38wOHj5d!Nm>xvuFM*~s* z643;>oBI-l0Cb9b}usarz$54+~(~>cG9Ngig@IFyS^VPE^ z8*dkylUGPoY=68tS8XI`)A?G(cq$uTE=FCj(-&T!*6en#;yJ7*aI2q{Cl$UXy3J`! zxUNAZ9xncRsWp#Goqe=_QxUl)Y>y<6Za`bw4U^y_ZKF-;FZ0K;(k}R*ay7&R$l&a)Q zv-fcT+e__`3VyL>^AOIK{01DivP7e6NZHo+R zl0yeBCWAD-7k?ZaX2i7^b_Xf7y-9zA2|~{i94B}NZHdA;qdoZG)WH30_Fd3*BR)vJ z^SyH#KU6U^@Gy_oV(H|W`fw*oG~+tHJ+pOcvB1YXGGE;3atvPP(ynQYFdO+x!gKAX zSeiJT>HF|-O?^NHF~|8>^3PiJbs}k#ztZyRw@Wn^S-j^?t|pR*mH|i(KK7EB5bm<_ zsKr8O`kkqz;aFMwS12gp#wBy;x~bDJe-GCW5xd-{uU0nr-Ogi^PYTVkimVc}EK*xm90Qd^>~pt9qyaYUlU{zs#}a^q z6A%FdY0?kGT3>^IzX#vKL$BcfJE@)_(?$wIP~-rJSBwRIQcTVl#2R1}^0J!>c^JQM zFdjg-GaKF(B$yc=x9}OrcpGFAPDVHDID513xbW1WF~R?jBKcy$t*gyg(r~7@g@;X= zfUaZ=+gz+B9_HQsZPLT#W%(AJ+SUkxfRmZ5F6QUold_xJUE@93)_e8GjPgNpo#wx0 ztXPHG%-av=>zQ_f9v{xb-sMQ<(DbUP#I7+8=BrE(-~WYqb*7N~?NcijrOqEnn!@dt z>kSib^%;-&LA>2nRdBX?%X<<^s69ccn9dR?@npWO;tW~}3N!yeB#&Ba+*Mkc*znqy zqo}7WBuO0g-q-KmG%3NvV$=uz#&a24gpx2v=4m7A`dqA03VGZGw|HL5eeMW=z0iBZ zXdcRy{nD?r`=Q=>|82A9aX7^1P9!ErJTJNOi|TB+CWF;qJEEr5<}%{Jqf{1Dhrybp zL(7A2w}z|ddd+qqKHtCRKP9TZBKh*>S(xu)i#H=A4FjJ(Pi_0H);a$uy9T2^2~zb( z27_T!U12((PiNhkjH;dugTRuuw{vb+P6w7gN~`AP*B|3+owZgzvDItNN}FaI|K@7j zb%}LoyD_{}H}%34W$WL#l0_f=_O*C3Ywefzr=)`QbxvX2^e6lGEGV<%PnLRqMV0CB zy&nkh!ev%?*B8y7#*kt#u;w*ct|ktQ|B~8AvG3k|*EDLg&HLeGo4PA92^vcy``nCt zUHMb-CdzxvpWH!%JOm$X&}-|bmllg?EAZ8-K(s}LPoVJZ_Xcg?Y=2}fp2BEE3g&g zs=&dA;TY9!`ws5sD=DLpapM7*K|!a2F~T2dFULWfuiV!3M8E8ZwqBjI^wi)j=%hJ@VZJ)@UKH!%w8Z$w6y8hQJSZ&w+b+*}A$o^<>1>m~SscFyo7PYB)`{=8{~rEc zIcolHVvHb}h?`6_=a`HnX|4^srWs$HdKEA1n$ zv=1o_Lln0&0ZyWe*ir(X&;gj_Bl^s&4F%nylevc#r4m>8L-v zwD^kwfSd(2bn|VE+tX9$|A2kvrvkhR35GWO2tsBe?AMTVu%DGU`A=1t1p(%f`x7VY z_uA8?xR7OvzbB<=r6)ZhzY!;DML&)vBAe@Eepei+67sf-n*FMtUl8>;Il(h+?hnia zcPN>WmS8za@g>We)3w9uay8_r&u5cje4^_?jnYT*e+YHOLs!d>nUSyNEeV-%-VW)s zFn)jzlf&AEl^QcUijYbTqV!+w}h$)f{YgBneV}o#! z1{ih6-Q9$-5CwJL_HZN*`Vm5|ON6D%TQ_eKu)ZuEw^Vc&Pnk2wSiHE%X-z`@wxH#{ z`&0K%c-NDgJ%k(KQ^qaIRg+|va#*gC!F)8N+VjEbjZ+;WwK%TxSsS_UhM640(eAY3 z&RnHi*6dk6A3)V9Cag zt^WpM^OAu20HGH3{|mVR=R7iM{`JcYwC^W!rSVVh9@XwGz#l^lAvMKylY&vt(QsKw z&=#(9*|ZnMy3D6S2UkpS-cFTkXkb2dlMInz6}-sqpzRM;ZL@N<(aRw`HATUkjY?_g zG&4cW3qVSjHn}FWWYf1g0qmg zREJOXtgF4QQGQOTYv-K7JG+zM%@-Q?i?tZO(qCUMj2Msp6qwxNWa6Ul*7R>~FYKyU zTmQCL!CwX;k1%^1|rL-1|Jbul{klo|uJ@WJYghS)ZtFGl&JC zpDiz1&#tukB@U%9y%nh7ww#U-I47a1TyC9aNlD|&K_W{+F`Fo+HvSW<8%){abuSY| zK2Lb-Fus$gB2tmLBwoBwXdKPzbvfmHQl_NQR~RGUMQ1Ud$G+aDuMzyJz7;^F(O*S| z+pUJ*avM(dhE*#xq95W3Fy+Ykp+tk}G~H(7HSDF@EzzJhRNy}$B6xVXy?jQqV?9anBil4qL8b1YMqN|);%ns zoW;xM=zqG!ikTPk=F^GJ*&ahgZ)|E0Ch8bh@%Lzd(kFTT`d{pxwwChYYVEeYS&+eL zOKBp@7>!Bi7fV+pnPNNzzl+`L!Hl!|T8Ui*zPuJC_=Cx_xE4j#?2<3nFqHA73SH2zd3nGbxo+p9v;C`TF%T z@yg5S(I#6Bfy{?}txx@fMbG0y1;T)iq%W4Kg34*Caqgi4o7d|2VA9Wxf%l((2NqZ+ z4TW5%L%fqQ30Nfn@sd+TAGOo{y-{RfDFIGEpFG*XdkH5pU%Kzq69Y={syC%dXS+3Q zhxd|D)h-t>{i_0T+}%8iaJ+(>2QQZhq|cB6c?1|}Hb(;UaAc%fIQQscQjd(XM`aMD z1VsbwF9B?lw*g?&R!IPvDQhrMzp|e95yvClCXQV?dX&}a3aDFb8y(hb_7@tV>bm$+ zBQ_Npj%xwe%XW-T^^fV6(_30p<3XkkKzd2Qnnqt*$UkIKHgs7MLM@)9YvTYzD9C5e z;3T}iyME8LJz|MlvxR$=Tlb)+fIp1L#si7?bvUn^Q*gU(xHIldcCvEJL^!~bU|*<_ zLyn!s*R#d=xy*35sQY}KP%a@}NsM76`;(@C$4+u`S&`E%Z@C_xhg>E%RMDyGLtFAO zSR<rR6QZ^bD#82swz^^35`1q?hR}5~Iyu zQeQ?^<=IUU@UW3p+AR63m_F#7Y^vI_6l>Ib*Nu)L>1gFDPvXVaXM3NzOgL63oKf+* z8DyfO-YxWG;}~|+;t&m|vas1p`<~wpqc9><5*t>24U^EJG?wuqT6@5FO__~rmVEQ; zl$+>b3l-s`1#cGRsws)?mjwsZ&P5|KZfnYf((GGaNn4$u&UB?L=jPB@sDITHyOd`a zG4oe~-(+!)zJDH#gE$6O9*gxoo4NqxVNLGH&?KDx`r*NFb?Og?1vQKBH}zJ{`bs{2 zNr@!D^jODo#7O zpHCRC8j$GwoZcjSSZ%cm+dl8FBB>~ZTFN^ytYk* zlEFe~Ak`}#?Sa4c=BF?$x}J3&&*F5&q$*nZ7@2fGp6X>x)GMR2fY0~2HW7Ipn_(|2 zHrs^A3am|WHcIphh*a*svWd2dq46km4KrTP6npSek`JdJ&x?J-kUz$!gQa4pVpIk# z)(7~yUi%cps#^()Z*!XR&Mb`gJS%d54UB~t4KcORr>y4#msYA+StwyWC(XvkN&ZQ0 zO*+!@s&Rn4hP0Mq>3L%~N_UA*(p0r|Guxy`O9!pE`BR=ePh*$tN1sv95l3^pJ}nw% zYNwU0cM4(L9m%D8wa}`qb1=1}X!QIu8F!Xosop!O#t2SFr#+#+M#AY40j<-2uLPr7 zsn@%g$PzG3VVjEN+ufubVn$6I#`7%>|4KTu$$1P5rufIPe58Kb?|3NqzQ)U6Gxb$m zt3sEsP0ujCd#P(qX2|2{_j)iTms#*PiP)$8k&G4I>Sc6PK76#Kk`b%~Ij?7@$vR_s z?jbs{udi(ZKipaNK9eGg8qOy!VE|%msRxJ{VW8PH0`G(G)*TYO5d*vM2ovQgqy%&X zjtjje!e)?pDEXG-R`StIy*Xcyg9oLd%>x& z_adHERx5O6`!kWdUH5vVTLy9Yd^XUtMuUDZ0dFL+@lbLom52CPOBHtuVCK0F4A9t~ zi6NqObSnY)8;T1ond-$T26_No_9%wJEP(2ph2lp|u)J7*Ci3`jZw?M)AfS^=ra&$f zWjH$TCuWKhOduE`=dtJcSX_ivLdFR}?JFvt(Q-bJ-*3F+osC3ZKUpsMOpVjGrbwoK zb9w_E2Zh6|$Y4A!X86&*49Exg_kI4^`BS{s1Gq6kTDC!XXlFik)exKM3Wqn14*$aI zyVNi}0V9(>l%_aQMUub9TZ?0&&E<&4c#4>ryMaIHP$k9CwtdB(&QIxA0(j22#?^3K zSzc5WM!`13(T)_nP_757@gy7EqUJ%8X;)i7l5vHQFyrj=J8By#EIT~Yx$upUhg z`fVk=!KUz9P%WOoU^Nywy`e3(w~Btka^t-Yr=ATZBY^=74GSVik~zc{%OB#jlc88e z)=!t!`gn59>2$ZO7fLD=^dV;$QJ2=sID}Te4m{VTz4Q~dL4#h`bkSV3$oTAdRG(-2 zH-}H$y24oxI0-PVgxodL_jrwiXtj9ugx~k7{#a}HbWGO0Wy--L;N z=gxNO+UZt4t;s=vY4-{1_sNSBZo6LScSuBM=1QlmNRz?> zM`I^2$l6kUiTkE%y!)SuIC`E87<1v+bW8eQ%3t&v;Np2#}UE@Or zPF^5O{H z2f;t}F;o&+CwW+7M~K#pUbAauTWkZpm;g&_AY#auz~kx1JcrvH`wKk{r2q}g9NUi zixps$>37e)fZ4V=CZLV;xBc;zG^A6s9XhY6d(|v=j7Rg6Fl7Q_da~77v@jE%vGWO@ zgkAWH7OS~MU4A1pGQyzTVK%F}AquaA7*hKbx%v-lXb21-5^w2a16~Hz{E{E^M^1X# zX0$r~$CF9ZCTMq!f)s5{eg?1>T_M`=+F3Z4Dyf zm2D5z-d_>M5H8dzFWH!W>At)d5W9RM5if4-Rp5`%*A8z{ad|GVEm6rF@)YW)g zLG!|ZGXY!dd*JUQqb4BsT+$a*(BW*Q_j$0czkTjRbdn|Fv3dboKG$kJEz*!2TCDL9+n(r4L=h`OVEiC8j{sdEy?wLK% z9FY-mIV6E%>n}0IawA~DK)1OA9ToYp7L#%B+G08D!>_N$3<~LdzMV)G&oS*@9S9ho z%F6P-d-J1}qh3mTw%R1+s>W;^lgs?UqN@I2{+DFb!4j>)0E(8Oo!Ho42 z4|kCY``n25ct6HWx!N7#z`3)nmLG?9E6cSoz|h)NMEeK1ud3aJ#L`sN%M;7JW)C3< z$Dnx{h(sjFWrj;_!m<*7CGRPKR#aYQ%T)eqh`XJBU^1_%&Zv2gH}BcuVyMXp3(Cm= zre}og$0NvqcHxWlA^m_AS~;sCOZ#S(cDb{)QRHLJg0oF$jkgWBZ5l_ATo8jxeTB(3 z-G-&=`a|u9hGMm{u1&@e5UXA#R& zauNeW`Ws5G>pSUrAY{>*WKreXTEl7&yO2RQFBen}h$;0$|V*VWk7TVeXbGSmc29O3+R8PNyZP zjD=uszUZD&SuEp-PcK~~o;8fk13&4-pX4SSuZL`WmQ#)NqAWY#J%Vv&Qm^})423(_ zkf6x#vYTvMKk+5N!U3CfT}5fUuDF{zXB{H3__Eo^i`N3IH_PYPTMMzpADJW>V`&M* zd^tDE#{I<|DHgKyRSYOie_EF%&5`vMav$e+W-peWP= zh`zXRIwAnX(?U7m?I&}ZM2be*B#`%HAW4V50X#4=0Iqa z)+D*IomZa}!1j+P<#?Gc3$d#WT~1zFb5n!4>yDz{j@GGStu&BT(e$dS{KH!>1eIw# zqqaiaZ&0HrL%hGTmyz|?8th5p@HmXbu!?nlSea;<7XxxAL>nBZw5pF-rlVV;LK1PE zy(%Lc@23uBy;@~%`(lUvsR{V(`Jl5rBN=jGq^yk$iq9dQDCQrCrtNgyy?y)h2wlF{ z6PcCwfo-DIB^4%7uLnuOzG;YePE}qSSS+?QP5g3gqO11!VO;6k@W8(hDOj4;&SB~b zmKCo6jV1Zt^n;aL5=~{y*P7925B$IXELOP=4D~)2^JQb*8EY46WI;MltIPTBFQvUk zLu?N87te`Fq0j}mt=c#hH-JX?WPRUG$OYNk49Q4+hFOnkCRt{p7wi$89Zf)gJ3ipn z`1ox-9O*QFTSlsvRc*p&m~UF)R<)5!v}>gKi5#pl&Y3n9k1qJV=`;2mu-wP*tV_(V z5KX1?J zIm)*CN3B$U^e-^^OW0#5?l40%y|LWQ`EEh-1}II7B(UY+lX zy(CB^L%ZUmAX#9hlL#k}o)9;6N!JprCqXUKdnb#{M2J1*%>bJI5uhuq^g0OiK5qvL zpzSp7oIwUO@h|5 z`Tb5cR*M{9`SGt_2?rNnw&otyF`8pW?F}xfTPREuzruFHf?{9TGZm=7Wp!sK3*P*G zFs9_#vz-W#m~gF7c$onN$O4WUNf^UZ_TcEJ_l#9)gfmb7c1LZ=EC`^mt*nw*(+&$NKI0Kd1CWQj7eG$-K0I?y-1qymSTp9~EBoSjVM4X`A zDvSVZvQIZI%BqrxIQ5CWai3(aAtoDmOuChg)kHX-97j@1RhM8BCDtQxD_#Oz)C!-6 zy4xKT+I5?hW$kySlh@_QGvFOxwm>yWUonyueAD*^RdhLRr}R;Uo)1sQoYut<8#MgL zj->VVa*9+CtbZ|&7v-Rd>tC1Bm@ z5hU}z`)F+?=Ac)((wWonqi%-F)@H!`K_24;AS9Wt$b~9EJN&F@_S|3mD3{fX-!KGZO%gzn^h0;1c7R~ zrM|j*V$@%vE@&NjZAO{r)jP#>b1^_WiL7g3i+{q9?RMU6@D@t@cbR$TQLd2^I?-^b zB)fpxF!Y6~fy46pZkx;I!|Tz=s!6*k6i8lqHK-fxBw!**8_vP9Z55hn%2{v2l*s4t zFDY`MV}dyMzHP#Qh0D8N05b(_B}Y_ow!*ko<8M|=skxng#-Dk;DoF;4!!+a*oxZks z>m{#vrl~QEIsVo*J%$V^!iJk)n;2MK})o;o-q(5&y>|i67{ONgA*P}i6moaciZgi?%B+wb50Upj+D z!7hrJv5mU&Kn1pu&M}_^7-Fgx@l#?(Y0kv$9q3Y{OUh3CAiMX50g)-$atX47$HUp1 zk1Wy3xwj^#ulQ3exJQ}|-k5;mn+mc&T;M+g&lZe-&I@h;(=FmmTKD!gsuR%NMc#oO zDr5~xSr(&KXGh=JGF)buYQ%AS)46{rrw(O8C{zD36MVhhAWRteE=FoH8JQE$W0+|0 z_}=sf;2hI=omu2x|G@HdD85V(j8qDp*p?d#9Irg>HREyQ{^Q@A{CEhCAkdyF%`$c@ zkfD96(OJp1?36Web|PXBC7M$x8g@Zc(3Zy~`D~AJwo=J(vv=3`pzex2PTY+%dAY~}5FWPddnIP4dh!tOOK46cMGS~f*2W|YLLvc)gziUk^jyG5vQ0a4!Qe4IK1( zM4&}#@Qte!pTx7%hCwZaf8kZYBc4IotPP4-4J-~5pKfdW2@qNE zjr6tK;UwA}Sr2b(XB)NUKEF3VF6l*zDN0>@8U8QpDOo1MlFviS%)}X}lBg7pOX@%} z;40HfXUiDRGqRbj#d1Cn94Ps*UbEEz(_=WgS$Aa0V6K_D?h`a=7Z2I%&5PT;z(lQA zJlI@Fo6h$5E%)qVPrIcD&UP8^CNO^heINYa4p5KLATK(79ET>Yyps>APx)NggWEb6 zuhW{MR6BV~)YO>RIo$qfF{%7acM5y=(KF&k-n^}_h^T<_8QUhKMz~4&Jl+=hb%^Kh zw${p_Yp!>hq0^2}Bf;|Dt)Wcqp=E5i`8J$T+!pk4*{IDf{LZs8{jPtGEw@;+lx_D`B~467CGCe+iF&zt ziwA?02A8!8+~o#oiZNt8VgDQn=rs6}tl!=Uh|{#-+V0I0&hZz%fRD+Ric$baBf-EC zpUObQ8WF3UrUWJar`n2{@h)3%p+uNp8#ICKRg$b9W-~g^m$0p8ewlN5o9Q2$mSMHJ zZ@#H&0tjF^h?17z#l*rlo^oBbjo2oU=c!eFo;SCYLUW_tld^dYyFi>#vD%igh@@)vGym2;-LEdzgQ zWCN|m+4?E{o5>heVsuytwcK}yUzgF9YUL?WTzl!#Ic6Iu!p9yz*S&MqI#CBZcNbvb?JA^Bg!qZ8uJvI)V}^(h)*f2??4wm}GI)GkoC0x78fuP#~B`)M`nxv!% zq5oAtV9&KO&94;%nJBHNB<*l=*EK{IcfS6d;tw`Z9OdiGaH9W%y6*o3hh6bk5mZbs z-Ga4yq&z0%thx;u5T9#{NpX z?f=mXXJ|8iqCqoKZ*O>Vc1EMh-Z8;Iu#Dg6 zsglI1{?IXzx5Qzgp-D``>8h2eYU_w;!-ErR?}nu6@;Jl_TA{%$KDU?IrpG~E@wA(* z<3JE{3r=|aT5F`jd-4|HP7256MB1jdQC>?m^K&Eqti#+&YdWaSQ9~N6Ixbe#<-XTG zT>TUJ$dnucgwnW1a=+v9)1K*Ob@o0aphQPqpZ2&G>sJOloqR8M-V;hrt((peNKAXj zVa$Zx_^uxjO@#M#3&UHX- zNd~`=PXEJGf?WRE((DkY8mMKCEjL~^nD=%g#GP(nDUu6i0fi-_ZTlCsTs5~!w1&Hm z31$DS*>$Y7f6r5hN;vgw)fVli6VJEWY%&-+kBs;aTR}-G^9AjI-NQ!L9NUnNy}5n5 zVNI+tToaiat{H}RvF{=@tDY^?CQ$>wzAvO7S+o+Ol8ZO$)@##zF``=sYLS z(OHuKG&W_!c=jgkRoc`|GMJkR1C*d$*a?^>K=ScAxP&N5+PJSjmdkQ3LEwBcVT;39=l{pvdxu4}C3~QB(?AnMGLn=a zO3olbrO6q|IVm|v1_c8RVjzkj86-#+BnJu7#{fzO5dlF^6eO#N2#CJgaOT`|@0~d_ zbKm>xee-=fV>s@;R_zME`c>6hA`z0bpXO>!u9n>va?aq?vvhmW(L(1(Wane^1>Zh* zzAa_x$y=g@@geK2J9^Y9UvErA zZyEn+xeL6m(6WRL8EP9z19p~%{g1-o2cK^*ul-n?6RjAlx?a>{qjvO^eag=qt2gv9 zq^u(qxEYPW!Zf?VSn4xz2GfrUGff&o$!?v%`OxxQE57}l-mpAvP>u8oU_8U7s?+l@ zzgu@{!*EbYRyka|zGa}nR>O;or)6f`Jxn`wb}GQDO8#*#mp*hj(r7Hn5JL;K%>52! zC7dKOI?Q5zhKm)f08g47&HLa83QCK9@1Eq*@~9i^Y_0Pf9kwN?=_rRo_n`6S50;NF zV_dlpJUvQ~Gw5|WFJZl6dh)?|{@tr0>(F;_ z@%*p&u30S|o8rt~lX(nv*ZT56>^HC1G#{N)?Xn2P4V13BszPXo#bHbSNzqYgtFM9*eZ*k1-S0z=JIwgG*n3Jm(n{3A>dj z74nvq>!O&{th|e_s8zjhO4E}7iw8TJ2^s6aT2&?FhgP#c33@js`>d~idQ+-C&yR(O z>FTG4XFQ?hU^8%;xq#!>+qL4G+ynO%-R;{$6c*&p*U9d)6)+bl{Sk3G?)>9ESy_vF zCf@UrC&+^LU8VXWv@Z+g82(EndWnViS*u0QP|;=Q=JT_+n_L92{X*Jvy1dIA#=p6~ zc~Q|OIC!O{v9PTG6t&w^s?`V(P*b1V}cJz{Wl;bXW#E6F7M+CKq$^XUpUXUH~6 z>^9Lxy;}{F7fXXuPb!UXie{d=ywiN6eQhpLG3SSzv`eFp4P(0X*MMxXbG(eA*3aEu z&z(ACyc2!3u&qB%!ns@P1X&z8clppc0?Tm=Y>mVC`RDx?GwzUxBRzc*@kYB4^V1nQ zHjL|42S=rjL_L&}gyzNVtz`LQm3njf9L0Qt`C6@93{~IgWdfi(A3C$TcDX@fIaZ9 z0s|BrBM?Q$%MJ4(WyZzSKnxn@=fU){X!MJkfY9IveiT0UFRidVJwK^MjWHLR>eI?!OW7&^R<%d4c@#(M~xBCb^p8+pJdP zz|jg<8uzv{3mB#>9P`mZ`crchFAu~CGP^;N_(aKsI>Wyd%|u=Ge6elgcliGE;7MiG zce$tMwO>A+zsz);{X|tJ)aZ4AuIAGJ%pr0S^X|nnFLB4Gc_E(XqD?l~zmF8RyPQ_8 zY>49>1`axgXlBD_N_9HvN`%)h4v4txf;wy3*&;mk9|u%0_vNtPw_fTr2hXz$=N!ux z$&%w=Ii1(p!|#W0+WAiXc06RY`nz-A?ZLpc1>V7Hl9=*w zzaFP=k!C@Hn#JvM785m&rv!|ek3ans&0AxVbuw6UY4cU3ok8kD757KJ_Jg9^=jUgM zhuA+@9_cc>uXVR*vF4bC1hq*3!OF)b=AplyPjsqSHC8*I?5AnWtY&^Xm8Ji{kRB@B zs9NwX2di3E-VQM7bMB{^=qZe&O7^aDKKC3hn#KT-DnM8$8o?EH}Ya9B0 zdgn&x!0PjTWPPl{#soCS&x?2DLk;To(aQ*h99z$emR>Y`SHS=ARew(YMM~F84u^mu zx2&rzVyB4FmbJe6vo>$8n?>&nqX?Ho)2EFuog1qkJ6^^-855TMRB^1*qT2mSME%gY zsJyV-TS84`z&9;ld3A@=(8yz;hNDm`$MT|vU0Yy0w2AWnct&9YO_^8=j5{XizhZX0ykoeH#FDx0iJ53JZnev>lcmdfi&@R!ot>VDs)vc zCB}DBdS8$$d~ksoqKm!M9-~GZLQfyzYp03eH1R!NEyX+q|VbMyDv*DM>+AzD&?!yJc$R!^sb~1 zGe#tz=@;}_*P<@3Mqec^ERtzZ7r8Rv`OE@rNLqWAg5k;g^;6b0@n4t(M7%$|XlMN5 z$|-We!EHH|D%UJKR4a^n^nn^?ZryTUkK7gFm&BdAh1$}KY-gElU;wLY{jJ>ho(`q2 z^%wLFA3f2!65A=d??E6D+4G9(QNNiNx79qa6l@$CYiFyllz5dPqROU!54w)-s>gVL zJ=phcpxV6xS6r_bbP#w{y*uq4@A0Zf<^yHak*q<7 z^*%?V03iB>aG#flwCQToh@XxLx`@2yi+Ja6+-07URR9Hp&RePJ(^D<$8+X+!6w$E+Dh{B?w69iV&KN!NWrAH~(bCUMzPH2Zt#J$t0Qs*fCX^1?PZ z9T|~C01W4}405~-0&7eW!hunKpE1medR|4B8W%HhhnJb&Ga9W@dcp3Y$47W0MfFg4 zDm39criND`*kOMh$53X|xB3YS452>HI%tI_+v@}2NY1Ejn^Qc4?%Wb_r%x+Q1k&bt zd_VR5{!DqZVUlB1U2E>|qNUqPFWp4FbMoH2I3IOttfpczK*FHeTO^xswDAS2vJ+Md~m{6kQY}W_p4vok`hv0V_Y;E z`u7xFhcw3<9czb+TTx_7I;JHe`n7CDuTvY1m4O9)dkG3uHyIAhy%?-D_m;Mbz<16j zq63cJWGY@+FJ1i*5Y2e>&a;mLPl{un_b^^34jxP#OH#An@+ZA^A+cL{*kd%pW7w1K zV_j=?+xE9!!3@P9_Oq|6qEEJNn{S&;rAR87lo;M@S~>12Qsu<%%W>$2y zBCuQ&uKJdxCnsA=?adUCFs%CYXuzVtuu_Yy`qg^6n$Y^a_RBk6Bv$QbZpp%V(bI7( zVb>XjZEr#KKwn6`Y9wOY($_hUNOF3!+G4)_E?ME~p*NyGY$wWX&2MFgR{+ZxxcnvL zb8U5^?8q0Y(%@%&G)7K?m3I-8em6?t@^L7r^iL$KzyC!ReXB|K()W_?{J_uO1-i1t zO>_CJ=qOAoUd74yC>~y!pBTwJ61Ut4 zG?z2G&BM_7Rz6085&^srtt*`vvy`+9v%W^(Z~FSShX#2hcy(@NBo<+Re9;Lz)XsO| z^H*^|{5r9n#Yy|%=sf#;#Xd0_=>3Pb#)y2io2TiBV}!iz+I1`Kk27zmMlWgk&G6FX zUrTsj9BD5ryw&TO`b6s|9ATAE?8=~>CB*9|5abzm%4e$Ol1jnVUq6@c@vYIExR%Pt zMJWDtR+{?Ln+G9rb|)_=!#|IQCqJi|?~G*7J*){9y72leOo3fgSX>xPxo=^MIoF#GZ59!9 zQ?$SIi>s#&PP7QC@?0EJR4c1{f@Zo9TT3+25~MR!h*r$Je zo@t?ZRdw@0LA3BmnXH(h91`A;42ylo9vg1z{tD9DI9^{egu^P`yR*6Zqef_bnn;JiZ@nw*4Hin(<2G$#QgyQcfwSMnQIt-XF; zI7R;X);}5#sjqE4zsck*JT07|0jG1cS-TkB$zsua^Dy|aUD=C*n@xB4PP2!e53;72 zY^m${TQ2~YBXTqva>7{e)(6MZP-k0rnYMi#(y63zX~LJ|=wHWp-?2Vaa1mwy8jeUg zAgD1C82I%G$^6gMa~lRr7D`uNrF49tcIma(INK7>aH;yELw81$Nm1lyT|GGcFb~e? z8ibBO)$M(P9ftMVPs`O}f<=(S1(H-@6)d-Y(N3n@!5hB`26$9j-_g!9Gg2ML0yW!*1JN5n(XO`cYv|h|)5}H&M+&O1BA!`>n|HMQ4PASEu%`LsJV{qOH z>qvXW_r~K85zXAmK0@j;PNO?X~ zg!5BO>jdo%Gn05_e_$s&ZPJ3!ZpY*&+z{z|k^iIVxHLKn1|f?CWH!!iFyE{fIx6pp z<-1Rd&qGXWxn)BN=eWik?5z%tA~Dd(@CEm_h%LNMzz6-TLub!9s>nRykC|Fv!J1Z=YHG8 z^%T~6R?QY-ea*8vjQuJXWO@@WHwN20d<)UA&TE}IL-vxKZBwAm)-Uo+RoTZVS6#O^ zB?6zvPcI-M+`2ehCJCd?rYEk1K=C_`8|KcI_WifC1ll@HN({o``b*^^JFj%BlaXlF z*9V|#g~xZ(P80pg;0?IanyUwCw6H~E=JC6#^&WkoRado)<5GI==+f_Oky#o)(Ugbw ze&!MD$Q!rZ-Vc+Qw0iNTI>7>Tnn{vs5EC9l={Yw7c9pn3CO$bQ`ep!*bcK6PHonfx ziAF;e1PT;d=@JDDh2-V+Kn)JTU#o@w8m&HKkYS<~ar@_63DP-{H@O-&?Qcw5*os!fRd5M6>N}#AotKRU`_#b7 zrt{(Ak2**O^InKK)A$A;vm9&t@E=+3U|0o_1?EQRk@KSj3dj}O!{F?kRpK5vvyC8+w4!#BR|p}wqy$eLoP6Xf zj4Vz#%t3~e6VN}=H)5>p4ZIy+!eK4CFYD`tU*})EmX?S_2cLedx4SlJls;v@`}?qq zzw}`|!lb|u=im7f*|r4Q{m77UWD+o_yWM+fnslA+{MPrpPrvRl_d~jV zh+#}p;AvbAUSjTX1coo-&p%f9Wz-6kx9S8I~46d~nq7yBsTXlg$+g2URX)txj z!J!rBhc*{s7n>&uR3I@gWOnrR`8;W?Y$CiSz96m|heLt-@Db74!W94Bk2I(S63N^! zL%Lqgj=P6)?9KsLaADn${aAf2SbGI$->hB2v%r5pib%ELv$7|RFLLTLyB3>cI35ja zG$7(`{^JZT%t~lM~c#* zgLU7k6KBHmA(4%QGoKQKwD$wffg+oT0MFn))%%~t{YP^D(YgN^#D84Lf1L1tBE)}U z>;E57$&aw5NwV7vrsEx-&GYdNF?jT&20TT+ZqhzYBUP#c%xb4yd(v$EHcbpLs)pi% z4v$!fx%06C(VREvZgZZ%C?yKhVK5n7bE^}|9d|}c%bFKP+WXR9+z(j1Hf22%Y^n$s zJ>!0{)+%EH;qup99bV&h>IigcB}x)*fc=h<6^-eS8NrV7;|vJ;XFHS$7*quM2Fx1|IA^BPjGVHQ!s^;_%sM49U{4ix5VahLg_$&277PeJ~y#ACEeP^;OdTB|*yHaWlrv^)MffX+pPveLU+dTD27HeoJc90A=gv>uo6m-vb%<6zyiM}5f{@vWFrcK zTIY2_*}=_w)AiA{c6--4XYs(F%hfv^sfLFM$Q(V^$jvuByC8lJ)5;5fW4+$sUymUv zA1?I4&{QeI!vsy{=}`fQkY+SR;f5*rk7PVZm2qt0XJ0okHm5{03VPJ4GezLaNT}Im zIWK%$qFrZwkSt_r)RiJExL|rcE*DR2`gMf0iL)@Qjhy16y`$L;JaYSRV5!pra>4q# zv;!&JL!Lu4=lnTjkQJo>cO;FL_F7=m;A;>ts?9ipBU&d_L^$wi@tx4J&ckqb@d)|S z!~f~C+n5OVD7 z_;B*#=^?DFA#U=4<79JC;xFoh3w<0f@0l~(u1qT#5TY~=1qkTlDUG%c9s0lN&9peE z*+D_sZoMe@sJE*m`Otum zCxd1cf+}JVXNDN}3__nlcA?}hMfM0BtC}TiI~$1?B$1sDavn>oBg1@uJ>356-&*DF z%}7Sb&E6y*spjQS`k^Px{h5~NP(NjCjYm9}iX|LRljpud8C+0Yg$JA{wKD z(*X^y6B3LWQUz-~a@C1t&(Ih^+whLWaKn7WZ@Eq`F51Dt!FPTe8X7vx!=r)@$FCcX-fQ#=Z+ zW58a2Oh<85wN2goWX`D{m)lFsWk5gd;hTQTe0_pj3DxqH?(=FS_qCgCh!(dOg*)g!8P!_~?%t7m6kd0}W=V(@xyu@-3RdvzG;Yt+ z1z;XoH+Gljq&{1E5EJ)Gdhw@3v17~yxl(mg_pzlwxqFtx?yRz=0;YkhlVn&5i;Gp~ zOD=$p2*~KD;pjoGQv8Rz2aI3?DbCJ@d;9qT_Km-bLS9C=x9i5f?0slmU5BZMJL;Sc zBNI=*1bmaQZ2h|NMBabYE{=61xi%w5M2|I_+ud~}d8klZ&g+`msq49TPwfxiqv#lX zFGi(tqOtG6dB&=sQFGv`e7(=6vFvGD5$L4Y)m!?zDgs3u5`JbT;XZzPh8y&;vsRQC zW}#!ssI^(qq!7xK=PQ#J>g{|U_&a|ncPZAHQC~m292+`R^>Au%k(Y1!|aHL9=!w;t4PXE!%?e@chY;fP6DC~B- zclqG0T>Rv_*f~tN+}h=2@+8;pWAHORyG1zDXno;jA+#`uTrM*yO&mBMwzHmEZdrSz zzrQ(Xk#0dSB^%D3w-WK1vVrTUMS59tn7Tz$8oBr!gs!E>h@)f!;xXs26sGmUC! z_JK0FS(v0mLd4_5;|h={Lxw45#*$u1Oit!qkoKpKgY>n_DpiAz z@Fm<}-DZGzgr9*6$tR8wLegIgbX;sZ3_79n(xqb89_}!J44>E3x@Cg*t7pl2OLM~z z-`;y5&t@Vv7atSTIUK|I-P>xQ=?dWqe*j&Ifc?YPg2FqCfX_cw) zC+ADY%NciVF$qL-+%lNyY%S5zHkm7u>}YX{PmL@*w_`GC8GQXdr-h=z7ej9(@{h+#Q!fO6F`zuVIiDz@n_pb$&iNkUawhFp@}B>{Ub`z z+aL`#RJed?*^~Nqk+?>$u`?J_&~o0Xi)(T3-@kuSU#HF3-FCt`O*ViXm)9jFQ>(=Ru_ip}gy_Pg~SkB3G8cyTy z=znwR^CdRB6Y4k;%31{upL=p1_$;%#D=P%l9 zt9$AiaAwf7-!z4&Uv5;VLWC*?XE1wPTWmju0$km62`m=z-2o6_Gnmsp$SHHOQ|s_C z&;icbym<2S_D;Ut7`WTZC=tyuAMcGD5ObJZk4s|w8YJDpH4VK<04pJ zg>_?mh@$ARA3wKO15}8~_8*K*e=qZKxs=19y1iDCUDC!2hU+kX? z>wg88#kxnJDqSW0!yK50Ns1$0H$vR&JzvS7(HsiFx06ntl$+gg`+k$))4p?r{)n!a zGVwjLisPqiO7Bs1^jC z)UsYHT0}m_5m%h#>^it>cA)EiR1gJK)9WqXH&wbE;0ou}ar~9AH4)I?+ja#vKU7%n ztGUkoZTWTTh3nz-FN!;lXKxjc$UH)4D`eBJo!n%jW6(^C=U9VSI-Rg900;yB*sS6Z z2Lvmi?*x~4>h{>nn5-W={T&>bSxi`*J4dphdEY)VDUV^kqDyYK(+EE{*Ff}{$9&>T zlU%}I#41qL`Ai-z`gSW0FrZ!swHl(N1ONVOlVg6kubg3#c8S)_zQgh2M|Y~IC>$g& zei_=Cm|mQelF)DfpCSS4m~ox!%N7qBpPi7J_jgusAFET+T{h*3K#Lm>ibcaFsA(}Y zzM1fgzmT=ezZ903^&AEh6KH9^f5|*_SGsp+NBWhGBL)M*W)*v zHZHxk>@Ui!>OGucF7D*^Qrh?nK54Y1Yo8RF_(hRkj61qFbC{X&05;3Aa^M@pxHAQY zf5bYhtTtGLM)H;CoCz9`U65h+kV*z1m4Q}*P(Lj?>#KY|l`c9Qjs9_VARw1UpB>$ zj9h$x$HKRCu!ajH>5Aw#0LpqUNV`5=fvkD@j+MAEd=vOEbaarc(y%FT(WFAa@$(xz zfO-wMzb;PRnHZuuOWPcoUF+9#Av@Xq&f_;J^R0&OnfGI{HX$F94POrt9-tjNV#U$K zpujifpVwMT-9C6MIOW><&V#e$6spd5m12=lADIsrP*xqb-+4PCiqimxZ2$L+{GV;# z=IxEasjkJ<*dz{xbIb~Da6mWYc($=QHO}4ge*8WB`r#+wy3rEDyRkV?&SY%a7PfSZN^r@mJPGk6{Rm47M74Oh5HN=ou7C@Azlb9Z-7ymgCf!R)aYmeZjf zSc9cwjTclS(7`bmF0?YXSKKn!vJC_YB|M!Z{wGKKKcU7J1&^o{P?F_BQ zNICzxHzCD=OQVA=76+|1BgyHVo+WVAK>R&p4uxB}v1&*2yt9BlKH|mQOEDz65e00J z$smsVbqNbLya`Yw;^lT-HMU6y{CLa4Q?Fe=-a`+VF(gHQIVu-#EO@uS)vV%;!*Lgf zM=(y!{Ib;e=!2Pfg#u`D11-TF3Wu1|HIS$B;a=rKZ8t-NmRS3$58mJQGSf{Y;}i`* zGENNy3Ka|p(eFZ`7Eqm5>kDQGXgqJbn=+%29K}z-TsHVeAkX2dx0S8usJax3fm>ij zg#-F3@NbUIJ`-+uSDhss;T@hz(*v6D8?;ab)OFyWqpscXz{w4)&Mz?_m%`$Vs~@ei zH9i|??CfHucTwl~nHA6{eo)!~IuuLJy63hN5_xrEHE4U%#Cc|)(lOjfkG>X$BzgwB zG2x#ldLYuH@!|OqXyQX{YJlul$cdTU?-L4EXS)&_73tTXg}YhbJb2=LinVvtNbD5c zm0r>KCh_#+YftO^AqI#K^8G2^=HmYn*gkq&6{*Z+Sa~b6e~!L=O7y>De+v^hiEn1h zQzXH<*^V;3~!#j>KHNE#sgoJEY>niOHPPjG)@BM(!i?>Y&=*<*WhfyWxP78h-2N zFvt*&+0E>ooe|}RgubA-I40B?!pJs|8qVzxCV7~c84Lon6z<-i7+yXj*Z6@Y_*C3y zo5NzB5JA6uv#@OXe#~)Xm-qRG@IE$>I{9~UoI|)Cn>yv93<20f@N>{U=O;I4)%3%2 zab;yQZ~Vw9ni_+9*in@f@R?qA)p{R;XyvPtoW`@~m$l%xn&_Lhv=G{o-WJyg_eQ)rU3!!9&(E@?7CKy`nhy+be6wwIZQr3(*NlLJs+(LF57{EC!5ot9DFo8Csx)N->&C6^0deD?9@YB()|a8>@R*+8}{yZ54kpR z=W?l(m#_bDgVcb%!}A)`4qJ@N50g&~pVUZ5s5#^d6sZL3bRD1{^vk$s<@4{KXVzkY zmy+;5t8j!b_pH0+0rWUXqzHbMMhX@v#mB9)7r}F75qEV6YavR|s$B1g#pAd^9!K(L z`!03Fa|l+p|7`Xet;!oEVF#XNVk<}IL-W1YEdhBw;|$@3IhT&AOdS+_-LNWDr-)$I z7h1IF)wYd0tzU<99<=lF5%TCNfzLYs)a@n_x)j(dp_hyf7ctRHoP31A@ZG!-KZ~qK zRIEE7666riN1TMq#Y5isMxJk2xXskU;ZY>HA@iH+O92noW(5o2B%$pt8W`6-!VLCkRoh-1VfT##XHv*1M$-HaSshH*B2C4R)WU&J~-dGk!$H( z2yM*ifM6}-1M`^hGk<2Hf`1PY;W#3tU7D4-zzDyB=hC=&P#6$glCP}Il9z{BCHJ|B zzy~&gN|a=vmD4-IAAb;(hrn-(}2VuM;Q z5Wc@~`1-#8!9Zk7yaQhuTSd6dZ=A0`KQ8$Cx!$B`%FNAs4E4hMCXQCQ>%>!q`Lte? zI&fi1?KQSApKK-$@ro%R{O&P+e9|j%q`)`<930eUq?~30p-M_;3&^ za3-)=!zj01vE`OR(3BGPV(*5FY$89)MnGD@&|{f1-67WhG~3Cb6*sS|}qLdLFhBQBe!3Mm)}_fSRb?RNpwG>|TTL4DV;X;#37 zjTTqy25W0-VmoPLY-8ZgOUk%?%3rD zujbtSG6?R`h!1k}tKX?09FYTCn1bLlhZs;m>ogVsvH;w|hsk~OB!j-i#v*G-u!5Ab zaAYd#ZhwyA6qXYFv0%}Bz5JpskC<<8%r)`)VnYv%OP`e!22-@t#92g+_--sYBswJn z-&(`sqi(6gQ@G*7@kh8?fFeVApeAyDlor(u7{EfmhiDH26qw;DvrE@q1AVZ6MRZL{ zyvy`?=zct&b7VQ6(&|~gd0mnu+elOp^Y|ENmBXO+=Eqw8FY5)P5r;gq7sQDh--LQr z*TPrc_tX))`}SPiqrjybAbvV<1ZtnCVsQV3qmjyqH`BCoapt|wiAio6DpIw{AuFI` z!*PVB!sN$T?%AybkQF(12jlldpkb(1>7gu9~nk!06DHJ47&0 zgc`8NC)puorAdCmRFT4>%SL(F*8EW(F|i;IuS4>wqUIx5%90!p(6!hIcTQGvza7FWux%Un)`d~t%8yY^+J6&Z|~8zKxM{M2QY zE@y*8Q(M>JXFlVX-uH6wbF4HmKWNt&JaPgf{H}+`1L^eqSJ}a}%ThlyrW>bTJ&$}|8<#*R zd%5UXRBC!Jg_g!Zkyj@rHl+O#D=dWnWH4BG4D#8jbT?oj&B#KMhPcp0;FZMU661eg zND;CSOJ+rKkYwN-U>QqP|~OTcmFzKXEihZaFh*rf>tEc@|5K9^_!)qPkpB>goiH7W$!0)Y`v0jJ@@BFw>)PzNEkfP8)zauK0iDSh)^jIEyz4*g5>4dU=y z91L<_j=i#RM@FeNGF#*$ZhvO-^Av0Tv!7jF?Mw_GOgI^Ubr+ku0KNj|&XDg(d zmhRbD1JC{X(iT#i^KY;EufH_!<}c2od-bqdW_BlFm#GUkL9pCac}FlO33OH}@7>Oa zSPMbmfvoI9HTLl62ei#c-4OD$J)a(pfhBRD-%r)zH=~{|b~@s7?fK8|n#^r#oU{-q z`k7v~_D-GGI8HlRfaRQ`_vl+fII+4IXOqK&9_uQbS<L1Vh(v(@1A&==Q*k00JA_&UHMelp>EClIvdZ98c3}>(M5y8J zf{Wj{UiuZGi2+vXo?rja&2cu8l=WEIQQ^#Nkp({z?>8wTzaC>rdVYpP_1xLGw=@dl zuOHwZ;NP%hb!T78ygm7D^43QG=jg$MK>R~+b10T^FQ`^2qOleT zF{o|31vVxKfwopT`6R6v@FaM-_^*3`7Vk&`(S3ACOgs+rSGi0qUybK05gwsjW#U?w zUWuy(XP$mBstsi`-*iT=jIQSWIEn>3FoYQdAakS63Q(b4M)ni7eDonx7+{v zdG2+?vJ{jxgsl#|C1RxGGryn3f9c2&`DzZmq2pdD7;>DU*egV67sSv~4)1U}JcQ`H zrZ-Q!8=X-a5S?EbesGY;puk*FdfwHAaKI#Jl-8+J%rowm0MA0UKD~p+$uH@ge`zI9~y0 z=GHHcR3xQKx^}gOti|Ri{`&Fa`HT9C-bb?o6KV5xbaawx`{Z@X%qzV&(>_xXw&%{* zxq*8Lh77mLI_oo($t{tH#OM@;DTnp}hiu^!dl6|G3lRz7vXlcLB1t2Vy#gZAec|kH z^ycSsH#g^U0)U`j8(-{o!#)upOu2x~>l6|mUfOmVDYxvn zNwHtZu2nYqh4|SeP(GHoc&H&Ac#9t@)X5xae97_o=a|RoEe)b@CwTT@#4~UvP{6i~ z^7_)SPoQqZDRb0nFA10ioE6yI5U4w$1}|(bvCw^K3@K1(*~GYz3_CSjUiet8D_f=K zNV(tB@!=VaRfCs%Byon!SpmqW{hzi{W*%P6v9*>z5|2NMY$U%&hz?Z&dwWrqC8RD1 z0XBp2_J_UBkOU|wXS35oww3G6m7Si>KQ=ZNLTWsf+w<(1*qz5ce^MeTdk6TAUpfzk zF>Kwv>^c{`RNbjvVO3ADW7z(}8DN!2vJ(gPBN!O2zqv*L6A0Or?%YC+Oe1{AJ$i80 z;TQstea;nuUq>&z&dD>-9QJ%aySDjcb>q(oxg5{9po7{Cg5YWVJAtR$t`}}Ps;eNK zH^3Fmsd<8*YJ<4A+mDmW2RmYC+jC}aA^;A0DSN)`M#*S~=?DfgvfT)=J*PT6N(SBt zs7QxYsTuGymWVgv)l$T!A?+y_jY9zf0PH0gBHqYEE{$r}8^s~s2--Jj1~l;-{j7{h zg`By0V2Ar+x)NyyY=Fi>-e)5Wk9hgP@yp&iRGFuhT?2WncBTB09}2T47Q4!5s;uA< zW|S|$6CDN*$jNfzn;H?s4M5)%K^>}`GO(3uxj~9O`+h|M_FZ4-_-^-^P{mXd(82|4 z5bmN|!@jw_wUmMI4*6K3vbY|G<{2Z@!>^$JcRd`Q!Jsa$lxq?0D=YMm<;|UYg=jS= zsD#@ShqH%eY>lKObLHLxDL0#?1kFP^19x4ZhIEfBh^C!ZK6;%DD$#%KcvNv@s5gp> z25KXC{6ny_uNA_ADmZh{`@jmJ#UUkdyws*n`}SnUfB-w{{ZK%jE_fB{sE*^Lg@%qj zZ#cap*$FXI))M|j(!IwMkU@*VQ)ET{P6o{^zYA5O8K(ZvhvP1~m zuEpjzHo2zH!VcfuSG=b|hyx{fQ0>t(CJ5_8D5NMPHcwlE4Td&+6`D{7*K+oW3m@hk z&9sjswNM85!22L6xdWgn&*9%>*>KE)nOp@Y-QgFG4*=*U+bGjRwf7B;LEGK`4y$t2 zl508;WTVig+_N9$119n*c*pVt+M59w^f*(;K0D;oCTrK~?@GKMQH9z<*S9jeuD++| z_R`YQ@w-J(99CDqhC!p)Tpt@?Iek@uz+|Tc-*N+%_WFS;|lUvezuEii05USkqT|;aY01K!zyA#BdkOd1%T607i zy3F8X)<66T%T_#+z&siNJa6^}a()*P!5)3EypdFV6KF7~Kl z!Yt8G@6=uziG}d>8Ki$>D)t0KntFIh;YiLPEuTJA4?4khAe+emL~Fc;t_2hYNijD7 z$)~>oSrml@m_;l7O^FEA0*+~vw^tSbdkUD0i`LM7*c1TvsoMv4nQR*0+aL}OQ|cpk zOL4O7DF%Fpp+916^WwXWZMC)XK)77tMev_!(Ql z?&d&<#JRg4V0UFtvA%gpkN?)V^pEIGIiCNcb^8u^wu$o~+7y-%BJ-fsRYGhX`wYckzKb3>nPFROBdEbSEYd16W z!$PY98IvBQs^%amb}BGeC-^t|_o)LQMm&N@H`9cJq7m4pHJc0ACWs2p4{9KVGzRgR zLGDaVgULctXUWA!)s2TZHPW1XTY?yU_CJ?qggL zm@_t1>&y0Mg3j>8AFX0>wWs+oG_u^F@bM%Nr&M8yd!idfS{g;%J3w7(VU3bHiI-`_ z>_KRoMPGr0Ko;qa-riVEWs!7w2Hh|8E44r=UtLjf?z$bhEd-a*WL^3;W(1u(k=qU$ zm_WL}Y7vvtB=;!X4mldLBHp%EJxsicO?`@*)!H%^Q>buPzr~! z&+ccpcs>AyO8$_Nt;eTF=I2??AEyC`Lp7dD7=Dx%sz^*@@P zGP{EJ28jpW<6Be$hM=z-=~fLy6nJAKK7ALkomX{up2$1Y=wrVL-I4Uvlxb>(%4pbQ zGJpyjD7m?sHGT!Wi%5bOv4+TGecAy#kkB_?mvC&875wU6cjtRrC|S)p-w{%X35MoG zR4PNXJ2yg6oVRv(`{?Qrq?0ViyluKThe=*QwMG-FDDUfD=7BL4r zP-o9Z{8*?^jenU+?>c7q3jb1^y+fBK;T(PZ>wrqWGI6jMv;9TLZrz3oqBtIllcMi4 z{AKr6>G?OU^HGI;PZls-bj8DxG#CrqYx z<;ba%n*}AE)Ky$~Tp-|tyKIAKKy#lO(-AaB*a1mR*sGY~@qLKQ?u&S;;7%cyQ7(9I zNClMnV2~5zuFf1}ls*3`xAoW0?V(D0MMGxFo>8EvM4@Ts_PzVfU9l@4iIMj0p~WbP zT1{pI&^*c)cgCB~QFk__K6s8x-kBVYebAA8>PuezA}ZeTzD0YJY3E1Bza){+I?j8u?3oh?)UP|02V zj#K@%GAUU3aI1`pRZ3IFd*VQ$jfgP;F4z>wt)ZKU)%W?rB3C?-+YVY+IaaKX{58Vmo?@NpC2z=_D z%}=qf_b`Rlp_^Yn4yttB=AS|FrtErks7;D!h7=B!L*qI;H*HbN2T%f1_X#*L4;}&F zZfnmj+%*L4j_RsDgg8JVG&Li%_gz1}F%j_P$69~)NQHGwc40He*5mz(LK|-`RI*?C zI!J9x`{MWBP;qz*?`(%1QpC>1zq&~@auYrn_??!MJzy^sAgd0;{|QgSBS}nfI1&;Q z2OJ3*#cOE7=ND}wnX`Rw zTJ<^5hV#WEIaVB#|_iuEfdSr6zD?|ky3zux0z zqQ}cJ%M&NI&ZQx=X?eL2RI05xX%fDE4g7PrgR3g$zmV2Hfw~77I1f+%mfzy`+_^i2 zk?}x@ZA-&v)=iZg1huQNj%8V2JHpkBF7sfDQ!WdO%QM27xHt#SJ> zSaLT5)f599?!;^^w6gfW37OLZ2J3w!y*}=_Gr~BOJLtZWL>LD^(+ntML}|OUTtR7T zKxvbd=0Tl=5T!++IOl12Gx>|^-EuatjgfGMoNKEIxs>;W1vD>txsTVCLJMo52Wu?b zbwF@PF&@PR{=V+_lsXr${?v&d>juRr+~fS3!x$-ev@Qa+%@N)pXfy3NXy7ylN*Vp8 zR9diC(4Zyw?2HKU!W>vdDl|qYzsH3;i1DSTr$d;@b`2Kzcni8|ph^>7>!OYQh%`m- z4&YQl4+EaD>?`DVE(n#5z%Lv@1pM(*q6_<j8#vQ40|5wmp|Iwmrv^(2f}@IJ35LRPnZIf=Eui5 zZ3D9HtJo(AeF_*F8d_yEJE=FrXiHd_x7+l_`t`@e3rAh&^o>)DfrgnN;NX9C=aY6@ z-#Nfu3mr`@=^;f28tqLQEnpo#cwE%$Y5=kY&?Qwf+$1T0t_F~JLoG^X*X8ijavn~g z3nr~Ma9C?tevlWgmS>vS_T|`&H%XNDhwjv4Dcf{ z@P*(#g+3Gp0OW-|;S%vnkk>UJd7XkBF*i*uLKc>~?Ya{fcj7|EodlE7I%X9qn}RBd zZQM?+N1B5chM!}p03UDnt^}l8>YJ=21>iV@l@mYqO?kHtYL|i(UpX^iA8yJ&fYt`(F$1M+@Rv+` zOaixmr|Q~QSv*XaJSF4Z8x_(3O`gy{YHs7F*0VhzCilYIIzUT^p$pB1r49I5$diztOd!y_OSMGH% z{1Xe$fSv{=#v>mb3#HAF^xe`XAk*oetTkvv-IBq(+E~gQS@sP-^)QDEZfoXV&?g=Q zH5-h4w|(hPWk5$Os|+V6CrwV88QIqUpW6eb=gRCi!V>|CA{D*YXj33Q39Wr6N$VeQ{A2ylzuQ6r02Cy{t16WxV&XFntWblOm z7y8obVq9qdqNVfS0Xfu+0CEN%i3#^pJfUN98!ow3QiU6%t$u8t&unWQ5dQ(=mBevtaGA&gHIjLYb;RkKi_;=mT&OmtHs@Yg_0 zQbGbc$}kcELug19s2+6OJoV;tgmvgc(Ah5i8;^yRpND?#f=k=qa-Z@Mw0w|Oa)JxE ztFxr8vKT3#TywR!EJ@KB2)S8TB}9peL&(jhCZXPSc#Vy{G83X;vO3^RY8QUHu5o&~psaDv=0Ob=n~W~B z{yRSQf=eC#OpE3poXP#u9Lv9x0YYDs=5AjTUJFfmdE0i2D$Jhx;BgbTo6xJ948&Td z6ze1c6uu!7%qKn7m2pSG748xQ5Qt!28v+r{fGuQxlY%{46A0DW`_gd*AQCPbAbCL( z5!Fq66guakqKd>(U6GnjE|qJVFwUdPe(OHMr!4~baz=R5+`I5Jj(c4hGy_Qbc(UG! z7kgd}o?B^>5!4K`40yJC9a%_5Nrt7N^TpAoq0vVuk4vAagTPTRV5bnYaG02sN-RXR z8G(T1jL^)15a!^&fKPx5iZX6LtUPr1mMP~nFKBMe8;Y#;ww>O^9`Da`M_=j4A#* zX`LPfKCcLu1JJVO`)Qi%!LR!Nhqbqks6At(MY?-|h@iBT zbhk9pr6M5$N_Xd?yIE&G?)|>|-DjLJzVZFe8DssyAB?r0=bm%kab4Ga&$?xhAdr5P zJwI4k3BnbN2Hg^Qj&J)Zh5omukN;})`2T~60x1y2A1RQ4%zRi<^~y09Gm%gV(3|P+ z)Gy(L{2Ue>0xT8Ox{fXb@R*)`u%F(6Bu7Ce!Vg3(POt`lvIq2!Odvq%e>e&XI0u&f z$)5)zg@lAK@YjFxTJ=xuvwoKmO_*1e`{l^nAa`SCkPDyJy*RfJ*jS0e{4e?nY{U)X z=l%fb1Y4(Wa{!L-m;;bdNbh_CDMz7g1))6{m>{g+Mb6+kHndw0`N!3@C2OyXR6J$wQj>L0$v0i-#5x-I$~$o5c=KGZslf(Nil^Z!l@qMgB$ z-{(RibPR1zNVTilZAUWa>(VWx1sY5D?w_AhI{RmQ!kaKZO*QAW(}MbvcZW?K$XPa- z=d02MH{A5Ud@QpDtsh6qMf$A&5LdNB2tISrAQjMPZ~}|1bYKTad<7OG$T$CEF$eIw z-eZX)-&q{Lq#@!>b>D`ky%n3*;&d2dYsE2|^gbFU|8U&5EM+b3I4`j=UYYYm&+4WI z3Qhm+=kEUxNPiwQ2ntpiaGtdP6b%w|;Fkp0!NUE2FnS6}x99?aq^Lp~wfP=V_eAAT zeMx&~sURww%lS%TZ%&jL{MY*l@zVbj)e3t0hv(ui6kWCG(*FfKq+sp>Avz23q6TaV z9s-huRHh0T!as@I{4J%3=fj0}prN7cmtTQ$(g(*3xT>;shF@(Qn-1H87|=|jOmkIu z?3(BU;|3nS_0)yhR5yT7CeRq@E$L|d&z^}Gue>a|5!NX+;1yhn(<0ajq5{4K5F`Mx zGy3o9Qj;F|Or|YtxFb^e9aL?7In^6Uu`_p@EYhsb2#U46ACPd|u_*u(qYyEwoF=5h z=P*lo>ih|PU%Sjme-a?OX~G`Ntau)><4C~&X{(!^?JYcVURDrvU00X7T^u$MYzMrr zN?{uTlaj*e&P<+Rojq7w0t*(DBYeOjb2GNYC*I?!lKdkH+T6SQJt|4skaVu4VTmWpIdKfjXMPD63?LD=-3?u)#) zI81kdFK2{+q4fBvF5cB<`C-UrgK9`G(~ zAW>?2Y&WSr90CJPJM%_?!C0h9yR$73O&@Y?gu(QMT+qA#q055XHA9-bcRN9NK*QXc zsFwFXTR`(#e8&KYuQ2kMj1&nVG9X>?3e-<8fhwv-UuCHJ z1*)lNNHvvR9xxV!EZybDu>?!j%4`P+d}H(FPSXWRdOTe~fH}%=X~!RJD*GN#S@(h- zOS`lw?k-9OsKX8r4--{PbvSi88DH=ej3k1?seQJ%$;JJB7t8Mw4defW-5E$lX!M#Q=ZPb&(=}w2=fTro(X23lkHf{ zmB<7G7VX$r&=+jnRD_lsx;6gUkOV>U;jZD=>`w7~Bf%n#&+ms%5f8GCi_eS1f}SMy zbaFhq4~RgGL%D+C_?+Ijcdssb0fh2v(yhv6fI8<+!Dtzij&IlM$-;4F`|wuqBhO*$ z;N)V5jgdkP22KYm3d4O$SE{t9gvdSUg;PhxVe4${ndzHNpozqyG0e;KSfvT-B0&Xqm^QP4EcchR8936Nm&t** z(SAgU-@KU0)>g;_@Cx95qR2dyp8v^SAQ@mDrynE%_>0-WbNn2D1J2aKRZ4~*=WY~2 zZq#8`Gn2ZD#!#{Bg-^Q3__Yv&i2Cr!uLs|jXSi6%+Wc2%g_qx|QsoZ(s$<+dESK@i zEU*1?I(L7}bXc-eC5>4$-MjzHqj;ANSClrV?OK)Ed&O;{!KZG2x7=pVz-0S)PAcTm zWr*3!siW(WJKS}`pqrn*m&UKg=vP&eh@&*=((TPF!$(Un-X(djS3gM)hQ69Qj}cF_ zVM@8$4=L@yo#Xejn#iYW=8?VJhFKoj&0a}$X>YbP`ca(E!5UZ39dEkFGY}7@?P<a!Oa}vI^*hc4#Bsi?&L-TCx`3n>qBE8 zj{8ypg58?61cyKJV!v^)EJn`9|e2K^z5kmZ8%#R zJ>peQZ`4j`1fr)}3o`>QeqF8J1X&oiI!G4d=0{EE%`33vDJ8_M(bOB33AWj#doez0 zZN&N##Dx=s%cA^#qrTLr=tDT!*~k6uD+|8kXE$Lq{*+pz#d79%aN~q{5B-Jx7S&uB zTXrRKq7GB|<4I|n^~!vfo%CxK>z-e>tfwYCx~dF$Z_DGml_*EXh#>L0(iX~p?R&Jm zbTI!!+PRD3!cncES*7ITXs%X{NxAipMV%5XcA)Im?L>1onFEE|1%CU*Hj%mq!P&`_ zLj>YPIfYMUIPK1-@!_=F!>}c7g?WHvle$?WL90?8;C&Tvq0TNWJT)j=sQ#O0K!kzA zpr*o=QV3v7Ehz5WlT4=m=XoOM%dw9fW<`2Q3Zx(eE^)TSr&n+-)qFz06%xM4U^@1_ zS^94|m{}5Z*xXJ7uy(!M>sOj5Z*;Cv&P`7@^H#|3hEGj6OjT-G{WN7AfIUj;~w0lbNA3et@*kP``j~pzud>YiSHB#74CG~dRTPg46kiS&|0yiJ?W9x$-v?!+Qlim0jA=T>c;F+|-I+UbQN33_3ga!9O^?ED-s|ZhRc#+T2vIHPJM1c6wL! zgu*;+XdQcBN2If!;>*C3gdb06A`>m9Ol!ZQdlcRZ zo=i?NF{N&(OnF=cE8d^p(1DdD4zylMnvkErb80U9)mh$F{%F#hmo=LASylLrzZ_C8 z5Q+BwOyVIFl&P5vi8U$x(evDhw!-hy9nyq@SrK&`Gi|W8qH0})sUzTk$ms%skoO1o z_v!fV;lK@`G-D?|I~r2UbC_$ba$Jyl(y8%>37@Pd!~k<)Xz|JeFtNg^gmu%s;rElo zVgAic@^+@Rw)#srifoL(0L)Uj{SusLs8-=6{8J4af=K_-K`9F2{&G-V(J@%(4ey9> zEEUlB`_BOba+qiM44&-?BJ#!J^k^O->dj*IOBkXsyXQC$Qg-2Gx8YOpZ`5AuoTBAs zU;PanTV1^h%YO%7nE8B%fBxXVA$xGP&A)OUAdDbOqw##YdR5hNHWsM0*|TnaC$w?f z|3)R4;L(NH{ki`1S650i7ygrbbPawik8UgxehRCcHzVt6q6S%0-$_ZlQ;UVEj86BK z9_@}}N#x{r*P$70n`(=~SL$x#z`5omojsS>p(569!ZNrz z43}{u9iO~8{7Vb)mJ?UhYD>3P`6a`P{VhL_dr={*pJweG+T{IB>t@eJK2uHhVu|kF z|F!d0?Ar22>bJSU7f(+u&KH9VB3cg}ei(Sk%w~Np0qN`m>A}nGQ!G&;6aRC|0T@Ma z?5xbqgD+y}j4aWyvF5&qs==tkcHTNKeukF<5`6HJJ`V$XHCT{KSsJYz9Y^=;RqZxYO;3U@#*`J*`i6(?q)FiVkoEG8CcJmPxiu4UH7;Gv|6 zxCV7Y+RN)fd}#H^q!y@M6>_+##sMVLA%7 zL9QtL*(4_a!PuwxL;D8dy6b`6HYOHIQ$e-MwON|`11$)Rowla)gC`d4RCPp?1&vx? zcw4CZu4l;IHoM2laX9%HGk0tp7mP(+PR}pmzkHt^H5FA|*b1j^piwVr!YpheE#!#V z-!%0L0_?@hEyN+3L(jV|8q?gw3%Q$hhc+l1;_bBcoxJZvQx5-j6}G#{L{VqUy+av2 z(pI-W9*|wT>2sR6yGngCe{)hIL=goOs9G^CZ>;_{6?JVI<9FA8S9(d60OYX&@5)6p z3Q8M&q$b_?SMLy3T^uLiaHJ4V%=rbGCPcBQ^jx>-1tDY3CwFBeh+g8%;WIctQBzCw z*||^F9eUaBw2uS0(0WhhnGxg#$MLh(G)zeCsx?Z0juIZ-zLIFlDV8x1C$<54%S~cA=5#O~+i@J;BO^N}MHh6K#hwb;*5Xz(R3~Czh0sPtWm?y#b6X6S5~rmwR{I42S!49jz6EQ$gWf)Ny`gzS#%CkLZ-}_`Z#?pJuGlXhrlKN=@>hd$9ShhTB9&uI^uTjxw}p~u9kHX3biKLI zBqL)FCr^ayH=E23qs0L>7d74IQ>*buXfi^OJT&`E&g^it@1TbzBebzev8s+x95je8 z&-(1fgLZ!dHobldwi>IGqxYb%7f--(J^&!nmT?>Ip;n0G*m}>PvdFQUI~M z^4p+-z^^v}s&q)Glh?mloE>^$dWj{urz$f!LM3K_m}G^e6;r?E@1Pb_t;NoV>=I2_ z-{6+PJU4ZUd^G!>ui>3XoKtXZGTz>8B*RyIj4N{`f{PX20C; z%<24@No^%;n@}x!jM}>$U0=Y`=UE4<*U1cS#73dRX^F!*%>?~e31@2=nXqOgr&fM< zQHfjeaj}YMyOJ_xcSt%-h6cf9rho#GL+QE`7jC*6IF!DM=80dE*Y*pFaD9GUSnDeWH zQqTl2Z^`pr)Mxs6#s5v>gDYOkHoKi0tNLh1Ia4YSUdaS1(tv;@F>;9jdvIv?J!sgW}OWO!)gmt+ZaA;7BI5G_tB#5QK93@eI3D5%X z{y}xEHryb`!5YEtaCM#K^Na=un0we9yVd)AkADzr>HwzRc2$iV;gC;Smlpq9TKC%P z3^~_3H_*FAvSu3;65Z0}*gVOyZ^3>yok@}q<&^YmLUiYkdP#=mJF8A6$-T$~Im_?- zxV_!F?dki6iKWrUDYe}hDg36s+Z9Nz7I#|YZCH`yY6n=VZZPSXsC||Dwgk`xgtQh z+X85_9bRNPI7Jf$zkT>CI$oRMXeb%9tD9lYd*2|eW0W=s=^ndTGQT2&2}`xu)FJ|9 z!kL8#PVleYXS+mEvI`Y}o8G_=5Do77aO3H$u|wCXik{R2K<}m?*SXcJ>1CJ^fTE5obGln?6JJ#$?BTb&mma zn15X_^0NDEO{P=t(ofJX|HyZrw`Za?r+s|oM?!ox1#jFFgEWrB8XH{}c|Eg%! zJve->kH=OZFbQCtvQ$dJP-izSuJ~1j>&Bq0QUW`<8_ge8f-azxDl`*Y@fCowT*BFZ z3z`BVper#J9e`Z!A*%;!RFAJG!G7bE2ItaxD_HNu##+cRqU{dez~=b3uPh`60@|5l>g_L*jRB2^OZZi0hZcs%!T z&#Pymp*kR`(Q%~ar2i4A3 zOW3i+aqoX#i>Q#+m>SZj=?0}CpTC`(`g~b|X*9Q1D}QI>R&v301dQF3^O zYk3`zkWs2>KQX2#oCVZAz4BGuPT8a;UtY;6$UqIY~!` zeUC<=_|MEWG8&m;Ftj$6{ud~23Y5Pecx}xzjn(Vyf-#yh*VU85haCVb^aOHT%(|t| z&B)jR0hcJ?UP1?v+eR~!hAsTMDUA%eK(o2rL8$haI_>+vJ2?nG@>t`59(_E}!w^Uo z{o=L6dvM%y(bJznId(N_GXa6Ry|g@x`S$FON9dcwpXi2W{W)H$mINU~$i|&EkF(!M z0GGHg-h6B`ENJSfr>0-OxVQabxAlb0Yq@i$Q{0f8Nd21YX{)Bt?932Zuiie{`O4C1 zc0VsHsM}#47q&&$0FWWslS)Ar`DBka75D84^O{N_)`vEWL=A|B#E8lt3HLeL{0{=- z?i)9DpUa9Ne-*f)-npMx_ma5o9j7q6af_}u4wZ?SAY5>cK|XTWGRdhtD0a5KXPhn= z*#r}lbrsATBYPIeP`6$RcyfFKRv@LtENTA*HjYIg5HOHGLpq+r`3%S%9ndz-o$~vNc`kCl z9Q)){DZ6ud;dt&h6=rB|$OlHhwFnSNeA zTk%`KKhoCWWP=Ih2~FL+CdX}J2TQMI8`@62)y;NquASZHBvG4((d>r`)S@n*{!=D^y@Pr_kc*8Yc=R+x+L~_LxUUo^U~Oez)X(@mJcV^h@cmAD$ex zTHC`e8@QArtn!KX8F=Q%+5XhdT43kdRCIUYrUI@2!W}`ZCH+`+Vda+?a`9vOd)vJ> zVZbpQY*ec)*M^-29oBXE-;cua>m6@vZL1BxH*8vb_TInDW|;xE>|YqRni<`wcn}OR zXZ}dkC-8T(-B9PF=kw;M?mBhOIy-QCtUij-#EGy3GCS2$<0q{?OMCn`BP6@tIY8>2 zkvKi}Wc9l7=ep_4!n3R10U`se<8`!^EKWR{hT~6w%=xi(vKG=ksgi;b(^`?b+53r+aogr8VRK?sZSOABhxz10QFLPwJUDCGF`)y zs~L=0C1{`vlrubh-{B~G&F#+E`ZZvXw=J?ZbBg%dDj>#F}l>U^6_9L z_n5=u$%~nc29*)Yor_bQ!D1(dA#h-#S!Vt9-ujB3Vxl0-^N(T}h;lLm^w|1@Vu4~c`V;LXgIOAg$(yztdI*+xLKW=R>8Me|>mxw6edAoPe{rjM2xu}!>{LlezcnH6J z(bQ9$$q<& zfPy+qTYM*I?ou59mSO`}+6q~IhxvnRs|Yd=qIyt>S{&8Qk=xyw^(vVjKVQtXoo+oW z5dRtH(Rx$c{^;U#my6VNOC{z1AYW*|j|@h;&E9i4Yjc6h`M*p@yBF2Yp;yZh7nFHR z9s{`&$Vfu_-sz&}0=c~e+b}sK6;c4|TZ!MEaQA)Kr#ae%?J0=w>D*qVNZm@rTuD7= zUknPt=>fR-2R91-o|81;yg_c_T)7c8XQk5EaO*6Ch9ZZZa)UaP+MRK_5FF|_%$43A z#gXEBH1A8~aXvN_J(LGrZKpAj_M8o+4(ahT-)AW{1=LhoJB9Nlp5aMa_p8eDN^YWc zVD@LQlYV5O;w7vl8obx2_foU(tG_3H;^g5`mz^gW_9RO{_I=Z<%z9Ff5ahc!-!-Q( z?2&HPRBxKLtUO{P+Gb8?T)g=v_#IYf=h70Zgy*KeOnK}^vuj9MD5WqDc1FhC`sNkg z7D?H4bk~sGdwZmW6h2x?(VtT(Oh6mB$1IU0;Ynx-W0XMt`_r!+AcbVz&uJcpjZB0o zbH_4L5;=j1fcx=sgVWWYqt_KD?p8eiAu?3ee_fug_Y=+S{eC$jOc(M!c2wsFN6xG zitX>vx5Qnja}vsGLltC@=}9tUZehGgxfP5&*^k@8fCN3mzdpZ6z&F*z<9aW7f^Twc zPW^3ujhCq|A>Lq>3myYUIe;DsO_kc11&M{xacj{MTfgQf{UbqliAb9N@pmdg->E`5 z!;Hkj>ug2&(G3D}PXubg~K7CBiJH;n;lhM?Z{ zkgriK!x|q}`TYAs_pPVK&iniOUtXhP(U#`}Q3@v*k%E1o#DG)^8Q?)uCe7aq3jTLT9*{Pw!vornIwmVE@73KG>tS>}wHKFF=7)R{}UNhFjaqN3d9 zo!G_)3n3=P`7_Ug^3~g)X~}mf{$o+I1HgM}+)9%cB1ZDJUew;Oc&I7|ev9P^>o+>^ zhB#oW2*^{Dqhbg=2yB9`Iv&4IvS8VtNLhY4@^1I+MQlo)|R6lCJl|fdOo9_ure8Km; zSymhLQFH>`Q5w6!&ScS2%u@gWOS{BszFV4=)Zm!M++~9HgSAs69G5~0f0gG(x{Os2xBY0}Q zL#gt=PZs#2MiDw(tHCCMIMCr@(L2(HL5ItQS* z^!=^x$jM4*X<)f4Bm?Ae9c`im{<%nhKPG)5*efx;DLH~P;2Bf73c|a+z_QGXG*~c# zJ*p}d@EnOnk?|WXI9Yqk-=93D;GU)bI$We>4#sk+QVL2lAgw_w=CT~45q%5e?~e@h z%Jd;T@=hkud2+BPPg;UD$-YZBI{0z0NxuKJNo3}CmI_|*xWAW`0pow(*45P!%KlJS zXcq+bnNRhy)c)t8!Iut@3JhKNVR8)qK)PN|juHCHbA3cnkYGV!CHbY8%}cggFp#m| zy_@vAI(NsE&YBB9IacHT8B`eAQ^9JBm zZe^+m30T_n(JB9x^S1#XB*ZB*<0uV93wc`7c4={6yr}e|2@CVUe&0G$$LB88tw4cK3q_aM3Av6qo$Bnqo-zw<(^K;HGjO&kUxQ`CiD52q(u|~H9V|}cPG>l@ zZWnD$8S3bg_^LWT{=Jv1>P3>QJcM%sf$vwbfZHeSxGgvzA5%3cX9YyY4*dG3tC9k7 zEf9}k=`N$09B?L%QqckK(C^Jm|KfoR@sL5H@)ipUcvs!O2bg+~td zODOga${-X4S^;EX)zq(VMkv^m-zi9RAbUUdUmr0arQ){&CgJxuO8rmRRG>WoPlD7AW3-D6m&~5np58}&jU;X@ec681ob}hcz z&Mo2z58=@(Tbu~f3niQ)-q6fd_m{z?S0(?6Sdw4*n7`DUg3oPY#=3YK(R8}EcubM2 z@Bw`;_sZbAZAGSU#htUhCXagOL%wt|SfB@u7_CX_xI(N5xhBh#dlvoMN(nd=rL!=y z!`iKyP~W}zFNNTi8Ju^w#Kgotio?WI(}XNZVEw6rI`P3q4bB@Mu!2UJ!LF=HBK4O2 zPe#O;sRA6!3m&ecA5M_N5@^USxBwYZz>RZ%C=`a?fUP=8ct7k=)(|ZL_$fP-jRnf*iy?Q;c(yIDUGqf+#((mPS@3p{J# zr&P(p#MJlOI?H3NpWk%$!LRYDTK3FW4wP~q`lj>OQj1v;sgl_%rwibF^S^p?v7%&n zEJ?EA0=W@)_j6?4)-PXuM6_6N&Qia0pU(<-ZB%KrKkK#(I>`Io^73~4Sid>*^YfD# z_~^53f2lB=QPKi_8ll${ulN?%Ck{JBQX*NtM%a80%k*n8P)KLZioJ3RJ$raF{TcU( z`%X343owq472K!OA)NUIAKY(0zqyI!#mnpA;o<4&DPi)GF_RcFFcaYsI2?ax7w!0v zvC;LjqYR*YGND>}?no6ll9qv6oodLS`3D@RiuBg2KL5_XOWfh_eS`>!er57fzN^jl zXIGD1raccvWO!g)%`%ic{fc`@4(a?yfD)^*qZ`L%X6+nwv_L96Pj z?WV=)kxlr;Xpi|rrwyghwtOo*Ozd=CnjyVXt;CDZ_HSR$SxYbZ2Q8c~T1MMrpV$PC z@)kd>!BAzzn_VKO{MfX^b;aiwnIu75;WC%^BupH(8t)#`-?zk19a%1<90Fjf`t9`e^u{&mTa1}*kYPln zkD~rz7>Os4!_?$k4`TqXs2#srF5?*Tuq_tJP9VSta3H-$W!IW|wdl$|S)*foR_ex6 zQ>yjN=Eg@YIWib#r^B>8*9t42Of?drxKF@RJ89xR(M#VrUKz}zGvUo$q@dm^&A;^3 z|Fx&n@1keMaXo7-|iu*MxtL9@Xso#O3*&&AJ{0>614Y&8%dX|oyKy8nT(bCch} z>FTR&xKRa)2ep`{oq0p&n}H7vXFvM-*k|Y8ATNlfWBXrS$quidlsJvSNTOWVZ_ICd zTJ>P-k&>pi`s%!6!buYgBS>%1X^p&UYy3JqYpjtYqft$kTP&T(9Xss9ZTvh_=a`Mv z^kIgOy+ow7=fRa=WU64)#3tY3#dcG%aj}J>lVd-TTk36&7s%pKY43$~i>+)YYcRlM z7zQ!mUn8KDd2TgI zPrjZ!IXeY6dR2~C#dYPLTDW1AQ42>Hg4?{_Y=lO7#4m9BAi|J!r9O}?jmdJVNRXtB!tl$!P?WCn?h0A`SVAgpH@C! z0I9*VoeU}v@nuAyL@k|L{=bSKYG5}3HBNHHVNkiy+SI#f6I6r%qn@Zgr-0(J<1J(& zEMLVBTeSnab@#>2U~jf|#M=fbe&@*Y^k#1rBeFKs;4K?|jn8UMHH{j)DDL(qe)-6` z&GCrSa{p2#rF8bR*qG`XW@B&5FlJRY5fGwwy=UC`c-+E#-_%~fY2Q?Pd-}=DgY4H> z4_e}*0!!Va`~|KMREN%XstA+9r-cC{Ndlcd_d7kgcuXh;Yk3WH%57anN8!H*>M79DcQ!^Lf8epBK}`RCwg27poZsdxHQ(Wev)xkRbw0bY;M-v6QU}oC<@9q$DkC$R z@dMj!$$fwO&t1rP3lmiULxI*7_ycqb@mUpQe2`oQhUWIRLYWWk2e1wIQ0q6IpCu^f zaP+5}HoCDiMy7nS7ifKvz}CYhJTl~$Mj5iEQrDfT%^IzQ`n*>jTQ0_E&akg6lrVR= zg(Eh22YZBqjB7Vd5MwT1#qRgCHY=?>UFu$X-W$}>pRNlgY?!Hrt$0r6DZ8EgE#FwU)xvtGPAk%B`1uu=P~^RGqfi=t&i>M;=!s;tNDLu^9n*FCRUxb?&}m(DHa((7)gr`0|gLWwuK!94qGqg3mB z_U~=z+j=kWT!Q?iYSixi8kcF2^VymnyJp_5^sAR_BYX)rbB=J<h4Y!_!gSh5_P&^c~^oioq=wnCJd8i907y&Wc7KFjuZ|Z^5@V>PhzY+Iv3Gk zT{~5JJb#ae8IkbjwZxp@tbkwN*kZr`^+gsKi<-6mThJcdn1Klf7Uy;#=dUg-1cJU9 zFx-TzD_%gpBm8R&WR>oToZWvOyciX5%(|KT_fsQ)r|M78=v-2UQu2J;M8r4$>w?i$Ymi+8xgN}VXWID`oBNOzSK`8IkX+T=RqT?OOJ$)MP~cs zV1$g%Bj&M;QBOk=VZL6}`fQC)}h1sPp_W@a-H^}q^c$EPhJjQ3`y3OeM zrVZgGoXBikBbd+JXQ2g$}q-mHLT#8}|Bbw2<1#VGjW ziyiNSUkg@cImf&%G|G)mW_Wyw{+ODNWB|c6_^<`sZbZI!bW~GL$YQ&)xmofC8Q7UW zOqC5YRK2~ow-+PFN+D7SY%BRb%4_C-ZYxY^TcNr|Zv1TzYv@E)TgL4}#a{rpKOqcN z=$ab|@_jP3s?=GX^s7AVN^;1VYg__4~e8OHJvOS>cOZn}}T?+~F%cU?YI?F*u zt3ze?TK;t&JcLoBt*@yJ#>+OU$X#CH5OlVR<*(;d+g?4_;< zta71|-$jyC9ih}|FzMH{e*qFUVJaznbfCwrab<07t>6vvQdi31J1XH?0w6Uu|DOSU z0-{KwVW^{9vmqx8T}70*FNToC$BOg)Zz>V$TLSLL8?CC5-wyLB)&eLoewlyOK5lB} z8WTUeEO~V5Fxw(+6;(6N>-YM5>PzA0*rVHmnX^$F1h;3BA4^oT5%0yPq>P-$=Y!lz z(btC!OkYyZoM@k`yzZWRA3sBkYEZ+=d4BBue;5R})xg{^Ir0~Dc&SIDIu(wX78FQGEUC>2pi`p2ZhN3}92-mo> zyz9i+4z+wdWFrV<`>Z5)I!hf9`-H=6I@lnc{IV_!d%m@}Qr!1LLt33x-o5&ys+|LC z=J}(}2}`VbP>%N{F|8cr*|vUps&NbW8vJ~A)6!_7o@3F3C^E|B*!%HF@9Q>R*b?5$ z3Rtkhr*geA7lB;v?2moBJGM!W4dh|uQY7&C;3xa1c@IeV0r-QNBNu_GZ>>5$3BaF& z3`%}8HX=t*+kJgO{(%7k;$o`h=l+Sr&_h^$j7sW~AO#4=sT?`sT}e=>r#xZ7{_of$ z`W{t|tSed$oyE0WsUr6NhaF6zW>kRQVI(?}_RFQ8xC(G{$7c#!PsOLf1Su3im%0E>%N3oQ)=4u~~e-)v^N^~p1Zt5MUIm-iP`dWcH zU%KZqQTBv6%MRhEkCu)r&{1<^0UD9z8)9E9etKrFq%Kvhn%UnT<9WIe^!+;+iI`>H z#kF|%0I(3DWXP?pt;OcJxKR)90-)#P-o6#&`~Ui+;e%DR#;~qm07Yw{qx=sXMcQfb znt1lC6Zp_R5V(WX@(%Sz%;Wb{Ni)xoj7qChvCn%5aGCbFu*(P8!DfCt>4A4c!3c0CO83={>@p<W*QmGI@-vvJYp6YjQqfT ztFy5ME$)_?(O4vFJil7BNWXDQB&eo~`Cfit`Su$X={z@y5CmOPzco+8?~j}oWr*0c zL%TtKM*lIux54@1=q1quU2H+;Wr-tN86C+6sVoT;miIG&0f8dMpN{}Bv{Bg7u$eI- z3!(zu+<)V!0)$AS?@-13FW%T5`*$2BmJf5^hEWNGNnp{pwql5@3);svB&f z?j_Reh@aFiM>OszPPbK^qo1ZdB0S`?)g8MluPQKfNNrNdKHiA+;FPLrozv>)M*(na zp6A_f{=hJ=X=zQ%l#?H0U=T1$fE(PSg+1)+>+9cqBo9WWlk24TXKw2X(REZT8CfA% zz(!wD8HDKq(SeVZcw=T+0G*~xKV)wLEC>%FK(Fr%FDkwr+6Nyaj1MEKNoJi;;c9|| zs=K4<6lq({bD(ofk-)I#6mf=+YFP7q8x41aw}|cG-Wmz(nIB)rdvZd=OjFeHCC>By z$p|Id3g=-wW&PHBO*djilcYals9Y{j<(liAe*W0rRAH?+eS-@Euf_5M;Y(Ptz7EwI zo~XfNP_+|RkJ)#WqT&9|(-7Udx_slU#$KT=!NwOlzSRFs9^SZIs+i}@n!u7MWJ7Pi zHST$Fh1zd=`uzu0Q{~b)#z;St9Co(`-ozu{oNF`zOpz@?b1}~8dP{C+Py{^mzdB>x z>*juYbb8v=;&bY_zbKol5Q7XR)O%oeZR9aykhK~C=1dZFwZuK8b(n2-baT5K(?*2( z2Az~5Hed$Dq{a7Lor6iyN##<9tVrFN`&=(nbS=owAD8HP9$w_ZAL?!iIEEqegbdCi zzNUl4IFbQtAZnRXDTe~qfc`bxC1c8p*e4!1^LJwEsQG*a(#3=ak^SMW$hN2E62bIo_RBWc7V zL9^@vXvPM&Uqg4|l%w2!On`nDX^2ZNw^#xs%yCaM&Az-21&Pm$u_mh-KN}xoARa#1 zz9B!m;X|z%tpcYfM^s{TGN;vme*%5d{N<)g6GL608}MS1bIVF`Mn1pk-H+V%^^JMH zBa>v@mi`L%)`pPttN=vBEf!RaH&AN*Y^keBk(M4pz++q|`2V=gTS%gzs5@fb*aOU^ z5fjnIaN+z(lQYw^G2Nr|$1~%t0E1kgs(KkcG3|FnbCe;5=Ik0+^o})4Sxn8HI4So8b0S0WVr8$cCaSZf zTMWR4EbuM|kwL&^CHZ1vbpNhex_DRI%Bu?RF_UT7!LZHE^kRo(9zE(WkT>S4#SbE- zj~6;Q?G{y&wfL5fd>Jb@jf%V9(@)e7MzK|Z3s$zK8wjfo4cRaiECT`j{H#3y3Ebx? zH_Z4UM@S^F-L$N}p&?7VNK=Vjz2=2RM+C)K86Zh3um~vx=>oM_BA;JXaLLP|5@)~q z^fWYM{()~A8;7&!s&v+(CH_DkhXPw9JCWz<$=&9AI@Q9PzjNf}jq1+Meu&aNvh2Yb zK$eZ>C@7dYt)|2HNFd;H;IK7uLyEII^X`=%r{ro*QJzBloeAe5$fJ**$bhx%yFv7hkh}y#CZXJbTE2rwEnQSk>V1bPle!e9)`Q_H zD&O5{9S4qUzf27Yvj@y`O{b1aJANWQ$Cw%7ewy;4xvB|7UCDYmN;oHrAcO z5>I2*$07?WW(Emh6u31 zZn9Qx_t+SGn9YxS z08|>6Euyq6M)_Ke}i9|HQJbodaXeeWAY)IDUdKx4#v2KH6x!4zwusF;KmQVU;xF&vYhU^?zkyTdF^w$DdR= zb0!KY;sV#xO3yZXlIZBngBjy;G?v_Yku>XI7>$rW@a28^YSobw7tXSj z?rv_ft-j~L-97*Q-qEAZq)%>AlQnaKXs8mM8Sg1;#qJh|TXO)14={@=rD#Af5A*y_ zaZ|QUh4aIGmjwOkRb{Eb%IX`Lo&%lNKfC2&Fr({JWOrMGuLnX?No zJhibsTNG4LLOkpI7%w1xeX{Sas0$?A5s*t?&l7`eI?&8x^T)9&0)kyNvXJ)Iavk0H z@u?!OYx>Wm3Y%f|4rg9*dx^3Z|4Wj1q2rD=e*5Cu2)4NI>;2o=UoB_C8(hcy*=HNZ z=x4zNiqF9vnvDy1;Cfq8?>9ZMEaqUgaCAm2qvC4_tyGLz1!I+q;BnT3YtIKmQGzutXUeG5n>SHSF*WXL3 z@~2_vNHR5s*@VJw#B6PaEvB)xi)OmFOfXSj%l6y>r;iS(CnV4VD+Mu70EB<50Odbs zgOo*qtbhfUh|3z=ZkHcOmoKHh-a9|E{!Z&q`*VG=u+=+)LYNPf#!*29{bpw(4ykRc zC6fR&r*iU=7dXn7N{9$t za(9gMJ5+FuaghvLV1!g)VIPBhln`R6qL{TZFtO&)tY+Z5g8N2UczuiUJ?D$lS;#;BS+4S&jIDxybBOFi3G za?{7*LT?y3%~l(o{4+7k+HZKrf1g2PF;^k+f3f!#P*ts8->7Ur8foe74v~gU2}pOR zlypef-iUxSl1d{WU6LXpqKHZ;prmw)lt?M?Jqyn{dd?m1|GoEqzVU6=bSTT*0{$#R=u9ag?Mv$u*~;fii`+N1UQY>sQ4ho?Y=I2`f_2e_HEs_ z@4;^q==q7U^(t-?rmRr3WjM@FEUEhMt|9|zJ2cX+-6g@51e~in=x&u~37o;R>XV8H z2}>5Wh%2-dNwXDvX_4W_lZ1LdIt=+L9Ojq4I+V-Ym)||$Y&mk}(`{lx;epQcZ^jGU zf5EP}AWQim;H5g0RelCC>j+)udMq{5Rx){e({%iJVy<)|Y;py(c|#XNL$xo{q@>tO zi7p~O!k=EZN7Y(Q@>U33qPmI;THbynO-#D@egBK;t0?O-b}dlJQKXSGQ5c)SXDN5; z?d`35_D-3e9k!2&j(ZTxuGj+u1tZt$ZI^LGfcef{OM@Flz`83nd(+42-R^W}u!UK_ zHLiPsTih;(yb-l;^C`7X)Ys#Z?T$$8i@Q{p)10ruCpS+-iEtpb&x+My6v+kI?r5QGTfF3oqhQY)Jyr` zHo2O)`nn(sP!n-y3Vj#S9Y+dXYjKk6t)%>R$z9Al-&bB~FW@tN^>*n#UA#$SRJN=8 z8-?1Pl^+yFbskM7h)XV!p(Z}7334p2CpH|452$g!H$Hxn`#B1h*&G;@lsNrB=CYMX z4Ssq5L2Fo7w@(|-glgFP8lz4^V(kiR?>NdKRK_9xw+~O5*dE$Hx3@odjepvz+dDgy zOXGibnlmm;h^1Gqefi6iv&f>p+m~lvPvyw?P%n;Z5q7;21#-FfHj(U0TY)3T=d){) z4Gt0$XGU*UpVvHpS>QJOLAJimS9@2|vj?79tw&?WH45LKlF8)R%fd$I9IZ^WCbpN4 zpbNv@xQiOpM1_7evV&!RU+VZ_U_0xxZRO;= z7Et}6#wZSD`ss*kx3nWA%b!>PkG)&&S)QH#r)o4j^-epIl7SwBh34HoDq005=Kek> zqiS&k+{#z!Q81wB>ur{}O{WB49UiD+(J-Pg0cI4X_<4wQAp*RZ!c>p7A+~er)aVe( z=Z>!r)ek>XJEa?HGiI_qddXBjJv{1PGt;3vinb{o;6&&biAzyy+eg%>nn-p9fx!O3pwZS-QmuMcUR>3pt^x`ne zE0WD&tr~JzHF|s%+I^nY6JRwr8969T&;4xgUrTE<}cuP1wc8 zZ{X)WwG!?aO!??U+}C$#eNyZAfjRc`?v}T%Y|WL@=~DD#{5-2?Ep3Lx8&eG$-x$6I zy{U`!)gQ#Ric;@#Oc1MH6_GCmDr*#>!*oMT5jl^KlEtd>jPXq9`w z+r)+v1tGlTi|%STWdc^9)WA0%mI9n`O9mbD9CH!Q3II?J%6bGX0cR)!X04F`i!wVM zbqUJab!glp&ovgDmXb*om~0JM01SERBnQO)>58-sQcOoL2%jVs~1 zp*4CD^y|+rze2*U)#al75h-8vc}|VKNDhTU8uU?f94^5F^CZA=2$gIb0aWaM9P#eV!mwV(NdHe1W;V>;gIhvhfM>vvSs*x&CUpdTzNS}daanj>MDfYqOu5cC z9k$Q@Ra^!l7zc#?2+eQm)laaEJ0GUmOz8c z3?AsWsAu=S_{!<0FIoIIraW%=To_N|E5sUJAT7EPc6O=MT%4~+969DU?p1o{QzdAq zMC)$RX;A#W*y-65yVy9K38Y{Bn`8_;PO`{UgUc!A?_ERJ;)#|xnT|e|ZSOpkskV5M z@4HuHCi#(kShI6auFVYvr@DlUP!5UpJnZ?8n+v1-GJb0}rsjA?c_OACY0_!e%jqw2 z>6tZ-KlK$0{W9`;)!@jQf)bo%oR9xflU-mve9A;mc}?+nM|%CLI(R)v$zhG)V@@zJN-aSO4T`Ffk~ zap4IiuHpsGoz`R<-3xdiQ8y|c@9DUO|7zgy8c(}ci4GpKYO;?0lxV;ny?q>=n=QQ( zp^KQ8ER7K1Si_GWG0Klj>au}>wTAN&v+8`+K~vbJp4w|KU#`-tR!(K6cx_O2xA-fp z2ZeDx37ZtDUXF34RBP-Iw?yMJvT6F5L9;_nIag44kv(=H>fpxmAt6Cs!d$EI?#Iv~ z<(tNeT#~!5rIqP9p(sJxo>-F*01|Xj)W0InhEfaz4~$=)gSqA)45ddzUo~hziuv?x zwx%3wDww=h+1g2`+pHX`r7cWsWdud;eKr_zZ_xvy`=DZ4$7c^=1kg*Pg`0exKf*zl zuyp1ov(wLS;7ey-Lx=8V7h+H_mhdtanRg zud6YzwpADJb*4Ht+j~EXdsdW;I?8f6Bh#q;v*>&OA`xdwLg0u`Z`3<~e}6PgF6M(Y zImCCV_b@Adb?{R0$nqFh_G=T05<}*LY->L6FQ?O!>#k+@h7Jxc2!ubgHEeAV(8H@0 z?kzewH8>HIis45ikGpH(_f^ky zV0m4v^GU#Uqe#>tm-H1^Fe8B2>9x&CLmf&Xj+l+@yW{EWwj^U8c+I0_r6wz!Na&=W zUV0Uc^Ty(JoNN z@~UUT3WTEYXo?f0b%4M(-Zq3#44s2c? zq*ve7+o^(?6y%LgA!2kBBc0>UZ~c^_9&K&JqBX$jx#f^wA8E>wVdKRXe;^~i4YG?i zCMixVJBM>2js6nWn_i8RAJZG%O?1D6lcmt?>JUlQ_PKq^TQ?N!FfW(WkGtR6W1m-_ z#EuhVQ~xz|Z5eS#s;(9rJvf05Y5347Dd)&0(t8e7;;~boB<`1KmG&Ibn_iVOe%PK1 zFCMRbU1#|!P~->a@D~fW{*oK^cH%lmC`TWk!DDM7<+YQ1myIM#LaA%Fpn#K;h+6^K zvE8!MyrO1SoW{x*k-SH(}lmY|zb67-N84GFHBt8O^0&%V6+SO))O7O;Y)# z&I@=sMfCkmgN!5YuM<{bG~!XIWy2T84ITL|+K)O9(K!xvzp?j|5vdHnfpJj&S>41B z-hBGJf5Hh2&d_EQIc@V*JaYNGb%!CJ)bpUVBDTmnj5@-E??hbQDT zfqEHwypY2@%Z?@;a$H%CW!C98)4kNNM}tIS{sxM(AQ|pV+S)hGW@#o)EB;nE+$5QG zw;tAQJQa4S7iEu)u+a%LC;N&^Mp@7W!Wch-=5S9<+Hv2Iy}gp2?H8MOfvbr|HbyV~ z%p!>iqX-*eH2Z$-ZyzW{YQYtV8FOpxT`Bcm&P45(sp^DV zBq-O~F`5|*x<3#P?2@u(Ol3DD-8l^#$yjW0+}B&j#Hizl{gAJWoDCm#Vu|}wg@vwGVIQ2w#z$5?P3j_U@`2Yu@)KwSSyy_n zp!w|6^NY;0%4oQ9$V57sdC&IjXG9@}nI`1)r21r43YHnx&<5_~9t!4? z1;P?(rMf`}HV6T98{-mX=aqYWnu}v7M-o(rIILyT{EkEPjfd`~oK}uAt&amZtZxd1 zod&y1J6Vu2D#nmFeyF4X-kR02;vA)F_d*|u|=xiJ<6}%Rj6kAY&Gp{2TJTq?kaubGwZtFSv;>C-SiaePx9eqq( zQZih;C6`-e=}E&voiEz!yG%B|kF2}+*Kyz&Rxpu=KeiWEUD&&OquhA*I=)U{zG4+$ z$dALXeB0~06)%!8Wa9nNTM4!EaWj&hwdhKty8EXoshE8w6P zQ9e%~Mv-`hnzdM@wM;^gd}mB=tC8KP@|FjKaXYV)oiYpFZJPEUS~T8-sePN@!)I7} zA8F?4JywqzEW^gR1A@*taVm_gd3i{Wo@*DwG~%zSP~1a^G2%h3j)U%OMl`KhJhmBf^xL94FCp zj&bqHqNsZrV5oInN?B+@LR3izR04iXbc@c=_})aih5jrAws>wXN;fs3`x}RfOF`c!X1N!fn!k2@o$>!{krJ5QvTW48GVV zm+^3rT`SL9iX|*#)p{6tWc>WS?^vxhxqwZCF*-~{L}auB^Z~Lrvg?hB##Z3Aa*8~) zQ)(@%3%P)&G)qhivR3-#Sj^W`l|u*}ILScuOISg64#=Itl^4=pd}CO!FZ3VhTRK($ zj(JxLK9D36c1*`|&ENlwHdiX7^7JLZa^w$Om+@O?UTi_FD=dC}WBx}P+2#XmbQs?c z)7buo_KoA2Fy2|Sz&gOPN%hI=*q}#|()Z0y29_%6$ z>vk@U$6i#sK4$pml#eO^G7dBm@M~u`r`wVYb-A<)BaR{cL-ziKwu37l3}udqbWcs4 zljm)&6qSVEre0nz2LF%=DJg znG;o}%_H5N!AKjY_Zl58H>Z!^WNdon;GChPQd`f-6dhsn6(HiA32>C{P|7qIK)OQj zRCsnQS(d&*4n@qlO{YI_eZzgc&-Kj)A@-~Jv%R{`g`tj|>jYMb<4hz;L|YOX7I^h- zNtoz@U~s&G%Dst=t;1y0q?`pCVagwvVyG771ZZ|2Ys}=TbY#Ye>-(Xn z#h{~kv14B+^u8Kl)$i&TNqF>8baXzMsO-DBQd)yE>AFFXpPWpKAI8$x5aZxWW}8xl zcVJD+5j`H@iz8fyUD0w0nMn^>7sqy5Rt(zm#~yF_=L;wA||6EF5f7I z0=pInM(g8getIY-4fc&%CMvEg034cTpSIfB0u8`SFbCBrdGt^NY({)nc}Nt0SjCsG zN{=*hVuFu$6(7h1QYoXM)~DztP-B`o?SRC|`WxahX)C9J76RTK?Nx##JLjWfEx3Ca z1-ZLFF-R(rZ};l;+$HW^=hx|$KnBSL=Lq82zI#K9qrtxYaa~5BHV91L2;=w7nC0>d z&JpLCB4&*={c-5wc^4}qv$|z}3*AhVN~=VaGXhrcZFaqOQjlYqcWL6qqU86q$wc%v zZ%Z;`DnkypOV_hp!%g<2eCFAgHL0THgZwBJagh?QQ#xyjgkOc1KbsJ#x(j|=YWex9 zYrJJtJkAHDaUM%X2k{$i<8MTsKZX-7Y!UL1V8+N>4KUIZGN?GZ%*A1mi&R=Vx(R<8 zJ~-GZ9WEk;;V53yFh_XlJKZUBHx*He(1KSC*62+o)t}jb=u&sk?IW99;cFnycZbFM zQMoGcF7F7k^=5rUQ8J8pxk1Q#r^ND;JWTr>rA>%Eb{xz!k8qoQQnnrd4F1yFOPzHb zB$zVk8+XyVA&}IZdg{qf(ZU3x^*BPv>=Coa@^RSuE2L(gv}wGiFHRh-)C9*3zleN% zYEcAm9v1-Ko%#F!ca3PV_BZ4hY`WhN!z(X9|Wv}l?WuUW=B$

eAMzM}7Xs8?(xbuTlor#lAddP3RtZhmscDz)nI?Cx5bg$K#6^oTV`Q#q}HI z2NU$#*Ezg7Zo1X3seXWB7;+Jo*GEOqBJ zx`RrgycxyEtk&R=LdqJ%g^jyDPV1b(t9M`oPGf`&UV70+QMst}y&@4kHa#A7rdFi) zLdVn53hx1eTZ2oIQFZV%L54uMjG4aBzYLfznIGG?oh*xET;JK~4PYeDcmek;BAa51 z9{CC!72&qY%c79@%=Q5B@t9u}yz+=``}DTV@xZs2FQ}R;UvjQwg6*PN8=h>=dcTHL z1oN~vr*(UYUxl||qSMNRR$3I>KBd;z%J0h9?VitV0PiK;)s-(CGKAoj-zX)|PJ{@|q@K=Akl@jMX|} zJ#|nQ+`Ole^8ppajiNy2JCpapgtv6NXz+QcENf=&>}nQo@#R5T(DXc8*Qs~2{Al7_ z$pFu1>#cin^3N3_`#DvP_S7;3`7k#m_BNu6ThyjlL)h2?Mfb{AV6kSij zr@HijugUnvWLW^s#tiB9mVz^|1&j)u@Ms@8eptL|<0bVN|4Uc^Gj>~R7R+~D|2mc> z;;t7AYO22l$0Sm7SvfIAUoJodcL-E3bcpAKhY|Yh^xn&3Qh+F5BvyzweqjsojTl_ASE@*=#bx8 z7({7na5eszM`?5LA%4By0K^t1lRnYZ1*&u%ZPh1nw|%@lv17@3AmV>4I*W6vJxo^U zvh;v3#f7W=b@eu@e$2wjcfa-ZhyX6F%3J7}=^r(LrK_Bz@>rDDY69ouy=`m)#g>;b z@$#29xdjPE^)zqDM&jpl`#Oz$_+)*ptQ8G^J=-slX&P)fJUQwnWD$6K`X3teWZ?nR&?^IcZvTW*#5s^+2JHm zWC(lC+mQ$p9U1aya4yl#+2xZlm~OJSH6ueJ^%c|tAfxFC$UtJ^(@ii}f)ez@G-<5N z!%{DRE7-<|INgN2xovfv0HRb z+v{0Sx|CW|zs-xY-SqYroA;M%JVWk1J<2(ZB@z=sc*m%CCjN7a?`}Kli{wveKxJd! z#)_>HMaYF(EjLGo$sKG>`RU`iqoU-9`G>hN{~+W z7cP%{hyg1nLhC)6o)mG^X+)dviH)k6A}$CzBYf>p2ecW-JEZkyq~a~BRZX&Kyk1#l zMU1WZpdHs`7vI6#iKnNlQ%_Lve}pOhFxQ9)*or(o!aM%C@hFE#a_0UTiCPFLOGkfQa$Jts2QCNv~%tRQNAEW%>O4(QR!D!_! zS*!>H_9PBn)^B#EpiAc{eomL=6ABODH{}!GcAHeE=ez?ek?{YXm>u^35A}7c>0?=` z3F|NQiZ4~sW7A&hZ6T)HOYEP_W{9c?Ziog5E0dLcLCr`GJ6R{ib~w`UWEB;-y#~+v z$lF_!V?r@QDIQA3`O(R0G(+7vy<7Sui zu}%O9SWn|~d@3Uu0V`3W>+z0F)TLm(2j)yoGMt6n0 z?rzlUd1cvx7L!&MO@~b;?CiBMOUq)*Pi@TLZMf`LQaOW>B?HRJhbzi<4q?NR4?aZ?}X=XW2@7hH5lKC8q+UommF_8+B9bc7g z_x$|QhlE$#Kp)#vGgWjfxE=*dlnD5@jMAn$VwycL8%8f*p`);5Jxb8}ljm9jby>m^ zaSA6oBiZ56N*)JU^xcQH~L-uBaQjcD_kR4RaIE z_dSX2gN5Cv6QoQOJoFh89dV_km#ek+bDM_e2nOsd`@oAYirK&uC<%n7Wobk#B)QF+ zsVD^P>Lv#1P=l=drpne$exO;FO>YoAE>pR4r#_b=K6Xy$iTU*wjH_j|?@FvmeYo#n z67+P-vv032sp|2i4;!r&+$vrt7YpbZ-@z38Ak_KD(X5r-*3+`mt16OQJhbF~mY1#k z^XrQA5=SpWH{WAen1?pLUS5h_Nh&9dT*r(zX_8MwGVx4kPzzl6S`y0PK*!}v-2lQM z>XWy^x0~&EB{9E)a5yDj?CiA&5hTx3(4ZnLlnwU`@9U#@LvowmyRiWk%?#d6*<@z8 zU+xtv-^DoCEd2~@#BS);et(_MbIws##n&SMC(styX#ZZLM{ZZ`IXkkBc7O&>kG)0!QAeZt>;FPew+i@Kd6qdGvL5k-M;bd-pl=k6>i>w2vBXpcr^w@SPA1%g!KRHY^%VI1|)^Fxa=Y<=yrYa#x& zZzCe^rp%s9Hv1pWO5<;iN0P?cQw`c>-1+)$xIO-$kJ10DiJixX6#(HTJ8gfmudk%Q ztbQu|sUzSv0O!>D50Gq3)Ial|*&<%aw~0qgA0pLXw^8}J#S#?y^wP2$G%*sfUJt)K zm^XJ=5Rxh4N=+;EG;tv0;*E`=j`G-zR>?ZY6WZ8QxhIG1J}1}rHix>$689!s^n+Ls z{wK$z13BUaWEpsAghlgRF|JBUjQW(Zbq;Y<)e77fxh$H9C>vd3x9XP>s&6w}!Va3m z*Smr~kW96b(u)%myxRSu1bWvzr`a<5-EgxvK?6L8ztdCc2*`a?S82j*z(iq6;T+!|9Yj$?&9 zk~kztz0?-Z6Kn&%`!zjEyV-Ui_Zw@kuH)*8gG;SP*nXvSCuKsXqXxg@KK7xAbubvf98-M{+NOjR8F(D{hM1fr|GXW=TKN`(z@l`jgU z2|(Tt&D(szkoc?jScGOqRYAw4rD!SVEXX&pWq9J#9`lkDrAb+Z1-av$@w}f$+9~zz zZPPPzk*jG$`yf@UUA*(60aQqYEo)HRR$;=RJfRWuL3yoTW+$v??6NgnSL}H?L`z7C zZ2WuTg9y$>Dr48HPc03(Ek)mK_GJtU$*>Ms-bJ%)&6;M4t)j{LsExDw%y^GnU?^yN zm7a^-Ox%yM-zi-nhOP1&N9{{$gsq^`4@QM)qV#@kW9WnI;`lP&@zd?P$ zM*S=nLSll*D?DP}ZUw6H4pM-W*Xg1Rf-(VUP;e{bE8`(yGQ9tRa5Bg* z;$U!)L_f{;WOgNtzFvSx@(0euOWd{iZ*ML?2k2W0YGWhUp&t%cZcfwISD2`gRenEx zZ<1Clk{OL=Ygm&BG$T<8x36XEEsG;IoF^52w9Nqqt<>oJ-akA*%9K{8Ub?ogdQ~vZ zsXXT}k_qP0UwYEkRK9>(wAfyt5IWoBbqj*WXjRAxy^`w`PI*XJw2R=bRZT;4mL}ye zw)m3SIDbCs7$eh8o`9c(pa`stK(fB@fNt2dpp*zv5|;lKlao&NSqbe~MGXx`&S zcN%ezvA4(p2+|D!F)!=>_jktHeV8<8r5qbA#oXl3NYb2z;Z>3Sqr6A?+(nh)#Mq7} zN5Zv9Ow2{c&p&+$E2BBP_~y{u;FSP@{`wWY-rzWz-HN(G1o!5fcjQfK33c1K!dT83 zJU#6o#T&3RLvs|Hs_?ZbM_-tP60|crw-=ASrV!ESFB9o%dSJV4!6i3ADy3Yn%*H^9 z$4P2wwFyFaxBdg+-E|WLb7rrU+0x@*@3KhliY8>zDp8}rh`;BSg7`{|slh%wKOZg` zu*;qd2RXuIc5TKAEpPxiZSBTvRELv&TFn0TNRyV~8t&yHwVaDH_I>zeT^2?V@cRKU(l|YLS0WcVbmYiH@Ort&s2Zv+B9-EM2-|)IU7}70Xa->fE6*liy|p# zi+xkX+KJC@t;W0~y7v-Yso;)EN-;d0x>b&JFzaK@4k(#VYUP;6E<+^0WI_cnf=elh z@3*SXs>>XnZ>3e*oj zTH&DAMCdDRw*9gA&xWJ#h;|Q@b8mw{?)>E2ah~JmM|@2TC`wS`Y{cO$Jz3HX%tZ$= z!8+w+Mh|cA*ua%l*;w|9?lR=rrMX|LpaIR;9N8}KMWA(G zhtSFu6S85UR zKn++jr~*PN!)V^}GZM7;?-V+*DB(qGJy<4(a0PLa?^Hto@*kcT;4pF?Yh{6AtwbBt zdcA1-Inq&gCVdx9hxv+?s^+X%P-rxXA~&_rTtZrjBz#>!!JP74wAbl8v1nM~ya%AU zm3(o|0BH4c4p2x!meJJC@us16vN6C|0z z!G{1Bu&t=#f7vnufyND(vr9!5MyNS^f)U0&)j{Q^r-2757a28rkt)hP=6E_nC4hRH&?S0`Ws>7;5a+kb)B^2Sb7| zb(fgzc~2&SHHdPz7OAzA&<4!TH1!Ev{1*&rwRM>ZxETTDQJoO;$5KpouW`8^2MZA> zwqjSfVwI@PZgq(TFw6N0QF`gO=Z}Q+xYCJ3HEFq`bc@vD0nn(x|9;FNr!`~n@gCOY z!DFk9M<*b@1P`1x9holeg@aMXAiMQ7{Jen={dGWI0+unc(iCE&#;v4s=IyoN6)UD< zWqKn87m$QwvN~Rhq2|-7HWLGDH>$OY?m&B}1@|s1mWQ|o3evN}dM|DD^6`A02L);N zCm=k72OwyNdpi%uzw=D^%0805{rn26J1dG5RDqf`PUhJKCPjG6cA;cT1(Ah@qV)1! zz4;yu%yEVT97^L_3x0c{7CcJD;T1mA=q^fyl!gT&(+cn(5MP&42^H~>rkCh$yxX4^ zy|r0rj<3aRb(!@lBT7FZe{d_KgguzMQltO_ozm`9R5(Wm7i-Zgw2Ykx#h|Kq$hi&54X8gUS3Q+~MTlfC#57#}F2qs|I4CTwNze3S{lg5=n zggsKKEuWR}d58-D6*F%|Ztz3Gl&SYl8`?(?4bU1!)WL#6e~}>RHOged+3n)CgN2>1 zm!IK3U{b<|06IL}@6T&T5W%g2H{UZW3!@NXEe_;R@$&LA;79z?5$-2tXZg``kEC9R zSy+8Xu{-JTt^=?EC zxUIDVxwh2d(a{_zt^)=K;G@mV%ov^6_o*&|SB^lTb!S0hLh1qy)&2l_Dkwl<1J2&7 z`#bn6Kn3+vWw(W*t^~W!R&L>$wT6iFAk)~jA(DlE) z`k&RM$58E9sQ2KjMol2QUsj@Np+zEip;YXfhR=elu_A7Bd%~94Pu43eL0vJ85W`)BSWf{?3LAFXMn{jEbe7v0O2a$HteUQN7gd2diB!v-ng5JF51?tDJVI0H?z_a*8$#v>_b*E4wI z0nTETBlsoMf*A$eIr>8B#=YMj*L4#Z%=|ASUW6u-M}%9MOb}mT9-JgqiQU$0@2@s^&} zrSZ(S+OPcUgs~K$Lnvd))!v_7hA@SIe+5O<9FriA3OEaqz}lC;MgbLLAM-PA-349J zi~*fyx$oOj|IQJ~0Gxt+l}~*v(1J-5=$q`r=1pQ;B!-U_kquVQ;P6rc@E`bBm{rg9xKBGDj^zK=mv!C)iy5pJ6N{(mcb*tJcQ>R^TQIA> z{D}5@o4oI>aOoRF_mkz^WT^1~LLT&l>M9ss*&DzhLye~I+P18>?aP)9y$G~XzAGdP z5;A)Uzfl}+3T?1~G%Uz_@fBeloeV8-3545H!F0=8n^X6*gsvn>15jQP+7tL+g`d!S z&jJUP%wudsEMmcn8vfLOXC%zc&CU0WJN!Zn3N_;kU>)5*sofI)fbjbe(?Mx$~dm5Xd#*Ucc7w zojJPyEWH#5jh7JvY!{w?yaH}?C5Kl0+G~fk7i_&=A<;I-s|xoe2fW zZ;W`(kw##d7aV??lRsH>3{r&Ojq6yjjKG~Ptf2qB!tW0U@cfBL52(L%3zJ>c?@QAF zL_l$6I{-s$w?X?{(8?A7z~?qM@&9+p8aIT`YwoXwFc_x}3cfE2It)-3 zEIj@?3uKl+lBtOvSqi0<^^#XweqR^#nE)LkD{PheZ^CZ(UsrX?y#g5w&C_B~$aK2Mb;+B9Yg1_sGZA(ALF`@#y|M>^^U zYgfO9Rpq?AKMC494~PYQX_Wr_Ezp83r~Tl#>cD;HxbyUT(Ec?rf`XBZo}ECz+)QJU z^_E#?g)o*>IT;-r6M-(6Kq0WSH2?cR0R+MV%$V9+#~A%JWnh&D4jQ$e+B!JI4z8qR zXJ-@LDJd>iv9~WNdi3aX`vshGCUz^?6yrh%FuKma$%iE2ulWSfqyjy2FpOZdUlfeC z{y7W--em!v9C#p2fEhirb@>~G;o{K;^XT5F#>Rl$Se2k z$T{FzU9Xzz*?)T?0X#n#{CNZSx8HSN1{={qs>G<33+}Lw4(3%6kPJ18=tcf@Z-`(D z5Wz-NTIv6MAOi6W+7vf8SuneOWhM3)mlg{P>t1Fi`+CY;HMo`E$$c867Pw}N?(d6N z1i#~Ux~d9IHvLJB(xwZtNGIZ#d$88l)_ZAbECB%lEXyhy8tGt2hl+~I-L)TPPrbm( z)2KYrU~XWdgokQw0O7AuCg9U$un#SWfz)5m$_1l4^8G%yU{(2r>G|v_pV4X;w1a1F ze39Y;qqBd}00&UhXzW({FKQChfdxgc&3iG56fAU`RjI6MHy(;oATt4NqB;03|LyB& zdtgXqzM)L%#lP+e#wtO~6qPj;c%Y=Ol(@G=NIC(wyX(`Z$6mi43?_+afjJ|0)E)YM zEt?Q`zNFPj&!IPcrh2NAUTv56^!(PdysnY&(|zkeML=Yt;f?Y7e|w%P-dmG2i; z|M>BXm9Kz?8h7P}-@j)T64HG?Jp9c9fj&w|Qk&n${q8ZKh2*3-_TOKooEHveE81rx z|Jr`58nBz~L3EgCJzEFRK7$>(_uv8Nf4|S9=;+*xR%O554O7q53ivvs9TzXdyDoyI zU%ho-I1mg|LKsRYP5*Ws7+Zw~md6NRAo=ZmuY;YpQl%#VoQmAe7R5#pfi}HhAp39M zM5uyqnuR+|)S_K=|75o5`Ta|?rO}Ev0mQmpGceG-GOfG2TaX!?Fz!(bJHhWzrcZULuM}#L{TV7ti4yNBNE-j_8vx|ut3knJ{v#=;8%JEc(=ObS9JFHCr zE*!oOeuO?Ohzo*k0ElxFuPJ*^coyTzbgymt0oRR*9{ zHnBX2C#1f6IFu;OUH7Dh?LfLH%-bHCvipmn!6&2_qpJ z-S_pc{nr8py?FyfCw&QaAfJ+eySoJZ2)ej>y08{g<`-lfVrGfHaC!>@XBGT!<@`nT#)j z4W0D_&!%;!U!nqLwrh32Ui6=fc*+F+Akki^gGMl931sF}s!$GE&VLqaKwNTG@WtB5 zdTKB;84~=8A8O~Oc{>A0M-%M7Fh5{gus&eXN+*$6#S2JsNJR?pXPj#;u#fq0P-_r@ z;DY4d+5s%-9|U6uZ-J14G@61YxOk7dl?aBp%B7{HMSpa3R49F0Nlq@DnUyu>*tP}I z(&l0IO7y8VY}Ma`q|AW~AuLh;cWUl#` zXaxMwbVG)CTMD4Ab?E<}x^oawaUh~LAQ=Hs|MaGyt)d0mX?EwLPRBby&Nw%|*gxF4 zevBIVKd}IR>8&maaM9CYE>7U(oB(CKI>W>XsWlfMa8~je#t#i47p~EJ1e*H$i@N}y zTZ1p+Io?j3K89y%ie8?Jy1j*a|?OWg`Ml66E%Y7dkp%Fww?h84p zcK}2uzzvw#5ifdVF2@gAju|!uP<%3WN8!7!ZgK7&LdW@cp@o5E7f$To24)HerTG&yKJVZSf^{7n#K;DQE6R7LPA4A86GWCo*Q0So@- z+9jy|<=V*(*#T~jeiZr zzlP$c7xO=JI{DX7{Cm29{IP#e7r#za|3{uK{xuZ;8j61n#lK;we;l;`4MUxWF#l%) zVE-D5e?w2_;kDmFPn4J&pW02A`?J_4o81#z9=zdgQ~OY9th@Zyb-hhG&yU8Y3x3syp6>_kZys!>j$}7qVQu{_wCamHczZh^w zYc)aq3)ChD7KJ624nZmR{r%iDrawb{f5bEi^pT*Cd>`nfkkf1TnIB4e1#CaVSjzSu zcvnE}1Vy*XxofSS!6>AITh#KT+f8a1N_TsTHAD}lymOMeJbP)Bw#%PnW+z^NX_L;f z-qI7h;Uy4ALTtbr1J{EhoOhCA|DjMpxRNMS%)11aT2w3ac#X|a7K|`wV^T`EMVnF; ziUnerC}>YPp-f!BYC#k;7uTD#TKLCm^QDgOn<>N^qR1qKp$MT5lU40++N17uL&KH6 zw{J@1s5VVa34P#7wH^vB?DF;OtaM_si-5N&AblKd#3K9Z>6I8?L2?)N;DF5F_ zG(fyk4!d*b4usbYm_=gPg3-g;?Y?%Gb zrBuA2ZB1Ojmq$6i4~U5lS7gpOK&mhPC|K=$cdbf5h*IeL>+-iy1pFEuXj1gYZvVOY zCRm^pN{Lk?oBd!WtdrZaLz1^q7)s!_uF$THKYlb)VaUhpEr)HhS!xgTb0QV6#sfRQms9v;6ih(2Zy5zC&&@ zA=9;2uhY10n7D%|H!;!bbX)UCjipQpz$pT9bg83!J4Vhuk9s3A6HJ^^iwM+f#tz8w z`Cxo6DQR|D=ROrA%v~42b1q&a6Uup_fW8AY%Z@Yejx(l0QDtFP|sffohz>!f}~u5#an<_6TDN zi0OMkmb^tFNzT=HsjB`2i9}1H;-nIyz=$u_|XEm+B9HG=uP|)D(zawLSmR7U^+o@qR zTlOgT9vD$+P!=ifv;Hbx z(LNDSv;TNdnrr=I?Hp*p;M50n93SBBJZi6340GAt0uU92Ve1?`#H?x_7F+cIM44ob zd>K!ry7j3&=Mh%v$7TYeTKlY8Ewngd=&?Ni-D9y!E!**_uBzpRogQ7EsW5+RufF!F zy^WYxbZx2yDf zx?Ylpryonc_|c;%Z;FJw1yG`G-s>h>IY1BOKR{&vbK5V#l5wCRbr&zua)aXJ^6if` zoXkU2rXrB)ruOV1OxApPA9{Us7?p_IEsHOOZrPwISG9r>RLwq+V!YY$-EHL?&w6JK zgxczQ14F{$KSDc82WOL0Q<=-k$_k|^HUTWj<73`9A*f{3LY(|Fg$755fnt*+G4JJB z;u}aB3Ex)$4r~irDJ%^XeafkFb7Wy=?rFJqXwd;$Ar*#>SDbzXu*ll8DbQo}CeZ!C zv35*McI;fc?jY7x9!6^p5g4d8N#@*vB3{<2Mz-UC@;ryu3^&Mn(p3cY{ zSV3qs@1QsTR0ND9^vnbdX2P1f)+pa>QNE422m=r^BL-bCKLe8UNjg5^t&OqdgRchf zhT@-N0i2QBX*MT!K5cJRI1Gc!Oh*6 z;_uP`AWacq>P7542Y?{?bi>xKr3cht4Lq}ggLDP0t{|z0HfRLw5C(>3E)otv zeP7o(fs!X8B2sAYzW(1sNeCI3yRO#i=L3~CpnGxT?DS+SOts3S(WThKXBwo~mK*@^ zEjk>?3p>5nT~3_EUl1cvk-s2D#jiguMyx_8yJ|K=dHM(76${Jy8vmifB{{T=m1kqZ z{LtlUITP5>rERP(ZyCWYfnM@Uh|+*5vV-w0$6!*4O*Fo3r*Ije&Ir)M~U zXBafkl)qTvHRYAzHDQ$DHk%K=CjP|t%R1Uc4auDzWxi(t43MFh0s;$H#l4;o2qtO zv$X((0mT=!>K(^?O_pR6s-|d=)9_RuJh{ zx|Gf_2x;j~6;wo!lt#J-=^hl77(%H*YCsqUhK?ZynEBmv;ythDx4!RZoj-KBT>9`l zcU;%L_O)FhYsP--yWoZpWA}#cT}M|{VD(@P)IHMBs1%=q{ijhHPpUtrm;@&+?ISS~kuH zI%Q^07|3tCOmnmLVP(R@jaDR{2k-f#GkF?j0?KZ=&B(5LN>=?_in|HqTqIDh*V44# zRlv8h1m{qa_0oY?X(uAHzbv*YWss8?T8xj(U{gL&`_FnEc@@S2=6-kSv=Q6CrNh4kps*hx+7V?k(H|8b zK72>_$9r54y4rPkQPW;#@%`hBD#fn1%mQ-Br~mgSe9v&@K)yVw({Eo%H7iiqj1vBd|WUF+Qq7Tw)XpNtx>#?eD9V$IXWvFQw z1cK&f1@P1%`?sDig`Nn4WFlCWfvkx5rD1Jt_Nd>7{~d9VoCTtXv>%`=rw02IWa-UK5#UaN+Ne?qjlZ6n_kBGXANzVuF!|0D0E_=~ANRhMKK;KcF*|C&WD&L45{7IU zRUJ3Gu5s`S60wg8!*vTrrSR@4Nr1S2f%wLTU+jAsmO>PdH3g2iHiOX~mg&R&B}#yx zp+XMEVZt;SZod##&4ozDTuCQnYDE^T(2B_<|JmnQH?5o@NIDyh@QN)`6ne}_l7ccIjSbQ-hMZHTu@+youc^`NmnaPr70g?1j1($Wo#&Jxd`=zPVf9tKp``D*V?z$EM)&e z!flPB#CH|{yLUCG0&ulijy_894k?XJsxagv!(zP&p!}qVoxtbLica@B?*rw_W~WJg zKzgpq8?-j#BG$h2fA^{`#8;iXN~w}#P6|gdbPpuQDkxeSD)}FIHm}&9@@y;gc13Ya&Ob6R3sG48a8bi2|Rkq#uesYokOfdwDlDc1NxUq&m|I3O%{+~mV#j` ze$Ci*{UVVW*}~f0{wufu|9}#N&noeYRcf=DP*@VhMk{1J0+4$FP1rGuwND351wer9 zBt@kPqSe1nj&v}9CskI7@sU0RV|On4)wBQFr;qp))!CApEtyD8$b8l()?|%lW9veb z`6_|)1d+hj(AFM-ZvJ6>N1rXS(ef$@SuQ__aitlS=Sk*4jQa!mh;iv5_!%s&ECs*Q zcif&2rXo^luOsoLV-bS$Es6jA8bBp(+Nt21wT0SRuIouJ9 zdoed#5ywRp@XVWEP(y#&g+x&F0W6m)6)<~M4~Ol3*4fAIpt-4;G~1fJ-xU1>kjuZc zqZG^3TV4m2<$wV>xK7cwwX>c45`9(Ss31MbAi(sSK~yp3#QNz!hoT0CU;Up*!xGHv zETv3>BmdKYK^auaU9Akvx9MS%0fGsSjzEm7*|5TiCU?lDd(&lRVDX;eznJ|U3;`}>p1%LRMjaDjWq9v;;I+p247zSc#Vn^7r|2uu#-$`Jt( zM@+8vZ`0qw34lMhcS7AAm5#BTj>3w`j3UbN>Bf@t^T4H#$ksq^`N_FcN04Scp}PyH zz?T3IGA(g-=sd}6z850&cS7|)KTi08lfGh|Ev73Qz7Q)fH2eWjPrbn7TztcFx0i$W zqMTMG;J-eC&2`ZNQfVzt;;L-Y50A-Pe#eCAd!^_H*4<~%U%ww|I~C3gmcmQY>nS(x zcP!>~usY!rvtnaK8ldyvViLfu3|D{d3`K+3aW~zJlT;OKf*ZLp-vlE-AEF`tZvd2{ zC0Gn!lG1x*xgU6fx5n%hWhH$rGfr7H2iHC1n?S8uR_tt0m*`*i#nb@G91mXZm)pP_ z22eJd6Q-5^rpRJ*1sU;|YX2P;hAKJU8?KAasYzlD+WYve*wAjc2yFF7rE!IG1gX)^ zuP1=L17{Y(0{8kXjsLCc4bEB z`R>IEivo>2RgVWXh4CKaAMTgg^=nd7MZeCks`@Ua%M8g=rA80J5#>p&jK{)(lvG?N zOL_Tshwt$5^LyaUVn9aH?1#ObuC*--+R>^MBPdMNzgtkTh{`bOKU~S>s&XBJ&E1XKQezBR7$R#Pd!||>;YGal@2Yc zJlPGC7d^=%^n~&7bk|3aqBlfVhs?4tq4rI~tLA06qvd@u75dBks#6}ZFPbGV%H%bOemA!i>rGT~`j8=}fgU;pt3XpoTyU~emEepW3yBvBK_gj?1#_Si!z^PoNvRPaq!fSA7`N-Z}d+0 z3Vo{GlyUH?lV)WRDgn2YmODUKJx1Ru*UEPn=&p-X|xY zA>f$ot;Gk_H+WB=&6dqGAt|dfHAM#puOmn?g@;%bwcbUSNliGw1Hi4nn&kxC=2 zZ@bG}mkCEBm%3C%VF8p%3drGY%#r8kiTVDk63KiVXJlXl&;fmkonUA?%NUno?94Se zPEC~b4&S#4a>4$Mc z9Dambt|R)^flpuV1|Bc}I{a7MFRB`|8B#joY2}0oTUgs^WJ-H`u<$wK0Jj(rWyVUn zNDO5~DdlA0e&A0pqeMN~E~bJ)=by#GUlTFg^Dqi_66|gmVQQ?)f1&ki6pL5Rl=mn@ z{9foi`WFv< zF@pLS($blceqg1io1$By2Jb?K@Jw$oEnW-kb??$lP^Cs?4#MJnqOPq5K^k?8C=H%680$So*Q2i|(ipPT`L)Pr*{@a1ZTmyTo#`+L%sn9pmx=Tj?P!ik z&zQeIQJX9^AM_RKsUNz(EDN)g-y}f?+7@J;zj?HB4ef9tCHv5=%c|}6b+={K?JpzC z+x>$u#z86ng9qrIQw3eIFd?tc%>0TUzEKd=-vvU~gu9Ba@d2NKWZUr3cr0uX)l=do zd35*1?d@b?&$o5ryf?N^Vr_Bukfe|021Rd!Km-4sGp$~x3K7`nnv#_R%=S-MZbYm_ zZAEnzLCygA1>}t1APYEd$^D$nu*mg~y{bN(=%j-=_(D{L8mpJ9dl| z(>60u{{ies8AEgSNw$YXYrv3FLtte0zCTO~sqj-_*k4GIbAEMl;}RhPnQ1WEk4uTV z;Gs-W&;`{>?hOst;|s}+kY3-Vlc8rV%=-LvIqcJb0;0^Ki^5#iLro8jy@BY>m~??ykSeF^$$;RgX? zOuvKt+Au!$a4=HpXr@3J*clU8V>b4HS8A`jQ($#Emq7rP$P~c9N|??^FLa!wCrRRe zXc(Y=Xh0bV(^xdYByu`Hr3qoE$KTjU2>yyZfwa_%9+muHS0TleoFOz-xNS+*WM5<6 z{)oqTQ}SRN3l7fI1(b#b1cYCG*O%?_BdOY9Wd^|Uj3hatBVXI2Fj$A^F4@}xG3wXBlzM4KDFM-b{MN0gw|LU z85qr~L3jX7?`>tw@zS4-Mt5mWKC?&;Y#NfNR5?)&MUt~*N&fUN2)l|&I?WmrSU%Kl z{oO5R;dnRNbIO}I69fj!mp1sl%MdQ6250xmf-IXHmN{1Fhpf09Qf6_dk>aPlbAvjG zMMd^f$ZW)6_Q3G9y_lhu%-bHtJFlZ&M+Q)T(cxfmZ#ND<$MEJHi*yp4Dl&6&LiUFcZiAfKEEQCIj<{Q+GZn%%h^GzFTz%{*tZY&6(rFOy3+dJ zE-cMI7xcOktCPwd+j?AT@D}F{!zB^gXO=FoGp)DX z4?pQ$JIC{++V;ptYCA&UHM&NM_s0B=v%mXZhUi`tYe=JZrnFJY8hwH^I1&9kDH;sU zd9XFVoJ!JC73|Pi0{#p>*X3R_iPN#`6Auv@anFlxnQJp6vSNhYK)>guwb>pKv@ z2iP%5XE38@l44Sj!H_euQZrJ5lC2kejxjxC7;g#;;^ZjO9KFor6PFMxH$g!?QejY3 z9e*7kvQzJ}J?}8G<{{ox!J$(3S22xs{mI9 zP4=CKdEMiB>o%_FfqZZID{hk2f6Biu;%Y&*wf+xM0}+@p0LQ-V^-{c>({9lV_>{uL zhovUate`_w3hf4sNCqHYsH}2T$u9oGdE=}!vEx(Znh2r#8)m-KnNyjIvl z7v0{B7wN#CEtFl(5_S&(t7JC@Gek#@d}&NV!a!f_Os0TCBKCcncHa#1%iOgKI!@ia zTDQ+Yj=8)WZP&hdEERt^>c0`PRkw?cNz$>XPfl$KX$$Zex~bL^DwD}cSo5mB-ZUa|#M3TkQP_vqPcElS zwD=rCeYIQYa<)i=Z;PqpXt|kLDnke~qz<|NvB(Co4m(Y%1NB<8oUTO(h&%N1K1h;=TCR*H(|dF=+PmHUKJO2x7LT!8SJyiTgC6F7#Mc_el*b z=7gCOk~qF~7rIRLzSntwLkYM^=$90j(11|!JJa<({kycUy_?J5RqxjsZ%A?G=tr=g zR>M{zSBCS)PZI(RbBtHKYov9=BBh$0wl~=ZNg5IB0h?z9%(Vq+P ze!iv@9zLiS&w5#iZ@5$G*%=L0Rn?^Z*Cx8)qmy*+0kaK|-)WXO<1#aaCsqmWAPs z_LL&XV7yxhzV4vxN2(m^b3Zw_`2s^s6pwCl7bJ5%hm=qkMX0tY^z)M*lboOg_yb4%H)p&1Z^FUJMIgCH@TTm)U#x=*Djqi&3+b+9+We3=m)3``j5KF5(*bi z41~7zf-in{pL&S--&z1t3~nbB!fC&US?Nn^gm4_9uqON!b-N^=U>%_kH&02eEYwm$ zKZXo*FZ+$^R0nrmK)(lzyfFCSm5n^^xtIx{AUQW;dhR#qW>T(Dg^)k5A*?S8pb2FL zNjln64?k@u4f#${*(Q4)%tgV$eL6a3cB$T=KCG`31XL4Y5J;ejDbXDI zLLfGd{+hS>En{|EAW2syEzJ`^;Q4s0gA?c(8KkJQWP_?Qd_KFgrw0?BU$$8;9UX8- z`^cN6iMv|mJLOld=e9hwbr5H)AYv(K4{B4XLcj=d+W&gaPl)!?ZeZEA$E%`S zd#xeg(I7}-j#wE6x1bkFwlOb{q6tlgDyUL}>~3-2@h%mb)#A2-qTO5n?n6Ph;`x!l z<$ZcaP37t4ZEW=XKjHtaF(ndlv(1&W_#gE#tx^HcR}nc%sqv#CDpZQA-|w4&9GBf2#Tqlx z>m!agm`_kgt;md(G9KB!fJcT+ZTipwQg$M6WLT5p9(@2q&X!_FLx+&A@0S^+=M0Hv*3RB@v1JHd-@uRpOjE(gh(E z_zep&+qKA`B6l684W85#aPwb&=IrxRw_Sf;2c+PA>)_D0HYXc9ztUTSTYMARr$q#Y zSN+JyM-nbJPkBT+Pc-+ZDja?v^FWNIz!hRo6db0iK9nPCe;%T6j|~cYM52==yKVvd zBM?8|bzYD!Zd@##ku>@A%)oUCCHqP#=GldD2&hO|mxF>zYoDYo^?d{k|Do;HrBrz| zUFuH1EV^+52onSKWUqq)vb>kzp`C^e-wl0ax#gdd`Qxr@`%;iY=7=(QVSeQCahdG? z;-sjEZ@3o-ac6cL?+^5}wj3O!L{4$o=X(P(05cwaghU4+S{-taH!;!dD-=Aun>|w- zs9q(NXsD-O*)!CDfN;_XM{FDrME3q-C|dvwhtJ+y%1gx$?hfd|B)b&ka$*6Ws0lW> z^xOx&VN@9`g89=*+Kq=1UQ6FU&d9T2?rqP#VL^K(%{5H(f+C35(@!>n)bT#3*HNWMkv)Jh;TJM%V|n=N^uA`) zUJuvX3P*j#%<_n{y^j5mPp^Bqp$09<;7)7{VaztP_kz&=5rB8ycTdhTC~)QEMi=u? zXkQc6zbalVlIga@rDaV1n#M?c%BSbW&~oGBT(JHU{>?e5RO+=g&&J&Av>kyPEOcfx z4SZ~GZ-5urXE45t=HqF@ZP|;W@&ec&lLS+Oj~3PFZ1y@gpZ6ImS4g)6>orb_+T#+0nueshl`kPv`uj z@*;4@z=K#d^cAgaP6~BIeJ0XH=jZiou{dhf{ad9F$iRA{FcB0^uuN5(%XqKskzU#E zqY_=~jI~9q%W;9a6!&!6-yW`WIfDm_8FOzpV1I25AOQfug7WnQag**>YIU*T`8v4) zH*iTNwje}vVGP(`2s6uc=d3_|vUr~!am2!RWAW%~V!7xTk2QaL6Zp3kp234-Cs1#x z*(Tov2vO?DS}_A%kjZjEoE#Gefr3j%(6Ct6kEGbf-Cpn{S_ZzZ~I3mz@1^Y@&SP z5&E-qwAy{en2%pDF_IbsXBSG7k@z|)a5h)U@cn!gIZQ_Ho<72Or?|a1eG5hHoe|*u zJRn#3S4d2*ndS?GdDSY78KWr&xr~oGxaA%V7 zds#ZMpo9NCkH!$lWs?B+Q#|#O1Kfz+VJhp#g}b>0UBo#1NU0~MKd*f z1+>bt92j0)e^A?z;jhp=FE%4w^9|$`4bT~SV=1FV!ZF#Kd-2A75GdcdU{`aA`tX&% z(}yR5Mtw&lQIbq1pG}B_HwWDJibxxIY2$`e?xw0|_OP$L2lC=YbjCg9ip?z%}bH!=TRqhQa8^ zFQVa4R0j=JN_Wm>?xa-AYRzEs2bHLAdCwaMmGs%x7qo#%94#@OUVJ*(&kHcFPjMZUVBFQCHp6CHlyS_hS1Y%onMUcLmbmu1h3!sA%JFx+0OOQ6; zE4h7B>V8j5K)y&plrJipgmZ<94;}T`_a-8MA~qGo)=FEGGU0z?qnCML0Z2X)V<0^P z_QQ>wSAw5R!4tpLC%^dpi3~u!@W7WXENpwJhU>M~?G<8RY=QJK`46!@!IJvdnu^5|EG zq>9o=EnEcpvZ1!)ml)75rDft_4-2CLfYwscUXx5#5CpN#{yeXL_s)NJ=Q{?XnXqY) zp{*~Dn^&ncQwQd2mjLY`(9a!zr_w6+hylP@`x^y##dz?FDYL2dmf`=iXif`^t?AH{ z5ll2V^Im-~`BJB^N7j+82l@LGD!vu^p!a@vR z!QVflpacpr4#v+wY&)s`|L)NOW6r978gu&ThndiT@bF|3-y{r4WAzExLX?FBLgFt> zA)mEOy@}tEa7`|((9L7Ds})-{0y2p7*Qe*%Kp>H1Gx4MhFa)-eYHX3EQyJ&4fLHQg_56uI(UCSKm4`f!O zG?Qg>MEPIX|C`Qu@bT0`lIZloA==Hbj#6)?g~FSU;fAF%VzS^&7fpE%hU*gjxxqI% zihjCV(ywzATl2ALQ8{7OCsl1%fl>!{`fq|2u(f3bwziFp$DebrABu%Ob|>$rOIHVO znwI4+OT6h!FPU<*Wd{{%n=wm!#Zl04FB!WUV%T{vvTzV>JAZl8D~0=MtccV6L*E`K z{%=VhPZ8POKRw@?*X~q8vva~$gmO-i@q_r&u?XVXUy`#RBb(p@_Z=%sTpIOZe86Gd zSBB$TF1fdyrpCX%es3=EXQ<#S`&X!#?v2ru0|J7_oV*va3|ik_=e;Q6&<2=S;N9vB z<^Er#IB+m`rtKt_rt(T8m%q#1M*TAa_O?Ho{Z|AGa2B6S(jqD?BS`0@zOL{B)lK}E z6U)CGTfYNK-LQ4$T={fRl#NdXkNtjjv$@5F@ zuuP*0N;pbCABd_GHL?c1RS%%gaIy}zL8V@$AFnCW2L}nSB?A74VZIoaPT1L~el%(h z4vV&MGB?7m1&_=P<-0Et7!AdFa~92NZC)&#)Ej~e6!|eiJj4sG@(AvYV)l}c_t)hH z9%2~yK*wEB{%*xu3#*6iRy;#>QI-CMafN$t6>i_%a~tDaur<=bSc*H2m9vXG;e-2y zEE;+mc0%5DqLDZpI(tZ^d z1xDMi(Ld3(6~wrCi$2odwq*Dw**U7`=r8xcae%<^K{VldUf}?**u8&AK0bnWj3T%)~@ka^G+(X4Pa)XYJ`T()8PI zNl~onydpXwEq?eR7rFIuo2H_{I*%UmB9rm0x=`0RMwh{-?Iscvr(vP9WUN^kn`yGR zFPvuetqXaTqcMkXJav;k#?GuwEr__a;uo(q?WU0on50 zmW!)R4q}UvO0S1F4qvLE7FUGhC*78fub&^8DCohQ~k!mi|7>iT$i0BVf@y|kL6afMPFKzZg-GMVscSjF) z+w5&7(micf#fFvZ^{3+o@dsQE!_Bs-^Pedwiw|`b8j1wX>P5>d;(zW?`a9cXd&C+^ zB<;%-*X(3AKZJ2@Hd%V^oNV`wn6X1E90_=MPyDzIOth;_?8sAUe@G@1rf{S{NKE-g z^NwIMWgL{yluK21>T|k*YRaMcN&HErQY`#^5qHX=F8Sp>xgQUaDH2wJAKvUw&&#^x zw0@}cHH!_j2^dr3YBzp(8av(YZg^C`y}sL3(0>ojNU+LR3OAM95WtKsZ@{S=s1sZ! zsgx_#f`4uugmxd9=}Ad!j4rvuFQ!VOn9+l==V2MUnQnT%g&#f@y^^4zy-rL35STg z+Xsy%T2tq;Z@b$x=`oHsd8UlW%;x_RDk3HKz3>OeGIMNlds~ZE)ck}N;Ked{sEX5J zBCVn|2jeXpt*WZ>-&^{8PG;wwrNka@B;3@MD|qHvwP(8a} zu)STj(VM5I>ydRTCo-VALq(-GRJ!Z&<0AF!z;E<}9~|#H8F{aPL#&I0?G{<_hiN;t zkyHb6Ix|5>3BMk5P*Z;SY{nptPHz^wWsgUt2!k`hifTry?$-!f)T7^(Pbrac(vn4-qj>pJotEm(9eSmE zcgX4AJW|o8WxB2|xXY~`bB;DnnD_oA$0dq3x`6yhjEtDS+3g-X-(jDP^^*aqj2Ej@ zGGf^Ta-{#hFS@ia;(BIQ=;u7V83*s|J=8X?hnt&Q6L{$s>#Z!#&vSr_#B${w)`?nK z>rhp-4x+<+<$syChTkH6PCf+V-EAo#5bhL&KH{;?%yHZXf`;0eJJgH*J4ns?W|0b%XE#&-ZgoC`NQp=&V593 zkpE6^KLKAeWuws+NoP-I17DZh-Z+fD-@%hTwY&jEzZ1yhF-qua3gKAJM3$PxLh%`N z^98E+cI5p8tp^~@f_ScIxRot@Nt{O_(6fp>=&+aJXYkU?3^`xxO3N1R7nBF;heIJA zQ4qcsaoXMf~TuA+1h+C{|ewWeg?N>Z7LB-$f7(W-?hfQ#ICwlq z*_IHFb5qZfZL%FMEx9jLb^kRxxUuNEFIjz}nEl{#Hf+XhO+L;mPu6>7AIp2Vx^j;% zB;#j}qOK&Q$-{_$)Y-)!d#B5uHQ`mBe9fU-bpW;Mbd+^%tKGweeb!eK{z_7b&1EHc zw;amM4Ed^&MFG;0^-~xeb*Y(=2f9~i<*|=?itYzdTvxlpR3a=iiP7WN!WFs8wuL@F zHuF{WI}%D!C!R4eF-hIs>aU`swLXrAg`J|HrcUbUP~1?vaX<-BeygJP;0VYc+EyWq zzw7PuMHEdt>SLOv2oF;7i*7{I3-V6dAIH0z&LXKU=i|ggFigl&@1XLg^Q);|p54aj z%}-zLUKKSA9hJ?`I-Y|1zIH{6SjB&!t+B!VD)2b=8cK*QnQTiE(1=B6CG)UqAAWJb zgv3|}8z9GxUA_X5W$TlB{sn|nW^K0V1uLcQo%xS`if;eV%UeJN8 z(YLcGXfRe*-WHoKR<9=8*ytD1RqvkwtZ#tjGkx-3lsi39XJ?If?>8i>4^7_3H zP$DJRs7zJTTihu#mq+}f_1}Mi_Oj|cLtvwgPRNQssXi*(_7IqqC_+PD$KR}3)Z86g zOP02XEs9{OU~%Mit99Zi0O!ja_JZEse}EZ6iJ}Hk4yM4=LdIc)*MZ5;&1vQ;eWOHU zW{9YKQ;ExTx&(zrL(sn5xYBJ`&E}swmXnL3-cu%gIB81CV@Z$Z^M?-AA<~vEF|b8w ztpp)K?~gj4=#fy)W#o15&b`$Swm7?|7$1utM?wnxCS!U&t{Ei|l?P=)7VWxevOh+* zjXux8hB9`bp=0pQSeWZ_URH0m)ZwI;j{J#Z{x0hc^H8Yk9XHFHUx)S&2ZHaoR6r*Z zgqV6(mm7RYtGtz0*q$}hR=U2lkf@VUOuw+P%vy4>^8@pabENdhJ_3r23p<)HRX&fg zPo<_@x@uFiQ@kaH!ZyTLvJ!^4Y)twD`7DbcG!Kj^F)%!NHF?8epW0zN{tk4qG!*7~ zD3a^bqCU+(`h@G~!nLGIsB8u7>difgk{|?gb)bPwTDwL4T5=jTdsx8)eILDB3s&kd z|Kjuxio14eMJwhFgv0I$(eq@n;=gX7AiG8hYAPN2sby~|ujA)<4?GQ0v!c|<+2{E2 zS4V$rba?Uxf096%TW=9!=D_Y{pLw#E=dHuYAP?k8*Dush8il$yE5&|Ld2D{rUI5yw zMLckx`DeX-Ha=ud?-?q}LF`UHqM7R~IW87&rh2^Bf-iMT(Y+5-^6*G_8s0o4Cp;?Rwr~jrnCDUM}cmpwDt1i z$efN$6nu$ib#1L&bhahCz!#S76*Fouw$+kQZ?EV2HiC34c=YxWxX(~yKj7X*xrkx& z2h`D;Q|RaRHmS&dM zCQq(A@{o?!SG@WzBW!wB{7mqbLP+O>X+uRta&p6T$x@glEW}FMcLPcYdV0iqAOCiU z4_Ah;eja?ol+gD>dbo{u>X=-KMB;MToWVJ8Bf`ODGI7z6nD-z2L8cN-4%vR<%ilU9dmev*-JwW788z6$ za5p~QFCFvM+G zbFtD?$R00Oq;e`&?&5Vkm)6VXPt~q!yxaJfh7(qj6u>x5Yl!<$F z{DzD?qe*|8zKjQ4tT$pc9B4iz%{#kf*V^o#-QZVcyYxQEAm2Eql&|m+GrhjVvdA}R z94YyBk<2s~d`!J&OhaNn*i9tYS6<&`EpqKXbSpY}UO3ydz?Y;7F;aG(@=&6-Ro9MC zFO^Grt!M2+GPGwqiD{QLv#Ckm_mcw7`wR&85u|3`#^OT8?H@B>Pc`&h@O`WcKMMDJ zC+?kGy|pG-hHxJ1Dn@8|1>h-8?6IDsS$?}psdP6v$v9|C<%X8oBi~8wBq)cMCBI3)VI8GCORkmzPDUlEb2kC-OIso&jyA_vZ>sD7>h^}) z+JAX_%+{WI7@lZIWqel)jX%9h1C*SvPKMi`nFjB72ux#j9@Q0`?B3PFCafy75$bCl z;(bpN{3?ZamRQTu3I3kwcgXe= zW!9ADHEBQ7G_PRzwlt}Q-nRCEc`VvoDZ5K#DVHuuR%wd>&yItAQ>86foPN(AX?`4x87*6AKs>JA2nKeYh! zKFo0)(i)QuW%d5pTP62S#wvxbP*WBoSXO5O)!%;2j(^;x*HfVN3a1f--M3hi%yHc= zg&mbvoCM}hL0Zhy4$aPzJ5L|z1&4WuV$)7xn@_FSG1FJIZ)LN*0e|C2#*n4&GV$i_ zP`l?4fS1X*n!@uQn2jz^k^SUhd;BQCC2>&wHFqhmgGRjTxG$?_gRqO3A#@dt)2I~w zz=6-xmkK^LD!|BZ)Y|!B5P^(IDF}s*p2R=9#P6k#sVwxkJ(m#@^Ck6}7MDq-WwNK- z(S5g9@0(0P3J^J4KRBu&5<2+UQA0~owj_Nlkt=KK1kg9?DS6+9mC-QX0*B2Xqh+r1uUY06!)q^2S zj0uES{@CepJ^8p~YG;^4&}P1asNawmoKJpZ?5?##<~D`K%1(nn6pC^PvVbZ+rKj&+ z-+G%ZC9v=9_tit}sWL1!-|Xd@OS=f9>3WRy8oQcqMy+6vu|C`8@&%9eM?(pvdp@u9 zk!9dcUm6Zl)hJtfXJ`F0m#=gsn*j^TGx+14jFBq1-*eb)?u0qwah!)S}1sh*{fF z*u)FPy0=#H$vw0?SXXLCMC1Zvn`_zgTN=2Hz@vI0o8Cr|npJRtZcIW~H4}mD%zDt{3H34gCur49@y~0hOdH z0}^L<5W#x3z<>X^_C;u~j7t1WP1}0Pj{vO@Y(6g2Ruhid;ux~L>lj5^XAtg4dm5+J znRE3v^cI7zzQuGAVkBn0w<5bH=w!c`6}3+3keyq5fwI_yy*&Gt^z?qD1+vjZhSE9u zJui81&`_pI_J=&1PmQ2!KBy@q%d&*O`W z>Z9Y z(cK6 ztCB}jTLMPfn)Lfg$A>!@lduJNiV3%NYWfFe^6G%o7CODSZ=uE!q|ZVQ?#`?n1t(Yj zno|kTLYTO&&BnoOf|~-cY4YgZY*@07OWIF4*hFfM%~v(S;5V1(O&i=>MuL~doUVvI z+g%;*x1BhE?tYjLNfm;&zT2I6^9j->w9)!gi6C#cqgyh)>3ANpvAe9GO|Wi@RB3zLNyh~ACFN=R15)zx){h=Nx)W?J5gHmwK}mTD ztKmtE2>UoOY0^EQWqKO?mo_9(ablwmAkifRz#s|GF1;a`i5{WM>Qwn86Y~5!GkEgK z{tz@%QnOIl@_Jk3`BAHp?9su5Nk9!GI1aY5p@l6+Cg|J6E}e` zIRj42^iQT6Kb$92nKXBTpo<|hPLi5Kj%Q9t&~d}}4eXcXV{e~JcFzuapBt(gLHhG% z7B*;vI@tR$(t0v*q4-u1YOIn59ON3@TjwD-Pvz?we1g@UKiOoy=UHHD^79#w#(E<@n>eH$kG0%`{G5(%cd-H|)1jDEQ9PIJ@>DJREwz-E(B(_H z)}e7zuajLk(IT8{$-nyElAfjMf@h9qRb6vS%u&RcmDm-NDiM}fi$AQCJ6qtmmOm}_?ZU|&Y1ns7 zLc2rR`ZgNkvN zzp~FiE3Jl^jm#k7l^n%}`VZUgDZ5A}S7!RBb|GUY79&ued4;viM-(7(+I0E3UAT5# zEJYaM=~r(?cV!5U@3*T<@|qoHMjaRAr`v+b{$bIeHMy@)PbO-pfSRhcW_Hjoi@ZQx z%#-YJb#_vWBs1Z04(+`2LAiecxow4GDW_P(I>cm1k?2M@FujA)5!Gh?}}y z`Tn|+cn#xXsTn~E=rbBF7-z_D78Pgmvjmq$2_Ada8I~AY`+V)woXd7Du}0=7U&bW; z(T-x$di2YvBoHEQwu7k1bgH(Lyqpbq`shzrE|Aa=FH4#ux4~Hxt((i>@LzvDkaj9O zgYHJ4sp7OLWk7pPhHN_9T`GpeH8F0D%1PJZS(!c7IJM@;Wn@|U5N5H~(OcT*-QyzN zl{nxZ!P)SERtyDqhb<{wSpBIZE$_3ksOphjhN$c*PW7~zTJ^saimM%d;mysPqfE*# z7-wD6zJe0+fp_o@Y;ZV4DuZu45g)F2_m@t1j z7ozz4L;F5_#6iWj$#i7NK>mHd|XvX-kxskLG$0Q%JBk*8UgYM zW570kg>n0k4P?OBGS57pT)hd*nYMP|<70DK&s`#O!o>9M(VWlbWewVXCTvyk8f7!M zh%`w2VXgn5`#ayZ?nb`QoYQDA|4o6S@iwvK;N$9yQk8VjV?|#Yi2%P(Td&La@u}H|`mrPAwSi@Oz zY3z|EqgutNvF--%riTPPCNxlqWQBAUly1qDlQ}9B_blo)evOv#Ve+iCHh1iU1!eSX zjv%Tcl5>$(fvDl^;hCxaYz0`kWz!Iq@q7FwV%{u6USP1Y1yK zGeW}$S$QPFdn2gt{4##vRIu~5!PIh0p0)c^NaqEd!{$Q0V~=DB1ZtP2;wlgTL)puW z^~ZITGOSrgkDa)<_!AHcE2z0H2m;N!W@7ovSEM>neg$6wxw+{WD`M04Ts=5QefnxO zZg9(Iux_K{oq9pm_FDRvI4K0b4LaQJ#aDRdzX|Q#G~YgD zPdA`ZaJ7PK4!M(jGlrhNYGy4*eJ%nJiungg{8^QQF-#H#W-IF+_J^X|PljP}{WkihL&H&}Yk+xS@#9(^e1;5BnP_;zU)JE9P=s75 z3$tya+#YWPdz*taD;$`=s6%zSJG-RGUpC)juS6fU!Pfdklc#9lrn10%a-doyu&ShIBTl6y=<<^ zvPELU#%=(|{tW&x3 zr;EO<*1JwPf0`67VaiR>uk@7J(wS0Ebmoxn>#*4|C9*_zN?6CwMGWnL{GI)YDLK;ij95S zu4~k|Cn9$ool$8u>KiyMSb9q9yj7gI#Qe~!I2)Eu=M{^d^`VzRXhx%;HJNL&O&!d; z{4M04`))dK9ZHWw(Ya>@q^Oxe3%8w*V${SccSS}kMNZInmY3ymE!(M+Y^p3wYAq!Q zsl0hPrJ8^JkjlNfYCl{t4?7aGOQ6R~B_77lpUz=W7c8L1&tQ?cMIOo|r`C`>_Bpw~ z>w&abVb;CUj55CYv?CT@peKNt+wi?CZozAv+30?+#V4X2xj{oM3EE$#PcR~G)^4U$ z1@L`zztqaFnANrL`^<5O1-X}czX_MnOtw8(AGh|V{k<%Njcz_)7TK&Ld-cTL$h$jx zsb03&G0OUJH@z^A(xx7hl=nTxwcT``eI4yHG1_YRD_1rG5;ocTiKC5$`_DJi2N#tV zb*a6EoSMopo10;?XSJB!`%aIoOLDHb&FgDCMRf*E>jzJhV1hQ$=0*AYFM$PLbwbsD zexy-x)3f7I{t@@YC(EmpA&sJX$0EBbKg;o3zhs=7t53@j?mQ`v-7uIPLQ-#!M!XzG03Nhd{Hufp zSd`mm{rvW($}e{q6%L1YDr4|PPShpBk)gC6rARiO1FU!MzBZ&E zV|_(L#m3O%y&+Sj2Zj0fgwL!7D!x-J)l0eQPV6yt>+oKax3`n6(@!z6G4Ok}Cd_|v zQ4pj#_4eGE=hF4o=8fj3s-A|0#&ewF<^*=ZdPJW`%8M8UwW(D~_Do!sBRy#AH7_RZ zY?eN@CpK}%okHovY_1BHx3&;V_(g|Rb*i1(5_Jpe+jF{df~4ZAcSh)owuO8EN#Z)& z-)ALEn3f#2vSe`j>^b>bZRqLI z7rQDf^;KC1y)Qi>z+Dy>Ho|B5ir;PXu6V?O#V?;yoe%8WIB(?kcw_Kb*%Tm!(@rf>3wS6^=RQA`I{$!$cvbZsk_ncv)9v)B?D zv}{N>HJ7bl;jE%}3_s*?f@g5*)|C>Xc^yuMgW9_oiE*c$21dL4bmR>mb@&&Qn6dV@W#fPV!Jzge`DNw zoO_(~10FGHuR}#05`F;bhV^e5-p9p}dOvnhWjwWazgS`I6S1}A?gKNU^pU(%tgU-P z#}$;=bD>&w48k&%2zu{j3FxCW&}m|ZPsn^}>V(xo4*znk!YYB!#OYk$JEHy58q^D> zQN4x*xy|kXE2sNYa_O?1r6-OkHrS~dD|Hoz?oz9|IOL}~9%&i)q`g<_pLlSP-_h}k z=^CI<%P*EGA`I~I)^<2M@5BqG$}nV~T@gG#ltVGWEh37Z!~70kG&ORJ<@*RJxEQRZ=tIP3oFd9NwR1(bI0_|ATPkZx@06yhPHk!3 zq$*qN=bP|9?=l_fF@4E$MPqhjNwK3XL16fNvOg4#W$bUd8uR*P)sFO#Im_q7%}96q z^1|QRO=5!xqS(1?2S2G~yq}g4cKMLx-CRx2dAhN1qH8#XRj)z&(tCGpXZ|Op(U5f) zT%_+T8&BiX$@EEhUpDdJiB;IKnZ?2mj*0_YYe6(&BWvD=#!t15->_PA$L*B;;-(hw z$v3s@v;ZDnc+KFY3f=>Q&KJWth8yd95B87t;br!uJeM9@7}L%(>v3k+OAn6S6de1c zGoD!ewtp-5P5>+3F*N7t*E#%bV;&x*J_)PDv@C-oOVbvkcx-FC7e5}W>>w4yz9qOeKB+Bz(#2<>H*U|j%_YV{FPF+KUuAO)=#@?Upg*& zW&TrV8E*Qq(wVh+*P7L1ft~FO0HLpgb9#5H>W;@o@?p(XlXnMnx-RtwukyNtF|n$S zJ7gH(tyXO2b>wIm%#Sp4BBY$jVNO%4A$?lXD8MnP_aD}Z$X0pBUnT zc1WNK&-)X4AQ?lSobfkvk3Y4IRx)fnym47v-2O|iTX*H9swSO0<%mq(oVDTlxHDCR zN$$gC_6sxdY4PhDAtxGx{1w#3^op)Q12Mg8GV?i`62yR{i!RJF$b!tIP>!NR}y`@BBSsa2adw4l&6LpJ`P@9c7<*nWQ!O3);Bg-*x2Md zSeq;0x4jcBqxf%;KE#W^HIxs2fHuP?q7iY@8+V*MXLnL9gO@sDGjQvM4_EkZADXjc zTe4-x+R{D~IZxzT^~tW7JlE04;mNh(2!9P|7=cG+93(c*_@o#H%yy^G&3}$vNpfaZ zSDA9o9a*{v`mt#rxjMvCQ8B|Kc=J4tx;gxzSg8Dn%^z<1g>s66-lF1@(y2c6 z;yt97CdKTf_jG!sfiG-vce<^oug79sw%FZNB3{%D67P16Ht6hok3woS&fXN*if@;= zxu=M#Q-W~f4KxGo_F0J9Y%R!H)kR~*vMz2tb`^G=EME7ROImmNoa)_KL+hQAJWo4p z>+(@@pSXu#(=EfuRC8crn|yqJLpAp4=Q5~pS>Kzru>}FdH*PGRMA$GKg`C>oi-eyDGDC^COaydO4KISnb>alr5?+dL+ zyxBBw#X@NL%BL8*lGNL(_Y$$PFGhh#HH|b1<#Br{Q!Rinsm=PL3Wbq?;lZm+=urIA z<>0#l#8Ij6r{Q5bWcj+zw4z)dcWCZL?7kxJe{U&uBG>p-f#dWEtdCK$u|)S=4|6?e zgH%U{7fHM0gjo8jsunp&CJqPerssi{@=h;~^os9*vYAL<0w}TxgHwvVk_3%;E&!gP zkB`3(>49u#S=02>^PyfKhn#_H;I8S>^`zt8>m^(UWfCo)E2|&Q_f+#T`&5SK98m8! zP*e`Db+6c6Et$^-L z&L3wG1#aECIu|e$?_0({Bxw$l=MjG6VCVBD0Yzoy@GDm^8VPTjYis2^y}V?++IkVY zS+RLL$QS7rtxx~k$?#CYQtj>AckiokDl33Q;yO&hd6#6fh;!%z(acAVoJ8Z|=Gz@ny-N*i zjMf<3htX$(WgmTY8!G63UU4vxP;erzJi@!yay`7IR#{%a%5QzeNu5DhI`R1OP$O%N zxThSQHR3@zi@@D7OE8q*<35|=x?_Fgan1l6*R$ehI#DNTO zRcuGy^3ikGJ^^Rc`2D+lm>KX>*+=yhBnEm7(d{Y&Ig1B|dv0i%p6}&}v8wReds!(C-&4%tpBw>?>SlLZq+kba}}aUw;WX+v)wgG#@f@RSV8 zfD4c3_m%0Qp@KP4L3_>Ja4f=2B~^XFp&=!&JFKUA`_~sAxD8Tm9zGh@hYkoz2nHq8 zFxPHA)KF-t33AM?m3O71B<*3ZVX>|A?%EYAeCh1#p^raDzAYdiB0{=R-fK9KqqO*E zUjXpXGa8!Dk0W%+Y<6PXx`1GRZd}yL8_>|G&Ecm0ZGBZq}9DhMOrhSu`6a0>gNXLe>zO0nuibB%$o4a zHyDD?qXjQ*rn&5WOXVZlybq6vGQ2*FRcU2@*ul#4L3~uAuLfsLIfySZ=O~+iiQsu{ zt&+0xZG>q&Gs7z(A@N_C#waNQQcAv)!coNNZk}A@<{i|lYoakD)?L|gyOpN+QiK043#I#)@$8-X9_5!=>PK=1`|&eFhh8atN7?EHIDKUw z0vuywc-jsg5-pg^^z-JB9^|S%l&9gF zh+Gc|J8nIFVBb}dwP4|r8FSF@6lbZ)@bvyJmRsk8m62OWeqiTtFc!wbFf%h>UoEf^ z7Z?9HF~K<=_6cEAPA{4}VkY7523CI;uX04ZBCiuL24`1yT6U$h6f5lUn9Xe#hmlH= zF!M`3Me(k37W7Q&7HRpJ=_^Or9bO-z!X zzj%={N=8b`aR2`OJ^S`uk3)*Lic4^a@A%RpdrtS;p3@x!t}#=?b7nGmCO)(G=T^K- z9ZUHk_t&{4SJ@3nY`zgHsB7!Zn*UG$S-Yd0_71$>quJ-l+>4Rg(6S-wt_Ya$6zF51 z&NSF{o%$YP=iDrRAMtc&FBlIymh;aJQR!5=#dwN%oh~JW;VYrYl(Zp5g+^AkfsM$g zixYw-Bh$EheAWx}OJmu@S3WCs^Ro<3H&8G`+G7lzK6uh2QP*5I-#cb=4{ED-d0Le;M^ra;awT_SGzFao*B?{v zt_zqcXI|=5ERp$A8rYLEEyerpgn4%*nORU5oNCPNKWU~iFv1i$ATv;Pwtk4g4ry>l7QfB<<8jZoD zIpB8gP6BI72+t*Np79-}4rHfi>f=c zUV5`bfUnRhC@>f=?4f#R4HEB~u}W9>*t}OR*+jAp*Jt0)+t;2Z8Ij*P2=|1~P^C$f z39+`Te`#$WfFW>oT!|{DA`*g`cyAv+xN`NX+hfU>k+SLxgJ!ZP&W%j_YcdV#B5aX( zi{X@r{q@<}J(=mfj&GvRXFJd!J-f=rF?D@NiURb&0!Y zp@7S6;f~jEkiGY6%ceIAOS;*q4Uff}e&bF(^-vqOkkF}Pk#>3KfsTkhFD^wYMebp- zn4%opYQE52%&xiw*d(jn0W-y`BJ12EEu+O8_^p{vF1k-V`fKSOLteA)SAwLS zqq?z|a;E1}J99;&pGAqo2#^>8>HuKnb7zk>;y~{09@#xVnXtoI0|UkzRYxTyC2t=w z(0T=ZHrN}XbG9_xz5w!@7k_QGho20JwfdBw?_*3X0YL{01+B7Y@Y!5{d9OUi99nud z&ZH3zTao7{y;@#$XCK~XAt_l_o|Ref6vYLmsYxioCKJ2 zEnXb)`Xi}$V|8S>VYU96&!HPe86PjMW-v%(txlQFY%;Buxp-7E^n1@mG_ME?Oy6Nj zQ)*eN?5w5YIjJ7OyN|oj67Q{O!XnK~X?|vUA;8$YJ2|S^9iXHRt6FdWz6t+dn9@5? zeLK)Pe16`Lc|chn%dLGCGyW!PLWZ*?J_Qsuog7mfOMw^f$_C^N!w&_n=pQezjGkWT zm_{RDnFwciNaFrei_^FnF#>{SMR?4!0W?(a(HYf zgD-y)2F)#+cDjii#e`JuOHxGPqMv}Swl)3L+rf85jKm$@gG95oD03+^GAS)pCB}W( z!oh46gJ=J;GtS(){exKaQS<2dXOI~W^R6&zY;&aP%T@ETcak!!Xke%~_O)=wx{U{I z(d9SjLD@YyexbM1@|co;YTV*uaAFX`qkJpl)yeF|LEdYPRJgU{;*g_1$#{skE_Bz< zveO_~mKD^yP-+NoVvNFM^F87@#XM?!_p<|xgW8MuFdC0_AKr&DdlNNbd_t2u4?`N31$_#hn#_Q>D(_oZym4Tk`tWNN*G0~4bW9+M9Fz; zgAzgqSzpxwvsPwfeKqg^a7*o?#dz6i)YFYoC8S$-noETC7g@g5=C|)v_L{^!SeR@y z(?y#-R`+=Cyk?hoX}@(*-yy0&?NL6wW&sFSU1j5Dr{vr;Z1kjU&os z%~HGlso$5Dg4b$qdMtx%-m=nraBYTrird~^=XZ(+oWGtuFB5|o@0C#`-K0O+AvET6 zoJK^g;qGE%o3PMZClFnI^Je)VJQ!2L0@F%5(wsie&5?m;uaR(M307*GbK(J$@sf|x zCIpFF63EQRpQYMY5Ts-_)gq7b0RiHB%`lRJ*He>0w#Qc9WDJ(=#;G>pHuzHIMpU7W z_daozq%LcUGg~g1gu5I)zZ|As@9fb`TAn%gdbcL+<)9ns4R&hnrk?O>0Z3_vjR!i+J_)>7T?+ zdoI!g2RK7QLiSKmMJ735vA$QXT~jnRP8;{AE; z*Z^m4)3&IVcCHo;bz}SIo?~x^&wIrZ?m!G_@0l$7Qe~xxtPr!TvWc1eJ>;W6YoHc4 zyC!~zkWgBcc5*D`>?R2ay6ug93NYG9_TjaulE3sJweUf+$VBU!kPxKD;ZkPxu(d~Q zFr~G`xdANF{X#>1 zV6mt&6qqUF=aFp4r_KE~REujLv{)wnlVet4SBYsU4qmDhQBO$IGiK?2a_Cv_iz83R z2?(PS>*xxk5x+#|FZ~jvVpLhng7Tiq*YFqRCo^%*#Aw|v?bW--=jCYk)B!6*Yhx_@ z+cNPB%dn5j1g8M1pB2nSF2+(PHHQf_YWU0n~xzv-$Qcu}X#whqH z-O%Pj`AM;=2|GoA7pu55-IoI6e^nRL!mPmqdGBPJX_&8jjW~|+z`1k7(P{S@x;EYy zlwm2z*$nUBU|x?YaH!q1b{Kj!Z>mXio|gP=fyKj)@}+6=erP-Vm3_a(RnbMoOh4OL z;5Pr!GC?P(C6YHsU%2qyCmPzoME95d0SI4lBJsiK0xXs6vRl0jpGL&t&BauhP%~zLQM}7Byl~jD0HFt#W#4W- zGdlYFHD0Na*lbCih`ZG1sdzM)Q#*T{zBzV1 zOLTFlhP`s;gmHoAYNz~$K$YeTQ{X~fG&D-Ai~(O+>8Qd+T5D1Q1X$tP*zkWVUB{joizUU!aI_8EM1|MymjYJWK`5X1lsHGH@bZJvh;t2_EMp4!T6zlzRpf?4}9;QaUS)p28d30 z>QuR_UBxZzg$wEJwXa(%0R@;TlZ>BUu55~}ILVu7jG}j2xq5oOV{c%Jq?-@>r!yz3 z%#8atAI$emo-mz!-aiZ`vUKK-|NBTmBg@Dg$?g@-d0>n--KzUs6t~0;QcMVpgl$Ko z%6lK*eLmgG+c_@IWZ{DD9PfI>-oG$&--S*csJawN;iLmv2p%2em+ubnDRLd_a53^mI{XOvjuNF%2iZ&tcK#4EvE+EL|U?#}VD`>d~4bYxR7#2Id#Jfyl&(*_I`ZIu5ok?a}oPff)S^GjFyQ)|ldOmy4y zX$p5rTGY?9SB61M;?i+>$q*?)=`7cYwdu6_d1zm1zuHvA zzM5gM>$sDvG}6=scj87mElEcZL7dSAZi}fSEl2@|+tKqc@r#k+DI$y12T;hA-D1g~%rxRd zDRTnqQIN9q?rMUtkHnYSgx6Ia*{GN=(_x(2Ii~Iv2)u{%;opgY;qLLu2?4mY(6Gza zYG_OhZeB!O+3vcGc7oLzQ^1~{S2+@t6zQ36SfI^&LLRUJg5mG(v8Bm!OQDm4VqpTE z8RH)K^kuW1{phTn*Jr~Di$v!vEW2iLV)I!Z{c>ixp!kmdNN%kLGkWuvh2pczHw5yS zY!X;<)tid08$NJ`sj?;Qx>{Q4OmH-;GTZ4vfH!oyOrB=V@ar9~|O_oRFxgx$Jgw6!8Id9#{;xfF61$jH=v zNyN$@x6qJpgTe6(=_0|#{`7=M_`Qo88mu)OFykd6%J`4lbQg9 z$paZ>u=J*ht3W?~{CE#FHM^ai9lI*ccOWaO0@wZtF&si2AN5X#0e;ZwJP*tmepv(# ziit>wkZ7OVbki=sO#GAS>Jl9)7K;-ha&#fR(0y^(ktRj~J{WyvkPpTdm4H5r3=>GC z{aV^J|Cgm*_{pFnt69!c`XNoPH(>M+6boxUI?bKW)$@7+X4H+&^9f@}a}iW_*q9ml z#*F<>MkN0#+fAAa*qd+TvjceZ!;P%ao+=bZJFCyosFylkyriEhmvbZ0S1$5SJ(Dql z0HksheV}Soxlj`+|5rQ*9P}9YNds-OCXnvzKeH zq~J*Qq2SSf3lw`S&RO5s%-bHXRMe}*rf=T_$Q#sYu$@3qov`0OPY!=eCHQ`aMRCxo z$ko1gGYFr7fe~~CF)1H1kbCgWwdh}4=zUvo6oRoR5H|CZ&7$Fr4b`P1pu-(;~VbmH;LGlzNFaQkd^-f-Y~?JBdW^f&D~RO$Y|1;bS8M?|NQhU zf??C?i>3zo)+5=>%mJ7e#|5EQ9V0^kQ?NfAr9Ek+)~_?@{@7wj2}vL+A%7gSEL64_ zU*X^jF!6i-Tv|b1o)q%xo}Ql1ySlXhYhL~T-Z~!A>pz-Z9M=00I1!mWCHf!|R0uW!qD1C|&@e$SL~01uJM#OsCy7(^%AUn^Py5b$xWzk-0j zeUunUZZljp25}xxhl7-2JYK90@eI%NS3~L@Y+<%E*L;cKz>0CA5B{t$zaQ~;bt;Oa zThdN8(0soABUkl!0DwLuGY82k2kbs%k~FbCO{$qYyiRHr24_1_u`PnV{7S3GR_nsw zy;5v#p+L{g%|W!zgy{6OH6?ZR;Qy-A|5&0Ray=YS`{+=ZSdia=BNVh`SgbTeZqy9_ z@JzW-D1czdZ4VxkIv6YgOQXg-5cw4v_yZ~E1_y)kPDK@ z`DmU5KKoun?(}cOkFNqc@Dv0{wAfXd78Qno$+}N z;HN#TM$*c!*jjg@Ft-sb3ICKaN&UYthyVB188G|=F`O&#{oWRas_F8$nJjU+r%Ssuf<^+n{12Xm-^W&{k{jrFeU(} z3lhez$RK02jBw#k)IWuP5dHm8jC=5tFW)Q&6!R4eDL4O_1pKDFzJJ*_QL%x&oX*A> zWb*YzW`@SzO-kUzd>kBPIdn)C0kxKvA`=rCegU=qT*Nj@v_Y=NCp4cZ=pn?xzQ}R> z+Z@UPe(XOyQ(ZKg2T^5`R@WpM5arH~FYB_c?#hF)$yn!G_Tm`QI%LwbFiWEw*{mx3 z^j=r{^e`=aw){a*Ci*{33bxuG?t-iBT-0&&A@H1y^DgJ>WAZ>Q7;gE}1Yt?ws_vhr zxb-I&piV+xHSpIB{I;pS0?6KJB<)7y?~{N-CVN;-6Z&7U!GE@mrP!ajAQw<5)SmtO zIsE+mc%ty6{*XyuJ9wLYMaP zTS$o#rtzaeERBD0EX)et#@_#^PvpA{2g`KQPnemA`5!l>`{TR6Z(R=g@=~viu;bA4 zEuXk>semvaAIT1v=!<}OG7mSduxyk{c}3){>w1}AgAxhiQ(t0_zWl)#h;sblu#IrN zR5FL@_DV#JdF7ID9NOU_GTZ1neAhfg31`zexTIlKub}wL=<=`N!;XI;{7@WA3S1@C zbh8=Z42>`D_Qx=J;NxPQE$n%=+Y<~w1fdLjFVLJ*^tgLk`1?^x9oRp ziyv6A6I52+yVC1(r-R@jhs-V4>1kZB@E=13nwb$dP4l0)Y1j4^J5QC8U@~>9q+s9` zi-1uw7t+a!ia;`1z;ffltj>yiQoC!mb&r6p3uvv z7#D^0If3}{Sgsa>id2SlSp?AbG*urNQ8vh}1yt`Avo4H?u`fmJ~kvYmdhl?53QP+XWh=I3-^(Bwl~Vcvx? zy%JaU!?h9Gsmj#s(0}Eo5sOkpQ-^DsY8(_b1tl*#O}>4s9M>4_Pe`!jBA2r_~&gFzRBr;87$a_2dAb?d0>8+Gs3nSB&wU=CWKL@4TH+` zMheEM;Qo!g>Z*T)xiZbN2mSOE=!>>2GM>$6+dZ zAa|z$Pz3HO)dcall(C3KUUu$#y}Zrz8JsTKrA$gA4NXtM;6HeI7(ePyI!z!iXc~lg zxScjL|&EuJ``b}vb>-5}Csz$Noh%i=^= zBm5H(UGzXtcle&$=~9CI$MlA^9L$Qa0T@lWRds5wFZ!;yeBPj#|E{ zOOm-Y1c(r|o?a^>9+Whe&M$-o4ox=*f7U}oRKh_J=b*#z&#O;=lRtBXfeY&wA&05F zRqD=tUajU)7k;|0?<&-LW^gsv^pJ?V%_ox(r5p7;v2J`lKR?{eG!^fzv&AuC#3)sJ zd>mTNgpl@^Y5wf{= zUNJ#jbE2^Aaf@)=^w>RqR)D+(s|3gH*mfCB$TBi%YD@HtzpS4+&P-a?z7o^1TXfdf zbf+JrRZHC)429prP2mzDM(VZ4j_x>}l~{w$(mQ6m8r4Ayoi*+S;W}VJGYk9PYfej+ z?Ivy*BdwDKP1Ak7K4Tk85AHa?E`CkvcDFh4B6#`}T}XAP!sf*W#+vWD&V7uq#X{23 zK=@R9V4>S*Go=dyFlJsgYilzRr=kWMCc7lu*Zw!^;@5(@ zd2?N63~4E&4xhi)&gxU{cAbRyi_~2Qj&-2?Ru?BkIulf<$#g2*3rUDGbsj&U zbh!!~D;_+|-`UdxuF-o_dBVvC%^N{@XBS~J8>6E)ZCLvC@en(p< z>SjNYTir;jmpTUCT?k!2guGv=$~*twgKVS0Qwk0l9I3mR&P`&j4NbmiNnOV?(rCV< z_}k&Z6pIvGT$tkGR{$FzfJD z^Oy;5Fkwuu4hypcM@6wgg;bJwR?M9{ca~RIZ>}bnAO_WAgPIoQ0S3KTeA|m_n-3=- zNL6i8Gt=!NjT~g-X7{M=u*FWlS8*arH}XIM;N>l{$sx znKmAuRN6U1Cgi@5PE0SZ|EVP*Awvc@B0z#R)c^&li*bDVp`#A3j@S=-2yr6!1l4T2 z&2>D8_GGDdvm#g*zm!5k5G!=om?~X9gz-4T>uduN1{E$+GP3))xQKTaj0Rn3*D4%? z0mZKpEbO}9A{>dbeqi9=mjA1J7JLdGp-VC3dInvq7Jv7{d7sDEc3LmY#chb1tXu|aETX~Hf8rzOi=P3?@Qb)Uvz!?=bK9~wbefZoJ zElwaklkbho)BU-nT=chuiL&HoEz%raqr!{ECw&Lmb#ej~E`|`ZbOL!^#w1c132gW| z)MH;fBDQlcu1S!qYdpWK7~(r%ghhE=W{|*MX5e6xzIZ=iGk)Y#b6$+qu^8phzPOe5 z4nmyW(IQLU@dR5-rT_&09e+_RC42Q)*h@kYsUbR{D5KHJ5e z45Y>=BMB7kJJUtp2nj(0mPom--Lvu~o_s`^I)xI4D#X;@STENQe=h3x{(QVwiKj$* z^~)5eQ~=3c$j0M+4&Qes7@tPiUiy$)JePo3Q=PLk2JdpR4l%AD1At(pCU|Fs@R4zW z1J2vUJ6MKl^v2p!Bq;S|8kxCsFHBym7z}?wbzxi!^u_n#rB+s{rduFQqzth8sLLaH_{&H#b~iJL#1E|H z$4`k2cqDY9Y)H);7%7I@#)8|~ye_@=g&k!J&uH!ple~~7<~}I*pnM;2dt6!;S-*j`*zm*J`*1t zquXGh;84}p=4atidsC%GYe6G!!7r1f$HUGiuki2*c$X-9d&JWSxxTgR$KcJ!#gg=4 z-urBMA@iyxSV7UkEUW4mP;%2mn9HDov9oWkg>08y6H><(ExgIC1_#|f)+>^vdFa*C z$C_CdFTTDZzLi?HH5$FO#@{-U3^QrcK0Q6Yx1!cjFdU{t5lHwMXN6YOA~SO{GBT19 zrn$Mel%Z9l%!!MB5G0f8_+$t9^1^2K{$AVm?Y%Mgt0%kj!Pu~yp`L3$I-Qb4Oxq!q zJ=r0OZ~ioR{)};}_KEuBqQT(=+A>e)EFu#luf@Av=Uo@E-r6s^H)zG&Pa1m8$xw^A z7vvv|^1@<5!@^h~h&^W0J8HLa+-Y3i$|_S@R(6MpzWxG|lAGS~Up7O30O*r~XZ=hY zBZk>p*WP{MPv1JF`Jp#Yn643ZpEcJ5YHLR}T86_)g!_r6dWs?sN?ntOvWjw55U$bA z1Trx4E-1?T{#&mSWuF=`$&Mm9xK~+X0s!6p#}&sX^P?6pi|QhyIqq0Da{bXs9^Q&< z>r0!lr}Ct)HwB$t-6P~aS=uZ3~xk9q3sQF+Zhm6S)NvDpyiI3hcIe8_x8VaD5bq302DjV%O3V9wLDy^w5)Gm58 zr$d5o=vP8ic~k`7>ixW0kiS*u!@C3o`tD;X+2;uehw5IUUJ zh5@e1I>s+Io$yYE74dAw1rgSs)sTmSKU45m^^XQB(|p5P@LNi-;v1$R*i zRMkFVd=y4fhXFXXA3uI%K76?KJSDi@{GQ9Uz{w!^(A|B06oWUZA!l}04Nme)CTISy zHCf*bt*vkMnM)O>hH?X!%JQ@v#%OyC_ji=l^=k=blVaXv_cJCu=MCenT;N^4FV)sX z-0R$9EiNago=AGLmIX@K64C7J?4jY|%&TMX85tRwA)mhxMv2Uo^IIGl$6rQB4jbF0 z-h8q6M43iahGwr~*@>ul7Cb1smM(CD5b;uVW`0}}rit1@pV@qOP+){7tow82m$J_` zeWeOODoQr2h;<2o9Tnu<`L%X|JQ!jp^yQQ@n0d%3V8t2Qwzs@~l&`HLkMhkfc5QMW zPcRtg+DrNR#Kxya!hz#L8ek2V3-MzNR^JRjD zq#>r7cfu#~g_3r|$CMNG@g2C{PHhDjwf2&ZzP|5oR9i+btygF@vyh_sl^9I)i?S{cIZL#r-38#$UjriM73oTdAf$ zB6tcazXU%bfQKIZx_jf70ijM{8e5)Vw;YA7{Db?=r3GbQSjv z>sIumu?Q~RwnBew=fhJDPByAL^B-VUB|V`e{XS1MqvNc0@P0g>zA$NU=PCk>(7fH^1uZ&h-DTuSsnQMz@%ZaB-C) zxa#v8x>KGhUr5xb(c9BkkUcs5MTWfG zWvS%YuGDOU+xd`ka_>UK4o=1%mq~s<-vwiA-sJnRvMzp;N^dxM6(OwAVRiK!s@bE^ z`Q-Er{wp~co=7DRv!9ELg%p&P7xq~=Iu;_zy5o?Zgg6B$KtEf;hiDG#00 zcE%Z-n1~6Y5kB_}y?;MkIGIZQRORUW6urAQHM0~`7Fr&z_EY0A9fbtOZ@lm`1rSRHbCE=W;lAZ^U4nI)iKu3E3oh@ z&-E9a(BL;5qIsRHu$LWTclp~rJq$3Z(`sxVq>+}{6Ggg5x89q)Z$0$)edNls$CCf# z0(>=N>I3MtFHkNV90DA!De<8<@vmMsVz@ZG8$HG>U@lI;o%5Gn`67Idz7_qN7xBr6fs> z$x2CsxeYJx*ToDuVhDkKg++aAJGw1~0*Z$X@n1qmO7R)gbwCGEML53CkqVr*sg1@b zFMz|lOqu#?zxE$4G6N2nEVS<{7BXz2I-brIo|Ds}2DkSQ-v|sNWkYz6SD!V>Y%8rG zcF(|5R4;z7zYB#SH1d@heJ0p4MUkOl^FoNu?-@j#>aK6}fdRYi$Jve`UoP8x6}AzJ z1~3Z>lUTtMq+_}s6cq8HCIDItDErF3G(}3rYbggtwrb5jd9lE~0eh)o8?*T!Zg=IHr>Bs!7qX`)iqUv#|d>fetq!6VCq^4~* zz!;%?9OL590xy5}*Ff&t-w5PjzYhRMA=l0>dOuKdzJ5;jAAb&BH^c+()OGTkXOAPL zN=%KflDCViAhe%`m!32{`zZhivF=-9*ucwGvbSHC!2gat<6-ve;MnhPdJM@e4u0f6 z3XL{?7H-BLA&&${HGBtJABX3T?{3(O-rxHd{JC$wy8vo@o13l-S4kA3sj11#&aSY~ zULhkbEr0T4_$VuPF7ibaEuCbuERbDX`;EZ<{j%pG+_VFfci9mQd=R0fKPP-?OADW= z))+R+`}29f;*0}`5>U-J@)?*bKgIO$9FOl`{QVYuyXpUf=lBL-VSaIPYU)h@-H`TS zyLVIlP0;Pvn+P{VRW$;**WJZ-4{BQ`Ix?8PvG#B1_`)$@igEPmQ1r0h2=24}1{{Fv zlzUbq8b-Fe;^*Sit_xz05^nB4EI9(du(g?r7T*3!yww3o47}CY0aEwvbLfBiIYg#= zfk7gftWMk>0;{Cabw0E$u?+|93{-RDB|?D{7%(@-muFCa!+nM_fX9doQAK6|6hiis z=SK?dXa3OI#F{9Oe$oN7tsBM@+>wDV|HnpQtPn=iH5pSyU>F|^4h>~gP*6a?Ek;I3 zpl?Y3O>hh7V?W&xQvAapum?}`U7JA6Vi1R5*sl$@zaLu~tj$(2C7KnE<0tC^HxYOH ztMEU0jwq(Bt&REU(Nl2auU@^X0R8m-)s2T6VsD@KXSC>T_`_Fu{x^LU{I>`TeAlgp za1c-_F!uvh2mem2gROoEl4zx`p9-X3*{`1?`^TRHnI578sbai(E-DD2kJjZ0T-@HX z$SngAEz^!#0W}A(md|eW|Blsx3>w+DE=s|rM!U?rs# z6c0|MTs2aFV)OSr8^p}-lKAIl9{F70KXwlgnO*{6AY~`7aZH9)%0HkyJ(u z7sc%l$B|*<4;q?H{tsu29TZ8d=EYA4RSg4kiTnn#b}=Dt=F+cD3)u8ORQS(s=KojE z+3iwS!e2WfuB6bx0JP*@on6*YPgYUa6X0(K+C z;>rKYxG}J>$b^Q5N(0I(Eqz5=M&`!fM0r8K{^b5Kke&m9mCQ6I?$SVx(gSNwu*N;*kB+F*-3gYf7mJPG=4H(?oFkaN6rY(nI~K=UX+}tm4Ov?5PB%KIM9F1gPGv3 z1N9-vXZ{^1EEC8ABLNhoHEl|UDO+30pAhjBV;AnU_ZazXus|P~W`P>uOPjtcxM*m3 z#C8CC5m3$eHSw+W&nZB3a}!V4OJ<9t3)A{hP4wIQ6PH!7{^*(V&q1E+bG7#HMA1OC zgpUhvfcPDmwt171n*(8y#MqSHNW{itgaNhSC%PbSyb1ly7rTraKbe?c-FF8_*X82%~Ma`a~^?8wo%)1gg_Hg1+?9i z(j47+lboFqF!xZP`Q5Bsia`n@hthm2&zVA|1;Ijr(;5sLIby!#y>tqafF-Nrj}Uh) zN$rs(_NKEe)`i@4_^!6ehi6xHop-WvvG!eMcpwXG5$?=Jds>VYk_dW|VtQ`-?J(Yn zGQfJk0S-exfOlc+g8V?~M0OZ!f~O{QlRGooHF6n(a$=NMpCSZ71Vo;5FR1Ki2cAmC zEenstGHDySr4SH6!}ibacdZr*WqGUC^Dua6B%~2>R<7LI^w>+e z44qwvp$p~z!`_?6L*0J=<0TRbAwrglC|g;RwIs?~L|H3)mMQx-!?dXEl6{SkY}uJ% zMoF^o`#9)|V=6B8gx$pP;{=Dzr-~0al`Tafmr-#a1^Lm|gu5-@yJkRT#=NXT- zhU~Ne|43VN68u}-Ye6mcW)HQsQ#pERu~-yQ_iF?n z(Zotvx{xd)DO?<2`*v5TfB_%dQ)sIUTqUK;bbk!&f1iGUhij&j%Ry7s(GYZ;xdEWl z9ZD0|H2%e((z$`%2;1+6?$u>RD4%oe&p+F#wfLZwL868qT&P4k#3!m9zJ6j_V2GP#9pTdAvTV5VL ztsOSF@(E3N#70E|aMTau;OU=OD#d!?{LI@MJ$g2+@fS(}mw}2iEa*UL0LceOEd+$f zeCS!Aiv!xe>l7iFg{U>+y2*F^`ka_=^%Row&t&s^6!L8(Cluh67~AEkhex4_*Zt-Z&RV74l_8WV{8Zz&LH zkzyW7VRri*`2J-)Mmw3LOSkQSJ~>A{u!w!S6HyR}{^>oYFw zEwY>tRmCq4833=FmR9fXZppRqsOYAJS3u_&@2KO|$oj6zW6=`Sz@0u1Yz4fTpZYWS z6Xs65CmM9AU!RHAYX%g$q#P|usd8ES{Jpw;UFuu_qUUFJqC$GENEVs-tdtMT3|I!V z_51)HM#O8(BX0Cj{gGS64;mCVKV2VUuuq?!j!=nx#VT~e)zvliy}Ml!U{0C3)T~{5 za>9pYMu6HC^k?DsvN-Wy5Y0o>F?B#ity2KIowf$IfvUMofitxMAyHlV8Z1d)oGI7-jpZSz-q!&aU#5Q`j!Ei(@3DWt34<=lMCLx^0 zYvhgHif-q{9_E(S28?$hV8+dT)h9rYavzWjMas#w16TDkw@>}#+T^#W({pFr=TrT3 z-5Kly62-rS@+z4G9p*d*NR%7%Y~d7MB-J?z1j+~9h|*4wREH5)7IXQ(9Q`jA zv_I*Py(&m1xQ=&D238sYyZ4!d)6S5H*tqU2tr6=vZD9M!0zUv%Fe#@W`1-^Z0}SB} zp`dd7VJf9#6C&FC$%8_ZOfX`uX#|263)tI?8@SL1+Q1%U;uSqW%lPiv<(o1Gzf)?B zx_K9I{{;y)Wmq0V8Uq%MP!Wpo0l0Uc*4t0_sx>wpL?e`V+R7Dm~dXDhY!o_ zI(UKCm9QM|UC1wOr(fPzjaZ=ubT0>_{IT9=fTr+9bniE6+Ml0?@_3UJzo_!|qNp(y z@SAe?_nW788Rgy*#F0R-t9~$Z4}<~N{rKnhJm(lXKN)`v<#I?@Grbo1G*b0PUo+$B zQ^J!0$LQ#{Aq`Rg>s|kNbvAGV98PsV%it((V{U6@q|JrucAilT@XnKzj&_5({8lGT zguWUqeY$G_J*Io@k&Yu9C|f2UurWMIxiv^;0d1`(f=~YxEfQu1N~igJBZn-J&S1~Q zc8|fug$@)jN(05OI$G_&RX*C{k44W)6w6Ndq?|IS0xIN<&R$)oBpjX zM&R7;)lU8&GPqbEk~>q=*8g7c1!WbBM^C8wk1T=8yM8!ZspwIuI;r+f~-F-@8$&s8M=%MEO(->9v8W^yafAYTvNlcyxtMoJI>enGo zD*Z`E|4KXf?_CG_6javzH{0b=JXi$-G<`wK9k@rp6=@+e4pKqjy zWp$iob+@usJw%;Uw>I5q9k65}l@HFwP1VQF9UNii1U-$YqbQNB0Z938BO6$KnipxQ zoIuLv9N64D*z|t>4E5M?PG+hdu}8~;H?NPCj<}l=l1pw8lHFWAUd5=uL@GB?&B+FD zr+n6DJJ9n>{*<=jcE9BYkiX9jTo2LLNN?T0i?)q%oH-B_9$Asn!*OV(lU@o80h>9$ z)q)&maz}DY$6t;ML2Nb#0wr!u_tH=<&{wJ-;2rD zGC2z*^kd&SFBhL&gGzYW!+_`RVJm{iIzoQXX#(aH1L$4|hHml8nT$sW{=HCM#Mhw} zNKttv{m}+OdWdV|7YVLDe*GLLWuup0n8sg*qsE561(oOFoQ&VzvZ>x1e=!q8sti6z zXMbo$;3EdjmLN&G`zmb*m8|&qR8D;xUQjDla9~3E&MM44lrGQP5m0-p!;dQ{-m??`p` zsmQeAAeuHRQtnj(CLk47T>dr&d)m5q@ED{}X)(oSuGz$2~`J`wLEf-I?r{=Z|(O zu2GQJtsw8kVYW@NF#RBr$_4U}2bKoJ7qXThU#k8)*h#54;BLUa%szVX79O05@}cwbkeEv)&nI zmOF$7J6G}wwC$y7*`hF$%4W%iJ+}?XLVVZk?rskxL$Be5FT+;4YEaRt_#-qS7KaRkKQ~9XN<@$NH#I|v_c`juEe{;0&;kSl3vqa|r6zJzUaQ4>KV0Mu? zFPgFW{CTiXuM#{RPjmMd%0jo1nSn%_N~asKsXR&5unqUor0x}jGAwMdV?wT}0$uU# z(;o4iQ}v@KJKm&Atewjue|4Sd@|J2A=e}`+c4MYbW8LS>R?X! z@S7ToTliI3yvBC*Y}TB5d98~Fwtx@v6;&A5vo_LUxf>$P+pWr)EWrUZ3l0kK^Jndt+tX zp+X95yn;q$XK3LuZi*dyfkYR~=@xde^D5mFMR&DWW;2GP3e^;!P{o{UiOZ8kq7?iW zBAsp+y;zxpbxvS><78yN$Bs%DG^&`A->3wTbr^XR`2kp0+)FefZdCTKlIIrW3CMJ` zmM>qLK}B+|T{)MGo0K$;i+x4S5|a);tE7Sx;gYoI%Q#3PrRJKiBLsSiQjJkp1ao~J zJ9iDJcQPNcq4qGQxw4`BpF;}@RxQjsX1@^EsXt3R0?_Zd}F&=@;!EfZNQI;8XUoUXsyF2LfQR7@m|b8yz@?}=?S9y zSLAwcy;lt(>L}2^S-1>)dbk>Ula=>d?xHxW)OdnGp7HKvViHjv;XN{&m@4_B z{pgvOlLd=Po3rz4lFr>CwwI+(t4_g|X*euTK@T!UVF--p_3G^gF3F$ScQe-1M-aqd-ZTOSZ+v$-Gh6;>OTP*%&LP?&wt+$U zn7MTOsCZRX#m2InCV}z9lE<2l<&s6UtyyMW7{S=s`pZ;Xc%MT^?4cDyL@XPjh&)Z##Eykcv*Fvh>F3zwW@6@j=yFP70 z%4aesyIAvDTGzH%cben1=3NGSQ<4robU$f1GHR1|6`RY4BHcQJ-Yo+`GiEkf3!~Ab zrdKNvC-Z8{Axox9SvhI%?fY^+$H8~iqmQxmbEsQr7cmSNzdR5z>)2i+`(=d2X!l2X zjl$xwu8vkR%t2qRQRdO$UN=H5&TQZ{EAyd)znLhA5r20OXFR&!d^rh>@#8F56-Za0 zx9@pgERxtC3$U+5G!gj-rh^H}_gD?TR*v?QU3c zD(%YRaJFJ2-_-O_0+kbkI$O)bi=`cKHH~jd5sHhCwRMYl7yNEJ4t*4FN_NBX;G19G z)SJJfj#pK03wd~bcbz<;UN;p^<$E7*&f{|8;#FQQV#dJQ?iR*`^RXkJ@q7MheslNM zL+7n`wF`t$avJW%y{0`4yFz>jttl0lL}N8(KS5yc9ezGBXK^fPTrP99x(~GTkb_J= z?5s-A_V?1Q(3_d}(!aMe^;~kQe`955#B=GK8kSFNuh0Qg{3r?7J_8az(QnMiJBaPM zmL?pq-4sX3Cv5#jIdHa3gHc}9>^soD{Bau{>u%b$yHvb{=6z!~`7_(hHZprrWhhIJ z7XQe8^QVmCiGKR&zHJP%wcA;e?<~E#i0#IXBRl6_`4OX?n7RUlRu|lKVu`XyRsx8{ zOre}{vyS*N1?6q0YJnoMdMebtY9LVSYZKSK_)C}&rvp5Q17GV>Q`*9*hh#Pp*kU`K z^TMaC4aE9^;5KHG(G+rx+|=z8z<(ph8> zS0SPzE;-=7n7rr4`+UpN8`--4pRDdntfnN`9=(`3%f{B3!nE4~_7mIDc_S&3bZP;Y}T@R+nR|MdRL+ z;jr~S@>~wtMcQ6KiX9d`YqGO;0H3a!;(MemZy)Rq>dUh5W9Kx}RuKXBqhRIZVOBp7Gh-z$O1uhP8fl8^KL3p zH}TmjoB1IbIFxQcqH^ZqletLLSevCsv2#BV>6FFdGx_gSvra8ObZt=B!dnfIb<(v3rlbJG5+{hWgR;`7qdycFiCD<{< zfgC8}i>R-iOJjI;Q)kkH+4(QnHe=3_6+7j2XqU%Em9cu-!>xYzmhF?!qQ_#PDm?gD zccl0p-QF`uTyrN($t-8la~mOss;nU7@XhvHn9ue$MbBY`8uc!~pR|ghpHhjSEL~D< z2aNJ`&37DIY)cQ+?M0}}7avc%z?c%zu4S4OZrm(lpwhm6Xq`E2NTkq6(oiGE>gD}M z^y9})rG`agAY99v%9y^a=WD(FdqFVCt=Y48!+X?;NLn)ptWcTjNvCnLwAYbH(~S4CvvM7qU$(yDz))jh8|z>t0Ne%MOZC38YXJs32*RE)d)yfHuhke zGCt4gMY`StCce>;&06u{#i^8v2<5-=VsGEk{9vdup%_!7H{{5z{65ZqZ0RLr)+1|sXSXEwVr!N;Wp#%#WqVqt3t?x!#^zF(yc zt9{MSYc*~z1;^>!9_p~ZQIC&DFGcU*(05PU#fhgB6cayJ5POyoclgZnGBDtp;4;bDN7hhX*1^c=K2*l~h@E<#5d5& z2kGB)_T>L&&n0~~=FuD%7h`v1WTmI{%F8l`M=1M8*OEvu47S>@hhU#lwkUo^N|`J> zQoQJRdt)LdG}c^yG73ri3gvPoY1K6YnX>&bVqEGDi6adb8ss4@a7B&ZkWx-5Vgq*Z zU-~}$QBINnM&o|Ll4G-c(IC%K!0D0F5v=k?r3Lg;d4Z>ANY!d1l9p!Ux;fO+%M+_S zfXmk5k|+x2wnB8SkkGsGPz7RWtUQ?f&{NCVh>2b-Si0@8LO)5GDOI@)_qd+-E&%T- z9@n6f_Aa3n)>y+AKG7m$Su8=6kam@d#!=pRo_F>tE<_ zxVVYrlRSmm0j?^@SReeeSw~Xyy0rIYS{nbA)bt|fctsMYl>b;WDJ+70JGrB*sYtUJ zbklV5_LVWjPwhblZju-nzwGPqDujSbY3B9`yrwYb?|(N`c&Ds<*7Zure|Vt*^(IZ6V11@$%8dporAN~8&|5LL%XqQ>#z<~2s zZ8`Z?h1LMOhq-Be2g&uh57kBF9O_UGb^~^4RVR%lp)!~3xu?Rb$6&`u@4q!>67k#^ zlEPF~5uJZG#vZjK=N+$j-Zps$5&*Wk8CJwJ*yUK^3aYC^lRVOsw$BSfC<}2hGP4!s9w*YB+#VTCBlWw*7LwH33m(lic|rCT zMEFelluAoKaq~gXv`OeOIa-%JVWh9ZDn|K)*1MqEH!s02*RRGQ`ED@T%~UwN%+h(f zl!5EpBKW1Hcnq7oP$I@F#42=5FExQM-m~>KUq#B zifC=f97h}o?CYQT{Ad8%82WtTThh!0H{}}Sv_`nnt1nzwMbdaP&{I-M7sOoaUUHR#U3k%>E*>W76Hyl!Aw`-WA2#nC zVgpkl_G3!^fjj4Czv#OC(X1=K!ZF0>2%Y^MexzqdAVz&6sbJ+RTy5rUv4pK(zMsA- zN1@055#yGcNqM)&PkO`i=PxxV2|qHrbGB4-(eVY9(%N_A!RCyeQXRh4k#|IHwnr1X zW@<;d!oQ7sWH?Go7@w47U$R=2Pv8!NlDGUmcYD$cbo zD6vK%{ALw4R$SH;<%p|bfTJ*b730P}&)7FS4i+eQM%juD$GiE9u}H*Asqu0@ezAxK zFwc(fiYz)C%S(NHbCkLoHNhW&qrW8lLv0;>YiYT(HoR|-4yTdkqwca0kZ0-E?BBVX zLcWo6rrb=W-~8~X7x%Y9gCm~#+ayPv$JZ-W2fY!}DmX)^F1p@_>jyb)hL=s416C+X zw#n;Q9gP?;rncV}%E?wpb|Sik0Ng;`lN5K#K_fig{H%K=oV78cC5Nxp(IVmPN5Arm8bOl><9M^ReD9p_okU2+FgvcVirD3IsL}`190@R=p<>}4XQ`T_+pJC zz?X+h)EQ<_BZT;(O}hJIH_SLzYGv^0fki8$QtfRu-j|@O#@!#U6GV=d&3baXDa|Kn zti}xSp{&{=UD=u11pe{eO7vy;Q`#-9`RsD_O2vUK((ddnm1eh3D7!}65*ve(9c7%+ z0znEaihA<>vx!~BtB&Q0vp%&$H!Mt7v@k7iIB})WF*vcBhud;yE%?dkxT}nKmF8SP z>6sP>OQ^Em*YFc(0NB>DZhj}Xw5!!{sRCKZNG*r8zUl!gF1Bxl<&NH{Wvwws6wLOsVfE7RI7$0#%Y^`^ap3_E`@|eK zDcx@8bq}OJbV?Dim04ptpw9ytj`Z}t}f$XYw58gm&t9oKIJsxt5AfTlH!z@@OjK$?)tLu1Uxr+ z&Se(Wk_$m;@S_1He23#76a{FD1Bs_9#CMGpv8_aO?Ga*clR?QI7Hwu};}o#EatFv& zCBzi5FOQ*^Qw+zxv`XpLUm+DSy>o%7xv?iZo)42z;lfwgHjy64s{oFFk^%iM=4wDX zDvH>b&?W19N2nH2c0OQp%Me8x9lKL0J0O%&>X;WmxOvcWqG9t(neyXNR7;=y_+_!v zBFJ&S)ag$aj6cd3{1ztnC+v)y&Rt&(#h2TyW^BmY%z!RuF`vAY_z{A&6%Nr~Tk@vA z#*_Q;-yC0lPV-cow;!rmGj>~`Ky^H)dDku$fQcRQVlbpagx{X|qN|QxK=zy2_}{@h zS-pAN#T1APttE49yTLRajK z>5dvv^T6n%zcrZZzwl4?(7DJR=#UR*w(?q$9?8!Uh`d-rFKSh81Ls3G_RuVCyu%*b zW_sobSBlTlu##}P-WAJja20*HOJ3R-!U6_+$ zsCEF9PmUv&Grb$vx3RHevCkl}%^$vCXYGfNMi`iQEu4CcZDliG=*y{33)tO{t3_{i zTNP0<^>IRuJobNrEq1Ny8qfv+7=-=2u#(fo>r3#~qM^=|)8~wHV_DISc&_&MhSkA4 zLoZDsZ)QU{63Oj4!l#Wn*rI?=s?faeyw_MHXHXh#B0L)&$Hq;f8S2f8>V?1Y(8%U% zy+#?Admj0e#zVsnBNT`7!jj}0P4!j1 z!gjEE5=9bZCt^!Y53H$qB5Ol_-a5g`jHsXLjb9!=%fp>QXY0R<#%Af(2Kg*$>G_fi zUP+R2HO%Mri0-eh%7ir;1o-l;5U`dG{UtiFI5(>-q4|v^_?wjgt;*QV8Nh0uGs#R% z;vc|szgel$OTH-HCQ+y_tU$k8Zd3ZD!f}SHg$&!hedt}IlSDN~<(j{+6_9r^3Ba5^>_oiQZ%8dP04Q&fr$K1vI6X)4egc0@j| z<5};FUexPD$^l0fL@g~knJ4IvGz{9p+-Ib4ju(t3?4HNCrcCj&b@IV#Jxwe0c-QY;R}mVCvqUo=Me6l_sZ*fB=J1wX{kXvug2LF(!i2li)%j-M9(R(QqSq%C0#R zf7sIlzl1DRPwVd}RJ!;M{XU|?LT=&=Qu})jOn{IJ+MAbo&56cdYwNaLiR>En()&^I0}IPv_3o6E{9MlLECUoc1M5H? zumL~?PJ0oh@Cm?0(d}b}ZElu`mQOKVQLdo8-J=2W@R?2g{2W5X{3G8ZXN|i^-z=Zo zFIE@yEALwwtL*TwZ9f%-~d4#DvC+j zbhVW1CDhpTY@S!D(NKj z=eK*$QS6sFm^@Mub@*nfOzVE4fldI5{V0xwty39WbJhs9duFekabDd}rifqP5z@AI z-FA%g6l~`q+A)ET76IugEV_P&)M6wL+l?>#V5;Ljzu$^9EWOyh=bIKs^lh4#^nV0# z)9!lEFoWC#)X*cVZF_yx9XkFRh0BlCDxihiuife;GtwBvbY*BW=e z-#=gt?mNX#6jLyqwHq(}4w^|!h?`pzO1Lh8Ye)E-0TNQfR?}yiJ)pERA8H6(N1!R! zuK9t=eK)A!4@}?`gnxYM9hjLF;=8bCd8-oczuv&4+{g_oDhZ$tfr)=|n0`SWQSn~n zEZIqF00|K9EV8qgor3b7S2hJfBaed|(4IL{XK%?BMBN@eT|A^Pxbf2=YDx@nNs23JKJ=7gQpf{9U68g^TI;r6?82OhsHYrH8kQxDf2+9`TqCcpSetjB z!_|sETDp?s*K&fbQ|RO7_2uPZd<%PvdF6&XbcJ-)kFvL#tWK{+(zw~ilce2$bVo;M zE~Spiiyxnt5&l8JL3$dwbUU+6e_)Ju$4%zMM%{e7Er{L@%k}Hve`nS`Q`&l#zt4(P zA^Lq_7XrcsigaFsXd2rxV)-2>J`RA`>S9O26W6{a8Jq7+nAQMH&lI7zs$wp#GFncV z&}yfGpBOieg+;-%N7{|AkB%%N*{bC5W1K9r~D( zl#qj#K^qwH+aLPQ7wIhwmLu6DEIxbe0_dsfNJ^%<7e{Bd-dqIKp~M}zdM#~^)6$la z&U-}gG=Lkq%6vR~4@prUsTVbgA`uQlbS!r|=RX(~n(J@GFS?;$2kEpcz723JDl9qo zUBQj;-fmj^V!g4O%J_(g0xuf!(qERS*7JNdKmF6Q<>5ERsWEaEKJA<3mFa=7of$Q+ zrqPIQOL%wsV<{gASZ>FKr97QgWI@DL%W>Ud_oZ_>re#}ssVAPPo(DY}K3e<})<-1W z!j(kvNSm^R5Y~5|*0p94>$xwmv3hnjj_r<}?{g`jwgz9qQ!0creufj5W!+R-V&>TN zxmwb7A;GK54)*%4-Zx+}1FYHrqr=4RB4~(WN|$b|6J+L02q+Kp-Zpz5I{JLlq6TNL z6!cTgm8|9xzj^c=8tdbof4ByRa@t!5&@khK&z?=f_AYB7N=iZ&X=#{dVYxAM0+;#n zL!bJDK~dUxB5zKSY-fWVw6@-EqHxVyERYz^TE5g9Bd2UJ>2`Ev4EMp&FUk0>ea~cL zo6<0{0ulgF+nJ?QXl?lQ15}8QXK z_91-tYt`lCL~`9ZLT^98rQF3FTmfqL*6evw5Wb=!tIlds)3Uk~Ip67I`psD+6UQ@^ zhGkuqipMi_G!o7`XM=Wik}b*QXpYq5X9@I&ojsOh-b5&q+iNoFYZn=55T~E^%Vc^0 z+7MoGRwA}kD{kwe5HKbcA(+HdNL9B-SStgW z)6!{g+KaParGHeTF`OrwQcbtCf!V0RXlW#0gk4jTyIhEkBYmL8XM6Mqpqq{J1;|-W za+Wq6Y=duV#;JKFsRX=2y{`s^diOdygG!(T0{4tGl39RFSD5`Il#2HiRk_N4oRWwY z(f4<>qRYHreS$=!pa@>dXB3xa))7W|D7JaL#7qpc+X@AfPWTVBBUYlF>JfBd1)pTpE=A~1flb|q| z-tl;`r#!}Kd^>N^lH;qL*mY^WbUPP6PwX3u6sxOgq60>&qwB6CN~g1Q8Vhgbx;|T; zh&vbHG9<9Zhqg;yA5b@TMrh{BJcc-EFeVAjd*o0#$K)AGjZ9-$*N(O4fnoG1V!)Yv zgK+mZJsFZRGhM(KEc3y(%I|(_w?~xMxLkldyqO=r!L6wMzz-jn$kG2gI!CwJ5)FC% z+)X8xDMyzjrAQW{aKm~dPJST+|6>x#D~-I(CSqi`k=iM??eQI{(vap z6di&Mgw9=Tk{6P9o2!urr`80F>-BbGs}vqrEi@14X&ut+8JY(bw>uP0?Z_#62nEmB zT~ELF{I*e8j9*bykl7`VRW?58`Ab2h`H3jS{50P^Yo~RH^y3fc`Y*koOHKOt(rRR< zzjI6^eC7AKgKFw9&S%gPK+ z(h=gNQ5(aUg!NOJ3i3;hzke_1(~&t^>ft+p4Hy7AYOQl8!uRc`qCUjTuNwHTA5_Kn zUxu6c>lU7Z&)i=R%9Aj~>ys9G+V%K`D>epgpUO;c#-Yb-VM!{xmM37l>4@AVTAKE% zg+%X#RE4PuZuHn!WUf_IL#$@yR4R#kxM|zr2~|r0^8s4(<48!Q1EPT#8vb=$Y`izlF7zXMGaUK#s8I6If?#yP8h-S<+}gOp z>7>5ycXlrr6_t({Xb046b$1TBHBR)dB%d#(i+27IA-}hK&~!{_!bN5E2ec>fid(o& z<*;)2rOjlAioP^b4dH%V_MG+=ktW8&jXVfFMfZ-B#g8*@*L}I2B-_s3<%0>Fz?hfuiOA(AKhPhqT9w26ASoQ^aaZK_ z$Pwd)3$)Pr1jQP?^kYhXKcNU~KI+LwPH0+_JWM3;$*4@W`E)Cu~_@ZBKWKP&fl zEk|kr6W~aT&eNPM!O>=ogbHb_7i{>Y^0K>u3#3!@7|6}EW#&g6J&@iuV-?}^9w9%t z3#%>`)+{U^mvQ|fJ&+6y7ccH&H*JI;&<&kQY7FDqd`eXd`{*?4KY{L{ooaeYe1SzG z#dptdj#{_u)-oxb>ED_4D!Lm_xU2P)>bmfE<2(}oYv<|c({!o=6XK~|QF1`_(9hqA z4LZh_i56#k&L`GfdzQ8&%_lT63qiHVFDGddn&3g#Z(=%=oAxC6K z9wx2<6VX$W-;v$%$vG10M}j#Dn10c-8hxt`azNKzp#~_qs^F7Z zQ*{%%%hF{xY0x9>Y34kOR=X1d(GfjH^Lp$wuiwJ>afLbDwGJ%{z2Z-{=&%CMK^gYN z>g~hx?GO8s(^XwRHS*R7=_+h>9>1`>=}?$9ic!IqUf!L6y=b5p3Bbft>$P88eOW~c z4q2+e>Ur{c@Ah>Sk9a1P`Remm+qy=W`;ITJMVyrDht{j?_>@F{gH(*1tsqaQE;8~m zjS(XT$q^&(JaqSXWKMU^#87dNciu<`UhvR}AxuZ_MDq{mBC!)Q*aq8)!v|UH4*=(| zy8xP?A8GrO&-lZ6@W+8pC*sON1MgM$((9V$_he)TH!3|?nzr_^VO*S+FnQ%Mdg$UT ztarhau(=2v2fL-~V$~+xZb?~}_X(rl%IwB{&k`}xXC;mZYi&n9EE?yzORy7p$ApN6 z?2x9{;<{Jn@-{WcS5lqKQ~hyDJ72xm88`VvkyoC2b;#wAZbE7kTxinn2x0Up{45_u zr51Y^@mWi1er5+ejRyiJpLwo~uPA>LFH{gj;Kb&h#;YITSWam90zx zZ%8ovWzErv06RnX<=zXvq966e(?l_PR}Q2FY&`}WQS1n9uL@}M%%XigJjRzSK=Q@Z zwj~l;)~2V+<6dOfUcaI_hG^pq5l2~zBD{-iQy|RnhsP*8pY!i_zN6v2#B|lu6Ep42 zlFs^*w2v_dI&4!;;kQe5PcX+>ZqK`?jxk+4MRKqEUOg|pe?QeM^F{K;^wd1D zC9Lvgk9bYV35HU7Dls-@Hv5xiwLO0uQEtLZXn@gE!+PxcHcY3R@>sxM5FHAZVV(me zbZ;#AhjOP9gm_;8kEdrz_joc&sQppbh|5N)_hth*+lp}^z<>9<9x;ZmiJq=i6}T|+ z%1PnKiO$3S5yB|u=t>9p&Qor#Vf1g#3P(OW;7Q)IUE7t?%O2aEw%B`+rWTRg)ovmS zA7=LcUTycFu?_SRSWXWq3fq(&eAm@5;W^#+*d0CA(=qI|z#I@TgD#%@tmJs0`5c|} zNl>ID_X9CQ><|8D^aNjBO%1xIugONA}y>i6yD79NKk2>P+Wq@M&|5R^yzCF{l?`&Y;I++3!4xvIp0h zL%HNn1(|vIn0bCmxZu!{aS4^r(^X+=QvWNH_#dzEO%RAHq@zjZoPI^RDhuO9&(7y- zPi^9gy22^n#YXXd$NlH7Uzj0?JQ(G$;#Q9jfYVsLFvkb4cnyhMa$mlaq;I5i@6vBy z^X%`x=KKsoWU5C``GzriwFt(eC0ed6{_1GGMFj0CN-4!^brttD7L84EL~*7h`-g15 z^&=m(4CQ1sM|y)Mx2YMas8OuI76Rmey~#-*_)9*u4{in&Q@x$6{YQ?aZ$;4*xA@r6yF`9>9=|m3%Y=U4RR90n z#2nLu-~QkN{PIYDItTv|w0^nN|94`7ssX?)qAw-CU%1m>J_39`K}D6XEK48Efco0qPND+v9`Gw4%NsNj!@ zWvO+JMYa9!8@sh6+FnuK!}e_*XbfgG_2t!(!a*=VWnr**S~UMl)}c2B;30OP$Jp#q zF;V}r(trPO?i3hRanvZfpVfOV!H#QlVGJoJM0v&d=TxAxP8djRvOgUQ*vRBl2Tg_S z{_ma=G?N&iAV+;56V~T}>Z|+EP}3w%m|lj@nQ~tLBhC4s7^`jo$}!0YCnLkn;oGn6Pi-EY_Cxg^z)~QW9P*oNv$;sl{zp|!3TSodLu-&!nxoR z*SQ_<{(6yL8u`m!_+?tZ_Q)@<^~?SKiX(r@w*TkU{VTHnN>P60TEDX2UupdRI=%ou z`73Gsl{EfJ8h<5?zmmrPN0P>SvGm%>7ccPoEf>ct`nZM^A;*0&GQO(^m=rswmnNZQYj#iyPc7Mge!P&Y}t9Q^B=Lt>O5gXlF(_jcf2teM--Y?Pe z`G4%f^_zQ*v9}GNpv1w)jXYK_csv2%8I>M=h*>y8b)C2!#O?ADJl`1rW)5TsDw4LR z*T9eqxr<(5GW{=y?9!BQ$xY!G(0eP8o{2`c4kfQSTna$+Rsgux>w+yv{?G6QfI#?@ zLpAHoB9Aw^G#bO?#s{Z9syd3vfYJ1oW1f#&FEoC+r6ty9(v$cCeD*o>aapH3$*#?3 zAA^?iji8tJNZFi#z(>%Oy)khAfd^SJMl7Qbdt9n6!lwWN$W~-`4giXO`O@@*G#1|9 zpD#?H14cbZoSiEOGjDrkN1Rw2dHtM3OIszKjb1PSFfUz<#=>u<{33YG>Fgwz=1s0A zHI9SsJvu#+b?5T(S->3FW`z_SC&b<;l~fcj?enXy3)9uLD&&_=t| z|LAqido5y>pkP$#IL6N1I%(3lY8vw z&)&xsPRr}fcQS-`y(y2l3=`bJ=iv8VvEI$A~p?aU1H(+;7TJ*={Q>L$S6aEHk zhyhbbt+c?8(zHF%OJ1vHYnW>AFtdP>A`F2PP#)~x0QLXuWH<|hKoQ@?sh6jxzyOk> zg>wUU?NTm+cK=pD^+gLF+Pc7YGUx@q7YK+(K0A0uJ6^xoMnK`v39z$@R}mO`Iz#QD zFVqh;02=P;>zwtU>r=k*jrL@$smrU(2<1x@g9y$SrK6=-*P_~4TFU=Ri$&JJCby>} z^t^m?uim)_29W3mOhBr>2VEd8mpamb-*IIC1^6=*8q80=VEEd-<~L^_Plw5U1?D*g zwhE;0l#z);*2T3itfzfe0-sV|+*UMlR~a=OuJB;84!)K~-W58dn>_f?8W@Qg0Rb1Q z7(6u>ZxwEoG;o#rP;Ujmke%@pOFx6Bg{$gjnxdyaB6h}W0_qiXA6`9|C<5lwq`Aw%fh*S2On--eW^?pC zJMfAR7oT1FOK7I(w=3iA82w76V#dGFFtiW)b3na51Zr0AqGB)qB*d{(Kcjy5wxz** z+NrdS{rbIhyN=XFGKMU4RlgNQPtR5g=`-7WM#sAJnF>@T!$D+{V>f&BNfQ=u@sQya zU@9yhX5$U?(@#Ll=*1eklmEp9`0L)x;$UVGhK6X>)-9@0=M_|#{!I3Lf+{(?ZcmP&yX&r4=zo{|;pRIFP_=5kLb3SCNzDmhJ2YYf#eIgb+lVB&P-Bv%)MvOI$9^g55Ib>I_Nq#hy6L> z9~%drMgd2W{(;)a9IF@Be_B}ow)2M%4}ge1@C2Xyv)}oB@9-aGW_iTNc5^c66)Wb- z0Z150Rxxr{&USa(=&mkEHT^I9EVbAs4cr3O56Fi^$KOShFg=;;mhBn+?o z)gA)_BLn@?pHZcS?q!He|Co9iwYK|1u4jzi#1{k{!7+3GA)9;Ev$2kg((R5xU^%JC z!zWeK&jCOAA>+vNb651!^;d3*{&j!DT^R^&>2uT-l~_mf2v*_m11FK!KNOf|xG zY-+aWzG-@Ld*A=^&3ifpFX@+$TtyUv@iy1MP!`EF%(HAt)Z(E�ILv>B1==QZLca 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
  • - endswith -
  • -
  • format
  • @@ -410,10 +406,6 @@ split -
  • - startswith -
  • -
  • strrev
  • @@ -800,6 +792,10 @@ can +
  • + defaults +
  • +
  • nonsensitive
  • O2=MDniYQ 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 flatmap = 2; -} - -enum StringKind { - PLAIN = 0; - MARKDOWN = 1; -} - -// Schema is the configuration schema for a Resource, Provider, or Provisioner. -message Schema { - message Block { - int64 version = 1; - repeated Attribute attributes = 2; - repeated NestedBlock block_types = 3; - string description = 4; - StringKind description_kind = 5; - bool deprecated = 6; - } - - message Attribute { - string name = 1; - bytes type = 2; - string description = 3; - bool required = 4; - bool optional = 5; - bool computed = 6; - bool sensitive = 7; - StringKind description_kind = 8; - bool deprecated = 9; - } - - message NestedBlock { - enum NestingMode { - INVALID = 0; - SINGLE = 1; - LIST = 2; - SET = 3; - MAP = 4; - GROUP = 5; - } - - string type_name = 1; - Block block = 2; - NestingMode nesting = 3; - int64 min_items = 4; - int64 max_items = 5; - } - - // The version of the schema. - // Schemas are versioned, so that providers can upgrade a saved resource - // state when the schema is changed. - int64 version = 1; - - // Block is the top level configuration block for this schema. - Block block = 2; -} - -service Provider { - //////// Information about what a provider supports/expects - rpc GetSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response); - rpc PrepareProviderConfig(PrepareProviderConfig.Request) returns (PrepareProviderConfig.Response); - rpc ValidateResourceTypeConfig(ValidateResourceTypeConfig.Request) returns (ValidateResourceTypeConfig.Response); - rpc ValidateDataSourceConfig(ValidateDataSourceConfig.Request) returns (ValidateDataSourceConfig.Response); - rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response); - - //////// One-time initialization, called before other functions below - rpc Configure(Configure.Request) returns (Configure.Response); - - //////// Managed Resource Lifecycle - rpc ReadResource(ReadResource.Request) returns (ReadResource.Response); - rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response); - rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response); - rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response); - - rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response); - - //////// Graceful Shutdown - rpc Stop(Stop.Request) returns (Stop.Response); -} - -message GetProviderSchema { - message Request { - } - message Response { - Schema provider = 1; - map resource_schemas = 2; - map data_source_schemas = 3; - repeated Diagnostic diagnostics = 4; - Schema provider_meta = 5; - ServerCapabilities server_capabilities = 6; - } - - - // ServerCapabilities allows providers to communicate extra information - // regarding supported protocol features. This is used to indicate - // availability of certain forward-compatible changes which may be optional - // in a major protocol version, but cannot be tested for directly. - message ServerCapabilities { - // The plan_destroy capability signals that a provider expects a call - // to PlanResourceChange when a resource is going to be destroyed. - bool plan_destroy = 1; - } -} - -message PrepareProviderConfig { - message Request { - DynamicValue config = 1; - } - message Response { - DynamicValue prepared_config = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message UpgradeResourceState { - message Request { - string type_name = 1; - - // version is the schema_version number recorded in the state file - int64 version = 2; - - // raw_state is the raw states as stored for the resource. Core does - // not have access to the schema of prior_version, so it's the - // provider's responsibility to interpret this value using the - // appropriate older schema. The raw_state will be the json encoded - // state, or a legacy flat-mapped format. - RawState raw_state = 3; - } - message Response { - // new_state is a msgpack-encoded data structure that, when interpreted with - // the _current_ schema for this resource type, is functionally equivalent to - // that which was given in prior_state_raw. - DynamicValue upgraded_state = 1; - - // diagnostics describes any errors encountered during migration that could not - // be safely resolved, and warnings about any possibly-risky assumptions made - // in the upgrade process. - repeated Diagnostic diagnostics = 2; - } -} - -message ValidateResourceTypeConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ValidateDataSourceConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message Configure { - message Request { - string terraform_version = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ReadResource { - message Request { - string type_name = 1; - DynamicValue current_state = 2; - bytes private = 3; - DynamicValue provider_meta = 4; - } - message Response { - DynamicValue new_state = 1; - repeated Diagnostic diagnostics = 2; - bytes private = 3; - } -} - -message PlanResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue proposed_new_state = 3; - DynamicValue config = 4; - bytes prior_private = 5; - DynamicValue provider_meta = 6; - } - - message Response { - DynamicValue planned_state = 1; - repeated AttributePath requires_replace = 2; - bytes planned_private = 3; - repeated Diagnostic diagnostics = 4; - - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 5; - } -} - -message ApplyResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue planned_state = 3; - DynamicValue config = 4; - bytes planned_private = 5; - DynamicValue provider_meta = 6; - } - message Response { - DynamicValue new_state = 1; - bytes private = 2; - repeated Diagnostic diagnostics = 3; - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 4; - } -} - -message ImportResourceState { - message Request { - string type_name = 1; - string id = 2; - } - - message ImportedResource { - string type_name = 1; - DynamicValue state = 2; - bytes private = 3; - } - - message Response { - repeated ImportedResource imported_resources = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message ReadDataSource { - message Request { - string type_name = 1; - DynamicValue config = 2; - DynamicValue provider_meta = 3; - } - message Response { - DynamicValue state = 1; - repeated Diagnostic diagnostics = 2; - } -} - -service Provisioner { - rpc GetSchema(GetProvisionerSchema.Request) returns (GetProvisionerSchema.Response); - rpc ValidateProvisionerConfig(ValidateProvisionerConfig.Request) returns (ValidateProvisionerConfig.Response); - rpc ProvisionResource(ProvisionResource.Request) returns (stream ProvisionResource.Response); - rpc Stop(Stop.Request) returns (Stop.Response); -} - -message GetProvisionerSchema { - message Request { - } - message Response { - Schema provisioner = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message ValidateProvisionerConfig { - message Request { - DynamicValue config = 1; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ProvisionResource { - message Request { - DynamicValue config = 1; - DynamicValue connection = 2; - } - message Response { - string output = 1; - repeated Diagnostic diagnostics = 2; - } -} diff --git a/docs/plugin-protocol/tfplugin6.3.proto b/docs/plugin-protocol/tfplugin6.3.proto deleted file mode 100644 index b87effe43442..000000000000 --- a/docs/plugin-protocol/tfplugin6.3.proto +++ /dev/null @@ -1,362 +0,0 @@ -// Terraform Plugin RPC protocol version 6.3 -// -// This file defines version 6.3 of the RPC protocol. To implement a plugin -// against this protocol, copy this definition into your own codebase and -// use protoc to generate stubs for your target language. -// -// This file will not be updated. Any minor versions of protocol 6 to follow -// should copy this file and modify the copy while maintaing backwards -// compatibility. Breaking changes, if any are required, will come -// in a subsequent major version with its own separate proto definition. -// -// Note that only the proto files included in a release tag of Terraform are -// official protocol releases. Proto files taken from other commits may include -// incomplete changes or features that did not make it into a final release. -// In all reasonable cases, plugin developers should take the proto file from -// the tag of the most recent release of Terraform, and not from the main -// branch or any other development branch. -// -syntax = "proto3"; -option go_package = "github.com/hashicorp/terraform/internal/tfplugin6"; - -package tfplugin6; - -// DynamicValue is an opaque encoding of terraform data, with the field name -// indicating the encoding scheme used. -message DynamicValue { - bytes msgpack = 1; - bytes json = 2; -} - -message Diagnostic { - enum Severity { - INVALID = 0; - ERROR = 1; - WARNING = 2; - } - Severity severity = 1; - string summary = 2; - string detail = 3; - AttributePath attribute = 4; -} - -message AttributePath { - message Step { - oneof selector { - // Set "attribute_name" to represent looking up an attribute - // in the current object value. - string attribute_name = 1; - // Set "element_key_*" to represent looking up an element in - // an indexable collection type. - string element_key_string = 2; - int64 element_key_int = 3; - } - } - repeated Step steps = 1; -} - -message StopProvider { - message Request { - } - message Response { - string Error = 1; - } -} - -// RawState holds the stored state for a resource to be upgraded by the -// provider. It can be in one of two formats, the current json encoded format -// in bytes, or the legacy flatmap format as a map of strings. -message RawState { - bytes json = 1; - map flatmap = 2; -} - -enum StringKind { - PLAIN = 0; - MARKDOWN = 1; -} - -// Schema is the configuration schema for a Resource or Provider. -message Schema { - message Block { - int64 version = 1; - repeated Attribute attributes = 2; - repeated NestedBlock block_types = 3; - string description = 4; - StringKind description_kind = 5; - bool deprecated = 6; - } - - message Attribute { - string name = 1; - bytes type = 2; - Object nested_type = 10; - string description = 3; - bool required = 4; - bool optional = 5; - bool computed = 6; - bool sensitive = 7; - StringKind description_kind = 8; - bool deprecated = 9; - } - - message NestedBlock { - enum NestingMode { - INVALID = 0; - SINGLE = 1; - LIST = 2; - SET = 3; - MAP = 4; - GROUP = 5; - } - - string type_name = 1; - Block block = 2; - NestingMode nesting = 3; - int64 min_items = 4; - int64 max_items = 5; - } - - message Object { - enum NestingMode { - INVALID = 0; - SINGLE = 1; - LIST = 2; - SET = 3; - MAP = 4; - } - - repeated Attribute attributes = 1; - NestingMode nesting = 3; - - // MinItems and MaxItems were never used in the protocol, and have no - // effect on validation. - int64 min_items = 4 [deprecated = true]; - int64 max_items = 5 [deprecated = true]; - } - - // The version of the schema. - // Schemas are versioned, so that providers can upgrade a saved resource - // state when the schema is changed. - int64 version = 1; - - // Block is the top level configuration block for this schema. - Block block = 2; -} - -service Provider { - //////// Information about what a provider supports/expects - rpc GetProviderSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response); - rpc ValidateProviderConfig(ValidateProviderConfig.Request) returns (ValidateProviderConfig.Response); - rpc ValidateResourceConfig(ValidateResourceConfig.Request) returns (ValidateResourceConfig.Response); - rpc ValidateDataResourceConfig(ValidateDataResourceConfig.Request) returns (ValidateDataResourceConfig.Response); - rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response); - - //////// One-time initialization, called before other functions below - rpc ConfigureProvider(ConfigureProvider.Request) returns (ConfigureProvider.Response); - - //////// Managed Resource Lifecycle - rpc ReadResource(ReadResource.Request) returns (ReadResource.Response); - rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response); - rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response); - rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response); - - rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response); - - //////// Graceful Shutdown - rpc StopProvider(StopProvider.Request) returns (StopProvider.Response); -} - -message GetProviderSchema { - message Request { - } - message Response { - Schema provider = 1; - map resource_schemas = 2; - map data_source_schemas = 3; - repeated Diagnostic diagnostics = 4; - Schema provider_meta = 5; - ServerCapabilities server_capabilities = 6; - } - - - // ServerCapabilities allows providers to communicate extra information - // regarding supported protocol features. This is used to indicate - // availability of certain forward-compatible changes which may be optional - // in a major protocol version, but cannot be tested for directly. - message ServerCapabilities { - // The plan_destroy capability signals that a provider expects a call - // to PlanResourceChange when a resource is going to be destroyed. - bool plan_destroy = 1; - } -} - -message ValidateProviderConfig { - message Request { - DynamicValue config = 1; - } - message Response { - repeated Diagnostic diagnostics = 2; - } -} - -message UpgradeResourceState { - message Request { - string type_name = 1; - - // version is the schema_version number recorded in the state file - int64 version = 2; - - // raw_state is the raw states as stored for the resource. Core does - // not have access to the schema of prior_version, so it's the - // provider's responsibility to interpret this value using the - // appropriate older schema. The raw_state will be the json encoded - // state, or a legacy flat-mapped format. - RawState raw_state = 3; - } - message Response { - // new_state is a msgpack-encoded data structure that, when interpreted with - // the _current_ schema for this resource type, is functionally equivalent to - // that which was given in prior_state_raw. - DynamicValue upgraded_state = 1; - - // diagnostics describes any errors encountered during migration that could not - // be safely resolved, and warnings about any possibly-risky assumptions made - // in the upgrade process. - repeated Diagnostic diagnostics = 2; - } -} - -message ValidateResourceConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ValidateDataResourceConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ConfigureProvider { - message Request { - string terraform_version = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ReadResource { - message Request { - string type_name = 1; - DynamicValue current_state = 2; - bytes private = 3; - DynamicValue provider_meta = 4; - } - message Response { - DynamicValue new_state = 1; - repeated Diagnostic diagnostics = 2; - bytes private = 3; - } -} - -message PlanResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue proposed_new_state = 3; - DynamicValue config = 4; - bytes prior_private = 5; - DynamicValue provider_meta = 6; - } - - message Response { - DynamicValue planned_state = 1; - repeated AttributePath requires_replace = 2; - bytes planned_private = 3; - repeated Diagnostic diagnostics = 4; - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 5; - } -} - -message ApplyResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue planned_state = 3; - DynamicValue config = 4; - bytes planned_private = 5; - DynamicValue provider_meta = 6; - } - message Response { - DynamicValue new_state = 1; - bytes private = 2; - repeated Diagnostic diagnostics = 3; - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 4; - } -} - -message ImportResourceState { - message Request { - string type_name = 1; - string id = 2; - } - - message ImportedResource { - string type_name = 1; - DynamicValue state = 2; - bytes private = 3; - } - - message Response { - repeated ImportedResource imported_resources = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message ReadDataSource { - message Request { - string type_name = 1; - DynamicValue config = 2; - DynamicValue provider_meta = 3; - } - message Response { - DynamicValue state = 1; - repeated Diagnostic diagnostics = 2; - } -} diff --git a/docs/resource-instance-change-lifecycle.md b/docs/resource-instance-change-lifecycle.md index 0849bea628dd..f582825def0b 100644 --- a/docs/resource-instance-change-lifecycle.md +++ b/docs/resource-instance-change-lifecycle.md @@ -3,94 +3,45 @@ This document describes the relationships between the different operations called on a Terraform Provider to handle a change to a resource instance. -![](https://user-images.githubusercontent.com/20180/172506401-777597dc-3e6e-411d-9580-b192fd34adba.png) - -The resource instance operations all both consume and produce objects that -conform to the schema of the selected resource type. - -The overall goal of this process is to take a **Configuration** and a -**Previous Run State**, merge them together using resource-type-specific -planning logic to produce a **Planned State**, and then change the remote -system to match that planned state before finally producing the **New State** -that will be saved in order to become the **Previous Run State** for the next -operation. - -The various object values used in different parts of this process are: - -* **Configuration**: Represents the values the user wrote in the configuration, - after any automatic type conversions to match the resource type schema. - - Any attributes not defined by the user appear as null in the configuration - object. If an argument value is derived from an unknown result of another - resource instance, its value in the configuration object could also be - unknown. - -* **Prior State**: The provider's representation of the current state of the - remote object at the time of the most recent read. - -* **Proposed New State**: Terraform Core uses some built-in logic to perform - an initial basic merger of the **Configuration** and the **Prior State** - which a provider may use as a starting point for its planning operation. - - The built-in logic primarily deals with the expected behavior for attributes - marked in the schema as both "optional" _and_ "computed", which means that - the user may either set it or may leave it unset to allow the provider - to choose a value instead. - - Terraform Core therefore constructs the proposed new state by taking the - attribute value from Configuration if it is non-null, and then using the - Prior State as a fallback otherwise, thereby helping a provider to - preserve its previously-chosen value for the attribute where appropriate. - -* **Initial Planned State** and **Final Planned State** are both descriptions - of what the associated remote object ought to look like after completing - the planned action. - - There will often be parts of the object that the provider isn't yet able to - predict, either because they will be decided by the remote system during - the apply step or because they are derived from configuration values from - other resource instances that are themselves not yet known. The provider - must mark these by including unknown values in the state objects. - - The distinction between the _Initial_ and _Final_ planned states is that - the initial one is created during Terraform Core's planning phase based - on a possibly-incomplete configuration, whereas the final one is created - during the apply step once all of the dependencies have already been - updated and so the configuration should then be wholly known. - -* **New State** is a representation of the result of whatever modifications - were made to the remote system by the provider during the apply step. - - The new state must always be wholly known, because it represents the - actual state of the system, rather than a hypothetical future state. - -* **Previous Run State** is the same object as the **New State** from - the previous run of Terraform. This is exactly what the provider most - recently returned, and so it will not take into account any changes that - may have been made outside of Terraform in the meantime, and it may conform - to an earlier version of the resource type schema and therefore be - incompatible with the _current_ schema. - -* **Upgraded State** is derived from **Previous Run State** by using some - provider-specified logic to upgrade the existing data to the latest schema. - However, it still represents the remote system as it was at the end of the - last run, and so still doesn't take into account any changes that may have - been made outside of Terraform. - -* The **Import ID** and **Import Stub State** are both details of the special - process of importing pre-existing objects into a Terraform state, and so - we'll wait to discuss those in a later section on importing. - - -## Provider Protocol API Functions - -The following sections describe the three provider API functions that are +![](https://gist.githubusercontent.com/apparentlymart/c4e401cdb724fa5b866850c78569b241/raw/fefa90ce625c240d5323ea28c92943c2917e36e3/resource_instance_change_lifecycle.png) + +The process includes several different artifacts that are all objects +conforming to the schema of the resource type in question, representing +different subsets of the instance for different purposes: + +* **Configuration**: Contains only values from the configuration, including + unknown values in any case where the argument value is derived from an + unknown result on another resource. Any attributes not set directly in the + configuration are null. + +* **Prior State**: The full object produced by a previous apply operation, or + null if the instance is being created for the first time. + +* **Proposed New State**: Terraform Core merges the non-null values from + the configuration with any computed attribute results in the prior state + to produce a combined object that includes both, to avoid each provider + having to re-implement that merging logic. Will be null when planning a + delete operation. + +* **Planned New State**: An approximation of the result the provider expects + to produce when applying the requested change. This is usually derived from + the proposed new state by inserting default attribute values in place of + null values and overriding any computed attribute values that are expected + to change as a result of the apply operation. May include unknown values + for attributes whose results cannot be predicted until apply. Will be null + when planning a delete operation. + +* **New State**: The actual result of applying the change, with any unknown + values from the planned new state replaced with final result values. This + value will be used as the input to plan the next operation. + +The remaining sections describe the three provider API functions that are called to plan and apply a change, including the expectations Terraform Core enforces for each. For historical reasons, the original Terraform SDK is exempt from error -messages produced when certain assumptions are violated, but violating them -will often cause downstream errors nonetheless, because Terraform's workflow +messages produced when the assumptions are violated, but violating them will +often cause downstream errors nonetheless, because Terraform's workflow depends on these contracts being met. The following section uses the word "attribute" to refer to the named @@ -98,46 +49,47 @@ attributes described in the resource type schema. A schema may also include nested blocks, which contain their _own_ set of attributes; the constraints apply recursively to these nested attributes too. -The following are the function names used in provider protocol version 6. -Protocol version 5 has the same set of operations but uses some -marginally-different names for them, because we used protocol version 6 as an -opportunity to tidy up some names that had been awkward before. - -### ValidateResourceConfig +Nested blocks are a configuration-only construct and so the number of blocks +cannot be changed on the fly during planning or during apply: each block +represented in the configuration must have a corresponding nested object in +the planned new state and new state, or an error will be returned. -`ValidateResourceConfig` takes the **Configuration** object alone, and -may return error or warning diagnostics in response to its attribute values. +If a provider wishes to report about new instances of the sub-object type +represented by nested blocks that are created implicitly during the apply +operation -- for example, if a compute instance gets a default network +interface created when none are explicitly specified -- this must be done via +separate `Computed` attributes alongside the nested blocks, which could for +example be a list or map of objects that includes a mixture of the objects +described by the nested blocks in the configuration and any additional objects +created by the remote system. -`ValidateResourceConfig` is the provider's opportunity to apply custom -validation rules to the schema, allowing for constraints that could not be -expressed via schema alone. +## ValidateResourceTypeConfig -In principle a provider can make any rule it wants here, although in practice -providers should typically avoid reporting errors for values that are unknown. -Terraform Core will call this function multiple times at different phases -of evaluation, and guarantees to _eventually_ call with a wholly-known -configuration so that the provider will have an opportunity to belatedly catch -problems related to values that are initially unknown during planning. +`ValidateResourceTypeConfig` is the provider's opportunity to perform any +custom validation of the configuration that cannot be represented in the schema +alone. -If a provider intends to choose a default value for a particular -optional+computed attribute when left as null in the configuration, the -provider _must_ tolerate that attribute being unknown in the configuration in -order to get an opportunity to choose the default value during the later -plan or apply phase. +In principle the provider can require any constraint it sees fit here, though +in practice it should avoid reporting errors when values are unknown (so that +the operation can proceed and determine those values downstream) and if +it intends to apply default values during `PlanResourceChange` then it must +tolerate those attributes being null at validation time, because validation +happens before planning. -The validation step does not produce a new object itself and so it cannot -modify the user's supplied configuration. +A provider should repeat similar validation logic at the start of +`PlanResourceChange`, in order to catch any new +values that have switched from unknown to known along the way during the +overall plan/apply flow. -### PlanResourceChange +## PlanResourceChange The purpose of `PlanResourceChange` is to predict the approximate effect of a subsequent apply operation, allowing Terraform to render the plan for the -user and to propagate the predictable subset of results downstream through -expressions in the configuration. +user and to propagate any predictable results downstream through expressions +in the configuration. -This operation can base its decision on any combination of **Configuration**, -**Prior State**, and **Proposed New State**, as long as its result fits the -following constraints: +The _planned new state_ returned from the provider must meet the following +constraints: * Any attribute that was non-null in the configuration must either preserve the exact configuration value or return the corresponding attribute value @@ -155,216 +107,44 @@ following constraints: changed. Set an attribute to an unknown value to indicate that its final result will be determined during `ApplyResourceChange`. -`PlanResourceChange` is actually called twice per run for each resource type. - -The first call is during the planning phase, before Terraform prints out a -diff to the user for confirmation. Because no changes at all have been applied -at that point, the given **Configuration** may contain unknown values as -placeholders for the results of expressions that derive from unknown values -of other resource instances. The result of this initial call is the -**Initial Planned State**. - -If the user accepts the plan, Terraform will call `PlanResourceChange` a -second time during the apply step, and that call is guaranteed to have a -wholly-known **Configuration** with any values from upstream dependencies -taken into account already. The result of this second call is the -**Final Planned State**. +`PlanResourceChange` is actually called twice for each resource type. +It will be called first during the planning phase before Terraform prints out +the diff to the user for confirmation. If the user accepts the plan, then +`PlanResourceChange` will be called _again_ during the apply phase with any +unknown values from configuration filled in with their final results from +upstream resources. The second planned new state is compared with the first +and must meet the following additional constraints along with those listed +above: -Terraform Core compares the final with the initial planned state, enforcing -the following additional constraints along with those listed above: +* Any attribute that had a known value in the first planned new state must + have an identical value in the second. -* Any attribute that had a known value in the **Initial Planned State** must - have an identical value in the **Final Planned State**. - -* Any attribute that had an unknown value in the **Initial Planned State** may - either remain unknown in the second _or_ take on any known value that - conforms to the unknown value's type constraint. +* Any attribute that had an unknown value in the first planned new state may + either remain unknown in the second or take on any known value of the + expected type. -The **Final Planned State** is what passes to `ApplyResourceChange`, as -described in the following section. +It is the second planned new state that is finally provided to +`ApplyResourceChange`, as described in the following section. -### ApplyResourceChange +## ApplyResourceChange The `ApplyResourceChange` function is responsible for making calls into the -remote system to make remote objects match the **Final Planned State**. During -that operation, the provider should decide on final values for any attributes -that were left unknown in the **Final Planned State**, and thus produce the -**New State** object. +remote system to make remote objects match the planned new state. During that +operation, it should determine final values for any attributes that were left +unknown in the planned new state, thus producing a wholly-known _new state_ +object. -`ApplyResourceChange` also receives the **Prior State** so that it can use it +`ApplyResourceChange` also receives the prior state so that it can use it to potentially implement more "surgical" changes to particular parts of the remote objects by detecting portions that are unchanged, in cases where the remote API supports partial-update operations. -The **New State** object returned from the provider must meet the following +The new state object returned from the provider must meet the following constraints: -* Any attribute that had a known value in the **Final Planned State** must have - an identical value in the new state. In particular, if the remote API - returned a different serialization of the same value then the provider must - preserve the form the user wrote in the configuration, and _must not_ return - the normalized form produced by the provider. - -* Any attribute that had an unknown value in the **Final Planned State** must - take on a known value whose type conforms to the type constraint of the - unknown value. No unknown values are permitted in the **New State**. - -After calling `ApplyResourceChange` for each resource instance in the plan, -and dealing with any other bookkeeping to return the results to the user, -a single Terraform run is complete. Terraform Core saves the **New State** -in a state snapshot for the entire configuration, so it'll be preserved for -use on the next run. - -When the user subsequently runs Terraform again, the **New State** becomes -the **Previous Run State** verbatim, and passes into `UpgradeResourceState`. - -### UpgradeResourceState - -Because the state values for a particular resource instance persist in a -saved state snapshot from one run to the next, Terraform Core must deal with -the possibility that the user has upgraded to a newer version of the provider -since the last run, and that the new provider version has an incompatible -schema for the relevant resource type. - -Terraform Core therefore begins by calling `UpgradeResourceState` and passing -the **Previous Run State** in a _raw_ form, which in current protocol versions -is the raw JSON data structure as was stored in the state snapshot. Terraform -Core doesn't have access to the previous schema versions for a provider's -resource types, so the provider itself must handle the data decoding in this -upgrade function. - -The provider can then use whatever logic is appropriate to update the shape -of the data to conform to the current schema for the resource type. Although -Terraform Core has no way to enforce it, a provider should only change the -shape of the data structure and should _not_ change the meaning of the data. -In particular, it should not try to update the state data to capture any -changes made to the corresponding remote object outside of Terraform. - -This function then returns the **Upgraded State**, which captures the same -information as the **Previous Run State** but does so in a way that conforms -to the current version of the resource type schema, which therefore allows -Terraform Core to interact with the data fully for subsequent steps. - -### ReadResource - -Although Terraform typically expects to have exclusive control over any remote -object that is bound to a resource instance, in practice users may make changes -to those objects outside of Terraform, causing Terraform's records of the -object to become stale. - -The `ReadResource` function asks the provider to make a best effort to detect -any such external changes and describe them so that Terraform Core can use -an up-to-date **Prior State** as the input to the next `PlanResourceChange` -call. - -This is always a best effort operation because there are various reasons why -a provider might not be able to detect certain changes. For example: -* Some remote objects have write-only attributes, which means that there is - no way to determine what value is currently stored in the remote system. -* There may be new features of the underlying API which the current provider - version doesn't know how to ask about. - -Terraform Core expects a provider to carefully distinguish between the -following two situations for each attribute: -* **Normalization**: the remote API has returned some data in a different form - than was recorded in the **Previous Run State**, but the meaning is unchanged. - - In this case, the provider should return the exact value from the - **Previous Run State**, thereby preserving the value as it was written by - the user in the configuration and thus avoiding unwanted cascading changes to - elsewhere in the configuration. -* **Drift**: the remote API returned data that is materially different from - what was recorded in the **Previous Run State**, meaning that the remote - system's behavior no longer matches what the configuration previously - requested. - - In this case, the provider should return the value from the remote system, - thereby discarding the value from the **Previous Run State**. When a - provider does this, Terraform _may_ report it to the user as a change - made outside of Terraform, if Terraform Core determined that the detected - change was a possible cause of another planned action for a downstream - resource instance. - -This operation returns the **Prior State** to use for the next call to -`PlanResourceChange`, thus completing the circle and beginning this process -over again. - -## Handling of Nested Blocks in Configuration - -Nested blocks are a configuration-only construct and so the number of blocks -cannot be changed on the fly during planning or during apply: each block -represented in the configuration must have a corresponding nested object in -the planned new state and new state, or Terraform Core will raise an error. +* Any attribute that had a known value in the planned new state must have an + identical value in the new state. -If a provider wishes to report about new instances of the sub-object type -represented by nested blocks that are created implicitly during the apply -operation -- for example, if a compute instance gets a default network -interface created when none are explicitly specified -- this must be done via -separate "computed" attributes alongside the nested blocks. This could be list -or map of objects that includes a mixture of the objects described by the -nested blocks in the configuration and any additional objects created implicitly -by the remote system. - -Provider protocol version 6 introduced the new idea of structural-typed -attributes, which are a hybrid of attribute-style syntax but nested-block-style -interpretation. For providers that use structural-typed attributes, they must -follow the same rules as for a nested block type of the same nesting mode. - -## Import Behavior - -The main resource instance change lifecycle is concerned with objects whose -entire lifecycle is driven through Terraform, including the initial creation -of the object. - -As an aid to those who are adopting Terraform as a replacement for existing -processes or software, Terraform also supports adopting pre-existing objects -to bring them under Terraform's management without needing to recreate them -first. - -When using this facility, the user provides the address of the resource -instance they wish to bind the existing object to, and a string representation -of the identifier of the existing object to be imported in a syntax defined -by the provider on a per-resource-type basis, which we'll call the -**Import ID**. - -The import process trades the user's **Import ID** for a special -**Import Stub State**, which behaves as a placeholder for the -**Previous Run State** pretending as if a previous Terraform run is what had -created the object. - -### ImportResourceState - -The `ImportResourceState` operation takes the user's given **Import ID** and -uses it to verify that the given object exists and, if so, to retrieve enough -data about it to produce the **Import Stub State**. - -Terraform Core will always pass the returned **Import Stub State** to the -normal `ReadResource` operation after `ImportResourceState` returns it, so -in practice the provider may populate only the minimal subset of attributes -that `ReadResource` will need to do its work, letting the normal function -deal with populating the rest of the data to match what is currently set in -the remote system. - -For the same reasons that `ReadResource` is only a _best effort_ at detecting -changes outside of Terraform, a provider may not be able to fully support -importing for all resource types. In that case, the provider developer must -choose between the following options: - -* Perform only a partial import: the provider may choose to leave certain - attributes set to `null` in the **Prior State** after both - `ImportResourceState` and the subsequent `ReadResource` have completed. - - In this case, the user can provide the missing value in the configuration - and thus cause the next `PlanResourceChange` to plan to update that value - to match the configuration. The provider's `PlanResourceChange` function - must be ready to deal with the attribute being `null` in the - **Prior State** and handle that appropriately. -* Return an error explaining why importing isn't possible. - - This is a last resort because of course it will then leave the user unable - to bring the existing object under Terraform's management. However, if a - particular object's design doesn't suit importing then it can be a better - user experience to be clear and honest that the user must replace the object - as part of adopting Terraform, rather than to perform an import that will - leave the object in a situation where Terraform cannot meaningfully manage - it. +* Any attribute that had an unknown value in the planned new state must take + on a known value of the expected type in the new state. No unknown values + are allowed in the new state. diff --git a/experiments.go b/experiments.go deleted file mode 100644 index eb51dd2524bf..000000000000 --- a/experiments.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -// experimentsAllowed can be set to any non-empty string using Go linker -// arguments in order to enable the use of experimental features for a -// particular Terraform build: -// go install -ldflags="-X 'main.experimentsAllowed=yes'" -// -// By default this variable is initialized as empty, in which case -// experimental features are not available. -// -// The Terraform release process should arrange for this variable to be -// set for alpha releases and development snapshots, but _not_ for -// betas, release candidates, or final releases. -// -// (NOTE: Some experimental features predate the rule that experiments -// are available only for alpha/dev builds, and so intentionally do not -// make use of this setting to avoid retracting a previously-documented -// open experiment.) -var experimentsAllowed string - -func ExperimentsAllowed() bool { - return experimentsAllowed != "" -} diff --git a/go.mod b/go.mod index 0ac52855bf1a..3c29f6eda3be 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( cloud.google.com/go/storage v1.10.0 github.com/Azure/azure-sdk-for-go v59.2.0+incompatible github.com/Azure/go-autorest/autorest v0.11.24 - github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 + github.com/Netflix/go-expect v0.0.0-20211003183012-e1a7c020ce25 github.com/agext/levenshtein v1.2.3 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1501 github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70 @@ -24,7 +24,8 @@ require ( github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6 github.com/go-test/deep v1.0.3 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.8 + github.com/golang/protobuf v1.5.2 + github.com/google/go-cmp v0.5.6 github.com/google/uuid v1.2.0 github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d @@ -39,14 +40,13 @@ require ( github.com/hashicorp/go-hclog v0.15.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-plugin v1.4.3 - github.com/hashicorp/go-retryablehttp v0.7.1 - github.com/hashicorp/go-tfe v1.7.0 - github.com/hashicorp/go-uuid v1.0.3 + github.com/hashicorp/go-retryablehttp v0.7.0 + github.com/hashicorp/go-tfe v1.0.0 + github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f - github.com/hashicorp/hcl/v2 v2.13.0 + github.com/hashicorp/hcl/v2 v2.12.0 github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 - github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 github.com/jmespath/go-jmespath v0.4.0 github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 @@ -81,16 +81,17 @@ require ( github.com/zclconf/go-cty v1.10.0 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b github.com/zclconf/go-cty-yaml v1.0.2 + go.etcd.io/etcd v0.5.0-alpha.5.0.20210428180535-15715dcf1ace golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b golang.org/x/text v0.3.7 - golang.org/x/tools v0.1.11 + golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a google.golang.org/api v0.44.0-impersonate-preview - google.golang.org/grpc v1.47.0 + google.golang.org/grpc v1.36.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 google.golang.org/protobuf v1.27.1 honnef.co/go/tools v0.3.0 @@ -125,8 +126,8 @@ require ( github.com/armon/go-radix v1.0.0 // indirect github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/coreos/go-semver v0.2.0 // indirect github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect - github.com/creack/pty v1.1.18 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/fatih/color v1.9.0 // indirect @@ -135,7 +136,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect @@ -144,7 +144,7 @@ require ( github.com/hashicorp/go-msgpack v0.5.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/go-slug v0.9.1 // indirect + github.com/hashicorp/go-slug v0.8.0 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect github.com/hashicorp/serf v0.9.5 // indirect @@ -155,6 +155,7 @@ require ( github.com/jstemmer/go-junit-report v0.9.1 // indirect github.com/klauspost/compress v1.11.2 // indirect github.com/kr/pretty v0.2.1 // indirect + github.com/kr/pty v1.1.5 // indirect github.com/manicminer/hamilton-autorest v0.2.0 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/mattn/go-colorable v0.1.6 // indirect @@ -170,21 +171,24 @@ require ( github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.1.1 // indirect github.com/ulikunitz/xz v0.5.8 // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.3.2 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.10.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect + golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/klog/v2 v2.30.0 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect @@ -192,4 +196,14 @@ require ( sigs.k8s.io/yaml v1.2.0 // indirect ) -go 1.18 +replace google.golang.org/grpc v1.36.1 => google.golang.org/grpc v1.27.1 + +replace github.com/golang/mock v1.5.0 => github.com/golang/mock v1.4.4 + +// github.com/dgrijalva/jwt-go is no longer maintained but is an indirect +// dependency of the old etcdv2 backend, and so we need to keep this working +// until that backend is removed. github.com/golang-jwt/jwt/v3 is a drop-in +// replacement that includes a fix for CVE-2020-26160. +replace github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt v3.2.1+incompatible + +go 1.17 diff --git a/go.sum b/go.sum index 8732f7439a1b..dc7841146afc 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 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.61.0/go.mod h1:XukKJg4Y7QsUu0Hxg3qQKUWR4VuWivmyMK2+rUyxAqw= 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= @@ -86,35 +87,47 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d h1:W1diKnDQk github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +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 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.0 h1:P1ekkbuU73Ui/wS0nK1HOM37hh4xdfZo485UPf8rc+Y= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/Netflix/go-expect v0.0.0-20211003183012-e1a7c020ce25 h1:hWfsqBaNZUHztXA78g7Y2Jj3rDQaTCZhhFwz43i2VlA= +github.com/Netflix/go-expect v0.0.0-20211003183012-e1a7c020ce25/go.mod h1:68ORG0HSEWDuH5Eh73AFbYWZ1zT4Y+b0vhOa+vZRUdI= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1501 h1:Ij3S0pNUMgHlhx3Ew8g9RNrt59EKhHYdMODGtFXJfSc= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1501/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70 h1:FrF4uxA24DF3ARNXVbUin3wa5fDLaB1Cy8mKks/LRz4= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible h1:ABQ7FF+IxSFHDMOTtjCfmMDMHiCq6EsAoCV/9sFinaM= github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZylxvcg8H7wBIDfvO5g/cy4/sz1iucBlc2l3Jw= +github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antchfx/xmlquery v1.3.5 h1:I7TuBRqsnfFuL11ruavGm911Awx9IqSdiU6W/ztSmVw= github.com/antchfx/xmlquery v1.3.5/go.mod h1:64w0Xesg2sTaawIdNqMB+7qaW/bSqkQm+ssPaCMWNnc= github.com/antchfx/xpath v1.1.10 h1:cJ0pOvEdN/WvYXxvRrzQH9x5QWKpzHacYO8qzCcDYAg= github.com/antchfx/xpath v1.1.10/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= @@ -122,7 +135,9 @@ github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFU github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-shquot v0.0.1 h1:MGV8lwxF4zw75lN7e0MGs7o6AFYn7L6AZaExUpLh0Mo= github.com/apparentlymart/go-shquot v0.0.1/go.mod h1:lw58XsE5IgUXZ9h0cxnypdx31p9mPFIVEQ9P3c7MlrU= +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13 h1:JtuelWqyixKApmXm3qghhZ7O96P6NKpyrlSIe8Rwnhw= @@ -137,13 +152,18 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.42.35 h1:N4N9buNs4YlosI9N0+WYrq8cIZwdgv34yRbxzZlTvFs= github.com/aws/aws-sdk-go v1.42.35/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= @@ -151,7 +171,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk= github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= 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/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -163,44 +182,45 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= +github.com/coreos/go-semver v0.2.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-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/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 h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI= github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ= github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6 h1:zWydSUQBJApHwpQ4guHi+mGyQN/8yN6xbKWdDtL3ZNM= github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6/go.mod h1:6BLLhzn1VEiJ4veuAGhINBTrBlV889Wd+aU4auxKOww= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 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/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -208,9 +228,17 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 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-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= @@ -220,19 +248,26 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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= @@ -245,7 +280,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt 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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -282,9 +316,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -308,6 +341,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/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.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= @@ -324,9 +358,15 @@ github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5/go.mod github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d h1:fduaPzWwIfvOMLuHk2Al3GZH0XbUqG8MbElPop+Igzs= github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= +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 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +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 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/aws-sdk-go-base v0.7.1 h1:7s/aR3hFn74tYPVihzDyZe7y/+BorN70rr9ZvpV3j3o= github.com/hashicorp/aws-sdk-go-base v0.7.1/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY= github.com/hashicorp/consul/api v1.9.1 h1:SngrdG2L62qqLsUz85qcPhFZ78rPf8tcD5qjMgs6MME= @@ -345,8 +385,11 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= +github.com/hashicorp/go-getter v1.5.3/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI= github.com/hashicorp/go-getter v1.6.2 h1:7jX7xcB+uVCliddZgeKyNxv0xoT7qL5KDtH7rU4IqIk= github.com/hashicorp/go-getter v1.6.2/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= @@ -361,27 +404,27 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 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-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= +github.com/hashicorp/go-plugin v1.4.1/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= -github.com/hashicorp/go-slug v0.9.1 h1:gYNVJ3t0jAWx8AT2eYZci3Xd7NBHyjayW9AR1DU4ki0= -github.com/hashicorp/go-slug v0.9.1/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4= +github.com/hashicorp/go-slug v0.8.0 h1:h7AGtXVAI/cJ/Wwa/JQQaftQnWQmZbAzkzgZeZVVmLw= +github.com/hashicorp/go-slug v0.8.0/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= 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-tfe v1.7.0 h1:GELRhS5dizF6giwjZBqUC/xPaSuNYB+hWRtUnf6i8K8= -github.com/hashicorp/go-tfe v1.7.0/go.mod h1:E8a90lC4kjU5Lc2c0D+SnWhUuyuoCIVm4Ewzv3jCD3A= +github.com/hashicorp/go-tfe v1.0.0 h1:CmwoHrOs7WJfD/yEmVjJ65+dyKeVRrgvRHBLVSQQ6Ks= +github.com/hashicorp/go-tfe v1.0.0/go.mod h1:tJF/OlAXzVbmjiimAPLplSLgwg6kZDUOy0MzHuMwvF4= 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-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -394,8 +437,9 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws= github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= -github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= -github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8= +github.com/hashicorp/hcl/v2 v2.12.0 h1:PsYxySWpMD4KPaoJLnsHwtK5Qptvj/4Q6s0t4sUxZf4= +github.com/hashicorp/hcl/v2 v2.12.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -406,8 +450,10 @@ github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 h1:l+bLFvHjqtgNQwWxwrFX9PemGAAO2P1AGZM7zlMNvCs= github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs= -github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg= -github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI= +github.com/hashicorp/terraform-exec v0.14.0/go.mod h1:qrAASDq28KZiMPDnQ02sFS9udcqEkRly002EA2izXTA= +github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= +github.com/hashicorp/terraform-plugin-go v0.4.0/go.mod h1:7u/6nt6vaiwcWE2GuJKbJwNlDFnf5n95xKw4hqIVr58= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0/go.mod h1:6KbP09YzlB++S6XSUKYl83WyoHVN4MgeoCbPRsdfCtA= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -423,6 +469,9 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -432,26 +481,36 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 h1:kie3qOosvRKqwij2HGzXWffwpXvcqfPPXRUw8I4F/mg= github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 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.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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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= @@ -474,6 +533,7 @@ github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88 h1:cxuVcCvCLD9yYDbRCWw0jSgh1oT6P6mv3aJDKK5o7X4= github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 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.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= @@ -485,13 +545,17 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.4 h1:xmZZyxuP+bYKAKkA9ABYXVNJ+G/Wf3R8d8vAP3LDJJk= github.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= @@ -526,6 +590,7 @@ github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0Gq 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= @@ -533,16 +598,19 @@ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISe github.com/mozillazg/go-httpheader v0.3.0 h1:3brX5z8HTH+0RrNA1362Rc3HsaxyWEKtGY45YrhuINM= github.com/mozillazg/go-httpheader v0.3.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.7.11 h1:xV/WU3Vdwh5BUH4N06JNUznb6d5zhRPOnlgCrpNYNKA= github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI= +github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -559,6 +627,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 h1:dofHuld+js7eKSemxqTVIo8yRlpRw+H1SdpzZxWruBc= github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +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= @@ -567,24 +636,45 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/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= @@ -598,8 +688,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232 h1:kwsWbh4rEw42ZDe9/812ebhbwNZxlQyZ2sTmxBOKhN4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= @@ -608,17 +698,25 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.233 h1:5Tbi+jy github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.233/go.mod h1:sX14+NSvMjOhNFaMtP2aDy6Bss8PyFXij21gpY6+DAs= github.com/tencentyun/cos-go-sdk-v5 v0.7.29 h1:uwRBzc70Wgtc5iQQCowqecfRT0OpCXUOZzodZHOOEDs= github.com/tencentyun/cos-go-sdk-v5 v0.7.29/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tombuildsstuff/giovanni v0.15.1 h1:CVRaLOJ7C/eercCrKIsarfJ4SZoGMdBL9Q2deFDUXco= github.com/tombuildsstuff/giovanni v0.15.1/go.mod h1:0TZugJPEtqzPlMpuJHYfXY6Dq2uLPrXf98D2XQSxNbA= github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -627,15 +725,23 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.8.4/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.5.0-alpha.5.0.20210428180535-15715dcf1ace h1:wOZR+AfzmQYNRqx1F+LL9TX8vBVzSbndRoc0tr/Bp4k= +go.etcd.io/etcd v0.5.0-alpha.5.0.20210428180535-15715dcf1ace/go.mod h1:q+i20RPAmay+xq8LJ3VMOhXCNk4YCk3V7QP91meFavw= 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= @@ -644,8 +750,15 @@ 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.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -659,6 +772,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -674,6 +789,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 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 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -689,9 +805,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl 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 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/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= @@ -703,14 +818,16 @@ 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/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-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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -748,8 +865,10 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= @@ -781,12 +900,16 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-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-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -795,6 +918,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -832,13 +956,16 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -854,12 +981,14 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/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-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= @@ -901,6 +1030,7 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY 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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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= @@ -912,11 +1042,10 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= +golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II= +golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 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= @@ -977,10 +1106,10 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG 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-20200513103714-09dca8ec2884/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-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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= @@ -994,9 +1123,8 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1 h1:E7wSQBXkH3T3diucK+9Z1kjn4+/9tNG7lZLr75oOhh8= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1005,21 +1133,18 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac 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 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= 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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.32.0/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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1036,12 +1161,14 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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-20200227125254-8fa46927fb4f/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/cheggaaa/pb.v1 v1.0.27/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= @@ -1049,11 +1176,13 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 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.3/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.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1062,9 +1191,8 @@ 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= 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= @@ -1098,5 +1226,6 @@ sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNza sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/addrs/instance_key.go b/internal/addrs/instance_key.go index 2d46bfcbe0b7..ff128be5b4d3 100644 --- a/internal/addrs/instance_key.go +++ b/internal/addrs/instance_key.go @@ -2,8 +2,6 @@ package addrs import ( "fmt" - "strings" - "unicode" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/gocty" @@ -74,10 +72,9 @@ func (k StringKey) instanceKeySigil() { } func (k StringKey) String() string { - // We use HCL's quoting syntax here so that we can in principle parse - // an address constructed by this package as if it were an HCL - // traversal, even if the string contains HCL's own metacharacters. - return fmt.Sprintf("[%s]", toHCLQuotedString(string(k))) + // FIXME: This isn't _quite_ right because Go's quoted string syntax is + // slightly different than HCL's, but we'll accept it for now. + return fmt.Sprintf("[%q]", string(k)) } func (k StringKey) Value() cty.Value { @@ -136,56 +133,3 @@ const ( IntKeyType InstanceKeyType = 'I' StringKeyType InstanceKeyType = 'S' ) - -// toHCLQuotedString is a helper which formats the given string in a way that -// HCL's expression parser would treat as a quoted string template. -// -// This includes: -// - Adding quote marks at the start and the end. -// - Using backslash escapes as needed for characters that cannot be represented directly. -// - Escaping anything that would be treated as a template interpolation or control sequence. -func toHCLQuotedString(s string) string { - // This is an adaptation of a similar function inside the hclwrite package, - // inlined here because hclwrite's version generates HCL tokens but we - // only need normal strings. - if len(s) == 0 { - return `""` - } - var buf strings.Builder - buf.WriteByte('"') - for i, r := range s { - switch r { - case '\n': - buf.WriteString(`\n`) - case '\r': - buf.WriteString(`\r`) - case '\t': - buf.WriteString(`\t`) - case '"': - buf.WriteString(`\"`) - case '\\': - buf.WriteString(`\\`) - case '$', '%': - buf.WriteRune(r) - remain := s[i+1:] - if len(remain) > 0 && remain[0] == '{' { - // Double up our template introducer symbol to escape it. - buf.WriteRune(r) - } - default: - if !unicode.IsPrint(r) { - var fmted string - if r < 65536 { - fmted = fmt.Sprintf("\\u%04x", r) - } else { - fmted = fmt.Sprintf("\\U%08x", r) - } - buf.WriteString(fmted) - } else { - buf.WriteRune(r) - } - } - } - buf.WriteByte('"') - return buf.String() -} diff --git a/internal/addrs/instance_key_test.go b/internal/addrs/instance_key_test.go deleted file mode 100644 index 0d12888bf0fb..000000000000 --- a/internal/addrs/instance_key_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package addrs - -import ( - "fmt" - "testing" -) - -func TestInstanceKeyString(t *testing.T) { - tests := []struct { - Key InstanceKey - Want string - }{ - { - IntKey(0), - `[0]`, - }, - { - IntKey(5), - `[5]`, - }, - { - StringKey(""), - `[""]`, - }, - { - StringKey("hi"), - `["hi"]`, - }, - { - StringKey("0"), - `["0"]`, // intentionally distinct from IntKey(0) - }, - { - // Quotes must be escaped - StringKey(`"`), - `["\""]`, - }, - { - // Escape sequences must themselves be escaped - StringKey(`\r\n`), - `["\\r\\n"]`, - }, - { - // Template interpolation sequences "${" must be escaped. - StringKey(`${hello}`), - `["$${hello}"]`, - }, - { - // Template control sequences "%{" must be escaped. - StringKey(`%{ for something in something }%{ endfor }`), - `["%%{ for something in something }%%{ endfor }"]`, - }, - { - // Dollar signs that aren't followed by { are not interpolation sequences - StringKey(`$hello`), - `["$hello"]`, - }, - { - // Percent signs that aren't followed by { are not control sequences - StringKey(`%hello`), - `["%hello"]`, - }, - } - - for _, test := range tests { - testName := fmt.Sprintf("%#v", test.Key) - t.Run(testName, func(t *testing.T) { - got := test.Key.String() - want := test.Want - if got != want { - t.Errorf("wrong result\nreciever: %s\ngot: %s\nwant: %s", testName, got, want) - } - }) - } -} diff --git a/internal/addrs/map.go b/internal/addrs/map.go deleted file mode 100644 index 87b1aae266d4..000000000000 --- a/internal/addrs/map.go +++ /dev/null @@ -1,128 +0,0 @@ -package addrs - -// Map represents a mapping whose keys are address types that implement -// UniqueKeyer. -// -// Since not all address types are comparable in the Go language sense, this -// type cannot work with the typical Go map access syntax, and so instead has -// a method-based syntax. Use this type only for situations where the key -// type isn't guaranteed to always be a valid key for a standard Go map. -type Map[K UniqueKeyer, V any] struct { - // Elems is the internal data structure of the map. - // - // This is exported to allow for comparisons during tests and other similar - // careful read operations, but callers MUST NOT modify this map directly. - // Use only the methods of Map to modify the contents of this structure, - // to ensure that it remains correct and consistent. - Elems map[UniqueKey]MapElem[K, V] -} - -type MapElem[K UniqueKeyer, V any] struct { - Key K - Value V -} - -func MakeMap[K UniqueKeyer, V any](initialElems ...MapElem[K, V]) Map[K, V] { - inner := make(map[UniqueKey]MapElem[K, V], len(initialElems)) - ret := Map[K, V]{inner} - for _, elem := range initialElems { - ret.Put(elem.Key, elem.Value) - } - return ret -} - -func MakeMapElem[K UniqueKeyer, V any](key K, value V) MapElem[K, V] { - return MapElem[K, V]{key, value} -} - -// Put inserts a new element into the map, or replaces an existing element -// which has an equivalent key. -func (m Map[K, V]) Put(key K, value V) { - realKey := key.UniqueKey() - m.Elems[realKey] = MapElem[K, V]{key, value} -} - -// PutElement is like Put but takes the key and value from the given MapElement -// structure instead of as individual arguments. -func (m Map[K, V]) PutElement(elem MapElem[K, V]) { - m.Put(elem.Key, elem.Value) -} - -// Remove deletes the element with the given key from the map, or does nothing -// if there is no such element. -func (m Map[K, V]) Remove(key K) { - realKey := key.UniqueKey() - delete(m.Elems, realKey) -} - -// Get returns the value of the element with the given key, or the zero value -// of V if there is no such element. -func (m Map[K, V]) Get(key K) V { - realKey := key.UniqueKey() - return m.Elems[realKey].Value -} - -// GetOk is like Get but additionally returns a flag for whether there was an -// element with the given key present in the map. -func (m Map[K, V]) GetOk(key K) (V, bool) { - realKey := key.UniqueKey() - elem, ok := m.Elems[realKey] - return elem.Value, ok -} - -// Has returns true if and only if there is an element in the map which has the -// given key. -func (m Map[K, V]) Has(key K) bool { - realKey := key.UniqueKey() - _, ok := m.Elems[realKey] - return ok -} - -// Len returns the number of elements in the map. -func (m Map[K, V]) Len() int { - return len(m.Elems) -} - -// Elements returns a slice containing a snapshot of the current elements of -// the map, in an unpredictable order. -func (m Map[K, V]) Elements() []MapElem[K, V] { - if len(m.Elems) == 0 { - return nil - } - ret := make([]MapElem[K, V], 0, len(m.Elems)) - for _, elem := range m.Elems { - ret = append(ret, elem) - } - return ret -} - -// Keys returns a Set[K] containing a snapshot of the current keys of elements -// of the map. -func (m Map[K, V]) Keys() Set[K] { - if len(m.Elems) == 0 { - return nil - } - ret := make(Set[K], len(m.Elems)) - - // We mess with the internals of Set here, rather than going through its - // public interface, because that means we can avoid re-calling UniqueKey - // on all of the elements when we know that our own Put method would have - // already done the same thing. - for realKey, elem := range m.Elems { - ret[realKey] = elem.Key - } - return ret -} - -// Values returns a slice containing a snapshot of the current values of -// elements of the map, in an unpredictable order. -func (m Map[K, V]) Values() []V { - if len(m.Elems) == 0 { - return nil - } - ret := make([]V, 0, len(m.Elems)) - for _, elem := range m.Elems { - ret = append(ret, elem.Value) - } - return ret -} diff --git a/internal/addrs/map_test.go b/internal/addrs/map_test.go deleted file mode 100644 index e5a84f03d99b..000000000000 --- a/internal/addrs/map_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package addrs - -import ( - "testing" -) - -func TestMap(t *testing.T) { - variableName := InputVariable{Name: "name"} - localHello := LocalValue{Name: "hello"} - pathModule := PathAttr{Name: "module"} - moduleBeep := ModuleCall{Name: "beep"} - eachKey := ForEachAttr{Name: "key"} // intentionally not in the map - - m := MakeMap( - MakeMapElem[Referenceable](variableName, "Aisling"), - ) - - m.Put(localHello, "hello") - m.Put(pathModule, "boop") - m.Put(moduleBeep, "unrealistic") - - keySet := m.Keys() - if want := variableName; !m.Has(want) { - t.Errorf("map does not include %s", want) - } - if want := variableName; !keySet.Has(want) { - t.Errorf("key set does not include %s", want) - } - if want := localHello; !m.Has(want) { - t.Errorf("map does not include %s", want) - } - if want := localHello; !keySet.Has(want) { - t.Errorf("key set does not include %s", want) - } - if want := pathModule; !keySet.Has(want) { - t.Errorf("key set does not include %s", want) - } - if want := moduleBeep; !keySet.Has(want) { - t.Errorf("key set does not include %s", want) - } - if doNotWant := eachKey; m.Has(doNotWant) { - t.Errorf("map includes rogue element %s", doNotWant) - } - if doNotWant := eachKey; keySet.Has(doNotWant) { - t.Errorf("key set includes rogue element %s", doNotWant) - } - - if got, want := m.Get(variableName), "Aisling"; got != want { - t.Errorf("unexpected value %q for %s; want %q", got, variableName, want) - } - if got, want := m.Get(localHello), "hello"; got != want { - t.Errorf("unexpected value %q for %s; want %q", got, localHello, want) - } - if got, want := m.Get(pathModule), "boop"; got != want { - t.Errorf("unexpected value %q for %s; want %q", got, pathModule, want) - } - if got, want := m.Get(moduleBeep), "unrealistic"; got != want { - t.Errorf("unexpected value %q for %s; want %q", got, moduleBeep, want) - } - if got, want := m.Get(eachKey), ""; got != want { - // eachKey isn't in the map, so Get returns the zero value of string - t.Errorf("unexpected value %q for %s; want %q", got, eachKey, want) - } - - if v, ok := m.GetOk(variableName); v != "Aisling" || !ok { - t.Errorf("GetOk for %q returned incorrect result (%q, %#v)", variableName, v, ok) - } - if v, ok := m.GetOk(eachKey); v != "" || ok { - t.Errorf("GetOk for %q returned incorrect result (%q, %#v)", eachKey, v, ok) - } - - m.Remove(moduleBeep) - if doNotWant := moduleBeep; m.Has(doNotWant) { - t.Errorf("map still includes %s after removing it", doNotWant) - } - if want := moduleBeep; !keySet.Has(want) { - t.Errorf("key set no longer includes %s after removing it from the map; key set is supposed to be a snapshot at the time of call", want) - } - keySet = m.Keys() - if doNotWant := moduleBeep; keySet.Has(doNotWant) { - t.Errorf("key set still includes %s after a second call after removing it from the map", doNotWant) - } -} diff --git a/internal/addrs/module_package.go b/internal/addrs/module_package.go index e1c82e36ed7a..dc5a6621c798 100644 --- a/internal/addrs/module_package.go +++ b/internal/addrs/module_package.go @@ -1,7 +1,9 @@ package addrs import ( - tfaddr "github.com/hashicorp/terraform-registry-address" + "strings" + + svchost "github.com/hashicorp/terraform-svchost" ) // A ModulePackage represents a physical location where Terraform can retrieve @@ -43,4 +45,45 @@ func (p ModulePackage) String() string { // registry in order to find a real module package address. These being // distinct is intended to help future maintainers more easily follow the // series of steps in the module installer, with the help of the type checker. -type ModuleRegistryPackage = tfaddr.ModulePackage +type ModuleRegistryPackage struct { + Host svchost.Hostname + Namespace string + Name string + TargetSystem string +} + +func (s ModuleRegistryPackage) String() string { + var buf strings.Builder + // Note: we're using the "display" form of the hostname here because + // for our service hostnames "for display" means something different: + // it means to render non-ASCII characters directly as Unicode + // characters, rather than using the "punycode" representation we + // use for internal processing, and so the "display" representation + // is actually what users would write in their configurations. + return s.Host.ForDisplay() + "/" + s.ForRegistryProtocol() + return buf.String() +} + +func (s ModuleRegistryPackage) ForDisplay() string { + if s.Host == DefaultModuleRegistryHost { + return s.ForRegistryProtocol() + } + return s.Host.ForDisplay() + "/" + s.ForRegistryProtocol() +} + +// ForRegistryProtocol returns a string representation of just the namespace, +// name, and target system portions of the address, always omitting the +// registry hostname and the subdirectory portion, if any. +// +// This is primarily intended for generating addresses to send to the +// registry in question via the registry protocol, since the protocol +// skips sending the registry its own hostname as part of identifiers. +func (s ModuleRegistryPackage) ForRegistryProtocol() string { + var buf strings.Builder + buf.WriteString(s.Namespace) + buf.WriteByte('/') + buf.WriteString(s.Name) + buf.WriteByte('/') + buf.WriteString(s.TargetSystem) + return buf.String() +} diff --git a/internal/addrs/module_source.go b/internal/addrs/module_source.go index 905b77e2f5c1..c42bc9f04459 100644 --- a/internal/addrs/module_source.go +++ b/internal/addrs/module_source.go @@ -3,9 +3,10 @@ package addrs import ( "fmt" "path" + "regexp" "strings" - tfaddr "github.com/hashicorp/terraform-registry-address" + svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform/internal/getmodules" ) @@ -196,11 +197,30 @@ func (s ModuleSourceLocal) ForDisplay() string { // combination of a ModuleSourceRegistry and a module version number into // a concrete ModuleSourceRemote that Terraform will then download and // install. -type ModuleSourceRegistry tfaddr.Module +type ModuleSourceRegistry struct { + // PackageAddr is the registry package that the target module belongs to. + // The module installer must translate this into a ModuleSourceRemote + // using the registry API and then take that underlying address's + // PackageAddr in order to find the actual package location. + PackageAddr ModuleRegistryPackage + + // If Subdir is non-empty then it represents a sub-directory within the + // remote package that the registry address eventually resolves to. + // This will ultimately become the suffix of the Subdir of the + // ModuleSourceRemote that the registry address translates to. + // + // Subdir uses a normalized forward-slash-based path syntax within the + // virtual filesystem represented by the final package. It will never + // include `../` or `./` sequences. + Subdir string +} // DefaultModuleRegistryHost is the hostname used for registry-based module // source addresses that do not have an explicit hostname. -const DefaultModuleRegistryHost = tfaddr.DefaultModuleRegistryHost +const DefaultModuleRegistryHost = svchost.Hostname("registry.terraform.io") + +var moduleRegistryNamePattern = regexp.MustCompile("^[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?$") +var moduleRegistryTargetSystemPattern = regexp.MustCompile("^[0-9a-z]{1,64}$") // ParseModuleSourceRegistry is a variant of ParseModuleSource which only // accepts module registry addresses, and will reject any other address type. @@ -217,30 +237,147 @@ func ParseModuleSourceRegistry(raw string) (ModuleSource, error) { return ModuleSourceRegistry{}, fmt.Errorf("can't use local directory %q as a module registry address", raw) } - src, err := tfaddr.ParseModuleSource(raw) + ret, err := parseModuleSourceRegistry(raw) if err != nil { + // This is to make sure we return a nil ModuleSource, rather than + // a non-nil ModuleSource containing a zero-value ModuleSourceRegistry. return nil, err } - return ModuleSourceRegistry{ - Package: src.Package, - Subdir: src.Subdir, - }, nil + return ret, nil +} + +func parseModuleSourceRegistry(raw string) (ModuleSourceRegistry, error) { + var err error + + var subDir string + raw, subDir = getmodules.SplitPackageSubdir(raw) + if strings.HasPrefix(subDir, "../") { + return ModuleSourceRegistry{}, fmt.Errorf("subdirectory path %q leads outside of the module package", subDir) + } + + parts := strings.Split(raw, "/") + // A valid registry address has either three or four parts, because the + // leading hostname part is optional. + if len(parts) != 3 && len(parts) != 4 { + return ModuleSourceRegistry{}, fmt.Errorf("a module registry source address must have either three or four slash-separated components") + } + + host := DefaultModuleRegistryHost + if len(parts) == 4 { + host, err = svchost.ForComparison(parts[0]) + if err != nil { + // The svchost library doesn't produce very good error messages to + // return to an end-user, so we'll use some custom ones here. + switch { + case strings.Contains(parts[0], "--"): + // Looks like possibly punycode, which we don't allow here + // to ensure that source addresses are written readably. + return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname %q; internationalized domain names must be given as direct unicode characters, not in punycode", parts[0]) + default: + return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname %q", parts[0]) + } + } + if !strings.Contains(host.String(), ".") { + return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname: must contain at least one dot") + } + // Discard the hostname prefix now that we've processed it + parts = parts[1:] + } + + ret := ModuleSourceRegistry{ + PackageAddr: ModuleRegistryPackage{ + Host: host, + }, + + Subdir: subDir, + } + + if host == svchost.Hostname("github.com") || host == svchost.Hostname("bitbucket.org") { + return ret, fmt.Errorf("can't use %q as a module registry host, because it's reserved for installing directly from version control repositories", host) + } + + if ret.PackageAddr.Namespace, err = parseModuleRegistryName(parts[0]); err != nil { + if strings.Contains(parts[0], ".") { + // Seems like the user omitted one of the latter components in + // an address with an explicit hostname. + return ret, fmt.Errorf("source address must have three more components after the hostname: the namespace, the name, and the target system") + } + return ret, fmt.Errorf("invalid namespace %q: %s", parts[0], err) + } + if ret.PackageAddr.Name, err = parseModuleRegistryName(parts[1]); err != nil { + return ret, fmt.Errorf("invalid module name %q: %s", parts[1], err) + } + if ret.PackageAddr.TargetSystem, err = parseModuleRegistryTargetSystem(parts[2]); err != nil { + if strings.Contains(parts[2], "?") { + // The user was trying to include a query string, probably? + return ret, fmt.Errorf("module registry addresses may not include a query string portion") + } + return ret, fmt.Errorf("invalid target system %q: %s", parts[2], err) + } + + return ret, nil +} + +// parseModuleRegistryName validates and normalizes a string in either the +// "namespace" or "name" position of a module registry source address. +func parseModuleRegistryName(given string) (string, error) { + // Similar to the names in provider source addresses, we defined these + // to be compatible with what filesystems and typical remote systems + // like GitHub allow in names. Unfortunately we didn't end up defining + // these exactly equivalently: provider names can only use dashes as + // punctuation, whereas module names can use underscores. So here we're + // using some regular expressions from the original module source + // implementation, rather than using the IDNA rules as we do in + // ParseProviderPart. + + if !moduleRegistryNamePattern.MatchString(given) { + return "", fmt.Errorf("must be between one and 64 characters, including ASCII letters, digits, dashes, and underscores, where dashes and underscores may not be the prefix or suffix") + } + + // We also skip normalizing the name to lowercase, because we historically + // didn't do that and so existing module registries might be doing + // case-sensitive matching. + return given, nil +} + +// parseModuleRegistryTargetSystem validates and normalizes a string in the +// "target system" position of a module registry source address. This is +// what we historically called "provider" but never actually enforced as +// being a provider address, and now _cannot_ be a provider address because +// provider addresses have three slash-separated components of their own. +func parseModuleRegistryTargetSystem(given string) (string, error) { + // Similar to the names in provider source addresses, we defined these + // to be compatible with what filesystems and typical remote systems + // like GitHub allow in names. Unfortunately we didn't end up defining + // these exactly equivalently: provider names can't use dashes or + // underscores. So here we're using some regular expressions from the + // original module source implementation, rather than using the IDNA rules + // as we do in ParseProviderPart. + + if !moduleRegistryTargetSystemPattern.MatchString(given) { + return "", fmt.Errorf("must be between one and 64 ASCII letters or digits") + } + + // We also skip normalizing the name to lowercase, because we historically + // didn't do that and so existing module registries might be doing + // case-sensitive matching. + return given, nil } func (s ModuleSourceRegistry) moduleSource() {} func (s ModuleSourceRegistry) String() string { if s.Subdir != "" { - return s.Package.String() + "//" + s.Subdir + return s.PackageAddr.String() + "//" + s.Subdir } - return s.Package.String() + return s.PackageAddr.String() } func (s ModuleSourceRegistry) ForDisplay() string { if s.Subdir != "" { - return s.Package.ForDisplay() + "//" + s.Subdir + return s.PackageAddr.ForDisplay() + "//" + s.Subdir } - return s.Package.ForDisplay() + return s.PackageAddr.ForDisplay() } // ModuleSourceRemote is a ModuleSource representing a remote location from @@ -250,9 +387,9 @@ func (s ModuleSourceRegistry) ForDisplay() string { // means that it's selecting a sub-directory of the given package to use as // the entry point into the package. type ModuleSourceRemote struct { - // Package is the address of the remote package that the requested + // PackageAddr is the address of the remote package that the requested // module belongs to. - Package ModulePackage + PackageAddr ModulePackage // If Subdir is non-empty then it represents a sub-directory within the // remote package which will serve as the entry-point for the package. @@ -308,8 +445,8 @@ func parseModuleSourceRemote(raw string) (ModuleSourceRemote, error) { } return ModuleSourceRemote{ - Package: ModulePackage(norm), - Subdir: subDir, + PackageAddr: ModulePackage(norm), + Subdir: subDir, }, nil } @@ -317,9 +454,9 @@ func (s ModuleSourceRemote) moduleSource() {} func (s ModuleSourceRemote) String() string { if s.Subdir != "" { - return s.Package.String() + "//" + s.Subdir + return s.PackageAddr.String() + "//" + s.Subdir } - return s.Package.String() + return s.PackageAddr.String() } func (s ModuleSourceRemote) ForDisplay() string { diff --git a/internal/addrs/module_source_test.go b/internal/addrs/module_source_test.go index d6b5626ec682..9604c33744bd 100644 --- a/internal/addrs/module_source_test.go +++ b/internal/addrs/module_source_test.go @@ -59,7 +59,7 @@ func TestParseModuleSource(t *testing.T) { "main registry implied": { input: "hashicorp/subnets/cidr", want: ModuleSourceRegistry{ - Package: ModuleRegistryPackage{ + PackageAddr: ModuleRegistryPackage{ Host: svchost.Hostname("registry.terraform.io"), Namespace: "hashicorp", Name: "subnets", @@ -71,7 +71,7 @@ func TestParseModuleSource(t *testing.T) { "main registry implied, subdir": { input: "hashicorp/subnets/cidr//examples/foo", want: ModuleSourceRegistry{ - Package: ModuleRegistryPackage{ + PackageAddr: ModuleRegistryPackage{ Host: svchost.Hostname("registry.terraform.io"), Namespace: "hashicorp", Name: "subnets", @@ -92,7 +92,7 @@ func TestParseModuleSource(t *testing.T) { "custom registry": { input: "example.com/awesomecorp/network/happycloud", want: ModuleSourceRegistry{ - Package: ModuleRegistryPackage{ + PackageAddr: ModuleRegistryPackage{ Host: svchost.Hostname("example.com"), Namespace: "awesomecorp", Name: "network", @@ -104,7 +104,7 @@ func TestParseModuleSource(t *testing.T) { "custom registry, subdir": { input: "example.com/awesomecorp/network/happycloud//examples/foo", want: ModuleSourceRegistry{ - Package: ModuleRegistryPackage{ + PackageAddr: ModuleRegistryPackage{ Host: svchost.Hostname("example.com"), Namespace: "awesomecorp", Name: "network", @@ -118,68 +118,68 @@ func TestParseModuleSource(t *testing.T) { "github.com shorthand": { input: "github.com/hashicorp/terraform-cidr-subnets", want: ModuleSourceRemote{ - Package: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), + PackageAddr: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), }, }, "github.com shorthand, subdir": { input: "github.com/hashicorp/terraform-cidr-subnets//example/foo", want: ModuleSourceRemote{ - Package: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), - Subdir: "example/foo", + PackageAddr: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), + Subdir: "example/foo", }, }, "git protocol, URL-style": { input: "git://example.com/code/baz.git", want: ModuleSourceRemote{ - Package: ModulePackage("git://example.com/code/baz.git"), + PackageAddr: ModulePackage("git://example.com/code/baz.git"), }, }, "git protocol, URL-style, subdir": { input: "git://example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - Package: ModulePackage("git://example.com/code/baz.git"), - Subdir: "bleep/bloop", + PackageAddr: ModulePackage("git://example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over HTTPS, URL-style": { input: "git::https://example.com/code/baz.git", want: ModuleSourceRemote{ - Package: ModulePackage("git::https://example.com/code/baz.git"), + PackageAddr: ModulePackage("git::https://example.com/code/baz.git"), }, }, "git over HTTPS, URL-style, subdir": { input: "git::https://example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - Package: ModulePackage("git::https://example.com/code/baz.git"), - Subdir: "bleep/bloop", + PackageAddr: ModulePackage("git::https://example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over SSH, URL-style": { input: "git::ssh://git@example.com/code/baz.git", want: ModuleSourceRemote{ - Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), + PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), }, }, "git over SSH, URL-style, subdir": { input: "git::ssh://git@example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), - Subdir: "bleep/bloop", + PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over SSH, scp-style": { input: "git::git@example.com:code/baz.git", want: ModuleSourceRemote{ // Normalized to URL-style - Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), + PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), }, }, "git over SSH, scp-style, subdir": { input: "git::git@example.com:code/baz.git//bleep/bloop", want: ModuleSourceRemote{ // Normalized to URL-style - Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), - Subdir: "bleep/bloop", + PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, @@ -190,63 +190,63 @@ func TestParseModuleSource(t *testing.T) { "Google Cloud Storage bucket implied, path prefix": { input: "www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE", want: ModuleSourceRemote{ - Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), + PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), }, }, "Google Cloud Storage bucket, path prefix": { input: "gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE", want: ModuleSourceRemote{ - Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), + PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), }, }, "Google Cloud Storage bucket implied, archive object": { input: "www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip", want: ModuleSourceRemote{ - Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), + PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), }, }, "Google Cloud Storage bucket, archive object": { input: "gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip", want: ModuleSourceRemote{ - Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), + PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), }, }, "Amazon S3 bucket implied, archive object": { input: "s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", want: ModuleSourceRemote{ - Package: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), + PackageAddr: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), }, }, "Amazon S3 bucket, archive object": { input: "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", want: ModuleSourceRemote{ - Package: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), + PackageAddr: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), }, }, "HTTP URL": { input: "http://example.com/module", want: ModuleSourceRemote{ - Package: ModulePackage("http://example.com/module"), + PackageAddr: ModulePackage("http://example.com/module"), }, }, "HTTPS URL": { input: "https://example.com/module", want: ModuleSourceRemote{ - Package: ModulePackage("https://example.com/module"), + PackageAddr: ModulePackage("https://example.com/module"), }, }, "HTTPS URL, archive file": { input: "https://example.com/module.zip", want: ModuleSourceRemote{ - Package: ModulePackage("https://example.com/module.zip"), + PackageAddr: ModulePackage("https://example.com/module.zip"), }, }, "HTTPS URL, forced archive file": { input: "https://example.com/module?archive=tar", want: ModuleSourceRemote{ - Package: ModulePackage("https://example.com/module?archive=tar"), + PackageAddr: ModulePackage("https://example.com/module?archive=tar"), }, }, "HTTPS URL, forced archive file and checksum": { @@ -255,7 +255,7 @@ func TestParseModuleSource(t *testing.T) { // The query string only actually gets processed when we finally // do the get, so "checksum=blah" is accepted as valid up // at this parsing layer. - Package: ModulePackage("https://example.com/module?archive=tar&checksum=blah"), + PackageAddr: ModulePackage("https://example.com/module?archive=tar&checksum=blah"), }, }, @@ -266,7 +266,7 @@ func TestParseModuleSource(t *testing.T) { // is replaced by a deep filesystem copy instead. input: "/tmp/foo/example", want: ModuleSourceRemote{ - Package: ModulePackage("file:///tmp/foo/example"), + PackageAddr: ModulePackage("file:///tmp/foo/example"), }, }, "absolute filesystem path, subdir": { @@ -277,8 +277,8 @@ func TestParseModuleSource(t *testing.T) { // syntax to move the package root higher in the real filesystem. input: "/tmp/foo//example", want: ModuleSourceRemote{ - Package: ModulePackage("file:///tmp/foo"), - Subdir: "example", + PackageAddr: ModulePackage("file:///tmp/foo"), + Subdir: "example", }, }, @@ -310,7 +310,7 @@ func TestParseModuleSource(t *testing.T) { // Unfortunately go-getter doesn't actually reject a totally // invalid address like this until getting time, as long as // it looks somewhat like a URL. - Package: ModulePackage("dfgdfgsd:dgfhdfghdfghdfg/dfghdfghdfg"), + PackageAddr: ModulePackage("dfgdfgsd:dgfhdfghdfghdfg/dfghdfghdfg"), }, }, } @@ -344,8 +344,8 @@ func TestParseModuleSource(t *testing.T) { func TestModuleSourceRemoteFromRegistry(t *testing.T) { t.Run("both have subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - Package: ModulePackage("boop"), - Subdir: "foo", + PackageAddr: ModulePackage("boop"), + Subdir: "foo", } registry := ModuleSourceRegistry{ Subdir: "bar", @@ -363,8 +363,8 @@ func TestModuleSourceRemoteFromRegistry(t *testing.T) { }) t.Run("only remote has subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - Package: ModulePackage("boop"), - Subdir: "foo", + PackageAddr: ModulePackage("boop"), + Subdir: "foo", } registry := ModuleSourceRegistry{ Subdir: "", @@ -382,8 +382,8 @@ func TestModuleSourceRemoteFromRegistry(t *testing.T) { }) t.Run("only registry has subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - Package: ModulePackage("boop"), - Subdir: "", + PackageAddr: ModulePackage("boop"), + Subdir: "", } registry := ModuleSourceRegistry{ Subdir: "bar", @@ -565,7 +565,7 @@ func TestParseModuleSourceRegistry(t *testing.T) { if got, want := addr.ForDisplay(), test.wantForDisplay; got != want { t.Errorf("wrong ForDisplay() result\ngot: %s\nwant: %s", got, want) } - if got, want := addr.Package.ForRegistryProtocol(), test.wantForProtocol; got != want { + if got, want := addr.PackageAddr.ForRegistryProtocol(), test.wantForProtocol; got != want { t.Errorf("wrong ForRegistryProtocol() result\ngot: %s\nwant: %s", got, want) } }) diff --git a/internal/addrs/move_endpoint_module.go b/internal/addrs/move_endpoint_module.go index f2c1408d66b1..fdc8a5c25da0 100644 --- a/internal/addrs/move_endpoint_module.go +++ b/internal/addrs/move_endpoint_module.go @@ -5,9 +5,8 @@ import ( "reflect" "strings" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" ) // anyKeyImpl is the InstanceKey representation indicating a wildcard, which @@ -180,7 +179,8 @@ func (e *MoveEndpointInModule) InModuleInstance(modInst ModuleInstance) AbsMovea // while selecting a particular object to move. // // This is a rather special-purpose function here mainly to support our -// validation rule that a module can only traverse down into child modules. +// validation rule that a module can only traverse down into child modules +// that belong to the same module package. func (e *MoveEndpointInModule) ModuleCallTraversals() (Module, []ModuleCall) { // We're returning []ModuleCall rather than Module here to make it clearer // that this is a relative sequence of calls rather than an absolute diff --git a/internal/addrs/provider.go b/internal/addrs/provider.go index e8ceaa9a1cf6..e3902c3d968c 100644 --- a/internal/addrs/provider.go +++ b/internal/addrs/provider.go @@ -1,24 +1,32 @@ package addrs import ( + "fmt" + "strings" + + "golang.org/x/net/idna" + "github.com/hashicorp/hcl/v2" - tfaddr "github.com/hashicorp/terraform-registry-address" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform/internal/tfdiags" ) // Provider encapsulates a single provider type. In the future this will be // extended to include additional fields including Namespace and SourceHost -type Provider = tfaddr.Provider +type Provider struct { + Type string + Namespace string + Hostname svchost.Hostname +} // DefaultProviderRegistryHost is the hostname used for provider addresses that do // not have an explicit hostname. -const DefaultProviderRegistryHost = tfaddr.DefaultProviderRegistryHost +const DefaultProviderRegistryHost = svchost.Hostname("registry.terraform.io") // BuiltInProviderHost is the pseudo-hostname used for the "built-in" provider // namespace. Built-in provider addresses must also have their namespace set // to BuiltInProviderNamespace in order to be considered as built-in. -const BuiltInProviderHost = tfaddr.BuiltInProviderHost +const BuiltInProviderHost = svchost.Hostname("terraform.io") // BuiltInProviderNamespace is the provider namespace used for "built-in" // providers. Built-in provider addresses must also have their hostname @@ -27,17 +35,34 @@ const BuiltInProviderHost = tfaddr.BuiltInProviderHost // The this namespace is literally named "builtin", in the hope that users // who see FQNs containing this will be able to infer the way in which they are // special, even if they haven't encountered the concept formally yet. -const BuiltInProviderNamespace = tfaddr.BuiltInProviderNamespace +const BuiltInProviderNamespace = "builtin" // LegacyProviderNamespace is the special string used in the Namespace field // of type Provider to mark a legacy provider address. This special namespace // value would normally be invalid, and can be used only when the hostname is // DefaultRegistryHost because that host owns the mapping from legacy name to // FQN. -const LegacyProviderNamespace = tfaddr.LegacyProviderNamespace +const LegacyProviderNamespace = "-" -func IsDefaultProvider(addr Provider) bool { - return addr.Hostname == DefaultProviderRegistryHost && addr.Namespace == "hashicorp" +// String returns an FQN string, indended for use in machine-readable output. +func (pt Provider) String() string { + if pt.IsZero() { + panic("called String on zero-value addrs.Provider") + } + return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type +} + +// ForDisplay returns a user-friendly FQN string, simplified for readability. If +// the provider is using the default hostname, the hostname is omitted. +func (pt Provider) ForDisplay() string { + if pt.IsZero() { + panic("called ForDisplay on zero-value addrs.Provider") + } + + if pt.Hostname == DefaultProviderRegistryHost { + return pt.Namespace + "/" + pt.Type + } + return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type } // NewProvider constructs a provider address from its parts, and normalizes @@ -52,7 +77,18 @@ func IsDefaultProvider(addr Provider) bool { // When accepting namespace or type values from outside the program, use // ParseProviderPart first to check that the given value is valid. func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider { - return tfaddr.NewProvider(hostname, namespace, typeName) + if namespace == LegacyProviderNamespace { + // Legacy provider addresses must always be created via + // NewLegacyProvider so that we can use static analysis to find + // codepaths still working with those. + panic("attempt to create legacy provider address using NewProvider; use NewLegacyProvider instead") + } + + return Provider{ + Type: MustParseProviderPart(typeName), + Namespace: MustParseProviderPart(namespace), + Hostname: hostname, + } } // ImpliedProviderForUnqualifiedType represents the rules for inferring what @@ -82,7 +118,7 @@ func ImpliedProviderForUnqualifiedType(typeName string) Provider { // NewDefaultProvider returns the default address of a HashiCorp-maintained, // Registry-hosted provider. func NewDefaultProvider(name string) Provider { - return tfaddr.Provider{ + return Provider{ Type: MustParseProviderPart(name), Namespace: "hashicorp", Hostname: DefaultProviderRegistryHost, @@ -92,7 +128,7 @@ func NewDefaultProvider(name string) Provider { // NewBuiltInProvider returns the address of a "built-in" provider. See // the docs for Provider.IsBuiltIn for more information. func NewBuiltInProvider(name string) Provider { - return tfaddr.Provider{ + return Provider{ Type: MustParseProviderPart(name), Namespace: BuiltInProviderNamespace, Hostname: BuiltInProviderHost, @@ -112,6 +148,80 @@ func NewLegacyProvider(name string) Provider { } } +// LegacyString returns the provider type, which is frequently used +// interchangeably with provider name. This function can and should be removed +// when provider type is fully integrated. As a safeguard for future +// refactoring, this function panics if the Provider is not a legacy provider. +func (pt Provider) LegacyString() string { + if pt.IsZero() { + panic("called LegacyString on zero-value addrs.Provider") + } + if pt.Namespace != LegacyProviderNamespace && pt.Namespace != BuiltInProviderNamespace { + panic(pt.String() + " cannot be represented as a legacy string") + } + return pt.Type +} + +// IsZero returns true if the receiver is the zero value of addrs.Provider. +// +// The zero value is not a valid addrs.Provider and calling other methods on +// such a value is likely to either panic or otherwise misbehave. +func (pt Provider) IsZero() bool { + return pt == Provider{} +} + +// IsBuiltIn returns true if the receiver is the address of a "built-in" +// provider. That is, a provider under terraform.io/builtin/ which is +// included as part of the Terraform binary itself rather than one to be +// installed from elsewhere. +// +// These are ignored by the provider installer because they are assumed to +// already be available without any further installation. +func (pt Provider) IsBuiltIn() bool { + return pt.Hostname == BuiltInProviderHost && pt.Namespace == BuiltInProviderNamespace +} + +// LessThan returns true if the receiver should sort before the other given +// address in an ordered list of provider addresses. +// +// This ordering is an arbitrary one just to allow deterministic results from +// functions that would otherwise have no natural ordering. It's subject +// to change in future. +func (pt Provider) LessThan(other Provider) bool { + switch { + case pt.Hostname != other.Hostname: + return pt.Hostname < other.Hostname + case pt.Namespace != other.Namespace: + return pt.Namespace < other.Namespace + default: + return pt.Type < other.Type + } +} + +// IsLegacy returns true if the provider is a legacy-style provider +func (pt Provider) IsLegacy() bool { + if pt.IsZero() { + panic("called IsLegacy() on zero-value addrs.Provider") + } + + return pt.Hostname == DefaultProviderRegistryHost && pt.Namespace == LegacyProviderNamespace + +} + +// IsDefault returns true if the provider is a default hashicorp provider +func (pt Provider) IsDefault() bool { + if pt.IsZero() { + panic("called IsDefault() on zero-value addrs.Provider") + } + + return pt.Hostname == DefaultProviderRegistryHost && pt.Namespace == "hashicorp" +} + +// Equals returns true if the receiver and other provider have the same attributes. +func (pt Provider) Equals(other Provider) bool { + return pt == other +} + // ParseProviderSourceString parses the source attribute and returns a provider. // This is intended primarily to parse the FQN-like strings returned by // terraform-config-inspect. @@ -120,24 +230,146 @@ func NewLegacyProvider(name string) Provider { // name // namespace/name // hostname/namespace/name -func ParseProviderSourceString(str string) (tfaddr.Provider, tfdiags.Diagnostics) { +func ParseProviderSourceString(str string) (Provider, tfdiags.Diagnostics) { + var ret Provider var diags tfdiags.Diagnostics - ret, err := tfaddr.ParseProviderSource(str) - if pe, ok := err.(*tfaddr.ParserError); ok { + // split the source string into individual components + parts := strings.Split(str, "/") + if len(parts) == 0 || len(parts) > 3 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider source string", + Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`, + }) + return ret, diags + } + + // check for an invalid empty string in any part + for i := range parts { + if parts[i] == "" { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider source string", + Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`, + }) + return ret, diags + } + } + + // check the 'name' portion, which is always the last part + givenName := parts[len(parts)-1] + name, err := ParseProviderPart(givenName) + if err != nil { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, - Summary: pe.Summary, - Detail: pe.Detail, + Summary: "Invalid provider type", + Detail: fmt.Sprintf(`Invalid provider type %q in source %q: %s"`, givenName, str, err), }) return ret, diags } + ret.Type = name + ret.Hostname = DefaultProviderRegistryHost + + if len(parts) == 1 { + return NewDefaultProvider(parts[0]), diags + } - if !ret.HasKnownNamespace() { - ret.Namespace = "hashicorp" + if len(parts) >= 2 { + // the namespace is always the second-to-last part + givenNamespace := parts[len(parts)-2] + if givenNamespace == LegacyProviderNamespace { + // For now we're tolerating legacy provider addresses until we've + // finished updating the rest of the codebase to no longer use them, + // or else we'd get errors round-tripping through legacy subsystems. + ret.Namespace = LegacyProviderNamespace + } else { + namespace, err := ParseProviderPart(givenNamespace) + if err != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider namespace", + Detail: fmt.Sprintf(`Invalid provider namespace %q in source %q: %s"`, namespace, str, err), + }) + return Provider{}, diags + } + ret.Namespace = namespace + } } - return ret, nil + // Final Case: 3 parts + if len(parts) == 3 { + // the namespace is always the first part in a three-part source string + hn, err := svchost.ForComparison(parts[0]) + if err != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider source hostname", + Detail: fmt.Sprintf(`Invalid provider source hostname namespace %q in source %q: %s"`, hn, str, err), + }) + return Provider{}, diags + } + ret.Hostname = hn + } + + if ret.Namespace == LegacyProviderNamespace && ret.Hostname != DefaultProviderRegistryHost { + // Legacy provider addresses must always be on the default registry + // host, because the default registry host decides what actual FQN + // each one maps to. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider namespace", + Detail: "The legacy provider namespace \"-\" can be used only with hostname " + DefaultProviderRegistryHost.ForDisplay() + ".", + }) + return Provider{}, diags + } + + // Due to how plugin executables are named and provider git repositories + // are conventionally named, it's a reasonable and + // apparently-somewhat-common user error to incorrectly use the + // "terraform-provider-" prefix in a provider source address. There is + // no good reason for a provider to have the prefix "terraform-" anyway, + // so we've made that invalid from the start both so we can give feedback + // to provider developers about the terraform- prefix being redundant + // and give specialized feedback to folks who incorrectly use the full + // terraform-provider- prefix to help them self-correct. + const redundantPrefix = "terraform-" + const userErrorPrefix = "terraform-provider-" + if strings.HasPrefix(ret.Type, redundantPrefix) { + if strings.HasPrefix(ret.Type, userErrorPrefix) { + // Likely user error. We only return this specialized error if + // whatever is after the prefix would otherwise be a + // syntactically-valid provider type, so we don't end up advising + // the user to try something that would be invalid for another + // reason anyway. + // (This is mainly just for robustness, because the validation + // we already did above should've rejected most/all ways for + // the suggestedType to end up invalid here.) + suggestedType := ret.Type[len(userErrorPrefix):] + if _, err := ParseProviderPart(suggestedType); err == nil { + suggestedAddr := ret + suggestedAddr.Type = suggestedType + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid provider type", + fmt.Sprintf("Provider source %q has a type with the prefix %q, which isn't valid. Although that prefix is often used in the names of version control repositories for Terraform providers, provider source strings should not include it.\n\nDid you mean %q?", ret.ForDisplay(), userErrorPrefix, suggestedAddr.ForDisplay()), + )) + return Provider{}, diags + } + } + // Otherwise, probably instead an incorrectly-named provider, perhaps + // arising from a similar instinct to what causes there to be + // thousands of Python packages on PyPI with "python-"-prefixed + // names. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid provider type", + fmt.Sprintf("Provider source %q has a type with the prefix %q, which isn't allowed because it would be redundant to name a Terraform provider with that prefix. If you are the author of this provider, rename it to not include the prefix.", ret, redundantPrefix), + )) + return Provider{}, diags + } + + return ret, diags } // MustParseProviderSourceString is a wrapper around ParseProviderSourceString that panics if @@ -177,7 +409,36 @@ func MustParseProviderSourceString(str string) Provider { // It's valid to pass the result of this function as the argument to a // subsequent call, in which case the result will be identical. func ParseProviderPart(given string) (string, error) { - return tfaddr.ParseProviderPart(given) + if len(given) == 0 { + return "", fmt.Errorf("must have at least one character") + } + + // We're going to process the given name using the same "IDNA" library we + // use for the hostname portion, since it already implements the case + // folding rules we want. + // + // The idna library doesn't expose individual label parsing directly, but + // once we've verified it doesn't contain any dots we can just treat it + // like a top-level domain for this library's purposes. + if strings.ContainsRune(given, '.') { + return "", fmt.Errorf("dots are not allowed") + } + + // We don't allow names containing multiple consecutive dashes, just as + // a matter of preference: they look weird, confusing, or incorrect. + // This also, as a side-effect, prevents the use of the "punycode" + // indicator prefix "xn--" that would cause the IDNA library to interpret + // the given name as punycode, because that would be weird and unexpected. + if strings.Contains(given, "--") { + return "", fmt.Errorf("cannot use multiple consecutive dashes") + } + + result, err := idna.Lookup.ToUnicode(given) + if err != nil { + return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes") + } + + return result, nil } // MustParseProviderPart is a wrapper around ParseProviderPart that panics if diff --git a/internal/addrs/provider_test.go b/internal/addrs/provider_test.go index 0f50475736aa..40361058cbdc 100644 --- a/internal/addrs/provider_test.go +++ b/internal/addrs/provider_test.go @@ -124,7 +124,7 @@ func TestProviderDisplay(t *testing.T) { } } -func TestProviderIsDefaultProvider(t *testing.T) { +func TestProviderIsDefault(t *testing.T) { tests := []struct { Input Provider Want bool @@ -156,7 +156,7 @@ func TestProviderIsDefaultProvider(t *testing.T) { } for _, test := range tests { - got := IsDefaultProvider(test.Input) + got := test.Input.IsDefault() if got != test.Want { t.Errorf("wrong result for %s\n", test.Input.String()) } diff --git a/internal/addrs/set.go b/internal/addrs/set.go index b04d76778eea..ef82c5915888 100644 --- a/internal/addrs/set.go +++ b/internal/addrs/set.go @@ -1,37 +1,23 @@ package addrs // Set represents a set of addresses of types that implement UniqueKeyer. -// -// Modify the set only by the methods on this type. This type exposes its -// internals for convenience during reading, such as iterating over set elements -// by ranging over the map values, but making direct modifications could -// potentially make the set data invalid or inconsistent, leading to undefined -// behavior elsewhere. -type Set[T UniqueKeyer] map[UniqueKey]T +type Set map[UniqueKey]UniqueKeyer -// Has returns true if and only if the set includes the given address. -func (s Set[T]) Has(addr T) bool { +func (s Set) Has(addr UniqueKeyer) bool { _, exists := s[addr.UniqueKey()] return exists } -// Add inserts the given address into the set, if not already present. If -// an equivalent address is already in the set, this replaces that address -// with the new value. -func (s Set[T]) Add(addr T) { +func (s Set) Add(addr UniqueKeyer) { s[addr.UniqueKey()] = addr } -// Remove deletes the given address from the set, if present. If not present, -// this is a no-op. -func (s Set[T]) Remove(addr T) { +func (s Set) Remove(addr UniqueKeyer) { delete(s, addr.UniqueKey()) } -// Union returns a new set which contains the union of all of the elements -// of both the reciever and the given other set. -func (s Set[T]) Union(other Set[T]) Set[T] { - ret := make(Set[T]) +func (s Set) Union(other Set) Set { + ret := make(Set) for k, addr := range s { ret[k] = addr } @@ -41,10 +27,8 @@ func (s Set[T]) Union(other Set[T]) Set[T] { return ret } -// Intersection returns a new set which contains the intersection of all of the -// elements of both the reciever and the given other set. -func (s Set[T]) Intersection(other Set[T]) Set[T] { - ret := make(Set[T]) +func (s Set) Intersection(other Set) Set { + ret := make(Set) for k, addr := range s { if _, exists := other[k]; exists { ret[k] = addr diff --git a/internal/addrs/unique_key.go b/internal/addrs/unique_key.go index f57e884f7bbf..c3321a298b5b 100644 --- a/internal/addrs/unique_key.go +++ b/internal/addrs/unique_key.go @@ -21,7 +21,3 @@ type UniqueKey interface { type UniqueKeyer interface { UniqueKey() UniqueKey } - -func Equivalent[T UniqueKeyer](a, b T) bool { - return a.UniqueKey() == b.UniqueKey() -} diff --git a/internal/backend/init/init.go b/internal/backend/init/init.go index a7be1d552a21..f54271b10a86 100644 --- a/internal/backend/init/init.go +++ b/internal/backend/init/init.go @@ -16,6 +16,8 @@ import ( backendAzure "github.com/hashicorp/terraform/internal/backend/remote-state/azure" backendConsul "github.com/hashicorp/terraform/internal/backend/remote-state/consul" backendCos "github.com/hashicorp/terraform/internal/backend/remote-state/cos" + backendEtcdv2 "github.com/hashicorp/terraform/internal/backend/remote-state/etcdv2" + backendEtcdv3 "github.com/hashicorp/terraform/internal/backend/remote-state/etcdv3" backendGCS "github.com/hashicorp/terraform/internal/backend/remote-state/gcs" backendHTTP "github.com/hashicorp/terraform/internal/backend/remote-state/http" backendInmem "github.com/hashicorp/terraform/internal/backend/remote-state/inmem" @@ -42,10 +44,6 @@ import ( var backends map[string]backend.InitFn var backendsLock sync.Mutex -// RemovedBackends is a record of previously supported backends which have -// since been deprecated and removed. -var RemovedBackends map[string]string - // Init initializes the backends map with all our hardcoded backends. func Init(services *disco.Disco) { backendsLock.Lock() @@ -85,6 +83,18 @@ func Init(services *disco.Disco) { `Warning: "artifactory" backend is deprecated, and will be removed in a future release."`, ) }, + "etcd": func() backend.Backend { + return deprecateBackend( + backendEtcdv2.New(), + `Warning: "etcd" backend is deprecated, and will be removed in a future release."`, + ) + }, + "etcdv3": func() backend.Backend { + return deprecateBackend( + backendEtcdv3.New(), + `Warning: "etcdv3" backend is deprecated, and will be removed in a future release."`, + ) + }, "manta": func() backend.Backend { return deprecateBackend( backendManta.New(), @@ -98,11 +108,6 @@ func Init(services *disco.Disco) { ) }, } - - RemovedBackends = map[string]string{ - "etcd": `The "etcd" backend is not supported in Terraform v1.3 or later.`, - "etcdv3": `The "etcdv3" backend is not supported in Terraform v1.3 or later.`, - } } // Backend returns the initialization factory for the given backend, or diff --git a/internal/backend/init/init_test.go b/internal/backend/init/init_test.go index 609e97ce4fa8..5e89884c5f80 100644 --- a/internal/backend/init/init_test.go +++ b/internal/backend/init/init_test.go @@ -25,6 +25,8 @@ func TestInit_backend(t *testing.T) { {"azure", "init.deprecatedBackendShim"}, {"artifactory", "init.deprecatedBackendShim"}, + {"etcd", "init.deprecatedBackendShim"}, + {"etcdv3", "init.deprecatedBackendShim"}, {"manta", "init.deprecatedBackendShim"}, {"swift", "init.deprecatedBackendShim"}, } diff --git a/internal/backend/local/backend_local_test.go b/internal/backend/local/backend_local_test.go index 05573f0f9d97..32675e0949e4 100644 --- a/internal/backend/local/backend_local_test.go +++ b/internal/backend/local/backend_local_test.go @@ -6,8 +6,6 @@ import ( "path/filepath" "testing" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/clistate" @@ -22,6 +20,7 @@ import ( "github.com/hashicorp/terraform/internal/states/statemgr" "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" ) func TestLocalRun(t *testing.T) { @@ -221,10 +220,6 @@ func (s *stateStorageThatFailsRefresh) State() *states.State { return nil } -func (s *stateStorageThatFailsRefresh) GetRootOutputValues() (map[string]*states.OutputValue, error) { - return nil, fmt.Errorf("unimplemented") -} - func (s *stateStorageThatFailsRefresh) WriteState(*states.State) error { return fmt.Errorf("unimplemented") } diff --git a/internal/backend/local/testing.go b/internal/backend/local/testing.go index 3b9a3c40fbb0..d4fe51d9736f 100644 --- a/internal/backend/local/testing.go +++ b/internal/backend/local/testing.go @@ -62,14 +62,7 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.Pr p.GetProviderSchemaResponse.DataSources[name] = providers.Schema{Block: dat} } - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - // this is a destroy plan, - if req.ProposedNewState.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { rSchema, _ := schema.SchemaForResourceType(addrs.ManagedResourceMode, req.TypeName) if rSchema == nil { rSchema = &configschema.Block{} // default schema is empty diff --git a/internal/backend/remote-state/azure/arm_client.go b/internal/backend/remote-state/azure/arm_client.go index 13493ab13d1f..5419e41112f7 100644 --- a/internal/backend/remote-state/azure/arm_client.go +++ b/internal/backend/remote-state/azure/arm_client.go @@ -7,6 +7,11 @@ import ( "os" "time" + "github.com/manicminer/hamilton/environments" + + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" + "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" "github.com/Azure/go-autorest/autorest" @@ -14,10 +19,6 @@ import ( "github.com/hashicorp/go-azure-helpers/authentication" "github.com/hashicorp/go-azure-helpers/sender" "github.com/hashicorp/terraform/internal/httpclient" - "github.com/hashicorp/terraform/version" - "github.com/manicminer/hamilton/environments" - "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" - "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" ) type ArmClient struct { @@ -90,7 +91,7 @@ func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, erro SupportsClientSecretAuth: true, SupportsManagedServiceIdentity: config.UseMsi, SupportsOIDCAuth: config.UseOIDC, - UseMicrosoftGraph: true, + UseMicrosoftGraph: config.UseMicrosoftGraph, } armConfig, err := builder.Build() if err != nil { @@ -108,19 +109,37 @@ func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, erro } sender := sender.BuildSender("backend/remote-state/azure") - log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Resource Manager..") - auth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.ResourceManager, sender, oauthConfig, env.TokenAudience) - if err != nil { - return nil, err + var auth autorest.Authorizer + if builder.UseMicrosoftGraph { + log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Resource Manager..") + auth, err = armConfig.GetMSALToken(ctx, hamiltonEnv.ResourceManager, sender, oauthConfig, env.TokenAudience) + if err != nil { + return nil, err + } + } else { + log.Printf("[DEBUG] Obtaining an ADAL / Azure Active Directory Graph token for Resource Manager..") + auth, err = armConfig.GetADALToken(ctx, sender, oauthConfig, env.TokenAudience) + if err != nil { + return nil, err + } } if config.UseAzureADAuthentication { - log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Storage..") - storageAuth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.Storage, sender, oauthConfig, env.ResourceIdentifiers.Storage) - if err != nil { - return nil, err + if builder.UseMicrosoftGraph { + log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Storage..") + storageAuth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.Storage, sender, oauthConfig, env.ResourceIdentifiers.Storage) + if err != nil { + return nil, err + } + client.azureAdStorageAuth = &storageAuth + } else { + log.Printf("[DEBUG] Obtaining an ADAL / Azure Active Directory Graph token for Storage..") + storageAuth, err := armConfig.GetADALToken(ctx, sender, oauthConfig, env.ResourceIdentifiers.Storage) + if err != nil { + return nil, err + } + client.azureAdStorageAuth = &storageAuth } - client.azureAdStorageAuth = &storageAuth } accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) @@ -233,7 +252,7 @@ func (c *ArmClient) configureClient(client *autorest.Client, auth autorest.Autho } func buildUserAgent() string { - userAgent := httpclient.TerraformUserAgent(version.Version) + userAgent := httpclient.UserAgentString() // append the CloudShell version to the user agent if it exists if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" { diff --git a/internal/backend/remote-state/azure/backend.go b/internal/backend/remote-state/azure/backend.go index 4a41c96458df..91db6936e7fd 100644 --- a/internal/backend/remote-state/azure/backend.go +++ b/internal/backend/remote-state/azure/backend.go @@ -164,6 +164,13 @@ func New() backend.Backend { Description: "Should Terraform use AzureAD Authentication to access the Blob?", DefaultFunc: schema.EnvDefaultFunc("ARM_USE_AZUREAD", false), }, + "use_microsoft_graph": { + Type: schema.TypeBool, + Optional: true, + Deprecated: "This field now defaults to `true` and will be removed in v1.3 of Terraform Core due to the deprecation of ADAL by Microsoft.", + Description: "Should Terraform obtain an MSAL auth token and use Microsoft Graph rather than Azure Active Directory?", + DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSGRAPH", true), + }, }, } @@ -206,6 +213,7 @@ type BackendConfig struct { UseMsi bool UseOIDC bool UseAzureADAuthentication bool + UseMicrosoftGraph bool } func (b *Backend) configure(ctx context.Context) error { @@ -240,6 +248,7 @@ func (b *Backend) configure(ctx context.Context) error { UseMsi: data.Get("use_msi").(bool), UseOIDC: data.Get("use_oidc").(bool), UseAzureADAuthentication: data.Get("use_azuread_auth").(bool), + UseMicrosoftGraph: data.Get("use_microsoft_graph").(bool), } armClient, err := buildArmClient(context.TODO(), config) diff --git a/internal/backend/remote-state/azure/backend_test.go b/internal/backend/remote-state/azure/backend_test.go index fa5b0a9f1666..70bc272f892e 100644 --- a/internal/backend/remote-state/azure/backend_test.go +++ b/internal/backend/remote-state/azure/backend_test.go @@ -123,7 +123,7 @@ func TestAccBackendOIDCBasic(t *testing.T) { backend.TestBackendStates(t, b) } -func TestAccBackendAzureADAuthBasic(t *testing.T) { +func TestAccBackendADALAzureADAuthBasic(t *testing.T) { testAccAzureBackend(t) rs := acctest.RandString(4) res := testResourceNames(rs, "testState") @@ -151,7 +151,7 @@ func TestAccBackendAzureADAuthBasic(t *testing.T) { backend.TestBackendStates(t, b) } -func TestAccBackendManagedServiceIdentityBasic(t *testing.T) { +func TestAccBackendADALManagedServiceIdentityBasic(t *testing.T) { testAccAzureBackendRunningInAzure(t) rs := acctest.RandString(4) res := testResourceNames(rs, "testState") @@ -179,7 +179,7 @@ func TestAccBackendManagedServiceIdentityBasic(t *testing.T) { backend.TestBackendStates(t, b) } -func TestAccBackendServicePrincipalClientCertificateBasic(t *testing.T) { +func TestAccBackendADALServicePrincipalClientCertificateBasic(t *testing.T) { testAccAzureBackend(t) clientCertPassword := os.Getenv("ARM_CLIENT_CERTIFICATE_PASSWORD") @@ -216,7 +216,7 @@ func TestAccBackendServicePrincipalClientCertificateBasic(t *testing.T) { backend.TestBackendStates(t, b) } -func TestAccBackendServicePrincipalClientSecretBasic(t *testing.T) { +func TestAccBackendADALServicePrincipalClientSecretBasic(t *testing.T) { testAccAzureBackend(t) rs := acctest.RandString(4) res := testResourceNames(rs, "testState") @@ -245,7 +245,7 @@ func TestAccBackendServicePrincipalClientSecretBasic(t *testing.T) { backend.TestBackendStates(t, b) } -func TestAccBackendServicePrincipalClientSecretCustomEndpoint(t *testing.T) { +func TestAccBackendADALServicePrincipalClientSecretCustomEndpoint(t *testing.T) { testAccAzureBackend(t) // this is only applicable for Azure Stack. @@ -281,6 +281,169 @@ func TestAccBackendServicePrincipalClientSecretCustomEndpoint(t *testing.T) { backend.TestBackendStates(t, b) } +func TestAccBackendMSALAzureADAuthBasic(t *testing.T) { + testAccAzureBackend(t) + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useAzureADAuth = true + res.useMicrosoftGraph = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + armClient.destroyTestResources(ctx, res) + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": os.Getenv("ARM_ENDPOINT"), + "use_azuread_auth": true, + })).(*Backend) + + backend.TestBackendStates(t, b) +} + +func TestAccBackendMSALManagedServiceIdentityBasic(t *testing.T) { + testAccAzureBackendRunningInAzure(t) + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useMicrosoftGraph = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "resource_group_name": res.resourceGroup, + "use_msi": true, + "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), + "tenant_id": os.Getenv("ARM_TENANT_ID"), + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": os.Getenv("ARM_ENDPOINT"), + })).(*Backend) + + backend.TestBackendStates(t, b) +} + +func TestAccBackendMSALServicePrincipalClientCertificateBasic(t *testing.T) { + testAccAzureBackend(t) + + clientCertPassword := os.Getenv("ARM_CLIENT_CERTIFICATE_PASSWORD") + clientCertPath := os.Getenv("ARM_CLIENT_CERTIFICATE_PATH") + if clientCertPath == "" { + t.Skip("Skipping since `ARM_CLIENT_CERTIFICATE_PATH` is not specified!") + } + + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useMicrosoftGraph = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "resource_group_name": res.resourceGroup, + "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), + "tenant_id": os.Getenv("ARM_TENANT_ID"), + "client_id": os.Getenv("ARM_CLIENT_ID"), + "client_certificate_password": clientCertPassword, + "client_certificate_path": clientCertPath, + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": os.Getenv("ARM_ENDPOINT"), + })).(*Backend) + + backend.TestBackendStates(t, b) +} + +func TestAccBackendMSALServicePrincipalClientSecretBasic(t *testing.T) { + testAccAzureBackend(t) + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useMicrosoftGraph = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "resource_group_name": res.resourceGroup, + "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), + "tenant_id": os.Getenv("ARM_TENANT_ID"), + "client_id": os.Getenv("ARM_CLIENT_ID"), + "client_secret": os.Getenv("ARM_CLIENT_SECRET"), + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": os.Getenv("ARM_ENDPOINT"), + })).(*Backend) + + backend.TestBackendStates(t, b) +} + +func TestAccBackendMSALServicePrincipalClientSecretCustomEndpoint(t *testing.T) { + testAccAzureBackend(t) + + // this is only applicable for Azure Stack. + endpoint := os.Getenv("ARM_ENDPOINT") + if endpoint == "" { + t.Skip("Skipping as ARM_ENDPOINT isn't configured") + } + + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useMicrosoftGraph = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "resource_group_name": res.resourceGroup, + "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), + "tenant_id": os.Getenv("ARM_TENANT_ID"), + "client_id": os.Getenv("ARM_CLIENT_ID"), + "client_secret": os.Getenv("ARM_CLIENT_SECRET"), + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": endpoint, + })).(*Backend) + + backend.TestBackendStates(t, b) +} + func TestAccBackendAccessKeyLocked(t *testing.T) { testAccAzureBackend(t) rs := acctest.RandString(4) diff --git a/internal/backend/remote-state/azure/client.go b/internal/backend/remote-state/azure/client.go index 5d22767954b8..36154b79e01d 100644 --- a/internal/backend/remote-state/azure/client.go +++ b/internal/backend/remote-state/azure/client.go @@ -10,9 +10,10 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-uuid" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" + "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statemgr" - "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" ) const ( diff --git a/internal/backend/remote-state/azure/helpers_test.go b/internal/backend/remote-state/azure/helpers_test.go index 812b3f5a8530..7fc51801eccc 100644 --- a/internal/backend/remote-state/azure/helpers_test.go +++ b/internal/backend/remote-state/azure/helpers_test.go @@ -93,6 +93,7 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient { StorageAccountName: res.storageAccountName, UseMsi: msiEnabled, UseAzureADAuthentication: res.useAzureADAuth, + UseMicrosoftGraph: res.useMicrosoftGraph, }) if err != nil { t.Fatalf("Failed to build ArmClient: %+v", err) @@ -136,6 +137,7 @@ type resourceNames struct { storageKeyName string storageAccountAccessKey string useAzureADAuth bool + useMicrosoftGraph bool } func testResourceNames(rString string, keyName string) resourceNames { diff --git a/internal/backend/remote-state/cos/backend.go b/internal/backend/remote-state/cos/backend.go index 667fdd81dd16..8d0d0145417e 100644 --- a/internal/backend/remote-state/cos/backend.go +++ b/internal/backend/remote-state/cos/backend.go @@ -113,12 +113,6 @@ func New() backend.Backend { return nil, nil }, }, - "accelerate": { - Type: schema.TypeBool, - Optional: true, - Description: "Whether to enable global Acceleration", - Default: false, - }, }, } @@ -144,16 +138,7 @@ func (b *Backend) configure(ctx context.Context) error { b.encrypt = data.Get("encrypt").(bool) b.acl = data.Get("acl").(string) - var ( - u *url.URL - err error - ) - accelerate := data.Get("accelerate").(bool) - if accelerate { - u, err = url.Parse(fmt.Sprintf("https://%s.cos.accelerate.myqcloud.com", b.bucket)) - } else { - u, err = url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", b.bucket, b.region)) - } + u, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", b.bucket, b.region)) if err != nil { return err } diff --git a/internal/backend/remote-state/etcdv2/backend.go b/internal/backend/remote-state/etcdv2/backend.go new file mode 100644 index 000000000000..e6d3cf8ce98e --- /dev/null +++ b/internal/backend/remote-state/etcdv2/backend.go @@ -0,0 +1,96 @@ +// legacy etcd2.x backend + +package etcdv2 + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/legacy/helper/schema" + "github.com/hashicorp/terraform/internal/states/remote" + "github.com/hashicorp/terraform/internal/states/statemgr" + etcdapi "go.etcd.io/etcd/client" +) + +func New() backend.Backend { + s := &schema.Backend{ + Schema: map[string]*schema.Schema{ + "path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The path where to store the state", + }, + "endpoints": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "A space-separated list of the etcd endpoints", + }, + "username": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Username", + }, + "password": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Password", + }, + }, + } + + result := &Backend{Backend: s} + result.Backend.ConfigureFunc = result.configure + return result +} + +type Backend struct { + *schema.Backend + + client etcdapi.Client + path string +} + +func (b *Backend) configure(ctx context.Context) error { + data := schema.FromContextBackendConfig(ctx) + + b.path = data.Get("path").(string) + + endpoints := data.Get("endpoints").(string) + username := data.Get("username").(string) + password := data.Get("password").(string) + + config := etcdapi.Config{ + Endpoints: strings.Split(endpoints, " "), + Username: username, + Password: password, + } + + client, err := etcdapi.New(config) + if err != nil { + return err + } + + b.client = client + return nil +} + +func (b *Backend) Workspaces() ([]string, error) { + return nil, backend.ErrWorkspacesNotSupported +} + +func (b *Backend) DeleteWorkspace(string) error { + return backend.ErrWorkspacesNotSupported +} + +func (b *Backend) StateMgr(name string) (statemgr.Full, error) { + if name != backend.DefaultStateName { + return nil, backend.ErrWorkspacesNotSupported + } + return &remote.State{ + Client: &EtcdClient{ + Client: b.client, + Path: b.path, + }, + }, nil +} diff --git a/internal/backend/remote-state/etcdv2/backend_test.go b/internal/backend/remote-state/etcdv2/backend_test.go new file mode 100644 index 000000000000..6b8299178ac7 --- /dev/null +++ b/internal/backend/remote-state/etcdv2/backend_test.go @@ -0,0 +1,11 @@ +package etcdv2 + +import ( + "testing" + + "github.com/hashicorp/terraform/internal/backend" +) + +func TestBackend_impl(t *testing.T) { + var _ backend.Backend = new(Backend) +} diff --git a/internal/backend/remote-state/etcdv2/client.go b/internal/backend/remote-state/etcdv2/client.go new file mode 100644 index 000000000000..97e44f41bc94 --- /dev/null +++ b/internal/backend/remote-state/etcdv2/client.go @@ -0,0 +1,46 @@ +package etcdv2 + +import ( + "context" + "crypto/md5" + "fmt" + + "github.com/hashicorp/terraform/internal/states/remote" + etcdapi "go.etcd.io/etcd/client" +) + +// EtcdClient is a remote client that stores data in etcd. +type EtcdClient struct { + Client etcdapi.Client + Path string +} + +func (c *EtcdClient) Get() (*remote.Payload, error) { + resp, err := etcdapi.NewKeysAPI(c.Client).Get(context.Background(), c.Path, &etcdapi.GetOptions{Quorum: true}) + if err != nil { + if err, ok := err.(etcdapi.Error); ok && err.Code == etcdapi.ErrorCodeKeyNotFound { + return nil, nil + } + return nil, err + } + if resp.Node.Dir { + return nil, fmt.Errorf("path is a directory") + } + + data := []byte(resp.Node.Value) + md5 := md5.Sum(data) + return &remote.Payload{ + Data: data, + MD5: md5[:], + }, nil +} + +func (c *EtcdClient) Put(data []byte) error { + _, err := etcdapi.NewKeysAPI(c.Client).Set(context.Background(), c.Path, string(data), nil) + return err +} + +func (c *EtcdClient) Delete() error { + _, err := etcdapi.NewKeysAPI(c.Client).Delete(context.Background(), c.Path, nil) + return err +} diff --git a/internal/backend/remote-state/etcdv2/client_test.go b/internal/backend/remote-state/etcdv2/client_test.go new file mode 100644 index 000000000000..1d924d4f3c45 --- /dev/null +++ b/internal/backend/remote-state/etcdv2/client_test.go @@ -0,0 +1,45 @@ +package etcdv2 + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/states/remote" + "github.com/zclconf/go-cty/cty" +) + +func TestEtcdClient_impl(t *testing.T) { + var _ remote.Client = new(EtcdClient) +} + +func TestEtcdClient(t *testing.T) { + endpoint := os.Getenv("ETCD_ENDPOINT") + if endpoint == "" { + t.Skipf("skipping; ETCD_ENDPOINT must be set") + } + + // Get the backend + config := map[string]cty.Value{ + "endpoints": cty.StringVal(endpoint), + "path": cty.StringVal(fmt.Sprintf("tf-unit/%s", time.Now().String())), + } + + if username := os.Getenv("ETCD_USERNAME"); username != "" { + config["username"] = cty.StringVal(username) + } + if password := os.Getenv("ETCD_PASSWORD"); password != "" { + config["password"] = cty.StringVal(password) + } + + b := backend.TestBackendConfig(t, New(), configs.SynthBody("synth", config)) + state, err := b.StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatalf("Error for valid config: %s", err) + } + + remote.TestClient(t, state.(*remote.State).Client) +} diff --git a/internal/backend/remote-state/etcdv3/backend.go b/internal/backend/remote-state/etcdv3/backend.go new file mode 100644 index 000000000000..7285bda968f9 --- /dev/null +++ b/internal/backend/remote-state/etcdv3/backend.go @@ -0,0 +1,168 @@ +package etcd + +import ( + "context" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/legacy/helper/schema" + etcdv3 "go.etcd.io/etcd/clientv3" + "go.etcd.io/etcd/pkg/transport" +) + +const ( + endpointsKey = "endpoints" + usernameKey = "username" + usernameEnvVarName = "ETCDV3_USERNAME" + passwordKey = "password" + passwordEnvVarName = "ETCDV3_PASSWORD" + maxRequestBytesKey = "max_request_bytes" + prefixKey = "prefix" + lockKey = "lock" + cacertPathKey = "cacert_path" + certPathKey = "cert_path" + keyPathKey = "key_path" +) + +func New() backend.Backend { + s := &schema.Backend{ + Schema: map[string]*schema.Schema{ + endpointsKey: &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + MinItems: 1, + Required: true, + Description: "Endpoints for the etcd cluster.", + }, + + usernameKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Username used to connect to the etcd cluster.", + DefaultFunc: schema.EnvDefaultFunc(usernameEnvVarName, ""), + }, + + passwordKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Password used to connect to the etcd cluster.", + DefaultFunc: schema.EnvDefaultFunc(passwordEnvVarName, ""), + }, + + maxRequestBytesKey: &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "The max request size to send to etcd.", + Default: 0, + }, + + prefixKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "An optional prefix to be added to keys when to storing state in etcd.", + Default: "", + }, + + lockKey: &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Description: "Whether to lock state access.", + Default: true, + }, + + cacertPathKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The path to a PEM-encoded CA bundle with which to verify certificates of TLS-enabled etcd servers.", + Default: "", + }, + + certPathKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The path to a PEM-encoded certificate to provide to etcd for secure client identification.", + Default: "", + }, + + keyPathKey: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The path to a PEM-encoded key to provide to etcd for secure client identification.", + Default: "", + }, + }, + } + + result := &Backend{Backend: s} + result.Backend.ConfigureFunc = result.configure + return result +} + +type Backend struct { + *schema.Backend + + // The fields below are set from configure. + client *etcdv3.Client + data *schema.ResourceData + lock bool + prefix string +} + +func (b *Backend) configure(ctx context.Context) error { + var err error + // Grab the resource data. + b.data = schema.FromContextBackendConfig(ctx) + // Store the lock information. + b.lock = b.data.Get(lockKey).(bool) + // Store the prefix information. + b.prefix = b.data.Get(prefixKey).(string) + // Initialize a client to test config. + b.client, err = b.rawClient() + // Return err, if any. + return err +} + +func (b *Backend) rawClient() (*etcdv3.Client, error) { + config := etcdv3.Config{} + tlsInfo := transport.TLSInfo{} + + if v, ok := b.data.GetOk(endpointsKey); ok { + config.Endpoints = retrieveEndpoints(v) + } + if v, ok := b.data.GetOk(usernameKey); ok && v.(string) != "" { + config.Username = v.(string) + } + if v, ok := b.data.GetOk(passwordKey); ok && v.(string) != "" { + config.Password = v.(string) + } + if v, ok := b.data.GetOk(maxRequestBytesKey); ok && v.(int) != 0 { + config.MaxCallSendMsgSize = v.(int) + } + if v, ok := b.data.GetOk(cacertPathKey); ok && v.(string) != "" { + tlsInfo.TrustedCAFile = v.(string) + } + if v, ok := b.data.GetOk(certPathKey); ok && v.(string) != "" { + tlsInfo.CertFile = v.(string) + } + if v, ok := b.data.GetOk(keyPathKey); ok && v.(string) != "" { + tlsInfo.KeyFile = v.(string) + } + + if tlsCfg, err := tlsInfo.ClientConfig(); err != nil { + return nil, err + } else if !tlsInfo.Empty() { + config.TLS = tlsCfg // Assign TLS configuration only if it valid and non-empty. + } + + return etcdv3.New(config) +} + +func retrieveEndpoints(v interface{}) []string { + var endpoints []string + list := v.([]interface{}) + for _, ep := range list { + endpoints = append(endpoints, ep.(string)) + } + return endpoints +} diff --git a/internal/backend/remote-state/etcdv3/backend_state.go b/internal/backend/remote-state/etcdv3/backend_state.go new file mode 100644 index 000000000000..da4598603b17 --- /dev/null +++ b/internal/backend/remote-state/etcdv3/backend_state.go @@ -0,0 +1,110 @@ +package etcd + +import ( + "context" + "fmt" + "sort" + "strings" + + etcdv3 "go.etcd.io/etcd/clientv3" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/states" + "github.com/hashicorp/terraform/internal/states/remote" + "github.com/hashicorp/terraform/internal/states/statemgr" +) + +func (b *Backend) Workspaces() ([]string, error) { + res, err := b.client.Get(context.TODO(), b.prefix, etcdv3.WithPrefix(), etcdv3.WithKeysOnly()) + if err != nil { + return nil, err + } + + result := make([]string, 1, len(res.Kvs)+1) + result[0] = backend.DefaultStateName + for _, kv := range res.Kvs { + if strings.TrimPrefix(string(kv.Key), b.prefix) != backend.DefaultStateName { + result = append(result, strings.TrimPrefix(string(kv.Key), b.prefix)) + } + } + sort.Strings(result[1:]) + + return result, nil +} + +func (b *Backend) DeleteWorkspace(name string) error { + if name == backend.DefaultStateName || name == "" { + return fmt.Errorf("Can't delete default state.") + } + + key := b.determineKey(name) + + _, err := b.client.Delete(context.TODO(), key) + return err +} + +func (b *Backend) StateMgr(name string) (statemgr.Full, error) { + var stateMgr statemgr.Full = &remote.State{ + Client: &RemoteClient{ + Client: b.client, + DoLock: b.lock, + Key: b.determineKey(name), + }, + } + + if !b.lock { + stateMgr = &statemgr.LockDisabled{Inner: stateMgr} + } + + lockInfo := statemgr.NewLockInfo() + lockInfo.Operation = "init" + lockUnlock := func(parent error) error { + return nil + } + + if err := stateMgr.RefreshState(); err != nil { + err = lockUnlock(err) + return nil, err + } + + if v := stateMgr.State(); v == nil { + lockId, err := stateMgr.Lock(lockInfo) + if err != nil { + return nil, fmt.Errorf("Failed to lock state in etcd: %s.", err) + } + + lockUnlock = func(parent error) error { + if err := stateMgr.Unlock(lockId); err != nil { + return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) + } + return parent + } + + if err := stateMgr.WriteState(states.NewState()); err != nil { + err = lockUnlock(err) + return nil, err + } + if err := stateMgr.PersistState(); err != nil { + err = lockUnlock(err) + return nil, err + } + } + + if err := lockUnlock(nil); err != nil { + return nil, err + } + + return stateMgr, nil +} + +func (b *Backend) determineKey(name string) string { + return b.prefix + name +} + +const errStateUnlock = ` +Error unlocking etcd state. Lock ID: %s + +Error: %s + +You may have to force-unlock this state in order to use it again. +` diff --git a/internal/backend/remote-state/etcdv3/backend_test.go b/internal/backend/remote-state/etcdv3/backend_test.go new file mode 100644 index 000000000000..8b4039cfc077 --- /dev/null +++ b/internal/backend/remote-state/etcdv3/backend_test.go @@ -0,0 +1,107 @@ +package etcd + +import ( + "context" + "fmt" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/backend" + etcdv3 "go.etcd.io/etcd/clientv3" +) + +var ( + etcdv3Endpoints = strings.Split(os.Getenv("TF_ETCDV3_ENDPOINTS"), ",") +) + +const ( + keyPrefix = "tf-unit" +) + +func TestBackend_impl(t *testing.T) { + var _ backend.Backend = new(Backend) +} + +func cleanupEtcdv3(t *testing.T) { + client, err := etcdv3.New(etcdv3.Config{ + Endpoints: etcdv3Endpoints, + }) + if err != nil { + t.Fatal(err) + } + + res, err := client.KV.Delete(context.TODO(), keyPrefix, etcdv3.WithPrefix()) + if err != nil { + t.Fatal(err) + } + t.Logf("Cleaned up %d keys.", res.Deleted) +} + +func prepareEtcdv3(t *testing.T) { + skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_ETCDV3_TEST") == "" + if skip { + t.Log("etcd server tests require setting TF_ACC or TF_ETCDV3_TEST") + t.Skip() + } + if reflect.DeepEqual(etcdv3Endpoints, []string{""}) { + t.Fatal("etcd server tests require setting TF_ETCDV3_ENDPOINTS") + } + cleanupEtcdv3(t) +} + +func TestBackend(t *testing.T) { + prepareEtcdv3(t) + defer cleanupEtcdv3(t) + + prefix := fmt.Sprintf("%s/%s/", keyPrefix, time.Now().Format(time.RFC3339)) + + // Get the backend. We need two to test locking. + b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })) + + b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })) + + // Test + backend.TestBackendStates(t, b1) + backend.TestBackendStateLocks(t, b1, b2) + backend.TestBackendStateForceUnlock(t, b1, b2) +} + +func TestBackend_lockDisabled(t *testing.T) { + prepareEtcdv3(t) + defer cleanupEtcdv3(t) + + prefix := fmt.Sprintf("%s/%s/", keyPrefix, time.Now().Format(time.RFC3339)) + + // Get the backend. We need two to test locking. + b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + "lock": false, + })) + + b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix + "/" + "different", // Diff so locking test would fail if it was locking + "lock": false, + })) + + // Test + backend.TestBackendStateLocks(t, b1, b2) +} + +func stringsToInterfaces(strSlice []string) []interface{} { + var interfaceSlice []interface{} + for _, v := range strSlice { + interfaceSlice = append(interfaceSlice, v) + } + return interfaceSlice +} diff --git a/internal/backend/remote-state/etcdv3/client.go b/internal/backend/remote-state/etcdv3/client.go new file mode 100644 index 000000000000..3761f149f0e9 --- /dev/null +++ b/internal/backend/remote-state/etcdv3/client.go @@ -0,0 +1,211 @@ +package etcd + +import ( + "context" + "crypto/md5" + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/internal/states/remote" + "github.com/hashicorp/terraform/internal/states/statemgr" + etcdv3 "go.etcd.io/etcd/clientv3" + etcdv3sync "go.etcd.io/etcd/clientv3/concurrency" +) + +const ( + lockAcquireTimeout = 2 * time.Second + lockInfoSuffix = ".lockinfo" +) + +// RemoteClient is a remote client that will store data in etcd. +type RemoteClient struct { + Client *etcdv3.Client + DoLock bool + Key string + + etcdMutex *etcdv3sync.Mutex + etcdSession *etcdv3sync.Session + info *statemgr.LockInfo + mu sync.Mutex + modRevision int64 +} + +func (c *RemoteClient) Get() (*remote.Payload, error) { + c.mu.Lock() + defer c.mu.Unlock() + + res, err := c.Client.KV.Get(context.TODO(), c.Key) + if err != nil { + return nil, err + } + if res.Count == 0 { + return nil, nil + } + if res.Count >= 2 { + return nil, fmt.Errorf("Expected a single result but got %d.", res.Count) + } + + c.modRevision = res.Kvs[0].ModRevision + + payload := res.Kvs[0].Value + md5 := md5.Sum(payload) + + return &remote.Payload{ + Data: payload, + MD5: md5[:], + }, nil +} + +func (c *RemoteClient) Put(data []byte) error { + c.mu.Lock() + defer c.mu.Unlock() + + res, err := etcdv3.NewKV(c.Client).Txn(context.TODO()).If( + etcdv3.Compare(etcdv3.ModRevision(c.Key), "=", c.modRevision), + ).Then( + etcdv3.OpPut(c.Key, string(data)), + etcdv3.OpGet(c.Key), + ).Commit() + + if err != nil { + return err + } + if !res.Succeeded { + return fmt.Errorf("The transaction did not succeed.") + } + if len(res.Responses) != 2 { + return fmt.Errorf("Expected two responses but got %d.", len(res.Responses)) + } + + c.modRevision = res.Responses[1].GetResponseRange().Kvs[0].ModRevision + return nil +} + +func (c *RemoteClient) Delete() error { + c.mu.Lock() + defer c.mu.Unlock() + + _, err := c.Client.KV.Delete(context.TODO(), c.Key) + return err +} + +func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if !c.DoLock { + return "", nil + } + if c.etcdSession != nil { + return "", fmt.Errorf("state %q already locked", c.Key) + } + + c.info = info + return c.lock() +} + +func (c *RemoteClient) Unlock(id string) error { + c.mu.Lock() + defer c.mu.Unlock() + + if !c.DoLock { + return nil + } + + return c.unlock(id) +} + +func (c *RemoteClient) deleteLockInfo(info *statemgr.LockInfo) error { + res, err := c.Client.KV.Delete(context.TODO(), c.Key+lockInfoSuffix) + if err != nil { + return err + } + if res.Deleted == 0 { + return fmt.Errorf("No keys deleted for %s when deleting lock info.", c.Key+lockInfoSuffix) + } + return nil +} + +func (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) { + res, err := c.Client.KV.Get(context.TODO(), c.Key+lockInfoSuffix) + if err != nil { + return nil, err + } + if res.Count == 0 { + return nil, nil + } + + li := &statemgr.LockInfo{} + err = json.Unmarshal(res.Kvs[0].Value, li) + if err != nil { + return nil, fmt.Errorf("Error unmarshaling lock info: %s.", err) + } + + return li, nil +} + +func (c *RemoteClient) putLockInfo(info *statemgr.LockInfo) error { + c.info.Path = c.etcdMutex.Key() + c.info.Created = time.Now().UTC() + + _, err := c.Client.KV.Put(context.TODO(), c.Key+lockInfoSuffix, string(c.info.Marshal())) + return err +} + +func (c *RemoteClient) lock() (string, error) { + session, err := etcdv3sync.NewSession(c.Client) + if err != nil { + return "", nil + } + + ctx, cancel := context.WithTimeout(context.TODO(), lockAcquireTimeout) + defer cancel() + + mutex := etcdv3sync.NewMutex(session, c.Key) + if err1 := mutex.Lock(ctx); err1 != nil { + lockInfo, err2 := c.getLockInfo() + if err2 != nil { + return "", &statemgr.LockError{Err: err2} + } + return "", &statemgr.LockError{Info: lockInfo, Err: err1} + } + + c.etcdMutex = mutex + c.etcdSession = session + + err = c.putLockInfo(c.info) + if err != nil { + if unlockErr := c.unlock(c.info.ID); unlockErr != nil { + err = multierror.Append(err, unlockErr) + } + return "", err + } + + return c.info.ID, nil +} + +func (c *RemoteClient) unlock(id string) error { + if c.etcdMutex == nil { + return nil + } + + var errs error + + if err := c.deleteLockInfo(c.info); err != nil { + errs = multierror.Append(errs, err) + } + if err := c.etcdMutex.Unlock(context.TODO()); err != nil { + errs = multierror.Append(errs, err) + } + if err := c.etcdSession.Close(); err != nil { + errs = multierror.Append(errs, err) + } + + c.etcdMutex = nil + c.etcdSession = nil + + return errs +} diff --git a/internal/backend/remote-state/etcdv3/client_test.go b/internal/backend/remote-state/etcdv3/client_test.go new file mode 100644 index 000000000000..5a57e2418452 --- /dev/null +++ b/internal/backend/remote-state/etcdv3/client_test.go @@ -0,0 +1,103 @@ +package etcd + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/states/remote" + "github.com/hashicorp/terraform/internal/states/statemgr" +) + +func TestRemoteClient_impl(t *testing.T) { + var _ remote.Client = new(RemoteClient) +} + +func TestRemoteClient(t *testing.T) { + prepareEtcdv3(t) + defer cleanupEtcdv3(t) + + prefix := fmt.Sprintf("%s/%s/", keyPrefix, time.Now().Format(time.RFC3339)) + + // Get the backend + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })) + + // Grab the client + state, err := b.StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatalf("Error: %s.", err) + } + + // Test + remote.TestClient(t, state.(*remote.State).Client) +} + +func TestEtcdv3_stateLock(t *testing.T) { + prepareEtcdv3(t) + defer cleanupEtcdv3(t) + + prefix := fmt.Sprintf("%s/%s/", keyPrefix, time.Now().Format(time.RFC3339)) + + // Get the backend + s1, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })).StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatal(err) + } + + s2, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })).StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatal(err) + } + + remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client) +} + +func TestEtcdv3_destroyLock(t *testing.T) { + prepareEtcdv3(t) + defer cleanupEtcdv3(t) + + prefix := fmt.Sprintf("%s/%s/", keyPrefix, time.Now().Format(time.RFC3339)) + + // Get the backend + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "endpoints": stringsToInterfaces(etcdv3Endpoints), + "prefix": prefix, + })) + + // Grab the client + s, err := b.StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatalf("err: %s", err) + } + + c := s.(*remote.State).Client.(*RemoteClient) + + info := statemgr.NewLockInfo() + id, err := c.Lock(info) + if err != nil { + t.Fatal(err) + } + + if err := c.Unlock(id); err != nil { + t.Fatal(err) + } + + res, err := c.Client.KV.Get(context.TODO(), c.info.Path) + if err != nil { + t.Fatal(err) + } + if res.Count != 0 { + t.Fatalf("lock key not cleaned up at: %s", string(res.Kvs[0].Key)) + } +} diff --git a/internal/backend/remote/backend_state.go b/internal/backend/remote/backend_state.go index 54cdd0aadb84..53437d7b60c9 100644 --- a/internal/backend/remote/backend_state.go +++ b/internal/backend/remote/backend_state.go @@ -5,12 +5,9 @@ import ( "context" "crypto/md5" "encoding/base64" - "encoding/json" "fmt" tfe "github.com/hashicorp/go-tfe" - - "github.com/hashicorp/terraform/internal/command/jsonstate" "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statefile" "github.com/hashicorp/terraform/internal/states/statemgr" @@ -68,22 +65,12 @@ func (r *remoteClient) Put(state []byte) error { return fmt.Errorf("Error reading state: %s", err) } - ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues) - if err != nil { - return fmt.Errorf("Error reading output values: %s", err) - } - o, err := json.Marshal(ov) - if err != nil { - return fmt.Errorf("Error converting output values to json: %s", err) - } - options := tfe.StateVersionCreateOptions{ - Lineage: tfe.String(stateFile.Lineage), - Serial: tfe.Int64(int64(stateFile.Serial)), - MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))), - State: tfe.String(base64.StdEncoding.EncodeToString(state)), - Force: tfe.Bool(r.forcePush), - JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(o)), + Lineage: tfe.String(stateFile.Lineage), + Serial: tfe.Int64(int64(stateFile.Serial)), + MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))), + State: tfe.String(base64.StdEncoding.EncodeToString(state)), + Force: tfe.Bool(r.forcePush), } // If we have a run ID, make sure to add it to the options diff --git a/internal/builtin/providers/terraform/data_source_state.go b/internal/builtin/providers/terraform/data_source_state.go index f69a835343cc..c2088c8f319a 100644 --- a/internal/builtin/providers/terraform/data_source_state.go +++ b/internal/builtin/providers/terraform/data_source_state.go @@ -193,15 +193,10 @@ func getBackend(cfg cty.Value) (backend.Backend, cty.Value, tfdiags.Diagnostics) log.Printf("[DEBUG] Initializing remote state backend: %s", backendType) f := getBackendFactory(backendType) if f == nil { - detail := fmt.Sprintf("There is no backend type named %q.", backendType) - if msg, removed := backendInit.RemovedBackends[backendType]; removed { - detail = msg - } - diags = diags.Append(tfdiags.AttributeValue( tfdiags.Error, "Invalid backend configuration", - detail, + fmt.Sprintf("There is no backend type named %q.", backendType), cty.Path(nil).GetAttr("backend"), )) return nil, cty.NilVal, diags diff --git a/internal/cloud/backend.go b/internal/cloud/backend.go index d08754d230a5..989fcc26eb69 100644 --- a/internal/cloud/backend.go +++ b/internal/cloud/backend.go @@ -16,18 +16,18 @@ import ( version "github.com/hashicorp/go-version" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/disco" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" - "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" + "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statemgr" "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" tfversion "github.com/hashicorp/terraform/version" + "github.com/mitchellh/cli" + "github.com/mitchellh/colorstring" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/gocty" backendLocal "github.com/hashicorp/terraform/internal/backend/local" ) @@ -628,7 +628,7 @@ func (b *Cloud) StateMgr(name string) (statemgr.Full, error) { runID: os.Getenv("TFE_RUN_ID"), } - return NewState(client), nil + return &remote.State{Client: client}, nil } // Operation implements backend.Enhanced. diff --git a/internal/cloud/backend_common.go b/internal/cloud/backend_common.go index e03ff6666554..2f17ff578846 100644 --- a/internal/cloud/backend_common.go +++ b/internal/cloud/backend_common.go @@ -199,17 +199,6 @@ func (b *Cloud) waitForRun(stopCtx, cancelCtx context.Context, op *backend.Opera } } -func (b *Cloud) waitTaskStage(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run, stageID string, outputTitle string) error { - integration := &IntegrationContext{ - B: b, - StopContext: stopCtx, - CancelContext: cancelCtx, - Op: op, - Run: r, - } - return b.runTasks(integration, integration.BeginOutput(outputTitle), stageID) -} - func (b *Cloud) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { if r.CostEstimate == nil { return nil diff --git a/internal/cloud/backend_plan.go b/internal/cloud/backend_plan.go index 2688d65c1288..f35993485dd6 100644 --- a/internal/cloud/backend_plan.go +++ b/internal/cloud/backend_plan.go @@ -291,28 +291,6 @@ in order to capture the filesystem context the remote workspace expects: runHeader, b.hostname, b.organization, op.Workspace, r.ID)) + "\n")) } - // Retrieve the run to get task stages. - // Task Stages are calculated upfront so we only need to call this once for the run. - taskStages := make([]*tfe.TaskStage, 0) - result, err := b.client.Runs.ReadWithOptions(stopCtx, r.ID, &tfe.RunReadOptions{ - Include: []tfe.RunIncludeOpt{tfe.RunTaskStages}, - }) - if err == nil { - taskStages = result.TaskStages - } else { - // This error would be expected for older versions of TFE that do not allow - // fetching task_stages. - if !strings.HasSuffix(err.Error(), "Invalid include parameter") { - return r, generalError("Failed to retrieve run", err) - } - } - - if stageID := getTaskStageIDByName(taskStages, tfe.PrePlan); stageID != nil { - if err := b.waitTaskStage(stopCtx, cancelCtx, op, r, *stageID, "Pre-plan Tasks"); err != nil { - return r, err - } - } - r, err = b.waitForRun(stopCtx, cancelCtx, op, "plan", r, w) if err != nil { return r, err @@ -346,9 +324,20 @@ in order to capture the filesystem context the remote workspace expects: } // Retrieve the run to get its current status. - r, err = b.client.Runs.Read(stopCtx, r.ID) + runID := r.ID + r, err = b.client.Runs.ReadWithOptions(stopCtx, runID, &tfe.RunReadOptions{ + Include: []tfe.RunIncludeOpt{tfe.RunTaskStages}, + }) if err != nil { - return r, generalError("Failed to retrieve run", err) + // This error would be expected for older versions of TFE that do not allow + // fetching task_stages. + if strings.HasSuffix(err.Error(), "Invalid include parameter") { + r, err = b.client.Runs.Read(stopCtx, runID) + } + + if err != nil { + return r, generalError("Failed to retrieve run", err) + } } // If the run is canceled or errored, we still continue to the @@ -357,8 +346,18 @@ in order to capture the filesystem context the remote workspace expects: // status of the run will be "errored", but there is still policy // information which should be shown. - if stageID := getTaskStageIDByName(taskStages, tfe.PostPlan); stageID != nil { - if err := b.waitTaskStage(stopCtx, cancelCtx, op, r, *stageID, "Post-plan Tasks"); err != nil { + // Await post-plan run tasks + integration := &IntegrationContext{ + B: b, + StopContext: stopCtx, + CancelContext: cancelCtx, + Op: op, + Run: r, + } + + if stageID := getTaskStageIDByName(r.TaskStages, tfe.PostPlan); stageID != nil { + err = b.runTasks(integration, integration.BeginOutput("Run Tasks (post-plan)"), *stageID) + if err != nil { return r, err } } diff --git a/internal/cloud/backend_runTasks.go b/internal/cloud/backend_runTasks.go index 8b7e45bfd4a8..33c0a8da2d9c 100644 --- a/internal/cloud/backend_runTasks.go +++ b/internal/cloud/backend_runTasks.go @@ -52,7 +52,7 @@ func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output Inte stage, err := fetchTaskStage(b, context.StopContext) if err != nil { - return false, generalError("Failed to retrieve task stage", err) + return false, generalError("Failed to retrieve pre-apply task stage", err) } summary := summarizeTaskResults(stage.TaskResults) diff --git a/internal/cloud/backend_state.go b/internal/cloud/backend_state.go index f8cb9f24551d..57bdf7bdabb3 100644 --- a/internal/cloud/backend_state.go +++ b/internal/cloud/backend_state.go @@ -5,13 +5,9 @@ import ( "context" "crypto/md5" "encoding/base64" - "encoding/json" - "errors" "fmt" tfe "github.com/hashicorp/go-tfe" - - "github.com/hashicorp/terraform/internal/command/jsonstate" "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statefile" "github.com/hashicorp/terraform/internal/states/statemgr" @@ -37,12 +33,12 @@ func (r *remoteClient) Get() (*remote.Payload, error) { // If no state exists, then return nil. return nil, nil } - return nil, fmt.Errorf("failed to retrieve state: %w", err) + return nil, fmt.Errorf("Error retrieving state: %v", err) } state, err := r.client.StateVersions.Download(ctx, sv.DownloadURL) if err != nil { - return nil, fmt.Errorf("failed to download state: %w", err) + return nil, fmt.Errorf("Error downloading state: %v", err) } // If the state is empty, then return nil. @@ -66,25 +62,15 @@ func (r *remoteClient) Put(state []byte) error { // Read the raw state into a Terraform state. stateFile, err := statefile.Read(bytes.NewReader(state)) if err != nil { - return fmt.Errorf("failed to read state: %w", err) - } - - ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues) - if err != nil { - return fmt.Errorf("failed to translate outputs: %w", err) - } - o, err := json.Marshal(ov) - if err != nil { - return fmt.Errorf("failed to marshal outputs to json: %w", err) + return fmt.Errorf("Error reading state: %s", err) } options := tfe.StateVersionCreateOptions{ - Lineage: tfe.String(stateFile.Lineage), - Serial: tfe.Int64(int64(stateFile.Serial)), - MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))), - State: tfe.String(base64.StdEncoding.EncodeToString(state)), - Force: tfe.Bool(r.forcePush), - JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(o)), + Lineage: tfe.String(stateFile.Lineage), + Serial: tfe.Int64(int64(stateFile.Serial)), + MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))), + State: tfe.String(base64.StdEncoding.EncodeToString(state)), + Force: tfe.Bool(r.forcePush), } // If we have a run ID, make sure to add it to the options @@ -97,7 +83,7 @@ func (r *remoteClient) Put(state []byte) error { _, err = r.client.StateVersions.Create(ctx, r.workspace.ID, options) if err != nil { r.stateUploadErr = true - return fmt.Errorf("failed to upload state: %w", err) + return fmt.Errorf("Error uploading state: %v", err) } return nil @@ -107,7 +93,7 @@ func (r *remoteClient) Put(state []byte) error { func (r *remoteClient) Delete() error { err := r.client.Workspaces.Delete(context.Background(), r.organization, r.workspace.Name) if err != nil && err != tfe.ErrResourceNotFound { - return fmt.Errorf("failed to delete workspace %s: %w", r.workspace.Name, err) + return fmt.Errorf("Error deleting workspace %s: %v", r.workspace.Name, err) } return nil @@ -160,7 +146,7 @@ func (r *remoteClient) Unlock(id string) error { if r.lockInfo != nil { // Verify the expected lock ID. if r.lockInfo.ID != id { - lockErr.Err = errors.New("lock ID does not match existing lock") + lockErr.Err = fmt.Errorf("lock ID does not match existing lock") return lockErr } diff --git a/internal/cloud/backend_state_test.go b/internal/cloud/backend_state_test.go index 3b9833c38e5e..63c970438a3c 100644 --- a/internal/cloud/backend_state_test.go +++ b/internal/cloud/backend_state_test.go @@ -5,8 +5,6 @@ import ( "os" "testing" - tfe "github.com/hashicorp/go-tfe" - "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statefile" @@ -21,50 +19,7 @@ func TestRemoteClient(t *testing.T) { remote.TestClient(t, client) } -func TestRemoteClient_stateVersionCreated(t *testing.T) { - b, bCleanup := testBackendWithName(t) - defer bCleanup() - - raw, err := b.StateMgr(testBackendSingleWorkspaceName) - if err != nil { - t.Fatalf("error: %v", err) - } - - client := raw.(*State).Client - - err = client.Put(([]byte)(` -{ - "version": 4, - "terraform_version": "1.3.0", - "serial": 1, - "lineage": "backend-change", - "outputs": { - "foo": { - "type": "string", - "value": "bar" - } - } -}`)) - if err != nil { - t.Fatalf("expected no error, got %v", err) - } - - stateVersionsAPI := b.client.StateVersions.(*MockStateVersions) - if got, want := len(stateVersionsAPI.stateVersions), 1; got != want { - t.Fatalf("wrong number of state versions in the mock client %d; want %d", got, want) - } - - var stateVersion *tfe.StateVersion - for _, sv := range stateVersionsAPI.stateVersions { - stateVersion = sv - } - - if stateVersionsAPI.outputStates[stateVersion.ID] == nil || len(stateVersionsAPI.outputStates[stateVersion.ID]) == 0 { - t.Fatal("no state version outputs in the mock client") - } -} - -func TestRemoteClient_TestRemoteLocks(t *testing.T) { +func TestRemoteClient_stateLock(t *testing.T) { b, bCleanup := testBackendWithName(t) defer bCleanup() @@ -78,7 +33,7 @@ func TestRemoteClient_TestRemoteLocks(t *testing.T) { t.Fatalf("expected no error, got %v", err) } - remote.TestRemoteLocks(t, s1.(*State).Client, s2.(*State).Client) + remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client) } func TestRemoteClient_withRunID(t *testing.T) { diff --git a/internal/cloud/state.go b/internal/cloud/state.go deleted file mode 100644 index 73bea8ba5d9c..000000000000 --- a/internal/cloud/state.go +++ /dev/null @@ -1,160 +0,0 @@ -package cloud - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "log" - "strings" - - "github.com/hashicorp/go-tfe" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" - - "github.com/hashicorp/terraform/internal/states" - "github.com/hashicorp/terraform/internal/states/remote" - "github.com/hashicorp/terraform/internal/states/statemgr" -) - -// State is similar to remote State and delegates to it, except in the case of output values, -// which use a separate methodology that ensures the caller is authorized to read cloud -// workspace outputs. -type State struct { - Client *remoteClient - - delegate remote.State -} - -var ErrStateVersionUnauthorizedUpgradeState = errors.New(strings.TrimSpace(` -You are not authorized to read the full state version containing outputs. -State versions created by terraform v1.3.0 and newer do not require this level -of authorization and therefore this error can usually be fixed by upgrading the -remote state version. -`)) - -// Proof that cloud State is a statemgr.Persistent interface -var _ statemgr.Persistent = (*State)(nil) - -func NewState(client *remoteClient) *State { - return &State{ - Client: client, - delegate: remote.State{Client: client}, - } -} - -// State delegates calls to read State to the remote State -func (s *State) State() *states.State { - return s.delegate.State() -} - -// Lock delegates calls to lock state to the remote State -func (s *State) Lock(info *statemgr.LockInfo) (string, error) { - return s.delegate.Lock(info) -} - -// Unlock delegates calls to unlock state to the remote State -func (s *State) Unlock(id string) error { - return s.delegate.Unlock(id) -} - -// RefreshState delegates calls to refresh State to the remote State -func (s *State) RefreshState() error { - return s.delegate.RefreshState() -} - -// RefreshState delegates calls to refresh State to the remote State -func (s *State) PersistState() error { - return s.delegate.PersistState() -} - -// WriteState delegates calls to write State to the remote State -func (s *State) WriteState(state *states.State) error { - return s.delegate.WriteState(state) -} - -func (s *State) fallbackReadOutputsFromFullState() (map[string]*states.OutputValue, error) { - log.Printf("[DEBUG] falling back to reading full state") - - if err := s.RefreshState(); err != nil { - return nil, fmt.Errorf("failed to load state: %w", err) - } - - state := s.State() - if state == nil { - // We know that there is supposed to be state (and this is not simply a new workspace - // without state) because the fallback is only invoked when outputs are present but - // detailed types are not available. - return nil, ErrStateVersionUnauthorizedUpgradeState - } - - return state.RootModule().OutputValues, nil -} - -// GetRootOutputValues fetches output values from Terraform Cloud -func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) { - ctx := context.Background() - - so, err := s.Client.client.StateVersionOutputs.ReadCurrent(ctx, s.Client.workspace.ID) - - if err != nil { - return nil, fmt.Errorf("could not read state version outputs: %w", err) - } - - result := make(map[string]*states.OutputValue) - - for _, output := range so.Items { - if output.DetailedType == nil { - // If there is no detailed type information available, this state was probably created - // with a version of terraform < 1.3.0. In this case, we'll eject completely from this - // function and fall back to the old behavior of reading the entire state file, which - // requires a higher level of authorization. - return s.fallbackReadOutputsFromFullState() - } - - if output.Sensitive { - // Since this is a sensitive value, the output must be requested explicitly in order to - // read its value, which is assumed to be present by callers - sensitiveOutput, err := s.Client.client.StateVersionOutputs.Read(ctx, output.ID) - if err != nil { - return nil, fmt.Errorf("could not read state version output %s: %w", output.ID, err) - } - output.Value = sensitiveOutput.Value - } - - cval, err := tfeOutputToCtyValue(*output) - if err != nil { - return nil, fmt.Errorf("could not decode output %s (ID %s)", output.Name, output.ID) - } - - result[output.Name] = &states.OutputValue{ - Value: cval, - Sensitive: output.Sensitive, - } - } - - return result, nil -} - -// tfeOutputToCtyValue decodes a combination of TFE output value and detailed-type to create a -// cty value that is suitable for use in terraform. -func tfeOutputToCtyValue(output tfe.StateVersionOutput) (cty.Value, error) { - var result cty.Value - bufType, err := json.Marshal(output.DetailedType) - if err != nil { - return result, fmt.Errorf("could not marshal output %s type: %w", output.ID, err) - } - - var ctype cty.Type - err = ctype.UnmarshalJSON(bufType) - if err != nil { - return result, fmt.Errorf("could not interpret output %s type: %w", output.ID, err) - } - - result, err = gocty.ToCtyValue(output.Value, ctype) - if err != nil { - return result, fmt.Errorf("could not interpret value %v as type %s for output %s: %w", result, ctype.FriendlyName(), output.ID, err) - } - - return result, nil -} diff --git a/internal/cloud/state_test.go b/internal/cloud/state_test.go deleted file mode 100644 index 738ae721a44d..000000000000 --- a/internal/cloud/state_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package cloud - -import ( - "testing" - - "github.com/hashicorp/go-tfe" - - "github.com/hashicorp/terraform/internal/states/statemgr" -) - -func TestState_impl(t *testing.T) { - var _ statemgr.Reader = new(State) - var _ statemgr.Writer = new(State) - var _ statemgr.Persister = new(State) - var _ statemgr.Refresher = new(State) - var _ statemgr.OutputReader = new(State) - var _ statemgr.Locker = new(State) -} - -type ExpectedOutput struct { - Name string - Sensitive bool - IsNull bool -} - -func TestState_GetRootOutputValues(t *testing.T) { - b, bCleanup := testBackendWithOutputs(t) - defer bCleanup() - - client := &remoteClient{ - client: b.client, - workspace: &tfe.Workspace{ - ID: "ws-abcd", - }, - } - - state := NewState(client) - outputs, err := state.GetRootOutputValues() - - if err != nil { - t.Fatalf("error returned from GetRootOutputValues: %s", err) - } - - cases := []ExpectedOutput{ - { - Name: "sensitive_output", - Sensitive: true, - IsNull: false, - }, - { - Name: "nonsensitive_output", - Sensitive: false, - IsNull: false, - }, - { - Name: "object_output", - Sensitive: false, - IsNull: false, - }, - { - Name: "list_output", - Sensitive: false, - IsNull: false, - }, - } - - if len(outputs) != len(cases) { - t.Errorf("Expected %d item but %d were returned", len(cases), len(outputs)) - } - - for _, testCase := range cases { - so, ok := outputs[testCase.Name] - if !ok { - t.Fatalf("Expected key %s but it was not found", testCase.Name) - } - if so.Value.IsNull() != testCase.IsNull { - t.Errorf("Key %s does not match null expectation %v", testCase.Name, testCase.IsNull) - } - if so.Sensitive != testCase.Sensitive { - t.Errorf("Key %s does not match sensitive expectation %v", testCase.Name, testCase.Sensitive) - } - } -} diff --git a/internal/cloud/testing.go b/internal/cloud/testing.go index cfb49cf9b352..4297540fca8a 100644 --- a/internal/cloud/testing.go +++ b/internal/cloud/testing.go @@ -2,7 +2,6 @@ package cloud import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -15,9 +14,6 @@ import ( svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/auth" "github.com/hashicorp/terraform-svchost/disco" - "github.com/mitchellh/cli" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -27,6 +23,8 @@ import ( "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/version" + "github.com/mitchellh/cli" + "github.com/zclconf/go-cty/cty" backendLocal "github.com/hashicorp/terraform/internal/backend/local" ) @@ -119,69 +117,7 @@ func testRemoteClient(t *testing.T) remote.Client { t.Fatalf("error: %v", err) } - return raw.(*State).Client -} - -func testBackendWithOutputs(t *testing.T) (*Cloud, func()) { - b, cleanup := testBackendWithName(t) - - // Get a new mock client to use for adding outputs - mc := NewMockClient() - - mc.StateVersionOutputs.create("svo-abcd", &tfe.StateVersionOutput{ - ID: "svo-abcd", - Value: "foobar", - Sensitive: true, - Type: "string", - Name: "sensitive_output", - DetailedType: "string", - }) - - mc.StateVersionOutputs.create("svo-zyxw", &tfe.StateVersionOutput{ - ID: "svo-zyxw", - Value: "bazqux", - Type: "string", - Name: "nonsensitive_output", - DetailedType: "string", - }) - - var dt interface{} - var val interface{} - err := json.Unmarshal([]byte(`["object", {"foo":"string"}]`), &dt) - if err != nil { - t.Fatalf("could not unmarshal detailed type: %s", err) - } - err = json.Unmarshal([]byte(`{"foo":"bar"}`), &val) - if err != nil { - t.Fatalf("could not unmarshal value: %s", err) - } - mc.StateVersionOutputs.create("svo-efgh", &tfe.StateVersionOutput{ - ID: "svo-efgh", - Value: val, - Type: "object", - Name: "object_output", - DetailedType: dt, - }) - - err = json.Unmarshal([]byte(`["list", "bool"]`), &dt) - if err != nil { - t.Fatalf("could not unmarshal detailed type: %s", err) - } - err = json.Unmarshal([]byte(`[true, false, true, true]`), &val) - if err != nil { - t.Fatalf("could not unmarshal value: %s", err) - } - mc.StateVersionOutputs.create("svo-ijkl", &tfe.StateVersionOutput{ - ID: "svo-ijkl", - Value: val, - Type: "array", - Name: "list_output", - DetailedType: dt, - }) - - b.client.StateVersionOutputs = mc.StateVersionOutputs - - return b, cleanup + return raw.(*remote.State).Client } func testBackend(t *testing.T, obj cty.Value) (*Cloud, func()) { @@ -213,7 +149,6 @@ func testBackend(t *testing.T, obj cty.Value) (*Cloud, func()) { b.client.PolicyChecks = mc.PolicyChecks b.client.Runs = mc.Runs b.client.StateVersions = mc.StateVersions - b.client.StateVersionOutputs = mc.StateVersionOutputs b.client.Variables = mc.Variables b.client.Workspaces = mc.Workspaces diff --git a/internal/cloud/tfe_client_mock.go b/internal/cloud/tfe_client_mock.go index 2ac259a2eba8..9888599faede 100644 --- a/internal/cloud/tfe_client_mock.go +++ b/internal/cloud/tfe_client_mock.go @@ -16,9 +16,8 @@ import ( "time" tfe "github.com/hashicorp/go-tfe" - "github.com/mitchellh/copystructure" - tfversion "github.com/hashicorp/terraform/version" + "github.com/mitchellh/copystructure" ) type MockClient struct { @@ -30,7 +29,6 @@ type MockClient struct { PolicyChecks *MockPolicyChecks Runs *MockRuns StateVersions *MockStateVersions - StateVersionOutputs *MockStateVersionOutputs Variables *MockVariables Workspaces *MockWorkspaces } @@ -45,7 +43,6 @@ func NewMockClient() *MockClient { c.PolicyChecks = newMockPolicyChecks(c) c.Runs = newMockRuns(c) c.StateVersions = newMockStateVersions(c) - c.StateVersionOutputs = newMockStateVersionOutputs(c) c.Variables = newMockVariables(c) c.Workspaces = newMockWorkspaces(c) return c @@ -926,7 +923,6 @@ type MockStateVersions struct { states map[string][]byte stateVersions map[string]*tfe.StateVersion workspaces map[string][]string - outputStates map[string][]byte } func newMockStateVersions(client *MockClient) *MockStateVersions { @@ -935,7 +931,6 @@ func newMockStateVersions(client *MockClient) *MockStateVersions { states: make(map[string][]byte), stateVersions: make(map[string]*tfe.StateVersion), workspaces: make(map[string][]string), - outputStates: make(map[string][]byte), } } @@ -977,7 +972,6 @@ func (m *MockStateVersions) Create(ctx context.Context, workspaceID string, opti } m.states[sv.DownloadURL] = state - m.outputStates[sv.ID] = []byte(*options.JSONStateOutputs) m.stateVersions[sv.ID] = sv m.workspaces[workspaceID] = append(m.workspaces[workspaceID], sv.ID) @@ -1031,49 +1025,6 @@ func (m *MockStateVersions) ListOutputs(ctx context.Context, svID string, option panic("not implemented") } -type MockStateVersionOutputs struct { - client *MockClient - outputs map[string]*tfe.StateVersionOutput -} - -func newMockStateVersionOutputs(client *MockClient) *MockStateVersionOutputs { - return &MockStateVersionOutputs{ - client: client, - outputs: make(map[string]*tfe.StateVersionOutput), - } -} - -// This is a helper function in order to create mocks to be read later -func (m *MockStateVersionOutputs) create(id string, svo *tfe.StateVersionOutput) { - m.outputs[id] = svo -} - -func (m *MockStateVersionOutputs) Read(ctx context.Context, outputID string) (*tfe.StateVersionOutput, error) { - result, ok := m.outputs[outputID] - if !ok { - return nil, tfe.ErrResourceNotFound - } - - return result, nil -} - -func (m *MockStateVersionOutputs) ReadCurrent(ctx context.Context, workspaceID string) (*tfe.StateVersionOutputsList, error) { - svl := &tfe.StateVersionOutputsList{} - for _, sv := range m.outputs { - svl.Items = append(svl.Items, sv) - } - - svl.Pagination = &tfe.Pagination{ - CurrentPage: 1, - NextPage: 1, - PreviousPage: 1, - TotalPages: 1, - TotalCount: len(svl.Items), - } - - return svl, nil -} - type MockVariables struct { client *MockClient workspaces map[string]*tfe.VariableList diff --git a/internal/command/apply_test.go b/internal/command/apply_test.go index ef4484c401bb..dba659947919 100644 --- a/internal/command/apply_test.go +++ b/internal/command/apply_test.go @@ -3,9 +3,11 @@ package command import ( "bytes" "context" + "encoding/json" "fmt" "io/ioutil" "os" + "path" "path/filepath" "reflect" "strings" @@ -19,6 +21,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" @@ -26,6 +29,7 @@ import ( "github.com/hashicorp/terraform/internal/states/statemgr" "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" + tfversion "github.com/hashicorp/terraform/version" ) func TestApply(t *testing.T) { @@ -2051,7 +2055,81 @@ func TestApply_jsonGoldenReference(t *testing.T) { t.Fatal("state should not be nil") } - checkGoldenReference(t, output, "apply") + // Load the golden reference fixture + wantFile, err := os.Open(path.Join(testFixturePath("apply"), "output.jsonlog")) + if err != nil { + t.Fatalf("failed to open output file: %s", err) + } + defer wantFile.Close() + wantBytes, err := ioutil.ReadAll(wantFile) + if err != nil { + t.Fatalf("failed to read output file: %s", err) + } + want := string(wantBytes) + + got := output.Stdout() + + // Split the output and the reference into lines so that we can compare + // messages + got = strings.TrimSuffix(got, "\n") + gotLines := strings.Split(got, "\n") + + want = strings.TrimSuffix(want, "\n") + wantLines := strings.Split(want, "\n") + + if len(gotLines) != len(wantLines) { + t.Errorf("unexpected number of log lines: got %d, want %d", len(gotLines), len(wantLines)) + } + + // Verify that the log starts with a version message + type versionMessage struct { + Level string `json:"@level"` + Message string `json:"@message"` + Type string `json:"type"` + Terraform string `json:"terraform"` + UI string `json:"ui"` + } + var gotVersion versionMessage + if err := json.Unmarshal([]byte(gotLines[0]), &gotVersion); err != nil { + t.Errorf("failed to unmarshal version line: %s\n%s", err, gotLines[0]) + } + wantVersion := versionMessage{ + "info", + fmt.Sprintf("Terraform %s", tfversion.String()), + "version", + tfversion.String(), + views.JSON_UI_VERSION, + } + if !cmp.Equal(wantVersion, gotVersion) { + t.Errorf("unexpected first message:\n%s", cmp.Diff(wantVersion, gotVersion)) + } + + // Compare the rest of the lines against the golden reference + var gotLineMaps []map[string]interface{} + for i, line := range gotLines[1:] { + index := i + 1 + var gotMap map[string]interface{} + if err := json.Unmarshal([]byte(line), &gotMap); err != nil { + t.Errorf("failed to unmarshal got line %d: %s\n%s", index, err, gotLines[index]) + } + if _, ok := gotMap["@timestamp"]; !ok { + t.Errorf("missing @timestamp field in log: %s", gotLines[index]) + } + delete(gotMap, "@timestamp") + gotLineMaps = append(gotLineMaps, gotMap) + } + var wantLineMaps []map[string]interface{} + for i, line := range wantLines[1:] { + index := i + 1 + var wantMap map[string]interface{} + if err := json.Unmarshal([]byte(line), &wantMap); err != nil { + t.Errorf("failed to unmarshal want line %d: %s\n%s", index, err, gotLines[index]) + } + wantLineMaps = append(wantLineMaps, wantMap) + } + if diff := cmp.Diff(wantLineMaps, gotLineMaps); diff != "" { + t.Errorf("wrong output lines\n%s", diff) + } } func TestApply_warnings(t *testing.T) { diff --git a/internal/command/command_test.go b/internal/command/command_test.go index 7fb593d4a077..50ab641cb319 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -7,14 +7,12 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/google/go-cmp/cmp" "io" "io/ioutil" "net/http" "net/http/httptest" "os" "os/exec" - "path" "path/filepath" "strings" "syscall" @@ -1054,92 +1052,3 @@ func testView(t *testing.T) (*views.View, func(*testing.T) *terminal.TestOutput) streams, done := terminal.StreamsForTesting(t) return views.NewView(streams), done } - -// checkGoldenReference compares the given test output with a known "golden" output log -// located under the specified fixture path. -// -// If any of these tests fail, please communicate with Terraform Cloud folks before resolving, -// as changes to UI output may also affect the behavior of Terraform Cloud's structured run output. -func checkGoldenReference(t *testing.T, output *terminal.TestOutput, fixturePathName string) { - t.Helper() - - // Load the golden reference fixture - wantFile, err := os.Open(path.Join(testFixturePath(fixturePathName), "output.jsonlog")) - if err != nil { - t.Fatalf("failed to open output file: %s", err) - } - defer wantFile.Close() - wantBytes, err := ioutil.ReadAll(wantFile) - if err != nil { - t.Fatalf("failed to read output file: %s", err) - } - want := string(wantBytes) - - got := output.Stdout() - - // Split the output and the reference into lines so that we can compare - // messages - got = strings.TrimSuffix(got, "\n") - gotLines := strings.Split(got, "\n") - - want = strings.TrimSuffix(want, "\n") - wantLines := strings.Split(want, "\n") - - if len(gotLines) != len(wantLines) { - t.Errorf("unexpected number of log lines: got %d, want %d\n"+ - "NOTE: This failure may indicate a UI change affecting the behavior of structured run output on TFC.\n"+ - "Please communicate with Terraform Cloud team before resolving", len(gotLines), len(wantLines)) - } - - // Verify that the log starts with a version message - type versionMessage struct { - Level string `json:"@level"` - Message string `json:"@message"` - Type string `json:"type"` - Terraform string `json:"terraform"` - UI string `json:"ui"` - } - var gotVersion versionMessage - if err := json.Unmarshal([]byte(gotLines[0]), &gotVersion); err != nil { - t.Errorf("failed to unmarshal version line: %s\n%s", err, gotLines[0]) - } - wantVersion := versionMessage{ - "info", - fmt.Sprintf("Terraform %s", version.String()), - "version", - version.String(), - views.JSON_UI_VERSION, - } - if !cmp.Equal(wantVersion, gotVersion) { - t.Errorf("unexpected first message:\n%s", cmp.Diff(wantVersion, gotVersion)) - } - - // Compare the rest of the lines against the golden reference - var gotLineMaps []map[string]interface{} - for i, line := range gotLines[1:] { - index := i + 1 - var gotMap map[string]interface{} - if err := json.Unmarshal([]byte(line), &gotMap); err != nil { - t.Errorf("failed to unmarshal got line %d: %s\n%s", index, err, gotLines[index]) - } - if _, ok := gotMap["@timestamp"]; !ok { - t.Errorf("missing @timestamp field in log: %s", gotLines[index]) - } - delete(gotMap, "@timestamp") - gotLineMaps = append(gotLineMaps, gotMap) - } - var wantLineMaps []map[string]interface{} - for i, line := range wantLines[1:] { - index := i + 1 - var wantMap map[string]interface{} - if err := json.Unmarshal([]byte(line), &wantMap); err != nil { - t.Errorf("failed to unmarshal want line %d: %s\n%s", index, err, gotLines[index]) - } - wantLineMaps = append(wantLineMaps, wantMap) - } - if diff := cmp.Diff(wantLineMaps, gotLineMaps); diff != "" { - t.Errorf("wrong output lines\n%s\n"+ - "NOTE: This failure may indicate a UI change affecting the behavior of structured run output on TFC.\n"+ - "Please communicate with Terraform Cloud team before resolving", diff) - } -} diff --git a/internal/command/e2etest/module_archive_test.go b/internal/command/e2etest/module_archive_test.go deleted file mode 100644 index cb6a2979fde9..000000000000 --- a/internal/command/e2etest/module_archive_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package e2etest - -import ( - "path/filepath" - "strings" - "testing" - - "github.com/hashicorp/terraform/internal/e2e" -) - -func TestInitModuleArchive(t *testing.T) { - t.Parallel() - - // this fetches a module archive from github - skipIfCannotAccessNetwork(t) - - fixturePath := filepath.Join("testdata", "module-archive") - tf := e2e.NewBinary(t, terraformBin, fixturePath) - - stdout, stderr, err := tf.Run("init") - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - if stderr != "" { - t.Errorf("unexpected stderr output:\n%s", stderr) - } - - if !strings.Contains(stdout, "Terraform has been successfully initialized!") { - t.Errorf("success message is missing from output:\n%s", stdout) - } -} diff --git a/internal/command/e2etest/provider_plugin_test.go b/internal/command/e2etest/provider_plugin_test.go index 49fa793dc1ad..8b3aa3af7d83 100644 --- a/internal/command/e2etest/provider_plugin_test.go +++ b/internal/command/e2etest/provider_plugin_test.go @@ -72,16 +72,6 @@ func TestProviderProtocols(t *testing.T) { } if !strings.Contains(stdout, "Apply complete! Resources: 2 added, 0 changed, 0 destroyed.") { - t.Fatalf("wrong output:\nstdout:%s\nstderr%s", stdout, stderr) - } - - /// DESTROY - stdout, stderr, err = tf.Run("destroy", "-auto-approve") - if err != nil { - t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr) - } - - if !strings.Contains(stdout, "Resources: 2 destroyed") { - t.Fatalf("wrong destroy output\nstdout:%s\nstderr:%s", stdout, stderr) + t.Fatalf("wrong output:\n%s", stdout) } } diff --git a/internal/command/e2etest/testdata/module-archive/main.tf b/internal/command/e2etest/testdata/module-archive/main.tf deleted file mode 100644 index 8101c8094eee..000000000000 --- a/internal/command/e2etest/testdata/module-archive/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -// this should be able to unpack the tarball and change the module directory to -// the archive directory regardless of its name. -module "bucket" { - source = "https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/archive/v3.3.0.tar.gz//*?archive=tar.gz" -} diff --git a/internal/command/fmt.go b/internal/command/fmt.go index 64d12e796ff1..b57bc302bfd6 100644 --- a/internal/command/fmt.go +++ b/internal/command/fmt.go @@ -55,6 +55,11 @@ func (c *FmtCommand) Run(args []string) int { } args = cmdFlags.Args() + if len(args) > 1 { + c.Ui.Error("The fmt command expects at most one argument.") + cmdFlags.Usage() + return 1 + } var paths []string if len(args) == 0 { @@ -63,7 +68,7 @@ func (c *FmtCommand) Run(args []string) int { c.list = false c.write = false } else { - paths = args + paths = []string{args[0]} } var output io.Writer @@ -523,17 +528,15 @@ func (c *FmtCommand) trimNewlines(tokens hclwrite.Tokens) hclwrite.Tokens { func (c *FmtCommand) Help() string { helpText := ` -Usage: terraform [global options] fmt [options] [target...] +Usage: terraform [global options] fmt [options] [TARGET] Rewrites all Terraform configuration files to a canonical format. Both configuration files (.tf) and variables files (.tfvars) are updated. JSON files (.tf.json or .tfvars.json) are not modified. - By default, fmt scans the current directory for configuration files. If you - provide a directory for the target argument, then fmt will scan that - directory instead. If you provide a file, then fmt will process just that - file. If you provide a single dash ("-"), then fmt will read from standard - input (STDIN). + If TARGET is not specified, the command uses the current working directory. + If TARGET is a file, the command only uses the specified file. If TARGET + is "-" then the command reads from STDIN. The content must be in the Terraform language native syntax; JSON is not supported. diff --git a/internal/command/fmt_test.go b/internal/command/fmt_test.go index bfc1f2b7e100..b163121c6622 100644 --- a/internal/command/fmt_test.go +++ b/internal/command/fmt_test.go @@ -166,16 +166,7 @@ func TestFmt_snippetInError(t *testing.T) { } } -func TestFmt_manyArgs(t *testing.T) { - tempDir := fmtFixtureWriteDir(t) - // Add a second file - secondSrc := `locals { x = 1 }` - - err := ioutil.WriteFile(filepath.Join(tempDir, "second.tf"), []byte(secondSrc), 0644) - if err != nil { - t.Fatal(err) - } - +func TestFmt_tooManyArgs(t *testing.T) { ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ @@ -185,21 +176,16 @@ func TestFmt_manyArgs(t *testing.T) { } args := []string{ - filepath.Join(tempDir, "main.tf"), - filepath.Join(tempDir, "second.tf"), + "one", + "two", } - if code := c.Run(args); code != 0 { + if code := c.Run(args); code != 1 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } - got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) - if err != nil { - t.Fatal(err) - } - want := filepath.Join(tempDir, fmtFixture.filename) - - if got != want { - t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) + expected := "The fmt command expects at most one argument." + if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expected) { + t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) } } diff --git a/internal/command/format/diagnostic.go b/internal/command/format/diagnostic.go index 780592a08c51..6fbfe4113bf7 100644 --- a/internal/command/format/diagnostic.go +++ b/internal/command/format/diagnostic.go @@ -278,7 +278,7 @@ func appendSourceSnippets(buf *bytes.Buffer, diag *viewsjson.Diagnostic, color * ) } - if len(snippet.Values) > 0 || (snippet.FunctionCall != nil && snippet.FunctionCall.Signature != nil) { + if len(snippet.Values) > 0 { // The diagnostic may also have information about the dynamic // values of relevant variables at the point of evaluation. // This is particularly useful for expressions that get evaluated @@ -291,24 +291,6 @@ func appendSourceSnippets(buf *bytes.Buffer, diag *viewsjson.Diagnostic, color * }) fmt.Fprint(buf, color.Color(" [dark_gray]├────────────────[reset]\n")) - if callInfo := snippet.FunctionCall; callInfo != nil && callInfo.Signature != nil { - - fmt.Fprintf(buf, color.Color(" [dark_gray]│[reset] while calling [bold]%s[reset]("), callInfo.CalledAs) - for i, param := range callInfo.Signature.Params { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(param.Name) - } - if param := callInfo.Signature.VariadicParam; param != nil { - if len(callInfo.Signature.Params) > 0 { - buf.WriteString(", ") - } - buf.WriteString(param.Name) - buf.WriteString("...") - } - buf.WriteString(")\n") - } for _, value := range values { fmt.Fprintf(buf, color.Color(" [dark_gray]│[reset] [bold]%s[reset] %s\n"), value.Traversal, value.Statement) } diff --git a/internal/command/format/diagnostic_test.go b/internal/command/format/diagnostic_test.go index 95f2ed6aa1ba..184aa168071d 100644 --- a/internal/command/format/diagnostic_test.go +++ b/internal/command/format/diagnostic_test.go @@ -6,11 +6,9 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/hcltest" "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" viewsjson "github.com/hashicorp/terraform/internal/command/views/json" "github.com/hashicorp/terraform/internal/lang/marks" @@ -130,7 +128,6 @@ func TestDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedBySensitive(true), }, `[red]╷[reset] [red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] @@ -165,7 +162,6 @@ func TestDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedByUnknown(true), }, `[red]╷[reset] [red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] @@ -200,7 +196,6 @@ func TestDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedByUnknown(true), }, `[red]╷[reset] [red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] @@ -212,54 +207,6 @@ func TestDiagnostic(t *testing.T) { [red]│[reset] [red]│[reset] Whatever shall we do? [red]╵[reset] -`, - }, - "error with source code subject and function call annotation": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Bad bad bad", - Detail: "Whatever shall we do?", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, - End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, - }, - Expression: hcltest.MockExprLiteral(cty.True), - EvalContext: &hcl.EvalContext{ - Functions: map[string]function.Function{ - "beep": function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "pos_param_0", - Type: cty.String, - }, - { - Name: "pos_param_1", - Type: cty.Number, - }, - }, - VarParam: &function.Parameter{ - Name: "var_param", - Type: cty.Bool, - }, - }), - }, - }, - // This is simulating what the HCL function call expression - // type would generate on evaluation, by implementing the - // same interface it uses. - Extra: fakeDiagFunctionCallExtra("beep"), - }, - `[red]╷[reset] -[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] -[red]│[reset] -[red]│[reset] on test.tf line 1: -[red]│[reset] 1: test [underline]source[reset] code -[red]│[reset] [dark_gray]├────────────────[reset] -[red]│[reset] [dark_gray]│[reset] while calling [bold]beep[reset](pos_param_0, pos_param_1, var_param...) -[red]│[reset] -[red]│[reset] Whatever shall we do? -[red]╵[reset] `, }, } @@ -394,7 +341,6 @@ Whatever shall we do? }), }, }, - Extra: diagnosticCausedBySensitive(true), }, ` Error: Bad bad bad @@ -404,37 +350,6 @@ Error: Bad bad bad ├──────────────── │ boop.beep has a sensitive value -Whatever shall we do? -`, - }, - "error with source code subject and expression referring to sensitive value when not related to sensitivity": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Bad bad bad", - Detail: "Whatever shall we do?", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, - End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, - }, - Expression: hcltest.MockExprTraversal(hcl.Traversal{ - hcl.TraverseRoot{Name: "boop"}, - hcl.TraverseAttr{Name: "beep"}, - }), - EvalContext: &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "boop": cty.ObjectVal(map[string]cty.Value{ - "beep": cty.StringVal("blah").Mark(marks.Sensitive), - }), - }, - }, - }, - ` -Error: Bad bad bad - - on test.tf line 1: - 1: test source code - Whatever shall we do? `, }, @@ -459,7 +374,6 @@ Whatever shall we do? }), }, }, - Extra: diagnosticCausedByUnknown(true), }, ` Error: Bad bad bad @@ -469,39 +383,6 @@ Error: Bad bad bad ├──────────────── │ boop.beep is a string, known only after apply -Whatever shall we do? -`, - }, - "error with source code subject and unknown string expression when problem isn't unknown-related": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Bad bad bad", - Detail: "Whatever shall we do?", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, - End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, - }, - Expression: hcltest.MockExprTraversal(hcl.Traversal{ - hcl.TraverseRoot{Name: "boop"}, - hcl.TraverseAttr{Name: "beep"}, - }), - EvalContext: &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "boop": cty.ObjectVal(map[string]cty.Value{ - "beep": cty.UnknownVal(cty.String), - }), - }, - }, - }, - ` -Error: Bad bad bad - - on test.tf line 1: - 1: test source code - ├──────────────── - │ boop.beep is a string - Whatever shall we do? `, }, @@ -526,7 +407,6 @@ Whatever shall we do? }), }, }, - Extra: diagnosticCausedByUnknown(true), }, ` Error: Bad bad bad @@ -536,37 +416,6 @@ Error: Bad bad bad ├──────────────── │ boop.beep will be known only after apply -Whatever shall we do? -`, - }, - "error with source code subject and unknown expression of unknown type when problem isn't unknown-related": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Bad bad bad", - Detail: "Whatever shall we do?", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, - End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, - }, - Expression: hcltest.MockExprTraversal(hcl.Traversal{ - hcl.TraverseRoot{Name: "boop"}, - hcl.TraverseAttr{Name: "beep"}, - }), - EvalContext: &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "boop": cty.ObjectVal(map[string]cty.Value{ - "beep": cty.UnknownVal(cty.DynamicPseudoType), - }), - }, - }, - }, - ` -Error: Bad bad bad - - on test.tf line 1: - 1: test source code - Whatever shall we do? `, }, @@ -906,40 +755,3 @@ func TestDiagnosticFromJSON_invalid(t *testing.T) { }) } } - -// fakeDiagFunctionCallExtra is a fake implementation of the interface that -// HCL uses to provide "extra information" associated with diagnostics that -// describe errors during a function call. -type fakeDiagFunctionCallExtra string - -var _ hclsyntax.FunctionCallDiagExtra = fakeDiagFunctionCallExtra("") - -func (e fakeDiagFunctionCallExtra) CalledFunctionName() string { - return string(e) -} - -func (e fakeDiagFunctionCallExtra) FunctionCallError() error { - return nil -} - -// diagnosticCausedByUnknown is a testing helper for exercising our logic -// for selectively showing unknown values alongside our source snippets for -// diagnostics that are explicitly marked as being caused by unknown values. -type diagnosticCausedByUnknown bool - -var _ tfdiags.DiagnosticExtraBecauseUnknown = diagnosticCausedByUnknown(true) - -func (e diagnosticCausedByUnknown) DiagnosticCausedByUnknown() bool { - return bool(e) -} - -// diagnosticCausedBySensitive is a testing helper for exercising our logic -// for selectively showing sensitive values alongside our source snippets for -// diagnostics that are explicitly marked as being caused by sensitive values. -type diagnosticCausedBySensitive bool - -var _ tfdiags.DiagnosticExtraBecauseSensitive = diagnosticCausedBySensitive(true) - -func (e diagnosticCausedBySensitive) DiagnosticCausedBySensitive() bool { - return bool(e) -} diff --git a/internal/command/import.go b/internal/command/import.go index 51d3895e73e9..a576a29e4047 100644 --- a/internal/command/import.go +++ b/internal/command/import.go @@ -45,6 +45,7 @@ func (c *ImportCommand) Run(args []string) int { cmdFlags.StringVar(&configPath, "config", pwd, "path") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") + cmdFlags.BoolVar(&c.Meta.allowMissingConfig, "allow-missing-config", false, "allow missing config") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -134,7 +135,7 @@ func (c *ImportCommand) Run(args []string) int { break } } - if rc == nil { + if !c.Meta.allowMissingConfig && rc == nil { modulePath := addr.Module.String() if modulePath == "" { modulePath = "the root module" @@ -261,6 +262,10 @@ func (c *ImportCommand) Run(args []string) int { c.Ui.Output(c.Colorize().Color("[reset][green]\n" + importCommandSuccessMsg)) + if c.Meta.allowMissingConfig && rc == nil { + c.Ui.Output(c.Colorize().Color("[reset][yellow]\n" + importCommandAllowMissingResourceMsg)) + } + c.showDiagnostics(diags) if diags.HasErrors() { return 1 @@ -305,6 +310,8 @@ Options: If no config files are present, they must be provided via the input prompts or env vars. + -allow-missing-config Allow import when no resource configuration block exists. + -input=false Disable interactive input prompts. -lock=false Don't hold a state lock during the operation. This is @@ -354,3 +361,12 @@ const importCommandSuccessMsg = `Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform. ` + +const importCommandAllowMissingResourceMsg = `Import does not generate resource configuration, you must create a resource +configuration block that matches the current or desired state manually. + +If there is no matching resource configuration block for the imported +resource, Terraform will delete the resource on the next "terraform apply". +It is recommended that you run "terraform plan" to verify that the +configuration is correct and complete. +` diff --git a/internal/command/import_test.go b/internal/command/import_test.go index 8c4744c5d992..f81658223b2b 100644 --- a/internal/command/import_test.go +++ b/internal/command/import_test.go @@ -644,6 +644,63 @@ func TestImport_providerConfigWithVarFile(t *testing.T) { testStateOutput(t, statePath, testImportStr) } +func TestImport_allowMissingResourceConfig(t *testing.T) { + defer testChdir(t, testFixturePath("import-missing-resource-config"))() + + statePath := testTempFile(t) + + p := testProvider() + ui := new(cli.MockUi) + view, _ := testView(t) + c := &ImportCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + } + + p.ImportResourceStateFn = nil + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "test_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("yay"), + }), + }, + }, + } + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, + }, + }, + }, + } + + args := []string{ + "-state", statePath, + "-allow-missing-config", + "test_instance.foo", + "bar", + } + + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if !p.ImportResourceStateCalled { + t.Fatal("ImportResourceState should be called") + } + + testStateOutput(t, statePath, testImportStr) +} + func TestImport_emptyConfig(t *testing.T) { defer testChdir(t, testFixturePath("empty"))() diff --git a/internal/command/init.go b/internal/command/init.go index adc827fa44b3..3d326f1bcdfc 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -426,15 +426,10 @@ func (c *InitCommand) initBackend(root *configs.Module, extraConfig rawFlags) (b bf := backendInit.Backend(backendType) if bf == nil { - detail := fmt.Sprintf("There is no backend type named %q.", backendType) - if msg, removed := backendInit.RemovedBackends[backendType]; removed { - detail = msg - } - diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported backend type", - Detail: detail, + Detail: fmt.Sprintf("There is no backend type named %q.", backendType), Subject: &root.Backend.TypeRange, }) return nil, true, diags @@ -768,6 +763,20 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, // but rather just emit a single general message about it at // the end, by checking ctx.Err(). + case providercache.ErrProviderChecksumMiss: + // This is a special kind of error that can often be fixed using + // the `terraform providers lock` command. We're just going to + // amend the actual error message with some extra information + // about how to fix this. + + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to install provider", + fmt.Sprintf("Error while installing %s v%s: %s\n\nYou can ensure the current platform, %s, is included in the dependency lock file by running: `terraform providers lock -provider=%s`.\n\nIf this does not fix the problem you may need to reset any provider caching present in your setup or make sure you are connecting to valid provider distributions.", + provider.ForDisplay(), version, err.Msg, err.Meta.TargetPlatform.String(), err.Meta.TargetPlatform.String(), + ), + )) + default: // We can potentially end up in here under cancellation too, // in spite of our getproviders.ErrRequestCanceled case above, diff --git a/internal/command/jsonplan/plan.go b/internal/command/jsonplan/plan.go index 444bc6328991..0a4f9bcd6e88 100644 --- a/internal/command/jsonplan/plan.go +++ b/internal/command/jsonplan/plan.go @@ -442,8 +442,7 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error { changeV.After, _ = changeV.After.UnmarkDeep() var before, after []byte - var afterUnknown cty.Value - + afterUnknown := cty.False if changeV.Before != cty.NilVal { before, err = ctyjson.Marshal(changeV.Before, changeV.Before.Type()) if err != nil { @@ -456,18 +455,8 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error { if err != nil { return err } - afterUnknown = cty.False } else { - filteredAfter := omitUnknowns(changeV.After) - if filteredAfter.IsNull() { - after = nil - } else { - after, err = ctyjson.Marshal(filteredAfter, filteredAfter.Type()) - if err != nil { - return err - } - } - afterUnknown = unknownAsBool(changeV.After) + afterUnknown = cty.True } } @@ -577,9 +566,8 @@ func omitUnknowns(val cty.Value) cty.Value { newVal := omitUnknowns(v) if newVal != cty.NilVal { vals = append(vals, newVal) - } else if newVal == cty.NilVal { - // element order is how we correlate unknownness, so we must - // replace unknowns with nulls + } else if newVal == cty.NilVal && ty.IsListType() { + // list length may be significant, so we will turn unknowns into nulls vals = append(vals, cty.NullVal(v.Type())) } } diff --git a/internal/command/jsonplan/plan_test.go b/internal/command/jsonplan/plan_test.go index ef5b6cda2441..3c640cf6dadf 100644 --- a/internal/command/jsonplan/plan_test.go +++ b/internal/command/jsonplan/plan_test.go @@ -65,18 +65,6 @@ func TestOmitUnknowns(t *testing.T) { "hello": cty.True, }), }, - { - cty.TupleVal([]cty.Value{ - cty.StringVal("alpha"), - cty.UnknownVal(cty.String), - cty.StringVal("charlie"), - }), - cty.TupleVal([]cty.Value{ - cty.StringVal("alpha"), - cty.NullVal(cty.String), - cty.StringVal("charlie"), - }), - }, { cty.SetVal([]cty.Value{ cty.StringVal("dev"), @@ -88,7 +76,6 @@ func TestOmitUnknowns(t *testing.T) { cty.StringVal("dev"), cty.StringVal("foo"), cty.StringVal("stg"), - cty.NullVal(cty.String), }), }, { diff --git a/internal/command/jsonstate/state.go b/internal/command/jsonstate/state.go index 20491fdbce89..83d5c5c377d4 100644 --- a/internal/command/jsonstate/state.go +++ b/internal/command/jsonstate/state.go @@ -159,7 +159,7 @@ func (jsonstate *state) marshalStateValues(s *states.State, schemas *terraform.S var err error // only marshal the root module outputs - sv.Outputs, err = MarshalOutputs(s.RootModule().OutputValues) + sv.Outputs, err = marshalOutputs(s.RootModule().OutputValues) if err != nil { return err } @@ -174,9 +174,7 @@ func (jsonstate *state) marshalStateValues(s *states.State, schemas *terraform.S return nil } -// MarshalOutputs translates a map of states.OutputValue to a map of jsonstate.output, -// which are defined for json encoding. -func MarshalOutputs(outputs map[string]*states.OutputValue) (map[string]output, error) { +func marshalOutputs(outputs map[string]*states.OutputValue) (map[string]output, error) { if outputs == nil { return nil, nil } diff --git a/internal/command/jsonstate/state_test.go b/internal/command/jsonstate/state_test.go index c0cd2e81c38a..475b6d5ad678 100644 --- a/internal/command/jsonstate/state_test.go +++ b/internal/command/jsonstate/state_test.go @@ -6,13 +6,12 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/terraform" + "github.com/zclconf/go-cty/cty" ) func TestMarshalOutputs(t *testing.T) { @@ -93,7 +92,7 @@ func TestMarshalOutputs(t *testing.T) { } for _, test := range tests { - got, err := MarshalOutputs(test.Outputs) + got, err := marshalOutputs(test.Outputs) if test.Err { if err == nil { t.Fatal("succeeded; want error") diff --git a/internal/command/meta.go b/internal/command/meta.go index 594292f1b95c..459427a0df43 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -130,24 +130,6 @@ type Meta struct { // just trusting that someone else did it before running Terraform. UnmanagedProviders map[addrs.Provider]*plugin.ReattachConfig - // AllowExperimentalFeatures controls whether a command that embeds this - // Meta is permitted to make use of experimental Terraform features. - // - // Set this field only during the initial creation of Meta. If you change - // this field after calling methods of type Meta then the resulting - // behavior is undefined. - // - // In normal code this would be set by package main only in builds - // explicitly marked as being alpha releases or development snapshots, - // making experimental features unavailable otherwise. Test code may - // choose to set this if it needs to exercise experimental features. - // - // Some experiments predated the addition of this setting, and may - // therefore still be available even if this flag is false. Our intent - // is that all/most _future_ experiments will be unavailable unless this - // flag is set, to reinforce that experiments are not for production use. - AllowExperimentalFeatures bool - //---------------------------------------------------------- // Protected: commands can set these //---------------------------------------------------------- @@ -231,6 +213,9 @@ type Meta struct { migrateState bool compactWarnings bool + // Used with the import command to allow import of state when no matching config exists. + allowMissingConfig bool + // Used with commands which write state to allow users to write remote // state even if the remote and local Terraform versions don't match. ignoreRemoteVersion bool diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index 43e49152cf71..638be81df1f6 100644 --- a/internal/command/meta_backend.go +++ b/internal/command/meta_backend.go @@ -466,15 +466,10 @@ func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags. bf := backendInit.Backend(c.Type) if bf == nil { - detail := fmt.Sprintf("There is no backend type named %q.", c.Type) - if msg, removed := backendInit.RemovedBackends[c.Type]; removed { - detail = msg - } - diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid backend type", - Detail: detail, + Detail: fmt.Sprintf("There is no backend type named %q.", c.Type), Subject: &c.TypeRange, }) return nil, 0, diags diff --git a/internal/command/meta_backend_migrate_test.go b/internal/command/meta_backend_migrate_test.go index 24e5d9df9e82..d45cbdc0d962 100644 --- a/internal/command/meta_backend_migrate_test.go +++ b/internal/command/meta_backend_migrate_test.go @@ -1,6 +1,7 @@ package command import ( + "fmt" "testing" ) @@ -36,7 +37,7 @@ func TestBackendMigrate_promptMultiStatePattern(t *testing.T) { }, } for name, tc := range cases { - t.Log("Test: ", name) + fmt.Println("Test: ", name) m := testMetaBackend(t, nil) input := map[string]string{} cleanup := testInputMap(t, input) diff --git a/internal/command/meta_config.go b/internal/command/meta_config.go index 1df46d0493b5..d19389283928 100644 --- a/internal/command/meta_config.go +++ b/internal/command/meta_config.go @@ -334,7 +334,6 @@ func (m *Meta) initConfigLoader() (*configload.Loader, error) { if err != nil { return nil, err } - loader.AllowLanguageExperiments(m.AllowExperimentalFeatures) m.configLoader = loader if m.View != nil { m.View.SetConfigSources(loader.Sources) diff --git a/internal/command/output.go b/internal/command/output.go index 0bcde54e8fae..0f23a6109f43 100644 --- a/internal/command/output.go +++ b/internal/command/output.go @@ -82,12 +82,17 @@ func (c *OutputCommand) Outputs(statePath string) (map[string]*states.OutputValu return nil, diags } - output, err := stateStore.GetRootOutputValues() - if err != nil { - return nil, diags.Append(err) + if err := stateStore.RefreshState(); err != nil { + diags = diags.Append(fmt.Errorf("Failed to load state: %s", err)) + return nil, diags + } + + state := stateStore.State() + if state == nil { + state = states.NewState() } - return output, diags + return state.RootModule().OutputValues, nil } func (c *OutputCommand) Help() string { @@ -102,8 +107,7 @@ Usage: terraform [global options] output [options] [NAME] Options: -state=path Path to the state file to read. Defaults to - "terraform.tfstate". Ignored when remote - state is used. + "terraform.tfstate". -no-color If specified, output won't contain any color. diff --git a/internal/command/plan_test.go b/internal/command/plan_test.go index 9e145e6bf350..555771aba360 100644 --- a/internal/command/plan_test.go +++ b/internal/command/plan_test.go @@ -700,22 +700,6 @@ func TestPlan_providerArgumentUnset(t *testing.T) { }, }, }, - DataSources: map[string]providers.Schema{ - "test_data_source": { - Block: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Required: true, - }, - "valid": { - Type: cty.Bool, - Computed: true, - }, - }, - }, - }, - }, } view, done := testView(t) c := &PlanCommand{ @@ -1398,33 +1382,6 @@ func TestPlan_warnings(t *testing.T) { }) } -func TestPlan_jsonGoldenReference(t *testing.T) { - // Create a temporary working directory that is empty - td := t.TempDir() - testCopyDir(t, testFixturePath("plan"), td) - defer testChdir(t, td)() - - p := planFixtureProvider() - view, done := testView(t) - c := &PlanCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(p), - View: view, - }, - } - - args := []string{ - "-json", - } - code := c.Run(args) - output := done(t) - if code != 0 { - t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) - } - - checkGoldenReference(t, output, "plan") -} - // planFixtureSchema returns a schema suitable for processing the // configuration in testdata/plan . This schema should be // assigned to a mock provider named "test". @@ -1451,22 +1408,6 @@ func planFixtureSchema() *providers.GetProviderSchemaResponse { }, }, }, - DataSources: map[string]providers.Schema{ - "test_data_source": { - Block: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Required: true, - }, - "valid": { - Type: cty.Bool, - Computed: true, - }, - }, - }, - }, - }, } } @@ -1482,14 +1423,6 @@ func planFixtureProvider() *terraform.MockProvider { PlannedState: req.ProposedNewState, } } - p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { - return providers.ReadDataSourceResponse{ - State: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("zzzzz"), - "valid": cty.BoolVal(true), - }), - } - } return p } @@ -1523,14 +1456,6 @@ func planVarsFixtureProvider() *terraform.MockProvider { PlannedState: req.ProposedNewState, } } - p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { - return providers.ReadDataSourceResponse{ - State: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("zzzzz"), - "valid": cty.BoolVal(true), - }), - } - } return p } @@ -1550,14 +1475,6 @@ func planWarningsFixtureProvider() *terraform.MockProvider { PlannedState: req.ProposedNewState, } } - p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { - return providers.ReadDataSourceResponse{ - State: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("zzzzz"), - "valid": cty.BoolVal(true), - }), - } - } return p } diff --git a/internal/command/providers.go b/internal/command/providers.go index c55a91774a9f..5bc0d4e6c387 100644 --- a/internal/command/providers.go +++ b/internal/command/providers.go @@ -4,11 +4,10 @@ import ( "fmt" "path/filepath" - "github.com/xlab/treeprint" - "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/getproviders" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/xlab/treeprint" ) // ProvidersCommand is a Command implementation that prints out information @@ -150,7 +149,7 @@ func (c *ProvidersCommand) populateTreeNode(tree treeprint.Tree, node *configs.M } const providersCommandHelp = ` -Usage: terraform [global options] providers [DIR] +Usage: terraform [global options] providers [dir] Prints out a tree of modules in the referenced configuration annotated with their provider requirements. diff --git a/internal/command/providers_mirror.go b/internal/command/providers_mirror.go index 5890ed8d995c..e816a114fb42 100644 --- a/internal/command/providers_mirror.go +++ b/internal/command/providers_mirror.go @@ -94,9 +94,8 @@ func (c *ProvidersMirrorCommand) Run(args []string) int { // generality of go-getter but it's still handy to use the HTTP getter // as an easy way to download over HTTP into a file on disk. httpGetter := getter.HttpGetter{ - Client: httpclient.New(), - Netrc: true, - XTerraformGetDisabled: true, + Client: httpclient.New(), + Netrc: true, } // The following logic is similar to that used by the provider installer diff --git a/internal/command/show_test.go b/internal/command/show_test.go index 6e0103e26149..2dd0e5abe6eb 100644 --- a/internal/command/show_test.go +++ b/internal/command/show_test.go @@ -1006,14 +1006,7 @@ func showFixtureProvider() *terraform.MockProvider { Private: req.Private, } } - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - // this is a destroy plan, - if req.ProposedNewState.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { idVal := req.ProposedNewState.GetAttr("id") amiVal := req.ProposedNewState.GetAttr("ami") if idVal.IsNull() { diff --git a/internal/command/test.go b/internal/command/test.go index 1f18689f1bc5..fc6d7ef70047 100644 --- a/internal/command/test.go +++ b/internal/command/test.go @@ -317,7 +317,7 @@ func (c *TestCommand) prepareSuiteDir(ctx context.Context, suiteName string) (te locks := depsfile.NewLocks() evts := &providercache.InstallerEvents{ QueryPackagesFailure: func(provider addrs.Provider, err error) { - if err != nil && addrs.IsDefaultProvider(provider) && provider.Type == "test" { + if err != nil && provider.IsDefault() && provider.Type == "test" { // This is some additional context for the failure error // we'll generate afterwards. Not the most ideal UX but // good enough for this prototype implementation, to help diff --git a/internal/command/testdata/plan/main.tf b/internal/command/testdata/plan/main.tf index 7b30915731c7..070388113edb 100644 --- a/internal/command/testdata/plan/main.tf +++ b/internal/command/testdata/plan/main.tf @@ -4,10 +4,6 @@ resource "test_instance" "foo" { # This is here because at some point it caused a test failure network_interface { device_index = 0 - description = "Main network interface" + description = "Main network interface" } } - -data "test_data_source" "a" { - id = "zzzzz" -} diff --git a/internal/command/testdata/plan/output.jsonlog b/internal/command/testdata/plan/output.jsonlog deleted file mode 100644 index d823fbf29cd9..000000000000 --- a/internal/command/testdata/plan/output.jsonlog +++ /dev/null @@ -1,5 +0,0 @@ -{"@level":"info","@message":"Terraform 1.3.0-dev","@module":"terraform.ui","terraform":"1.3.0-dev","type":"version","ui":"1.0"} -{"@level":"info","@message":"data.test_data_source.a: Refreshing...","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read"},"type":"apply_start"} -{"@level":"info","@message":"data.test_data_source.a: Refresh complete after 0s [id=zzzzz]","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read","id_key":"id","id_value":"zzzzz","elapsed_seconds":0},"type":"apply_complete"} -{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"terraform.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} -{"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","changes":{"add":1,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"} diff --git a/internal/command/testdata/show-json/unknown-output/main.tf b/internal/command/testdata/show-json/unknown-output/main.tf deleted file mode 100644 index d97891e22464..000000000000 --- a/internal/command/testdata/show-json/unknown-output/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "foo" { - value = "hello" -} - -output "bar" { - value = tolist([ - "hello", - timestamp(), - "world", - ]) -} - -output "baz" { - value = { - greeting: "hello", - time: timestamp(), - subject: "world", - } -} diff --git a/internal/command/testdata/show-json/unknown-output/output.json b/internal/command/testdata/show-json/unknown-output/output.json deleted file mode 100644 index 8a52b8dc57c2..000000000000 --- a/internal/command/testdata/show-json/unknown-output/output.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "format_version": "1.1", - "terraform_version": "1.3.0-dev", - "planned_values": { - "outputs": { - "bar": { - "sensitive": false - }, - "baz": { - "sensitive": false - }, - "foo": { - "sensitive": false, - "type": "string", - "value": "hello" - } - }, - "root_module": {} - }, - "output_changes": { - "bar": { - "actions": [ - "create" - ], - "before": null, - "after": [ - "hello", - null, - "world" - ], - "after_unknown": [ - false, - true, - false - ], - "before_sensitive": false, - "after_sensitive": false - }, - "baz": { - "actions": [ - "create" - ], - "before": null, - "after": { - "greeting": "hello", - "subject": "world" - }, - "after_unknown": { - "time": true - }, - "before_sensitive": false, - "after_sensitive": false - }, - "foo": { - "actions": [ - "create" - ], - "before": null, - "after": "hello", - "after_unknown": false, - "before_sensitive": false, - "after_sensitive": false - } - }, - "prior_state": { - "format_version": "1.0", - "terraform_version": "1.3.0", - "values": { - "outputs": { - "foo": { - "sensitive": false, - "value": "hello", - "type": "string" - } - }, - "root_module": {} - } - }, - "configuration": { - "root_module": { - "outputs": { - "bar": { - "expression": {} - }, - "baz": { - "expression": {} - }, - "foo": { - "expression": { - "constant_value": "hello" - } - } - } - } - } -} diff --git a/internal/command/views/hook_json.go b/internal/command/views/hook_json.go index 38e24de39d6c..733e62d94855 100644 --- a/internal/command/views/hook_json.go +++ b/internal/command/views/hook_json.go @@ -7,14 +7,13 @@ import ( "time" "unicode" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/command/format" "github.com/hashicorp/terraform/internal/command/views/json" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/terraform" + "github.com/zclconf/go-cty/cty" ) // How long to wait between sending heartbeat/progress messages @@ -60,10 +59,8 @@ type applyProgress struct { } func (h *jsonHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { - if action != plans.NoOp { - idKey, idValue := format.ObjectValueIDOrName(priorState) - h.view.Hook(json.NewApplyStart(addr, action, idKey, idValue)) - } + idKey, idValue := format.ObjectValueIDOrName(priorState) + h.view.Hook(json.NewApplyStart(addr, action, idKey, idValue)) progress := applyProgress{ addr: addr, @@ -76,9 +73,7 @@ func (h *jsonHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generatio h.applying[addr.String()] = progress h.applyingLock.Unlock() - if action != plans.NoOp { - go h.applyingHeartbeat(progress) - } + go h.applyingHeartbeat(progress) return terraform.HookActionContinue, nil } @@ -106,10 +101,6 @@ func (h *jsonHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generati delete(h.applying, key) h.applyingLock.Unlock() - if progress.action == plans.NoOp { - return terraform.HookActionContinue, nil - } - elapsed := h.timeNow().Round(time.Second).Sub(progress.start) if err != nil { diff --git a/internal/command/views/hook_ui.go b/internal/command/views/hook_ui.go index 2c5c0f5704fc..9f410519962a 100644 --- a/internal/command/views/hook_ui.go +++ b/internal/command/views/hook_ui.go @@ -65,7 +65,6 @@ const ( uiResourceModify uiResourceDestroy uiResourceRead - uiResourceNoOp ) func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { @@ -90,8 +89,6 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, case plans.Read: operation = "Reading..." op = uiResourceRead - case plans.NoOp: - op = uiResourceNoOp default: // We don't expect any other actions in here, so anything else is a // bug in the caller but we'll ignore it in order to be robust. @@ -109,14 +106,12 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, idValue = "" } - if operation != "" { - h.println(fmt.Sprintf( - h.view.colorize.Color("[reset][bold]%s: %s%s[reset]"), - dispAddr, - operation, - stateIdSuffix, - )) - } + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: %s%s[reset]"), + dispAddr, + operation, + stateIdSuffix, + )) key := addr.String() uiState := uiResourceState{ @@ -134,9 +129,7 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, h.resourcesLock.Unlock() // Start goroutine that shows progress - if op != uiResourceNoOp { - go h.stillApplying(uiState) - } + go h.stillApplying(uiState) return terraform.HookActionContinue, nil } @@ -208,9 +201,6 @@ func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation msg = "Creation complete" case uiResourceRead: msg = "Read complete" - case uiResourceNoOp: - // We don't make any announcements about no-op changes - return terraform.HookActionContinue, nil case uiResourceUnknown: return terraform.HookActionContinue, nil } diff --git a/internal/command/views/json/diagnostic.go b/internal/command/views/json/diagnostic.go index 1175792c72a2..6a4bba9b8ce9 100644 --- a/internal/command/views/json/diagnostic.go +++ b/internal/command/views/json/diagnostic.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcled" "github.com/hashicorp/hcl/v2/hclparse" - "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/tfdiags" "github.com/zclconf/go-cty/cty" @@ -96,10 +95,6 @@ type DiagnosticSnippet struct { // Values is a sorted slice of expression values which may be useful in // understanding the source of an error in a complex expression. Values []DiagnosticExpressionValue `json:"values"` - - // FunctionCall is information about a function call whose failure is - // being reported by this diagnostic, if any. - FunctionCall *DiagnosticFunctionCall `json:"function_call,omitempty"` } // DiagnosticExpressionValue represents an HCL traversal string (e.g. @@ -112,20 +107,6 @@ type DiagnosticExpressionValue struct { Statement string `json:"statement"` } -// DiagnosticFunctionCall represents a function call whose information is -// being included as part of a diagnostic snippet. -type DiagnosticFunctionCall struct { - // CalledAs is the full name that was used to call this function, - // potentially including namespace prefixes if the function does not belong - // to the default function namespace. - CalledAs string `json:"called_as"` - - // Signature is a description of the signature of the function that was - // called, if any. Might be omitted if we're reporting that a call failed - // because the given function name isn't known, for example. - Signature *Function `json:"signature,omitempty"` -} - // NewDiagnostic takes a tfdiags.Diagnostic and a map of configuration sources, // and returns a Diagnostic struct. func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnostic { @@ -271,8 +252,6 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost vars := expr.Variables() values := make([]DiagnosticExpressionValue, 0, len(vars)) seen := make(map[string]struct{}, len(vars)) - includeUnknown := tfdiags.DiagnosticCausedByUnknown(diag) - includeSensitive := tfdiags.DiagnosticCausedBySensitive(diag) Traversals: for _, traversal := range vars { for len(traversal) > 1 { @@ -294,35 +273,14 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost } switch { case val.HasMark(marks.Sensitive): - // We only mention a sensitive value if the diagnostic - // we're rendering is explicitly marked as being - // caused by sensitive values, because otherwise - // readers tend to be misled into thinking the error - // is caused by the sensitive value even when it isn't. - if !includeSensitive { - continue Traversals - } - // Even when we do mention one, we keep it vague - // in order to minimize the chance of giving away - // whatever was sensitive about it. + // We won't say anything at all about sensitive values, + // because we might give away something that was + // sensitive about them. value.Statement = "has a sensitive value" case !val.IsKnown(): - // We'll avoid saying anything about unknown or - // "known after apply" unless the diagnostic is - // explicitly marked as being caused by unknown - // values, because otherwise readers tend to be - // misled into thinking the error is caused by the - // unknown value even when it isn't. if ty := val.Type(); ty != cty.DynamicPseudoType { - if includeUnknown { - value.Statement = fmt.Sprintf("is a %s, known only after apply", ty.FriendlyName()) - } else { - value.Statement = fmt.Sprintf("is a %s", ty.FriendlyName()) - } + value.Statement = fmt.Sprintf("is a %s, known only after apply", ty.FriendlyName()) } else { - if !includeUnknown { - continue Traversals - } value.Statement = "will be known only after apply" } default: @@ -336,24 +294,7 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost return values[i].Traversal < values[j].Traversal }) diagnostic.Snippet.Values = values - - if callInfo := tfdiags.ExtraInfo[hclsyntax.FunctionCallDiagExtra](diag); callInfo != nil && callInfo.CalledFunctionName() != "" { - calledAs := callInfo.CalledFunctionName() - baseName := calledAs - if idx := strings.LastIndex(baseName, "::"); idx >= 0 { - baseName = baseName[idx+2:] - } - callInfo := &DiagnosticFunctionCall{ - CalledAs: calledAs, - } - if f, ok := ctx.Functions[calledAs]; ok { - callInfo.Signature = DescribeFunction(baseName, f) - } - diagnostic.Snippet.FunctionCall = callInfo - } - } - } } diff --git a/internal/command/views/json/diagnostic_test.go b/internal/command/views/json/diagnostic_test.go index 422dade9b3cb..640b82bc60cb 100644 --- a/internal/command/views/json/diagnostic_test.go +++ b/internal/command/views/json/diagnostic_test.go @@ -412,7 +412,6 @@ func TestNewDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedBySensitive(true), }, &Diagnostic{ Severity: "error", @@ -446,61 +445,6 @@ func TestNewDiagnostic(t *testing.T) { }, }, }, - "error with source code subject and expression referring to sensitive value when not caused by sensitive values": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Wrong noises", - Detail: "Biological sounds are not allowed", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, - End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, - }, - Expression: hcltest.MockExprTraversal(hcl.Traversal{ - hcl.TraverseRoot{Name: "var"}, - hcl.TraverseAttr{Name: "boop"}, - hcl.TraverseIndex{Key: cty.StringVal("hello!")}, - }), - EvalContext: &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "var": cty.ObjectVal(map[string]cty.Value{ - "boop": cty.MapVal(map[string]cty.Value{ - "hello!": cty.StringVal("bleurgh").Mark(marks.Sensitive), - }), - }), - }, - }, - }, - &Diagnostic{ - Severity: "error", - Summary: "Wrong noises", - Detail: "Biological sounds are not allowed", - Range: &DiagnosticRange{ - Filename: "test.tf", - Start: Pos{ - Line: 2, - Column: 9, - Byte: 42, - }, - End: Pos{ - Line: 2, - Column: 26, - Byte: 59, - }, - }, - Snippet: &DiagnosticSnippet{ - Context: strPtr(`resource "test_resource" "test"`), - Code: (` foo = var.boop["hello!"]`), - StartLine: (2), - HighlightStartOffset: (8), - HighlightEndOffset: (25), - Values: []DiagnosticExpressionValue{ - // The sensitive value is filtered out because this is - // not a sensitive-value-related diagnostic message. - }, - }, - }, - }, "error with source code subject and expression referring to a collection containing a sensitive value": { &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -581,7 +525,6 @@ func TestNewDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedByUnknown(true), }, &Diagnostic{ Severity: "error", @@ -639,7 +582,6 @@ func TestNewDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedByUnknown(true), }, &Diagnostic{ Severity: "error", @@ -673,61 +615,6 @@ func TestNewDiagnostic(t *testing.T) { }, }, }, - "error with source code subject and unknown expression of unknown type when not caused by unknown values": { - &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Wrong noises", - Detail: "Biological sounds are not allowed", - Subject: &hcl.Range{ - Filename: "test.tf", - Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, - End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, - }, - Expression: hcltest.MockExprTraversal(hcl.Traversal{ - hcl.TraverseRoot{Name: "var"}, - hcl.TraverseAttr{Name: "boop"}, - hcl.TraverseIndex{Key: cty.StringVal("hello!")}, - }), - EvalContext: &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "var": cty.ObjectVal(map[string]cty.Value{ - "boop": cty.MapVal(map[string]cty.Value{ - "hello!": cty.UnknownVal(cty.DynamicPseudoType), - }), - }), - }, - }, - }, - &Diagnostic{ - Severity: "error", - Summary: "Wrong noises", - Detail: "Biological sounds are not allowed", - Range: &DiagnosticRange{ - Filename: "test.tf", - Start: Pos{ - Line: 2, - Column: 9, - Byte: 42, - }, - End: Pos{ - Line: 2, - Column: 26, - Byte: 59, - }, - }, - Snippet: &DiagnosticSnippet{ - Context: strPtr(`resource "test_resource" "test"`), - Code: (` foo = var.boop["hello!"]`), - StartLine: (2), - HighlightStartOffset: (8), - HighlightEndOffset: (25), - Values: []DiagnosticExpressionValue{ - // The unknown value is filtered out because this is - // not an unknown-value-related diagnostic message. - }, - }, - }, - }, "error with source code subject with multiple expression values": { &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -779,7 +666,6 @@ func TestNewDiagnostic(t *testing.T) { }), }, }, - Extra: diagnosticCausedBySensitive(true), }, &Diagnostic{ Severity: "error", @@ -927,25 +813,3 @@ func TestNewDiagnostic(t *testing.T) { // are fields which are pointer-to-string to ensure that the rendered JSON // results in `null` for an empty value, rather than `""`. func strPtr(s string) *string { return &s } - -// diagnosticCausedByUnknown is a testing helper for exercising our logic -// for selectively showing unknown values alongside our source snippets for -// diagnostics that are explicitly marked as being caused by unknown values. -type diagnosticCausedByUnknown bool - -var _ tfdiags.DiagnosticExtraBecauseUnknown = diagnosticCausedByUnknown(true) - -func (e diagnosticCausedByUnknown) DiagnosticCausedByUnknown() bool { - return bool(e) -} - -// diagnosticCausedBySensitive is a testing helper for exercising our logic -// for selectively showing sensitive values alongside our source snippets for -// diagnostics that are explicitly marked as being caused by sensitive values. -type diagnosticCausedBySensitive bool - -var _ tfdiags.DiagnosticExtraBecauseSensitive = diagnosticCausedBySensitive(true) - -func (e diagnosticCausedBySensitive) DiagnosticCausedBySensitive() bool { - return bool(e) -} diff --git a/internal/command/views/json/function.go b/internal/command/views/json/function.go deleted file mode 100644 index f36986dae078..000000000000 --- a/internal/command/views/json/function.go +++ /dev/null @@ -1,112 +0,0 @@ -package json - -import ( - "encoding/json" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// Function is a description of the JSON representation of the signature of -// a function callable from the Terraform language. -type Function struct { - // Name is the leaf name of the function, without any namespace prefix. - Name string `json:"name"` - - Params []FunctionParam `json:"params"` - VariadicParam *FunctionParam `json:"variadic_param,omitempty"` - - // ReturnType is type constraint which is a static approximation of the - // possibly-dynamic return type of the function. - ReturnType json.RawMessage `json:"return_type"` - - Description string `json:"description,omitempty"` - DescriptionKind string `json:"description_kind,omitempty"` -} - -// FunctionParam represents a single parameter to a function, as represented -// by type Function. -type FunctionParam struct { - // Name is a name for the function which is used primarily for - // documentation purposes, because function arguments are positional - // and therefore don't appear directly in configuration source code. - Name string `json:"name"` - - // Type is a type constraint which is a static approximation of the - // possibly-dynamic type of the parameter. Particular functions may - // have additional requirements that a type constraint alone cannot - // represent. - Type json.RawMessage `json:"type"` - - // Maybe some of the other fields in function.Parameter would be - // interesting to describe here too, but we'll wait to see if there - // is a use-case first. - - Description string `json:"description,omitempty"` - DescriptionKind string `json:"description_kind,omitempty"` -} - -// DescribeFunction returns a description of the signature of the given cty -// function, as a pointer to this package's serializable type Function. -func DescribeFunction(name string, f function.Function) *Function { - ret := &Function{ - Name: name, - } - - params := f.Params() - ret.Params = make([]FunctionParam, len(params)) - typeCheckArgs := make([]cty.Type, len(params), len(params)+1) - for i, param := range params { - ret.Params[i] = describeFunctionParam(¶m) - typeCheckArgs[i] = param.Type - } - if varParam := f.VarParam(); varParam != nil { - descParam := describeFunctionParam(varParam) - ret.VariadicParam = &descParam - typeCheckArgs = append(typeCheckArgs, varParam.Type) - } - - retType, err := f.ReturnType(typeCheckArgs) - if err != nil { - // Getting an error when type-checking with exactly the type constraints - // the function called for is weird, so we'll just treat it as if it - // has a dynamic return type instead, for our purposes here. - // One reason this can happen is for a function which has a variadic - // parameter but has logic inside it which considers it invalid to - // specify exactly one argument for that parameter (since that's what - // we did in typeCheckArgs as an approximation of a valid call above.) - retType = cty.DynamicPseudoType - } - - if raw, err := retType.MarshalJSON(); err != nil { - // Again, we'll treat any errors as if the function is dynamically - // typed because it would be weird to get here. - ret.ReturnType = json.RawMessage(`"dynamic"`) - } else { - ret.ReturnType = json.RawMessage(raw) - } - - // We don't currently have any sense of descriptions for functions and - // their parameters, so we'll just leave those fields unpopulated for now. - - return ret -} - -func describeFunctionParam(p *function.Parameter) FunctionParam { - ret := FunctionParam{ - Name: p.Name, - } - - if raw, err := p.Type.MarshalJSON(); err != nil { - // We'll treat any errors as if the function is dynamically - // typed because it would be weird to get here. - ret.Type = json.RawMessage(`"dynamic"`) - } else { - ret.Type = json.RawMessage(raw) - } - - // We don't currently have any sense of descriptions for functions and - // their parameters, so we'll just leave those fields unpopulated for now. - - return ret -} diff --git a/internal/command/views/json/function_test.go b/internal/command/views/json/function_test.go deleted file mode 100644 index 48d4a1323350..000000000000 --- a/internal/command/views/json/function_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package json - -import ( - "encoding/json" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/function/stdlib" -) - -func TestDescribeFunction(t *testing.T) { - // NOTE: This test case is referring to some real functions in other - // packages. and so if those functions change signature later it will - // probably make some cases here fail. If that is the cause of the failure, - // it's fine to update the test here to match rather than to revert the - // change to the function signature, as long as the change to the - // function signature is otherwise within the bounds of our compatibility - // promises. - - tests := map[string]struct { - Function function.Function - Want *Function - }{ - "upper": { - Function: stdlib.UpperFunc, - Want: &Function{ - Name: "upper", - Params: []FunctionParam{ - { - Name: "str", - Type: json.RawMessage(`"string"`), - }, - }, - ReturnType: json.RawMessage(`"string"`), - }, - }, - "coalesce": { - Function: stdlib.CoalesceFunc, - Want: &Function{ - Name: "coalesce", - Params: []FunctionParam{}, - VariadicParam: &FunctionParam{ - Name: "vals", - Type: json.RawMessage(`"dynamic"`), - }, - ReturnType: json.RawMessage(`"dynamic"`), - }, - }, - "join": { - Function: stdlib.JoinFunc, - Want: &Function{ - Name: "join", - Params: []FunctionParam{ - { - Name: "separator", - Type: json.RawMessage(`"string"`), - }, - }, - VariadicParam: &FunctionParam{ - Name: "lists", - Type: json.RawMessage(`["list","string"]`), - }, - ReturnType: json.RawMessage(`"string"`), - }, - }, - "jsonencode": { - Function: stdlib.JSONEncodeFunc, - Want: &Function{ - Name: "jsonencode", - Params: []FunctionParam{ - { - Name: "val", - Type: json.RawMessage(`"dynamic"`), - }, - }, - ReturnType: json.RawMessage(`"string"`), - }, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - got := DescribeFunction(name, test.Function) - want := test.Want - - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("wrong result\n%s", diff) - } - }) - } -} diff --git a/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value-when-not-caused-by-sensitive-values.json b/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value-when-not-caused-by-sensitive-values.json deleted file mode 100644 index 8d3625dbe78d..000000000000 --- a/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value-when-not-caused-by-sensitive-values.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "severity": "error", - "summary": "Wrong noises", - "detail": "Biological sounds are not allowed", - "range": { - "filename": "test.tf", - "start": { - "line": 2, - "column": 9, - "byte": 42 - }, - "end": { - "line": 2, - "column": 26, - "byte": 59 - } - }, - "snippet": { - "context": "resource \"test_resource\" \"test\"", - "code": " foo = var.boop[\"hello!\"]", - "start_line": 2, - "highlight_start_offset": 8, - "highlight_end_offset": 25, - "values": [] - } -} diff --git a/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type-when-not-caused-by-unknown-values.json b/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type-when-not-caused-by-unknown-values.json deleted file mode 100644 index 8d3625dbe78d..000000000000 --- a/internal/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type-when-not-caused-by-unknown-values.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "severity": "error", - "summary": "Wrong noises", - "detail": "Biological sounds are not allowed", - "range": { - "filename": "test.tf", - "start": { - "line": 2, - "column": 9, - "byte": 42 - }, - "end": { - "line": 2, - "column": 26, - "byte": 59 - } - }, - "snippet": { - "context": "resource \"test_resource\" \"test\"", - "code": " foo = var.boop[\"hello!\"]", - "start_line": 2, - "highlight_start_offset": 8, - "highlight_end_offset": 25, - "values": [] - } -} diff --git a/internal/configs/configload/loader.go b/internal/configs/configload/loader.go index b9c3f4489c97..d861a8d1e656 100644 --- a/internal/configs/configload/loader.go +++ b/internal/configs/configload/loader.go @@ -148,16 +148,3 @@ func (l *Loader) ImportSourcesFromSnapshot(snap *Snapshot) { } } } - -// AllowLanguageExperiments specifies whether subsequent LoadConfig (and -// similar) calls will allow opting in to experimental language features. -// -// If this method is never called for a particular loader, the default behavior -// is to disallow language experiments. -// -// Main code should set this only for alpha or development builds. Test code -// is responsible for deciding for itself whether and how to call this -// method. -func (l *Loader) AllowLanguageExperiments(allowed bool) { - l.parser.AllowLanguageExperiments(allowed) -} diff --git a/internal/configs/experiments.go b/internal/configs/experiments.go index 1d1a36b02069..2ebf2d7006f0 100644 --- a/internal/configs/experiments.go +++ b/internal/configs/experiments.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/internal/experiments" "github.com/hashicorp/terraform/version" + "github.com/zclconf/go-cty/cty" ) // When developing UI for experimental features, you can temporarily disable @@ -26,7 +27,7 @@ var disableExperimentWarnings = "" // the experiments are known before we process the result of the module config, // and thus we can take into account which experiments are active when deciding // how to decode. -func sniffActiveExperiments(body hcl.Body, allowed bool) (experiments.Set, hcl.Diagnostics) { +func sniffActiveExperiments(body hcl.Body) (experiments.Set, hcl.Diagnostics) { rootContent, _, diags := body.PartialContent(configFileTerraformBlockSniffRootSchema) ret := experiments.NewSet() @@ -84,37 +85,9 @@ func sniffActiveExperiments(body hcl.Body, allowed bool) (experiments.Set, hcl.D } exps, expDiags := decodeExperimentsAttr(attr) - - // Because we concluded this particular experiment in the same - // release as we made experiments alpha-releases-only, we need to - // treat it as special to avoid masking the "experiment has concluded" - // error with the more general "experiments are not available at all" - // error. Note that this experiment is marked as concluded so this - // only "allows" showing the different error message that it is - // concluded, and does not allow actually using the experiment outside - // of an alpha. - // NOTE: We should be able to remove this special exception a release - // or two after v1.3 when folks have had a chance to notice that the - // experiment has concluded and update their modules accordingly. - // When we do so, we might also consider changing decodeExperimentsAttr - // to _not_ include concluded experiments in the returned set, since - // we're doing that right now only to make this condition work. - if exps.Has(experiments.ModuleVariableOptionalAttrs) && len(exps) == 1 { - allowed = true - } - - if allowed { - diags = append(diags, expDiags...) - if !expDiags.HasErrors() { - ret = experiments.SetUnion(ret, exps) - } - } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module uses experimental features", - Detail: "Experimental features are intended only for gathering early feedback on new language designs, and so are available only in alpha releases of Terraform.", - Subject: attr.NameRange.Ptr(), - }) + diags = append(diags, expDiags...) + if !expDiags.HasErrors() { + ret = experiments.SetUnion(ret, exps) } } @@ -153,15 +126,6 @@ func decodeExperimentsAttr(attr *hcl.Attribute) (experiments.Set, hcl.Diagnostic Subject: expr.Range().Ptr(), }) case experiments.ConcludedError: - // As a special case we still include the optional attributes - // experiment if it's present, because our caller treats that - // as special. See the comment in sniffActiveExperiments for - // more information, and remove this special case here one the - // special case up there is also removed. - if kw == "module_variable_optional_attrs" { - ret.Add(experiments.ModuleVariableOptionalAttrs) - } - diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Experiment has concluded", @@ -181,7 +145,7 @@ func decodeExperimentsAttr(attr *hcl.Attribute) (experiments.Set, hcl.Diagnostic diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagWarning, Summary: fmt.Sprintf("Experimental feature %q is active", exp.Keyword()), - Detail: "Experimental features are available only in alpha releases of Terraform and are subject to breaking changes or total removal in later versions, based on feedback. We recommend against using experimental features in production.\n\nIf you have feedback on the design of this feature, please open a GitHub issue to discuss it.", + Detail: "Experimental features are subject to breaking changes in future minor or patch releases, based on feedback.\n\nIf you have feedback on the design of this feature, please open a GitHub issue to discuss it.", Subject: expr.Range().Ptr(), }) } @@ -232,5 +196,51 @@ func checkModuleExperiments(m *Module) hcl.Diagnostics { } */ + if !m.ActiveExperiments.Has(experiments.ModuleVariableOptionalAttrs) { + for _, v := range m.Variables { + if typeConstraintHasOptionalAttrs(v.ConstraintType) { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Optional object type attributes are experimental", + Detail: "This feature is currently an opt-in experiment, subject to change in future releases based on feedback.\n\nActivate the feature for this module by adding module_variable_optional_attrs to the list of active experiments.", + Subject: v.DeclRange.Ptr(), + }) + } + } + } + return diags } + +func typeConstraintHasOptionalAttrs(ty cty.Type) bool { + if ty == cty.NilType { + // Weird, but we'll just ignore it to avoid crashing. + return false + } + + switch { + case ty.IsPrimitiveType(): + return false + case ty.IsCollectionType(): + return typeConstraintHasOptionalAttrs(ty.ElementType()) + case ty.IsObjectType(): + if len(ty.OptionalAttributes()) != 0 { + return true + } + for _, aty := range ty.AttributeTypes() { + if typeConstraintHasOptionalAttrs(aty) { + return true + } + } + return false + case ty.IsTupleType(): + for _, ety := range ty.TupleElementTypes() { + if typeConstraintHasOptionalAttrs(ety) { + return true + } + } + return false + default: + return false + } +} diff --git a/internal/configs/experiments_test.go b/internal/configs/experiments_test.go index 7d1b9dc4e391..36e84f140efe 100644 --- a/internal/configs/experiments_test.go +++ b/internal/configs/experiments_test.go @@ -22,7 +22,6 @@ func TestExperimentsConfig(t *testing.T) { t.Run("current", func(t *testing.T) { parser := NewParser(nil) - parser.AllowLanguageExperiments(true) mod, diags := parser.LoadConfigDir("testdata/experiments/current") if got, want := len(diags), 1; got != want { t.Fatalf("wrong number of diagnostics %d; want %d", got, want) @@ -31,7 +30,7 @@ func TestExperimentsConfig(t *testing.T) { want := &hcl.Diagnostic{ Severity: hcl.DiagWarning, Summary: `Experimental feature "current" is active`, - Detail: "Experimental features are available only in alpha releases of Terraform and are subject to breaking changes or total removal in later versions, based on feedback. We recommend against using experimental features in production.\n\nIf you have feedback on the design of this feature, please open a GitHub issue to discuss it.", + Detail: "Experimental features are subject to breaking changes in future minor or patch releases, based on feedback.\n\nIf you have feedback on the design of this feature, please open a GitHub issue to discuss it.", Subject: &hcl.Range{ Filename: "testdata/experiments/current/current_experiment.tf", Start: hcl.Pos{Line: 2, Column: 18, Byte: 29}, @@ -50,7 +49,6 @@ func TestExperimentsConfig(t *testing.T) { }) t.Run("concluded", func(t *testing.T) { parser := NewParser(nil) - parser.AllowLanguageExperiments(true) _, diags := parser.LoadConfigDir("testdata/experiments/concluded") if got, want := len(diags), 1; got != want { t.Fatalf("wrong number of diagnostics %d; want %d", got, want) @@ -72,7 +70,6 @@ func TestExperimentsConfig(t *testing.T) { }) t.Run("concluded", func(t *testing.T) { parser := NewParser(nil) - parser.AllowLanguageExperiments(true) _, diags := parser.LoadConfigDir("testdata/experiments/unknown") if got, want := len(diags), 1; got != want { t.Fatalf("wrong number of diagnostics %d; want %d", got, want) @@ -94,7 +91,6 @@ func TestExperimentsConfig(t *testing.T) { }) t.Run("invalid", func(t *testing.T) { parser := NewParser(nil) - parser.AllowLanguageExperiments(true) _, diags := parser.LoadConfigDir("testdata/experiments/invalid") if got, want := len(diags), 1; got != want { t.Fatalf("wrong number of diagnostics %d; want %d", got, want) @@ -114,26 +110,4 @@ func TestExperimentsConfig(t *testing.T) { t.Errorf("wrong error\n%s", diff) } }) - t.Run("disallowed", func(t *testing.T) { - parser := NewParser(nil) - parser.AllowLanguageExperiments(false) // The default situation for release builds - _, diags := parser.LoadConfigDir("testdata/experiments/current") - if got, want := len(diags), 1; got != want { - t.Fatalf("wrong number of diagnostics %d; want %d", got, want) - } - got := diags[0] - want := &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Module uses experimental features`, - Detail: `Experimental features are intended only for gathering early feedback on new language designs, and so are available only in alpha releases of Terraform.`, - Subject: &hcl.Range{ - Filename: "testdata/experiments/current/current_experiment.tf", - Start: hcl.Pos{Line: 2, Column: 3, Byte: 14}, - End: hcl.Pos{Line: 2, Column: 14, Byte: 25}, - }, - } - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("wrong error\n%s", diff) - } - }) } diff --git a/internal/configs/module_call_test.go b/internal/configs/module_call_test.go index af2269dca0bb..9c607f8e34c3 100644 --- a/internal/configs/module_call_test.go +++ b/internal/configs/module_call_test.go @@ -45,7 +45,7 @@ func TestLoadModuleCall(t *testing.T) { { Name: "bar", SourceAddr: addrs.ModuleSourceRegistry{ - Package: addrs.ModuleRegistryPackage{ + PackageAddr: addrs.ModuleRegistryPackage{ Host: addrs.DefaultModuleRegistryHost, Namespace: "hashicorp", Name: "bar", @@ -68,7 +68,7 @@ func TestLoadModuleCall(t *testing.T) { { Name: "baz", SourceAddr: addrs.ModuleSourceRemote{ - Package: addrs.ModulePackage("git::https://example.com/"), + PackageAddr: addrs.ModulePackage("git::https://example.com/"), }, SourceAddrRaw: "git::https://example.com/", SourceSet: true, diff --git a/internal/configs/named_values.go b/internal/configs/named_values.go index 9491224e1ef6..10882ac088f4 100644 --- a/internal/configs/named_values.go +++ b/internal/configs/named_values.go @@ -27,7 +27,6 @@ type Variable struct { // ConstraintType is used for decoding and type conversions, and may // contain nested ObjectWithOptionalAttr types. ConstraintType cty.Type - TypeDefaults *typeexpr.Defaults ParsingMode VariableParsingMode Validations []*CheckRule @@ -103,10 +102,9 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno } if attr, exists := content.Attributes["type"]; exists { - ty, tyDefaults, parseMode, tyDiags := decodeVariableType(attr.Expr) + ty, parseMode, tyDiags := decodeVariableType(attr.Expr) diags = append(diags, tyDiags...) v.ConstraintType = ty - v.TypeDefaults = tyDefaults v.Type = ty.WithoutOptionalAttributesDeep() v.ParsingMode = parseMode } @@ -139,11 +137,6 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno // the type might not be set; we'll catch that during merge. if v.ConstraintType != cty.NilType { var err error - // If the type constraint has defaults, we must apply those - // defaults to the variable default value before type conversion. - if v.TypeDefaults != nil { - val = v.TypeDefaults.Apply(val) - } val, err = convert.Convert(val, v.ConstraintType) if err != nil { diags = append(diags, &hcl.Diagnostic{ @@ -186,7 +179,7 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno return v, diags } -func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, VariableParsingMode, hcl.Diagnostics) { +func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl.Diagnostics) { if exprIsNativeQuotedString(expr) { // If a user provides the pre-0.12 form of variable type argument where // the string values "string", "list" and "map" are accepted, we @@ -197,7 +190,7 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, Vari // in the normal codepath below. val, diags := expr.Value(nil) if diags.HasErrors() { - return cty.DynamicPseudoType, nil, VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags } str := val.AsString() switch str { @@ -208,7 +201,7 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, Vari Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"string\".", Subject: expr.Range().Ptr(), }) - return cty.DynamicPseudoType, nil, VariableParseLiteral, diags + return cty.DynamicPseudoType, VariableParseLiteral, diags case "list": diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -216,7 +209,7 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, Vari Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"list\" and write list(string) instead to explicitly indicate that the list elements are strings.", Subject: expr.Range().Ptr(), }) - return cty.DynamicPseudoType, nil, VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags case "map": diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -224,9 +217,9 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, Vari Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"map\" and write map(string) instead to explicitly indicate that the map elements are strings.", Subject: expr.Range().Ptr(), }) - return cty.DynamicPseudoType, nil, VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags default: - return cty.DynamicPseudoType, nil, VariableParseHCL, hcl.Diagnostics{{ + return cty.DynamicPseudoType, VariableParseHCL, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Invalid legacy variable type hint", Detail: `To provide a full type expression, remove the surrounding quotes and give the type expression directly.`, @@ -241,23 +234,23 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, Vari // elements are consistent. This is the same as list(any) or map(any). switch hcl.ExprAsKeyword(expr) { case "list": - return cty.List(cty.DynamicPseudoType), nil, VariableParseHCL, nil + return cty.List(cty.DynamicPseudoType), VariableParseHCL, nil case "map": - return cty.Map(cty.DynamicPseudoType), nil, VariableParseHCL, nil + return cty.Map(cty.DynamicPseudoType), VariableParseHCL, nil } - ty, typeDefaults, diags := typeexpr.TypeConstraintWithDefaults(expr) + ty, diags := typeexpr.TypeConstraint(expr) if diags.HasErrors() { - return cty.DynamicPseudoType, nil, VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags } switch { case ty.IsPrimitiveType(): // Primitive types use literal parsing. - return ty, typeDefaults, VariableParseLiteral, diags + return ty, VariableParseLiteral, diags default: // Everything else uses HCL parsing - return ty, typeDefaults, VariableParseHCL, diags + return ty, VariableParseHCL, diags } } diff --git a/internal/configs/parser.go b/internal/configs/parser.go index 5a4b81078a2a..2a621b577211 100644 --- a/internal/configs/parser.go +++ b/internal/configs/parser.go @@ -17,13 +17,6 @@ import ( type Parser struct { fs afero.Afero p *hclparse.Parser - - // allowExperiments controls whether we will allow modules to opt in to - // experimental language features. In main code this will be set only - // for alpha releases and some development builds. Test code must decide - // for itself whether to enable it so that tests can cover both the - // allowed and not-allowed situations. - allowExperiments bool } // NewParser creates and returns a new Parser that reads files from the given @@ -105,16 +98,3 @@ func (p *Parser) ForceFileSource(filename string, src []byte) { Bytes: src, }) } - -// AllowLanguageExperiments specifies whether subsequent LoadConfigFile (and -// similar) calls will allow opting in to experimental language features. -// -// If this method is never called for a particular parser, the default behavior -// is to disallow language experiments. -// -// Main code should set this only for alpha or development builds. Test code -// is responsible for deciding for itself whether and how to call this -// method. -func (p *Parser) AllowLanguageExperiments(allowed bool) { - p.allowExperiments = allowed -} diff --git a/internal/configs/parser_config.go b/internal/configs/parser_config.go index 2e08580b5bc9..0281339c6ef0 100644 --- a/internal/configs/parser_config.go +++ b/internal/configs/parser_config.go @@ -45,7 +45,7 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost // We'll load the experiments first because other decoding logic in the // loop below might depend on these experiments. var expDiags hcl.Diagnostics - file.ActiveExperiments, expDiags = sniffActiveExperiments(body, p.allowExperiments) + file.ActiveExperiments, expDiags = sniffActiveExperiments(body) diags = append(diags, expDiags...) content, contentDiags := body.Content(configFileSchema) diff --git a/internal/configs/provider_validation.go b/internal/configs/provider_validation.go index 0cf7378d3639..cf3941e8c1e2 100644 --- a/internal/configs/provider_validation.go +++ b/internal/configs/provider_validation.go @@ -106,7 +106,7 @@ func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConf ), Subject: &req.DeclRange, }) - } else if addrs.IsDefaultProvider(req.Type) { + } else if req.Type.IsDefault() { // Now check for possible implied duplicates, where a provider // block uses a default namespaced provider, but that provider // was required via a different name. @@ -357,7 +357,7 @@ func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConf if !(localName || configAlias || emptyConfig) { // we still allow default configs, so switch to a warning if the incoming provider is a default - if addrs.IsDefaultProvider(providerAddr.Provider) { + if providerAddr.Provider.IsDefault() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagWarning, Summary: "Reference to undefined provider", diff --git a/internal/configs/testdata/invalid-modules/object-optional-attrs-experiment/object-optional-attrs-experiment.tf b/internal/configs/testdata/invalid-modules/object-optional-attrs-experiment/object-optional-attrs-experiment.tf new file mode 100644 index 000000000000..42dad02ab19c --- /dev/null +++ b/internal/configs/testdata/invalid-modules/object-optional-attrs-experiment/object-optional-attrs-experiment.tf @@ -0,0 +1,6 @@ +variable "a" { + type = object({ + # The optional attributes experiment isn't enabled, so this isn't allowed. + a = optional(string) + }) +} diff --git a/internal/configs/testdata/warning-files/object-optional-attrs-experiment.tf b/internal/configs/testdata/warning-files/object-optional-attrs-experiment.tf new file mode 100644 index 000000000000..1645fb0eca70 --- /dev/null +++ b/internal/configs/testdata/warning-files/object-optional-attrs-experiment.tf @@ -0,0 +1,35 @@ +terraform { + experiments = [ + module_variable_optional_attrs, # WARNING: Experimental feature "module_variable_optional_attrs" is active + ] +} + +variable "a" { + type = object({ + foo = optional(string) + }) +} + +variable "b" { + type = list( + object({ + foo = optional(string) + }) + ) +} + +variable "c" { + type = set( + object({ + foo = optional(string) + }) + ) +} + +variable "d" { + type = map( + object({ + foo = optional(string) + }) + ) +} diff --git a/internal/earlyconfig/diagnostics.go b/internal/earlyconfig/diagnostics.go index 15adad56385a..9a6b2663636d 100644 --- a/internal/earlyconfig/diagnostics.go +++ b/internal/earlyconfig/diagnostics.go @@ -76,7 +76,3 @@ func (d wrappedDiagnostic) Source() tfdiags.Source { func (d wrappedDiagnostic) FromExpr() *tfdiags.FromExpr { return nil } - -func (d wrappedDiagnostic) ExtraInfo() interface{} { - return nil -} diff --git a/internal/experiments/experiment.go b/internal/experiments/experiment.go index 41787c679abf..b5d67f58b388 100644 --- a/internal/experiments/experiment.go +++ b/internal/experiments/experiment.go @@ -27,7 +27,7 @@ func init() { registerConcludedExperiment(SuppressProviderSensitiveAttrs, "Provider-defined sensitive attributes are now redacted by default, without enabling an experiment.") registerConcludedExperiment(ConfigDrivenMove, "Declarations of moved resource instances using \"moved\" blocks can now be used by default, without enabling an experiment.") registerConcludedExperiment(PreconditionsPostconditions, "Condition blocks can now be used by default, without enabling an experiment.") - registerConcludedExperiment(ModuleVariableOptionalAttrs, "The final feature corresponding to this experiment differs from the experimental form and is available in the Terraform language from Terraform v1.3.0 onwards.") + registerCurrentExperiment(ModuleVariableOptionalAttrs) } // GetCurrent takes an experiment name and returns the experiment value @@ -92,7 +92,6 @@ var currentExperiments = make(Set) // Members of this map are registered in the init function above. var concludedExperiments = make(map[Experiment]string) -//lint:ignore U1000 No experiments are active func registerCurrentExperiment(exp Experiment) { currentExperiments.Add(exp) } diff --git a/internal/getmodules/getter.go b/internal/getmodules/getter.go index 82ea599afcb9..95f334762e64 100644 --- a/internal/getmodules/getter.go +++ b/internal/getmodules/getter.go @@ -83,9 +83,8 @@ var goGetterGetters = map[string]getter.Getter{ var getterHTTPClient = cleanhttp.DefaultClient() var getterHTTPGetter = &getter.HttpGetter{ - Client: getterHTTPClient, - Netrc: true, - XTerraformGetLimit: 10, + Client: getterHTTPClient, + Netrc: true, } // A reusingGetter is a helper for the module installer that remembers diff --git a/internal/getproviders/didyoumean.go b/internal/getproviders/didyoumean.go index e31ba20e4195..d888ccc08a4b 100644 --- a/internal/getproviders/didyoumean.go +++ b/internal/getproviders/didyoumean.go @@ -40,7 +40,7 @@ import ( // renaming suggestion even if one would've been available for a completed // request. func MissingProviderSuggestion(ctx context.Context, addr addrs.Provider, source Source, reqs Requirements) addrs.Provider { - if !addrs.IsDefaultProvider(addr) { + if !addr.IsDefault() { return addr } diff --git a/internal/getproviders/errors.go b/internal/getproviders/errors.go index 7d2720c0f8cf..cd110a2b1f33 100644 --- a/internal/getproviders/errors.go +++ b/internal/getproviders/errors.go @@ -5,7 +5,6 @@ import ( "net/url" svchost "github.com/hashicorp/terraform-svchost" - "github.com/hashicorp/terraform/internal/addrs" ) diff --git a/internal/getproviders/registry_client.go b/internal/getproviders/registry_client.go index 5da2a83ca2b2..428d362e2c55 100644 --- a/internal/getproviders/registry_client.go +++ b/internal/getproviders/registry_client.go @@ -437,7 +437,7 @@ func (c *registryClient) getFile(url *url.URL) ([]byte, error) { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s returned from %s", resp.Status, HostFromRequest(resp.Request)) + return nil, fmt.Errorf("%s returned from %s", resp.Status, resp.Request.Host) } data, err := ioutil.ReadAll(resp.Body) @@ -478,7 +478,7 @@ func maxRetryErrorHandler(resp *http.Response, err error, numTries int) (*http.R // both response and error. var errMsg string if resp != nil { - errMsg = fmt.Sprintf(": %s returned from %s", resp.Status, HostFromRequest(resp.Request)) + errMsg = fmt.Sprintf(": %s returned from %s", resp.Status, resp.Request.Host) } else if err != nil { errMsg = fmt.Sprintf(": %s", err) } @@ -492,22 +492,6 @@ func maxRetryErrorHandler(resp *http.Response, err error, numTries int) (*http.R return resp, fmt.Errorf("the request failed, please try again later%s", errMsg) } -// HostFromRequest extracts host the same way net/http Request.Write would, -// accounting for empty Request.Host -func HostFromRequest(req *http.Request) string { - if req.Host != "" { - return req.Host - } - if req.URL != nil { - return req.URL.Host - } - - // this should never happen and if it does - // it will be handled as part of Request.Write() - // https://cs.opensource.google/go/go/+/refs/tags/go1.18.4:src/net/http/request.go;l=574 - return "" -} - // configureRequestTimeout configures the registry client request timeout from // environment variables func configureRequestTimeout() { diff --git a/internal/grpcwrap/provider.go b/internal/grpcwrap/provider.go index 170cea63881a..4671606c266f 100644 --- a/internal/grpcwrap/provider.go +++ b/internal/grpcwrap/provider.go @@ -59,10 +59,6 @@ func (p *provider) GetSchema(_ context.Context, req *tfplugin5.GetProviderSchema } } - resp.ServerCapabilities = &tfplugin5.GetProviderSchema_ServerCapabilities{ - PlanDestroy: p.schema.ServerCapabilities.PlanDestroy, - } - // include any diagnostics from the original GetSchema call resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics) diff --git a/internal/grpcwrap/provider6.go b/internal/grpcwrap/provider6.go index af287d0f5444..dd82e0956c9c 100644 --- a/internal/grpcwrap/provider6.go +++ b/internal/grpcwrap/provider6.go @@ -59,10 +59,6 @@ func (p *provider6) GetProviderSchema(_ context.Context, req *tfplugin6.GetProvi } } - resp.ServerCapabilities = &tfplugin6.GetProviderSchema_ServerCapabilities{ - PlanDestroy: p.schema.ServerCapabilities.PlanDestroy, - } - // include any diagnostics from the original GetSchema call resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics) diff --git a/internal/httpclient/useragent.go b/internal/httpclient/useragent.go index d6aba31d4041..536703c6b90d 100644 --- a/internal/httpclient/useragent.go +++ b/internal/httpclient/useragent.go @@ -13,7 +13,7 @@ import ( const userAgentFormat = "Terraform/%s" const uaEnvVar = "TF_APPEND_USER_AGENT" -// Deprecated: Use TerraformUserAgent(version) instead +// Deprecated: Use UserAgent(version) instead func UserAgentString() string { ua := fmt.Sprintf(userAgentFormat, version.Version) diff --git a/internal/initwd/module_install.go b/internal/initwd/module_install.go index adc5dec5ec1e..e9ee4953183c 100644 --- a/internal/initwd/module_install.go +++ b/internal/initwd/module_install.go @@ -306,7 +306,7 @@ func (i *ModuleInstaller) installLocalModule(req *earlyconfig.ModuleRequest, key func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyconfig.ModuleRequest, key string, instPath string, addr addrs.ModuleSourceRegistry, manifest modsdir.Manifest, hooks ModuleInstallHooks, fetcher *getmodules.PackageFetcher) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - hostname := addr.Package.Host + hostname := addr.PackageAddr.Host reg := i.reg var resp *response.ModuleVersions var exists bool @@ -314,7 +314,7 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc // A registry entry isn't _really_ a module package, but we'll pretend it's // one for the sake of this reporting by just trimming off any source // directory. - packageAddr := addr.Package + packageAddr := addr.PackageAddr // Our registry client is still using the legacy model of addresses, so // we'll shim it here for now. @@ -469,9 +469,9 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc dlAddr := i.registryPackageSources[moduleAddr] - log.Printf("[TRACE] ModuleInstaller: %s %s %s is available at %q", key, packageAddr, latestMatch, dlAddr.Package) + log.Printf("[TRACE] ModuleInstaller: %s %s %s is available at %q", key, packageAddr, latestMatch, dlAddr.PackageAddr) - err := fetcher.FetchPackage(ctx, instPath, dlAddr.Package.String()) + err := fetcher.FetchPackage(ctx, instPath, dlAddr.PackageAddr.String()) if errors.Is(err, context.Canceled) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -494,7 +494,7 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc return nil, nil, diags } - log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, dlAddr.Package, instPath) + log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, dlAddr.PackageAddr, instPath) // Incorporate any subdir information from the original path into the // address returned by the registry in order to find the final directory @@ -540,7 +540,7 @@ func (i *ModuleInstaller) installGoGetterModule(ctx context.Context, req *earlyc // Report up to the caller that we're about to start downloading. addr := req.SourceAddr.(addrs.ModuleSourceRemote) - packageAddr := addr.Package + packageAddr := addr.PackageAddr hooks.Download(key, packageAddr.String(), nil) if len(req.VersionConstraints) != 0 { @@ -587,11 +587,8 @@ func (i *ModuleInstaller) installGoGetterModule(ctx context.Context, req *earlyc return nil, diags } - modDir, err := getmodules.ExpandSubdirGlobs(instPath, addr.Subdir) - if err != nil { - diags = diags.Append(err) - return nil, diags - } + subDir := filepath.FromSlash(addr.Subdir) + modDir := filepath.Join(instPath, subDir) log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, addr, modDir) @@ -761,7 +758,7 @@ func splitAddrSubdir(addr addrs.ModuleSource) (string, string) { addr.Subdir = "" return addr.String(), subDir case addrs.ModuleSourceRemote: - return addr.Package.String(), addr.Subdir + return addr.PackageAddr.String(), addr.Subdir case nil: panic("splitAddrSubdir on nil addrs.ModuleSource") default: diff --git a/internal/lang/funcs/defaults.go b/internal/lang/funcs/defaults.go new file mode 100644 index 000000000000..b91ae9395f77 --- /dev/null +++ b/internal/lang/funcs/defaults.go @@ -0,0 +1,288 @@ +package funcs + +import ( + "fmt" + + "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/convert" + "github.com/zclconf/go-cty/cty/function" +) + +// DefaultsFunc is a helper function for substituting default values in +// place of null values in a given data structure. +// +// See the documentation for function Defaults for more information. +var DefaultsFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "input", + Type: cty.DynamicPseudoType, + AllowNull: true, + AllowMarked: true, + }, + { + Name: "defaults", + Type: cty.DynamicPseudoType, + AllowMarked: true, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + // The result type is guaranteed to be the same as the input type, + // since all we're doing is replacing null values with non-null + // values of the same type. + retType := args[0].Type() + defaultsType := args[1].Type() + + // This function is aimed at filling in object types or collections + // of object types where some of the attributes might be null, so + // it doesn't make sense to use a primitive type directly with it. + // (The "coalesce" function may be appropriate for such cases.) + if retType.IsPrimitiveType() { + // This error message is a bit of a fib because we can actually + // apply defaults to tuples too, but we expect that to be so + // unusual as to not be worth mentioning here, because mentioning + // it would require using some less-well-known Terraform language + // terminology in the message (tuple types, structural types). + return cty.DynamicPseudoType, function.NewArgErrorf(1, "only object types and collections of object types can have defaults applied") + } + + defaultsPath := make(cty.Path, 0, 4) // some capacity so that most structures won't reallocate + if err := defaultsAssertSuitableFallback(retType, defaultsType, defaultsPath); err != nil { + errMsg := tfdiags.FormatError(err) // add attribute path prefix + return cty.DynamicPseudoType, function.NewArgErrorf(1, "%s", errMsg) + } + + return retType, nil + }, + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + if args[0].Type().HasDynamicTypes() { + // If the types our input object aren't known yet for some reason + // then we'll defer all of our work here, because our + // interpretation of the defaults depends on the types in + // the input. + return cty.UnknownVal(retType), nil + } + + v := defaultsApply(args[0], args[1]) + return v, nil + }, +}) + +func defaultsApply(input, fallback cty.Value) cty.Value { + wantTy := input.Type() + + umInput, inputMarks := input.Unmark() + umFb, fallbackMarks := fallback.Unmark() + + // If neither are known, we very conservatively return an unknown value + // with the union of marks on both input and default. + if !(umInput.IsKnown() && umFb.IsKnown()) { + return cty.UnknownVal(wantTy).WithMarks(inputMarks).WithMarks(fallbackMarks) + } + + // For the rest of this function we're assuming that the given defaults + // will always be valid, because we expect to have caught any problems + // during the type checking phase. Any inconsistencies that reach here are + // therefore considered to be implementation bugs, and so will panic. + + // Our strategy depends on the kind of type we're working with. + switch { + case wantTy.IsPrimitiveType(): + // For leaf primitive values the rule is relatively simple: use the + // input if it's non-null, or fallback if input is null. + if !umInput.IsNull() { + return input + } + v, err := convert.Convert(umFb, wantTy) + if err != nil { + // Should not happen because we checked in defaultsAssertSuitableFallback + panic(err.Error()) + } + return v.WithMarks(fallbackMarks) + + case wantTy.IsObjectType(): + // For structural types, a null input value must be passed through. We + // do not apply default values for missing optional structural values, + // only their contents. + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if umInput.IsNull() || umFb.IsNull() { + return input + } + atys := wantTy.AttributeTypes() + ret := map[string]cty.Value{} + for attr, aty := range atys { + inputSub := umInput.GetAttr(attr) + fallbackSub := cty.NullVal(aty) + if umFb.Type().HasAttribute(attr) { + fallbackSub = umFb.GetAttr(attr) + } + ret[attr] = defaultsApply(inputSub.WithMarks(inputMarks), fallbackSub.WithMarks(fallbackMarks)) + } + return cty.ObjectVal(ret) + + case wantTy.IsTupleType(): + // For structural types, a null input value must be passed through. We + // do not apply default values for missing optional structural values, + // only their contents. + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if umInput.IsNull() || umFb.IsNull() { + return input + } + + l := wantTy.Length() + ret := make([]cty.Value, l) + for i := 0; i < l; i++ { + inputSub := umInput.Index(cty.NumberIntVal(int64(i))) + fallbackSub := umFb.Index(cty.NumberIntVal(int64(i))) + ret[i] = defaultsApply(inputSub.WithMarks(inputMarks), fallbackSub.WithMarks(fallbackMarks)) + } + return cty.TupleVal(ret) + + case wantTy.IsCollectionType(): + // For collection types we apply a single fallback value to each + // element of the input collection, because in the situations this + // function is intended for we assume that the number of elements + // is the caller's decision, and so we'll just apply the same defaults + // to all of the elements. + ety := wantTy.ElementType() + switch { + case wantTy.IsMapType(): + newVals := map[string]cty.Value{} + + if !umInput.IsNull() { + for it := umInput.ElementIterator(); it.Next(); { + k, v := it.Element() + newVals[k.AsString()] = defaultsApply(v.WithMarks(inputMarks), fallback.WithMarks(fallbackMarks)) + } + } + + if len(newVals) == 0 { + return cty.MapValEmpty(ety) + } + return cty.MapVal(newVals) + case wantTy.IsListType(), wantTy.IsSetType(): + var newVals []cty.Value + + if !umInput.IsNull() { + for it := umInput.ElementIterator(); it.Next(); { + _, v := it.Element() + newV := defaultsApply(v.WithMarks(inputMarks), fallback.WithMarks(fallbackMarks)) + newVals = append(newVals, newV) + } + } + + if len(newVals) == 0 { + if wantTy.IsSetType() { + return cty.SetValEmpty(ety) + } + return cty.ListValEmpty(ety) + } + if wantTy.IsSetType() { + return cty.SetVal(newVals) + } + return cty.ListVal(newVals) + default: + // There are no other collection types, so this should not happen + panic(fmt.Sprintf("invalid collection type %#v", wantTy)) + } + default: + // We should've caught anything else in defaultsAssertSuitableFallback, + // so this should not happen. + panic(fmt.Sprintf("invalid target type %#v", wantTy)) + } +} + +func defaultsAssertSuitableFallback(wantTy, fallbackTy cty.Type, fallbackPath cty.Path) error { + // If the type we want is a collection type then we need to keep peeling + // away collection type wrappers until we find the non-collection-type + // that's underneath, which is what the fallback will actually be applied + // to. + inCollection := false + for wantTy.IsCollectionType() { + wantTy = wantTy.ElementType() + inCollection = true + } + + switch { + case wantTy.IsPrimitiveType(): + // The fallback is valid if it's equal to or convertible to what we want. + if fallbackTy.Equals(wantTy) { + return nil + } + conversion := convert.GetConversion(fallbackTy, wantTy) + if conversion == nil { + msg := convert.MismatchMessage(fallbackTy, wantTy) + return fallbackPath.NewErrorf("invalid default value for %s: %s", wantTy.FriendlyName(), msg) + } + return nil + case wantTy.IsObjectType(): + if !fallbackTy.IsObjectType() { + if inCollection { + return fallbackPath.NewErrorf("the default value for a collection of an object type must itself be an object type, not %s", fallbackTy.FriendlyName()) + } + return fallbackPath.NewErrorf("the default value for an object type must itself be an object type, not %s", fallbackTy.FriendlyName()) + } + for attr, wantAty := range wantTy.AttributeTypes() { + if !fallbackTy.HasAttribute(attr) { + continue // it's always okay to not have a default value + } + fallbackSubpath := fallbackPath.GetAttr(attr) + fallbackSubTy := fallbackTy.AttributeType(attr) + err := defaultsAssertSuitableFallback(wantAty, fallbackSubTy, fallbackSubpath) + if err != nil { + return err + } + } + for attr := range fallbackTy.AttributeTypes() { + if !wantTy.HasAttribute(attr) { + fallbackSubpath := fallbackPath.GetAttr(attr) + return fallbackSubpath.NewErrorf("target type does not expect an attribute named %q", attr) + } + } + return nil + case wantTy.IsTupleType(): + if !fallbackTy.IsTupleType() { + if inCollection { + return fallbackPath.NewErrorf("the default value for a collection of a tuple type must itself be a tuple type, not %s", fallbackTy.FriendlyName()) + } + return fallbackPath.NewErrorf("the default value for a tuple type must itself be a tuple type, not %s", fallbackTy.FriendlyName()) + } + wantEtys := wantTy.TupleElementTypes() + fallbackEtys := fallbackTy.TupleElementTypes() + if got, want := len(wantEtys), len(fallbackEtys); got != want { + return fallbackPath.NewErrorf("the default value for a tuple type of length %d must also have length %d, not %d", want, want, got) + } + for i := 0; i < len(wantEtys); i++ { + fallbackSubpath := fallbackPath.IndexInt(i) + wantSubTy := wantEtys[i] + fallbackSubTy := fallbackEtys[i] + err := defaultsAssertSuitableFallback(wantSubTy, fallbackSubTy, fallbackSubpath) + if err != nil { + return err + } + } + return nil + default: + // No other types are supported right now. + return fallbackPath.NewErrorf("cannot apply defaults to %s", wantTy.FriendlyName()) + } +} + +// Defaults is a helper function for substituting default values in +// place of null values in a given data structure. +// +// This is primarily intended for use with a module input variable that +// has an object type constraint (or a collection thereof) that has optional +// attributes, so that the receiver of a value that omits those attributes +// can insert non-null default values in place of the null values caused by +// omitting the attributes. +func Defaults(input, defaults cty.Value) (cty.Value, error) { + return DefaultsFunc.Call([]cty.Value{input, defaults}) +} diff --git a/internal/lang/funcs/defaults_test.go b/internal/lang/funcs/defaults_test.go new file mode 100644 index 000000000000..e40163265a19 --- /dev/null +++ b/internal/lang/funcs/defaults_test.go @@ -0,0 +1,648 @@ +package funcs + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestDefaults(t *testing.T) { + tests := []struct { + Input, Defaults cty.Value + Want cty.Value + WantErr string + }{ + { // When *either* input or default are unknown, an unknown is returned. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + }, + { + // When *either* input or default are unknown, an unknown is + // returned with marks from both input and defaults. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("marked"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String).Mark("marked"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hey"), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hey"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{}), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{}), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + WantErr: `.a: target type does not expect an attribute named "a"`, + }, + + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("hey"), + cty.StringVal("hello"), + }), + }), + }, + { + // Using defaults with single set elements is a pretty + // odd thing to do, but this behavior is just here because + // it generalizes from how we handle collections. It's + // tested only to ensure it doesn't change accidentally + // in future. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.StringVal("hey"), + cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "x": cty.NullVal(cty.String), + "y": cty.StringVal("hey"), + "z": cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "x": cty.StringVal("hello"), + "y": cty.StringVal("hey"), + "z": cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + }, + { + Input: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + Want: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("boop"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("boop"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + // After applying defaults, the one with a null value + // coalesced with the one with a non-null value, + // and so there's only one left. + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + "beep": cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + "beep": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + WantErr: `.a: the default value for a collection of an object type must itself be an object type, not string`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + // The default value for a list must be a single value + // of the list's element type which provides defaults + // for each element separately, so the default for a + // list of string should be just a single string, not + // a list of string. + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + WantErr: `.a: invalid default value for string: string required`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + WantErr: `.a: the default value for a tuple type must itself be a tuple type, not string`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hello 1"), + cty.StringVal("hello 2"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hey"), + cty.StringVal("hello 2"), + }), + }), + }, + { + // There's no reason to use this function for plain primitive + // types, because the "default" argument in a variable definition + // already has the equivalent behavior. This function is only + // to deal with the situation of a complex-typed variable where + // only parts of the data structure are optional. + Input: cty.NullVal(cty.String), + Defaults: cty.StringVal("hello"), + WantErr: `only object types and collections of object types can have defaults applied`, + }, + // When applying default values to structural types, null objects or + // tuples in the input should be passed through. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "x": cty.String, + "y": cty.String, + })), + "b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "x": cty.StringVal("hello"), + "y": cty.StringVal("there"), + }), + "b": cty.TupleVal([]cty.Value{ + cty.StringVal("how are"), + cty.StringVal("you?"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "x": cty.String, + "y": cty.String, + })), + "b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})), + }), + }, + // When applying default values to structural types, we permit null + // values in the defaults, and just pass through the input value. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("tada"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.StringVal("tada"), + }), + }, + // When applying default values to collection types, null collections in the + // input should result in empty collections in the output. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.String)), + "b": cty.NullVal(cty.Map(cty.String)), + "c": cty.NullVal(cty.Set(cty.String)), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + "b": cty.StringVal("hi"), + "c": cty.StringVal("greetings"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListValEmpty(cty.String), + "b": cty.MapValEmpty(cty.String), + "c": cty.SetValEmpty(cty.String), + }), + }, + // When specifying fallbacks, we allow mismatched primitive attribute + // types so long as a safe conversion is possible. This means that we + // can accept number or boolean values for string attributes. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + "b": cty.NullVal(cty.String), + "c": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(5), + "b": cty.True, + "c": cty.StringVal("greetings"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("5"), + "b": cty.StringVal("true"), + "c": cty.StringVal("greetings"), + }), + }, + // Fallbacks with mismatched primitive attribute types which do not + // have safe conversions must not pass the suitable fallback check, + // even if unsafe conversion would be possible. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Bool), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("5"), + }), + WantErr: ".a: invalid default value for bool: bool required", + }, + // marks: we should preserve marks from both input value and defaults as leafily as possible + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("world"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("world"), + }), + }, + { // "unused" marks don't carry over + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String).Mark("a"), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + }, + { // Marks on tuples remain attached to individual elements + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hello 1"), + cty.StringVal("hello 2"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hey").Mark("input"), + cty.StringVal("hello 2"), + }), + }), + }, + { // Marks from list elements + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello 0").Mark("fallback"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hey").Mark("input"), + cty.StringVal("hello 0").Mark("fallback"), + }), + }), + }, + { + // Sets don't allow individually-marked elements, so the marks + // end up aggregating on the set itself anyway in this case. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello 0").Mark("fallback"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hey"), + cty.StringVal("hello 0"), + }).WithMarks(cty.NewValueMarks("fallback", "input")), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("beep"), + }).Mark("boop"), + // This is the least-intuitive case. The mark "boop" is attached to + // the default object, not it's elements, but both marks end up + // aggregated on the list element. + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello").WithMarks(cty.NewValueMarks("beep", "boop")), + }), + }), + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("defaults(%#v, %#v)", test.Input, test.Defaults), func(t *testing.T) { + got, gotErr := Defaults(test.Input, test.Defaults) + + if test.WantErr != "" { + if gotErr == nil { + t.Fatalf("unexpected success\nwant error: %s", test.WantErr) + } + if got, want := gotErr.Error(), test.WantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } else if gotErr != nil { + t.Fatalf("unexpected error\ngot: %s", gotErr.Error()) + } + + if !test.Want.RawEquals(got) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} diff --git a/internal/lang/funcs/string.go b/internal/lang/funcs/string.go index 9ef709c7fb09..ab6da72778ec 100644 --- a/internal/lang/funcs/string.go +++ b/internal/lang/funcs/string.go @@ -8,58 +8,6 @@ import ( "github.com/zclconf/go-cty/cty/function" ) -// StartsWithFunc constructs a function that checks if a string starts with -// a specific prefix using strings.HasPrefix -var StartsWithFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "prefix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - str := args[0].AsString() - prefix := args[1].AsString() - - if strings.HasPrefix(str, prefix) { - return cty.True, nil - } - - return cty.False, nil - }, -}) - -// EndsWithFunc constructs a function that checks if a string ends with -// a specific suffix using strings.HasSuffix -var EndsWithFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "suffix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - str := args[0].AsString() - suffix := args[1].AsString() - - if strings.HasSuffix(str, suffix) { - return cty.True, nil - } - - return cty.False, nil - }, -}) - // ReplaceFunc constructs a function that searches a given string for another // given substring, and replaces each occurence with a given replacement string. var ReplaceFunc = function.New(&function.Spec{ diff --git a/internal/lang/functions.go b/internal/lang/functions.go index ee520965caeb..f367f6cf7a25 100644 --- a/internal/lang/functions.go +++ b/internal/lang/functions.go @@ -56,10 +56,10 @@ func (s *Scope) Functions() map[string]function.Function { "concat": stdlib.ConcatFunc, "contains": stdlib.ContainsFunc, "csvdecode": stdlib.CSVDecodeFunc, + "defaults": s.experimentalFunction(experiments.ModuleVariableOptionalAttrs, funcs.DefaultsFunc), "dirname": funcs.DirnameFunc, "distinct": stdlib.DistinctFunc, "element": stdlib.ElementFunc, - "endswith": funcs.EndsWithFunc, "chunklist": stdlib.ChunklistFunc, "file": funcs.MakeFileFunc(s.BaseDir, false), "fileexists": funcs.MakeFileExistsFunc(s.BaseDir), @@ -116,7 +116,6 @@ func (s *Scope) Functions() map[string]function.Function { "slice": stdlib.SliceFunc, "sort": stdlib.SortFunc, "split": stdlib.SplitFunc, - "startswith": funcs.StartsWithFunc, "strrev": stdlib.ReverseFunc, "substr": stdlib.SubstrFunc, "sum": funcs.SumFunc, @@ -175,8 +174,6 @@ func (s *Scope) Functions() map[string]function.Function { // the recieving scope. If so, it will return the given function verbatim. // If not, it will return a placeholder function that just returns an // error explaining that the function requires the experiment to be enabled. -// -//lint:ignore U1000 Ignore unused function error for now func (s *Scope) experimentalFunction(experiment experiments.Experiment, fn function.Function) function.Function { if s.activeExperiments.Has(experiment) { return fn diff --git a/internal/lang/functions_test.go b/internal/lang/functions_test.go index f2a6f738c468..9a69432bd1b3 100644 --- a/internal/lang/functions_test.go +++ b/internal/lang/functions_test.go @@ -291,6 +291,18 @@ func TestFunctions(t *testing.T) { }, }, + "defaults": { + // This function is pretty specialized and so this is mainly + // just a test that it is defined at all. See the function's + // own unit tests for more interesting test cases. + { + `defaults({a: 4}, {a: 5})`, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(4), + }), + }, + }, + "dirname": { { `dirname("testdata/hello.txt")`, @@ -314,47 +326,6 @@ func TestFunctions(t *testing.T) { }, }, - "endswith": { - { - `endswith("hello world", "world")`, - cty.True, - }, - { - `endswith("hello world", "hello")`, - cty.False, - }, - { - `endswith("hello world", "")`, - cty.True, - // Completely empty suffix value ( "" ) - // will always evaluate to true for all strings. - }, - { - `endswith("hello world", " ")`, - cty.False, - }, - { - `endswith("", "")`, - cty.True, - }, - { - `endswith("", " ")`, - cty.False, - }, - { - `endswith(" ", "")`, - cty.True, - }, - { - `endswith("", "hello")`, - cty.False, - }, - { - `endswith(" ", "hello")`, - cty.False, - }, - }, - "file": { { `file("hello.txt")`, @@ -857,47 +828,6 @@ func TestFunctions(t *testing.T) { }, }, - "startswith": { - { - `startswith("hello world", "hello")`, - cty.True, - }, - { - `startswith("hello world", "world")`, - cty.False, - }, - { - `startswith("hello world", "")`, - cty.True, - // Completely empty prefix value ( "" ) - // will always evaluate to true for all strings. - }, - { - `startswith("hello world", " ")`, - cty.False, - }, - { - `startswith("", "")`, - cty.True, - }, - { - `startswith("", " ")`, - cty.False, - }, - { - `startswith(" ", "")`, - cty.True, - }, - { - `startswith("", "hello")`, - cty.False, - }, - { - `startswith(" ", "hello")`, - cty.False, - }, - }, - "strrev": { { `strrev("hello world")`, diff --git a/internal/moduletest/provider.go b/internal/moduletest/provider.go index 52601d9d280f..d27a07ae27a5 100644 --- a/internal/moduletest/provider.go +++ b/internal/moduletest/provider.go @@ -240,16 +240,9 @@ func (p *Provider) UpgradeResourceState(req providers.UpgradeResourceStateReques // PlanResourceChange takes the current state and proposed state of a // resource, and returns the planned final state. -func (p *Provider) PlanResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { +func (p *Provider) PlanResourceChange(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { log.Print("[TRACE] moduletest.Provider: PlanResourceChange") - // this is a destroy plan, - if req.ProposedNewState.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - var res providers.PlanResourceChangeResponse if req.TypeName != "test_assertions" { // we only have one resource type res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) diff --git a/internal/plans/changes.go b/internal/plans/changes.go index b07d4f070155..d79ef0fcf473 100644 --- a/internal/plans/changes.go +++ b/internal/plans/changes.go @@ -1,10 +1,9 @@ package plans import ( - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/states" + "github.com/zclconf/go-cty/cty" ) // Changes describes various actions that Terraform will attempt to take if @@ -117,23 +116,6 @@ func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { return nil } -// RootOutputValues returns planned changes for all outputs of the root module. -func (c *Changes) RootOutputValues() []*OutputChangeSrc { - var res []*OutputChangeSrc - - for _, oc := range c.Outputs { - // we can't evaluate root module outputs - if !oc.Addr.Module.Equal(addrs.RootModuleInstance) { - continue - } - - res = append(res, oc) - - } - - return res -} - // OutputValues returns planned changes for all outputs for all module // instances that reside in the parent path. Returns nil if no changes are // planned. diff --git a/internal/plans/changes_sync.go b/internal/plans/changes_sync.go index 95920c1c6b98..eec7a8268823 100644 --- a/internal/plans/changes_sync.go +++ b/internal/plans/changes_sync.go @@ -185,22 +185,6 @@ func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeS return cs.changes.OutputValue(addr) } -// GetRootOutputChanges searches the set of output changes for any that reside -// the root module. If no such changes exist, nil is returned. -// -// The returned objects are a deep copy of the change recorded in the plan, so -// callers may mutate them although it's generally better (less confusing) to -// treat planned changes as immutable after they've been initially constructed. -func (cs *ChangesSync) GetRootOutputChanges() []*OutputChangeSrc { - if cs == nil { - panic("GetRootOutputChanges on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - return cs.changes.RootOutputValues() -} - // GetOutputChanges searches the set of output changes for any that reside in // module instances beneath the given module. If no changes exist, nil // is returned. diff --git a/internal/plugin/grpc_provider.go b/internal/plugin/grpc_provider.go index 2c4f2c036027..6f78c79b85ab 100644 --- a/internal/plugin/grpc_provider.go +++ b/internal/plugin/grpc_provider.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/internal/logging" "github.com/hashicorp/terraform/internal/plugin/convert" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto "github.com/hashicorp/terraform/internal/tfplugin5" ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/zclconf/go-cty/cty/msgpack" @@ -77,6 +78,39 @@ func (p *GRPCProvider) getSchema() providers.GetProviderSchemaResponse { return p.GetProviderSchema() } +// getResourceSchema is a helper to extract the schema for a resource, and +// panics if the schema is not available. +func (p *GRPCProvider) getResourceSchema(name string) (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + resSchema, ok := schema.ResourceTypes[name] + if !ok { + schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown resource type " + name)) + } + return resSchema, schema.Diagnostics +} + +// gettDatasourceSchema is a helper to extract the schema for a datasource, and +// panics if that schema is not available. +func (p *GRPCProvider) getDatasourceSchema(name string) (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + if schema.Diagnostics.HasErrors() { + return providers.Schema{}, schema.Diagnostics + } + + dataSchema, ok := schema.DataSources[name] + if !ok { + schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source " + name)) + } + return dataSchema, schema.Diagnostics +} + +// getProviderMetaSchema is a helper to extract the schema for the meta info +// defined for a provider, +func (p *GRPCProvider) getProviderMetaSchema() (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + return schema.ProviderMeta, schema.Diagnostics +} + func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) { logger.Trace("GRPCProvider: GetProviderSchema") p.mu.Lock() @@ -93,10 +127,7 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp // grpc response size limit is 4MB. 64MB should cover most any use case, and // if we get providers nearing that we may want to consider a finer-grained // API to fetch individual resource schemas. - // Note: this option is marked as EXPERIMENTAL in the grpc API. We keep - // this for compatibility, but recent providers all set the max message - // size much higher on the server side, which is the supported method for - // determining payload size. + // Note: this option is marked as EXPERIMENTAL in the grpc API. const maxRecvSize = 64 << 20 protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize}) if err != nil { @@ -130,10 +161,6 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp resp.DataSources[name] = convert.ProtoToProviderSchema(data) } - if protoResp.ServerCapabilities != nil { - resp.ServerCapabilities.PlanDestroy = protoResp.ServerCapabilities.PlanDestroy - } - p.schemas = resp return resp @@ -143,11 +170,6 @@ func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfig logger.Trace("GRPCProvider: ValidateProviderConfig") schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - ty := schema.Provider.Block.ImpliedType() mp, err := msgpack.Marshal(r.Config, ty) @@ -180,15 +202,9 @@ func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfig func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { logger.Trace("GRPCProvider: ValidateResourceConfig") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resourceSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resourceSchema, diags := p.getResourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -216,15 +232,9 @@ func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfig func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { logger.Trace("GRPCProvider: ValidateDataResourceConfig") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - dataSchema, ok := schema.DataSources[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName)) + dataSchema, diags := p.getDatasourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -251,15 +261,9 @@ func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResour func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { logger.Trace("GRPCProvider: UpgradeResourceState") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -299,10 +303,6 @@ func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) ( logger.Trace("GRPCProvider: ConfigureProvider") schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } var mp []byte @@ -346,20 +346,14 @@ func (p *GRPCProvider) Stop() error { func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { logger.Trace("GRPCProvider: ReadResource") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type " + r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - metaSchema := schema.ProviderMeta - mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) @@ -402,26 +396,11 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { logger.Trace("GRPCProvider: PlanResourceChange") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) - return resp - } - - metaSchema := schema.ProviderMeta - capabilities := schema.ServerCapabilities - - // If the provider doesn't support planning a destroy operation, we can - // return immediately. - if r.ProposedNewState.IsNull() && !capabilities.PlanDestroy { - resp.PlannedState = r.ProposedNewState - resp.PlannedPrivate = r.PriorPrivate + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -488,20 +467,14 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { logger.Trace("GRPCProvider: ApplyResourceChange") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - metaSchema := schema.ProviderMeta - priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) @@ -559,12 +532,6 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { logger.Trace("GRPCProvider: ImportResourceState") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - protoReq := &proto.ImportResourceState_Request{ TypeName: r.TypeName, Id: r.ID, @@ -583,10 +550,10 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques Private: imported.Private, } - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) - continue + resSchema, diags := p.getResourceSchema(resource.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) + return resp } state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType()) @@ -604,19 +571,14 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { logger.Trace("GRPCProvider: ReadDataSource") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics + dataSchema, diags := p.getDatasourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - dataSchema, ok := schema.DataSources[r.TypeName] - if !ok { - schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName)) - } - - metaSchema := schema.ProviderMeta - config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) diff --git a/internal/plugin6/grpc_provider.go b/internal/plugin6/grpc_provider.go index c6530e075468..90533a5cff17 100644 --- a/internal/plugin6/grpc_provider.go +++ b/internal/plugin6/grpc_provider.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/internal/logging" "github.com/hashicorp/terraform/internal/plugin6/convert" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto6 "github.com/hashicorp/terraform/internal/tfplugin6" ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/zclconf/go-cty/cty/msgpack" @@ -84,6 +85,39 @@ func (p *GRPCProvider) getSchema() providers.GetProviderSchemaResponse { return p.GetProviderSchema() } +// getResourceSchema is a helper to extract the schema for a resource, and +// panics if the schema is not available. +func (p *GRPCProvider) getResourceSchema(name string) (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + resSchema, ok := schema.ResourceTypes[name] + if !ok { + schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown resource type " + name)) + } + return resSchema, schema.Diagnostics +} + +// gettDatasourceSchema is a helper to extract the schema for a datasource, and +// panics if that schema is not available. +func (p *GRPCProvider) getDatasourceSchema(name string) (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + if schema.Diagnostics.HasErrors() { + return providers.Schema{}, schema.Diagnostics + } + + dataSchema, ok := schema.DataSources[name] + if !ok { + schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source " + name)) + } + return dataSchema, schema.Diagnostics +} + +// getProviderMetaSchema is a helper to extract the schema for the meta info +// defined for a provider, +func (p *GRPCProvider) getProviderMetaSchema() (providers.Schema, tfdiags.Diagnostics) { + schema := p.getSchema() + return schema.ProviderMeta, schema.Diagnostics +} + func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) { logger.Trace("GRPCProvider.v6: GetProviderSchema") p.mu.Lock() @@ -100,10 +134,7 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp // grpc response size limit is 4MB. 64MB should cover most any use case, and // if we get providers nearing that we may want to consider a finer-grained // API to fetch individual resource schemas. - // Note: this option is marked as EXPERIMENTAL in the grpc API. We keep - // this for compatibility, but recent providers all set the max message - // size much higher on the server side, which is the supported method for - // determining payload size. + // Note: this option is marked as EXPERIMENTAL in the grpc API. const maxRecvSize = 64 << 20 protoResp, err := p.client.GetProviderSchema(p.ctx, new(proto6.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize}) if err != nil { @@ -137,10 +168,6 @@ func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResp resp.DataSources[name] = convert.ProtoToProviderSchema(data) } - if protoResp.ServerCapabilities != nil { - resp.ServerCapabilities.PlanDestroy = protoResp.ServerCapabilities.PlanDestroy - } - p.schemas = resp return resp @@ -150,11 +177,6 @@ func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfig logger.Trace("GRPCProvider.v6: ValidateProviderConfig") schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - ty := schema.Provider.Block.ImpliedType() mp, err := msgpack.Marshal(r.Config, ty) @@ -180,15 +202,9 @@ func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfig func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { logger.Trace("GRPCProvider.v6: ValidateResourceConfig") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resourceSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resourceSchema, diags := p.getResourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -216,15 +232,9 @@ func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfig func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { logger.Trace("GRPCProvider.v6: ValidateDataResourceConfig") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - dataSchema, ok := schema.DataSources[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName)) + dataSchema, diags := p.getDatasourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -251,15 +261,9 @@ func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResour func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { logger.Trace("GRPCProvider.v6: UpgradeResourceState") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -342,20 +346,14 @@ func (p *GRPCProvider) Stop() error { func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { logger.Trace("GRPCProvider.v6: ReadResource") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type " + r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - metaSchema := schema.ProviderMeta - mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) @@ -398,26 +396,11 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { logger.Trace("GRPCProvider.v6: PlanResourceChange") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) - return resp - } - - metaSchema := schema.ProviderMeta - capabilities := schema.ServerCapabilities - - // If the provider doesn't support planning a destroy operation, we can - // return immediately. - if r.ProposedNewState.IsNull() && !capabilities.PlanDestroy { - resp.PlannedState = r.ProposedNewState - resp.PlannedPrivate = r.PriorPrivate + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } @@ -484,20 +467,14 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { logger.Trace("GRPCProvider.v6: ApplyResourceChange") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) + resSchema, diags := p.getResourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - metaSchema := schema.ProviderMeta - priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) @@ -555,12 +532,6 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { logger.Trace("GRPCProvider.v6: ImportResourceState") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics - return resp - } - protoReq := &proto6.ImportResourceState_Request{ TypeName: r.TypeName, Id: r.ID, @@ -579,10 +550,10 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques Private: imported.Private, } - resSchema, ok := schema.ResourceTypes[r.TypeName] - if !ok { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName)) - continue + resSchema, diags := p.getResourceSchema(resource.TypeName) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) + return resp } state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType()) @@ -600,19 +571,14 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { logger.Trace("GRPCProvider.v6: ReadDataSource") - schema := p.getSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = schema.Diagnostics + dataSchema, diags := p.getDatasourceSchema(r.TypeName) + metaSchema, metaDiags := p.getProviderMetaSchema() + diags = diags.Append(metaDiags) + if diags.HasErrors() { + resp.Diagnostics = resp.Diagnostics.Append(diags) return resp } - dataSchema, ok := schema.DataSources[r.TypeName] - if !ok { - schema.Diagnostics = schema.Diagnostics.Append(fmt.Errorf("unknown data source %q", r.TypeName)) - } - - metaSchema := schema.ProviderMeta - config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) diff --git a/internal/provider-simple-v6/provider.go b/internal/provider-simple-v6/provider.go index 1fb2ce127bf2..74fce935386a 100644 --- a/internal/provider-simple-v6/provider.go +++ b/internal/provider-simple-v6/provider.go @@ -3,7 +3,6 @@ package simple import ( "errors" - "fmt" "time" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -43,9 +42,6 @@ func Provider() providers.Interface { DataSources: map[string]providers.Schema{ "simple_resource": simpleResource, }, - ServerCapabilities: providers.ServerCapabilities{ - PlanDestroy: true, - }, }, } } @@ -89,16 +85,6 @@ func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers. } func (s simple) PlanResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - if req.ProposedNewState.IsNull() { - // destroy op - resp.PlannedState = req.ProposedNewState - - // signal that this resource was properly planned for destruction, - // verifying that the schema capabilities with PlanDestroy took effect. - resp.PlannedPrivate = []byte("destroy planned") - return resp - } - m := req.ProposedNewState.AsValueMap() _, ok := m["id"] if !ok { @@ -111,11 +97,6 @@ func (s simple) PlanResourceChange(req providers.PlanResourceChangeRequest) (res func (s simple) ApplyResourceChange(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { if req.PlannedState.IsNull() { - // make sure this was transferred from the plan action - if string(req.PlannedPrivate) != "destroy planned" { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("resource not planned for destroy, private data %q", req.PlannedPrivate)) - } - resp.NewState = req.PlannedState return resp } diff --git a/internal/provider-simple/provider.go b/internal/provider-simple/provider.go index 8e32dcc10ef7..74fce935386a 100644 --- a/internal/provider-simple/provider.go +++ b/internal/provider-simple/provider.go @@ -42,9 +42,6 @@ func Provider() providers.Interface { DataSources: map[string]providers.Schema{ "simple_resource": simpleResource, }, - ServerCapabilities: providers.ServerCapabilities{ - PlanDestroy: true, - }, }, } } @@ -88,13 +85,6 @@ func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers. } func (s simple) PlanResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - if req.ProposedNewState.IsNull() { - // destroy op - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - m := req.ProposedNewState.AsValueMap() _, ok := m["id"] if !ok { diff --git a/internal/providercache/errors.go b/internal/providercache/errors.go new file mode 100644 index 000000000000..75550d008921 --- /dev/null +++ b/internal/providercache/errors.go @@ -0,0 +1,14 @@ +package providercache + +import "github.com/hashicorp/terraform/internal/getproviders" + +// ErrProviderChecksumMiss is an error type used to indicate a provider +// installation failed due to a mismatch in the terraform provider lock file. +type ErrProviderChecksumMiss struct { + Meta getproviders.PackageMeta + Msg string +} + +func (err ErrProviderChecksumMiss) Error() string { + return err.Msg +} diff --git a/internal/providercache/installer_test.go b/internal/providercache/installer_test.go index bb71f1db2493..0a3677872c05 100644 --- a/internal/providercache/installer_test.go +++ b/internal/providercache/installer_test.go @@ -16,7 +16,6 @@ import ( "github.com/google/go-cmp/cmp" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/disco" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/depsfile" "github.com/hashicorp/terraform/internal/getproviders" @@ -1402,7 +1401,7 @@ func TestEnsureProviderVersions(t *testing.T) { beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), }, WantErr: `some providers could not be installed: -- example.com/foo/beep: the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms); for more information: https://www.terraform.io/language/provider-checksum-verification`, +- example.com/foo/beep: the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)`, WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { return map[addrs.Provider][]*testInstallerEventLogItem{ noProvider: { @@ -1448,7 +1447,7 @@ func TestEnsureProviderVersions(t *testing.T) { Error string }{ "1.0.0", - `the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms); for more information: https://www.terraform.io/language/provider-checksum-verification`, + `the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)`, }, }, }, diff --git a/internal/providercache/package_install.go b/internal/providercache/package_install.go index 655a441d8104..e07564b61bd6 100644 --- a/internal/providercache/package_install.go +++ b/internal/providercache/package_install.go @@ -45,7 +45,7 @@ func installFromHTTPURL(ctx context.Context, meta getproviders.PackageMeta, targ // so we'll return a more appropriate one here. return nil, fmt.Errorf("provider download was interrupted") } - return nil, fmt.Errorf("%s: %w", getproviders.HostFromRequest(req), err) + return nil, err } defer resp.Body.Close() @@ -116,10 +116,13 @@ func installFromLocalArchive(ctx context.Context, meta getproviders.PackageMeta, meta.Provider, meta.Version, meta.Location, err, ) } else if !matches { - return authResult, fmt.Errorf( - "the current package for %s %s doesn't match any of the checksums previously recorded in the dependency lock file; for more information: https://www.terraform.io/language/provider-checksum-verification", - meta.Provider, meta.Version, - ) + return authResult, ErrProviderChecksumMiss{ + Meta: meta, + Msg: fmt.Sprintf( + "the current package for %s %s doesn't match any of the checksums previously recorded in the dependency lock file", + meta.Provider, meta.Version, + ), + } } } @@ -198,10 +201,13 @@ func installFromLocalDir(ctx context.Context, meta getproviders.PackageMeta, tar meta.Provider, meta.Version, meta.Location, err, ) } else if !matches { - return authResult, fmt.Errorf( - "the local package for %s %s doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms); for more information: https://www.terraform.io/language/provider-checksum-verification", - meta.Provider, meta.Version, - ) + return authResult, ErrProviderChecksumMiss{ + Meta: meta, + Msg: fmt.Sprintf( + "the local package for %s %s doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)", + meta.Provider, meta.Version, + ), + } } } diff --git a/internal/providers/provider.go b/internal/providers/provider.go index 5d98d9bf3917..26d4345883e4 100644 --- a/internal/providers/provider.go +++ b/internal/providers/provider.go @@ -85,19 +85,6 @@ type GetProviderSchemaResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics - - // ServerCapabilities lists optional features supported by the provider. - ServerCapabilities ServerCapabilities -} - -// ServerCapabilities allows providers to communicate extra information -// regarding supported protocol features. This is used to indicate availability -// of certain forward-compatible changes which may be optional in a major -// protocol version, but cannot be tested for directly. -type ServerCapabilities struct { - // PlanDestroy signals that this provider expects to receive a - // PlanResourceChange call for resources that are to be destroyed. - PlanDestroy bool } type ValidateProviderConfigRequest struct { diff --git a/internal/refactoring/move_execute.go b/internal/refactoring/move_execute.go index 9a6d577cfb4f..76d152456f5a 100644 --- a/internal/refactoring/move_execute.go +++ b/internal/refactoring/move_execute.go @@ -26,7 +26,10 @@ import ( // ApplyMoves expects exclusive access to the given state while it's running. // Don't read or write any part of the state structure until ApplyMoves returns. func ApplyMoves(stmts []MoveStatement, state *states.State) MoveResults { - ret := makeMoveResults() + ret := MoveResults{ + Changes: make(map[addrs.UniqueKey]MoveSuccess), + Blocked: make(map[addrs.UniqueKey]MoveBlocked), + } if len(stmts) == 0 { return ret @@ -68,23 +71,25 @@ func ApplyMoves(stmts []MoveStatement, state *states.State) MoveResults { log.Printf("[TRACE] refactoring.ApplyMoves: Processing 'moved' statements in the configuration\n%s", logging.Indent(g.String())) recordOldAddr := func(oldAddr, newAddr addrs.AbsResourceInstance) { - if prevMove, exists := ret.Changes.GetOk(oldAddr); exists { + oldAddrKey := oldAddr.UniqueKey() + newAddrKey := newAddr.UniqueKey() + if prevMove, exists := ret.Changes[oldAddrKey]; exists { // If the old address was _already_ the result of a move then // we'll replace that entry so that our results summarize a chain // of moves into a single entry. - ret.Changes.Remove(oldAddr) + delete(ret.Changes, oldAddrKey) oldAddr = prevMove.From } - ret.Changes.Put(newAddr, MoveSuccess{ + ret.Changes[newAddrKey] = MoveSuccess{ From: oldAddr, To: newAddr, - }) + } } recordBlockage := func(newAddr, wantedAddr addrs.AbsMoveable) { - ret.Blocked.Put(newAddr, MoveBlocked{ + ret.Blocked[newAddr.UniqueKey()] = MoveBlocked{ Wanted: wantedAddr, Actual: newAddr, - }) + } } for _, v := range g.ReverseTopologicalOrder() { @@ -285,7 +290,7 @@ type MoveResults struct { // // In the return value from ApplyMoves, all of the keys are guaranteed to // be unique keys derived from addrs.AbsResourceInstance values. - Changes addrs.Map[addrs.AbsResourceInstance, MoveSuccess] + Changes map[addrs.UniqueKey]MoveSuccess // Blocked is a map from the unique keys of the final new // resource instances addresses to information about where they "wanted" @@ -301,14 +306,7 @@ type MoveResults struct { // // In the return value from ApplyMoves, all of the keys are guaranteed to // be unique keys derived from values of addrs.AbsMoveable types. - Blocked addrs.Map[addrs.AbsMoveable, MoveBlocked] -} - -func makeMoveResults() MoveResults { - return MoveResults{ - Changes: addrs.MakeMap[addrs.AbsResourceInstance, MoveSuccess](), - Blocked: addrs.MakeMap[addrs.AbsMoveable, MoveBlocked](), - } + Blocked map[addrs.UniqueKey]MoveBlocked } type MoveSuccess struct { @@ -327,14 +325,15 @@ type MoveBlocked struct { // If AddrMoved returns true, you can pass the same address to method OldAddr // to find its original address prior to moving. func (rs MoveResults) AddrMoved(newAddr addrs.AbsResourceInstance) bool { - return rs.Changes.Has(newAddr) + _, ok := rs.Changes[newAddr.UniqueKey()] + return ok } // OldAddr returns the old address of the given resource instance address, or // just returns back the same address if the given instance wasn't affected by // any move statements. func (rs MoveResults) OldAddr(newAddr addrs.AbsResourceInstance) addrs.AbsResourceInstance { - change, ok := rs.Changes.GetOk(newAddr) + change, ok := rs.Changes[newAddr.UniqueKey()] if !ok { return newAddr } diff --git a/internal/refactoring/move_execute_test.go b/internal/refactoring/move_execute_test.go index edc8afb9fda9..9d60ff6b60e9 100644 --- a/internal/refactoring/move_execute_test.go +++ b/internal/refactoring/move_execute_test.go @@ -10,7 +10,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/states" ) @@ -29,7 +28,10 @@ func TestApplyMoves(t *testing.T) { return addr } - emptyResults := makeMoveResults() + emptyResults := MoveResults{ + Changes: map[addrs.UniqueKey]MoveSuccess{}, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, + } tests := map[string]struct { Stmts []MoveStatement @@ -76,13 +78,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("foo.to"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("foo.to").UniqueKey(): { From: mustParseInstAddr("foo.from"), To: mustParseInstAddr("foo.to"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `foo.to`, @@ -103,13 +105,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("foo.to[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("foo.to[0]").UniqueKey(): { From: mustParseInstAddr("foo.from[0]"), To: mustParseInstAddr("foo.to[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `foo.to[0]`, @@ -131,13 +133,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("foo.to"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("foo.to").UniqueKey(): { From: mustParseInstAddr("foo.from"), To: mustParseInstAddr("foo.to"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `foo.to`, @@ -159,13 +161,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.boo.foo.to[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.boo.foo.to[0]").UniqueKey(): { From: mustParseInstAddr("foo.from[0]"), To: mustParseInstAddr("module.boo.foo.to[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.boo.foo.to[0]`, @@ -187,13 +189,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.to[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.to[0]").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from[0]"), To: mustParseInstAddr("module.bar[0].foo.to[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar[0].foo.to[0]`, @@ -223,17 +225,17 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar.foo.from"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar.foo.from").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from"), To: mustParseInstAddr("module.bar.foo.from"), - }), - addrs.MakeMapElem(mustParseInstAddr("module.bar.module.hoo.foo.from"), MoveSuccess{ + }, + mustParseInstAddr("module.bar.module.hoo.foo.from").UniqueKey(): { From: mustParseInstAddr("module.boo.module.hoo.foo.from"), To: mustParseInstAddr("module.bar.module.hoo.foo.from"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar.foo.from`, @@ -256,13 +258,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.from[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.from[0]").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from[0]"), To: mustParseInstAddr("module.bar[0].foo.from[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar[0].foo.from[0]`, @@ -285,13 +287,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.to[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.to[0]").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from[0]"), To: mustParseInstAddr("module.bar[0].foo.to[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar[0].foo.to[0]`, @@ -314,13 +316,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.to[0]"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.to[0]").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from[0]"), To: mustParseInstAddr("module.bar[0].foo.to[0]"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar[0].foo.to[0]`, @@ -352,16 +354,13 @@ func TestApplyMoves(t *testing.T) { MoveResults{ // Nothing moved, because the module.b address is already // occupied by another module. - Changes: emptyResults.Changes, - Blocked: addrs.MakeMap( - addrs.MakeMapElem[addrs.AbsMoveable]( - mustParseInstAddr("module.bar[0].foo.from").Module, - MoveBlocked{ - Wanted: mustParseInstAddr("module.boo.foo.to[0]").Module, - Actual: mustParseInstAddr("module.bar[0].foo.from").Module, - }, - ), - ), + Changes: map[addrs.UniqueKey]MoveSuccess{}, + Blocked: map[addrs.UniqueKey]MoveBlocked{ + mustParseInstAddr("module.bar[0].foo.from").Module.UniqueKey(): { + Wanted: mustParseInstAddr("module.boo.foo.to[0]").Module, + Actual: mustParseInstAddr("module.bar[0].foo.from").Module, + }, + }, }, []string{ `module.bar[0].foo.from`, @@ -394,16 +393,13 @@ func TestApplyMoves(t *testing.T) { MoveResults{ // Nothing moved, because the from.to address is already // occupied by another resource. - Changes: emptyResults.Changes, - Blocked: addrs.MakeMap( - addrs.MakeMapElem[addrs.AbsMoveable]( - mustParseInstAddr("foo.from").ContainingResource(), - MoveBlocked{ - Wanted: mustParseInstAddr("foo.to").ContainingResource(), - Actual: mustParseInstAddr("foo.from").ContainingResource(), - }, - ), - ), + Changes: map[addrs.UniqueKey]MoveSuccess{}, + Blocked: map[addrs.UniqueKey]MoveBlocked{ + mustParseInstAddr("foo.from").ContainingResource().UniqueKey(): { + Wanted: mustParseInstAddr("foo.to").ContainingResource(), + Actual: mustParseInstAddr("foo.from").ContainingResource(), + }, + }, }, []string{ `foo.from`, @@ -436,16 +432,13 @@ func TestApplyMoves(t *testing.T) { MoveResults{ // Nothing moved, because the from.to[0] address is already // occupied by another resource instance. - Changes: emptyResults.Changes, - Blocked: addrs.MakeMap( - addrs.MakeMapElem[addrs.AbsMoveable]( - mustParseInstAddr("foo.from"), - MoveBlocked{ - Wanted: mustParseInstAddr("foo.to[0]"), - Actual: mustParseInstAddr("foo.from"), - }, - ), - ), + Changes: map[addrs.UniqueKey]MoveSuccess{}, + Blocked: map[addrs.UniqueKey]MoveBlocked{ + mustParseInstAddr("foo.from").UniqueKey(): { + Wanted: mustParseInstAddr("foo.to[0]"), + Actual: mustParseInstAddr("foo.from"), + }, + }, }, []string{ `foo.from`, @@ -468,13 +461,13 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.to"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.to").UniqueKey(): { From: mustParseInstAddr("module.boo.foo.from"), To: mustParseInstAddr("module.bar[0].foo.to"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.bar[0].foo.to`, @@ -505,17 +498,17 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.boo.foo.from"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.boo.foo.from").UniqueKey(): { mustParseInstAddr("foo.from"), mustParseInstAddr("module.boo.foo.from"), - }), - addrs.MakeMapElem(mustParseInstAddr("module.boo.foo.to"), MoveSuccess{ + }, + mustParseInstAddr("module.boo.foo.to").UniqueKey(): { mustParseInstAddr("module.bar[0].foo.to"), mustParseInstAddr("module.boo.foo.to"), - }), - ), - Blocked: emptyResults.Blocked, + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{}, }, []string{ `module.boo.foo.from`, @@ -548,16 +541,16 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].foo.to"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + mustParseInstAddr("module.bar[0].foo.to").UniqueKey(): MoveSuccess{ mustParseInstAddr("foo.from"), mustParseInstAddr("module.bar[0].foo.to"), - }), - addrs.MakeMapElem(mustParseInstAddr("module.bar[0].bar.to"), MoveSuccess{ + }, + mustParseInstAddr("module.bar[0].bar.to").UniqueKey(): MoveSuccess{ mustParseInstAddr("bar.from"), mustParseInstAddr("module.bar[0].bar.to"), - }), - ), + }, + }, Blocked: emptyResults.Blocked, }, []string{ @@ -590,21 +583,19 @@ func TestApplyMoves(t *testing.T) { ) }), MoveResults{ - Changes: addrs.MakeMap( - addrs.MakeMapElem(mustParseInstAddr("module.boo.foo.from"), MoveSuccess{ + Changes: map[addrs.UniqueKey]MoveSuccess{ + + mustParseInstAddr("module.boo.foo.from").UniqueKey(): { mustParseInstAddr("module.bar[0].foo.from"), mustParseInstAddr("module.boo.foo.from"), - }), - ), - Blocked: addrs.MakeMap( - addrs.MakeMapElem[addrs.AbsMoveable]( - mustParseInstAddr("foo.from").ContainingResource(), - MoveBlocked{ - Actual: mustParseInstAddr("foo.from").ContainingResource(), - Wanted: mustParseInstAddr("module.boo.foo.from").ContainingResource(), - }, - ), - ), + }, + }, + Blocked: map[addrs.UniqueKey]MoveBlocked{ + mustParseInstAddr("foo.from").ContainingResource().UniqueKey(): { + Actual: mustParseInstAddr("foo.from").ContainingResource(), + Wanted: mustParseInstAddr("module.boo.foo.from").ContainingResource(), + }, + }, }, []string{ `foo.from`, diff --git a/internal/refactoring/move_statement_test.go b/internal/refactoring/move_statement_test.go index a6f0f9f6eee2..249d7df7eb4c 100644 --- a/internal/refactoring/move_statement_test.go +++ b/internal/refactoring/move_statement_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" diff --git a/internal/refactoring/move_validate.go b/internal/refactoring/move_validate.go index 585f62368732..13f2e50f9590 100644 --- a/internal/refactoring/move_validate.go +++ b/internal/refactoring/move_validate.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" @@ -45,20 +44,55 @@ func ValidateMoves(stmts []MoveStatement, rootCfg *configs.Config, declaredInsts Other addrs.AbsMoveable StmtRange tfdiags.SourceRange } - stmtFrom := addrs.MakeMap[addrs.AbsMoveable, AbsMoveEndpoint]() - stmtTo := addrs.MakeMap[addrs.AbsMoveable, AbsMoveEndpoint]() + stmtFrom := map[addrs.UniqueKey]AbsMoveEndpoint{} + stmtTo := map[addrs.UniqueKey]AbsMoveEndpoint{} for _, stmt := range stmts { // Earlier code that constructs MoveStatement values should ensure that - // both stmt.From and stmt.To always belong to the same statement. - fromMod, _ := stmt.From.ModuleCallTraversals() + // both stmt.From and stmt.To always belong to the same statement and + // thus to the same module. + stmtMod, fromCallSteps := stmt.From.ModuleCallTraversals() + _, toCallSteps := stmt.To.ModuleCallTraversals() - for _, fromModInst := range declaredInsts.InstancesForModule(fromMod) { - absFrom := stmt.From.InModuleInstance(fromModInst) + modCfg := rootCfg.Descendent(stmtMod) + if !stmt.Implied { + // Implied statements can cross module boundaries because we + // generate them only for changing instance keys on a single + // resource. They happen to be generated _as if_ they were written + // in the root module, but the source and destination are always + // in the same module anyway. + if pkgAddr := callsThroughModulePackage(modCfg, fromCallSteps); pkgAddr != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Cross-package move statement", + Detail: fmt.Sprintf( + "This statement declares a move from an object declared in external module package %q. Move statements can be only within a single module package.", + pkgAddr, + ), + Subject: stmt.DeclRange.ToHCL().Ptr(), + }) + } + if pkgAddr := callsThroughModulePackage(modCfg, toCallSteps); pkgAddr != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Cross-package move statement", + Detail: fmt.Sprintf( + "This statement declares a move to an object declared in external module package %q. Move statements can be only within a single module package.", + pkgAddr, + ), + Subject: stmt.DeclRange.ToHCL().Ptr(), + }) + } + } + + for _, modInst := range declaredInsts.InstancesForModule(stmtMod) { - absTo := stmt.To.InModuleInstance(fromModInst) + absFrom := stmt.From.InModuleInstance(modInst) + absTo := stmt.To.InModuleInstance(modInst) + fromKey := absFrom.UniqueKey() + toKey := absTo.UniqueKey() - if addrs.Equivalent(absFrom, absTo) { + if fromKey == toKey { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Redundant move statement", @@ -115,8 +149,8 @@ func ValidateMoves(stmts []MoveStatement, rootCfg *configs.Config, declaredInsts } // There can only be one destination for each source address. - if existing, exists := stmtFrom.GetOk(absFrom); exists { - if !addrs.Equivalent(existing.Other, absTo) { + if existing, exists := stmtFrom[fromKey]; exists { + if existing.Other.UniqueKey() != toKey { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Ambiguous move statements", @@ -129,15 +163,15 @@ func ValidateMoves(stmts []MoveStatement, rootCfg *configs.Config, declaredInsts }) } } else { - stmtFrom.Put(absFrom, AbsMoveEndpoint{ + stmtFrom[fromKey] = AbsMoveEndpoint{ Other: absTo, StmtRange: stmt.DeclRange, - }) + } } // There can only be one source for each destination address. - if existing, exists := stmtTo.GetOk(absTo); exists { - if !addrs.Equivalent(existing.Other, absFrom) { + if existing, exists := stmtTo[toKey]; exists { + if existing.Other.UniqueKey() != fromKey { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Ambiguous move statements", @@ -150,10 +184,10 @@ func ValidateMoves(stmts []MoveStatement, rootCfg *configs.Config, declaredInsts }) } } else { - stmtTo.Put(absTo, AbsMoveEndpoint{ + stmtTo[toKey] = AbsMoveEndpoint{ Other: absFrom, StmtRange: stmt.DeclRange, - }) + } } // Resource types must match. @@ -166,7 +200,6 @@ func ValidateMoves(stmts []MoveStatement, rootCfg *configs.Config, declaredInsts ), }) } - } } @@ -338,3 +371,24 @@ func movableObjectDeclRange(addr addrs.AbsMoveable, cfg *configs.Config) (tfdiag panic("unsupported AbsMoveable address type") } } + +func callsThroughModulePackage(modCfg *configs.Config, callSteps []addrs.ModuleCall) addrs.ModuleSource { + var sourceAddr addrs.ModuleSource + current := modCfg + for _, step := range callSteps { + call := current.Module.ModuleCalls[step.Name] + if call == nil { + break + } + if call.EntersNewPackage() { + sourceAddr = call.SourceAddr + } + current = modCfg.Children[step.Name] + if current == nil { + // Weird to have a call but not a config, but we'll tolerate + // it to avoid crashing here. + break + } + } + return sourceAddr +} diff --git a/internal/refactoring/move_validate_test.go b/internal/refactoring/move_validate_test.go index 56d767af51cd..aa4ec4f3b800 100644 --- a/internal/refactoring/move_validate_test.go +++ b/internal/refactoring/move_validate_test.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty/gocty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configload" @@ -16,6 +14,7 @@ import ( "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/registry" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty/gocty" ) func TestValidateMoves(t *testing.T) { @@ -335,7 +334,7 @@ Each resource can have moved from only one source resource.`, `test.thing`, ), }, - WantError: ``, + WantError: `Cross-package move statement: This statement declares a move from an object declared in external module package "fake-external:///". Move statements can be only within a single module package.`, }, "move to resource in another module package": { Statements: []MoveStatement{ @@ -345,7 +344,7 @@ Each resource can have moved from only one source resource.`, `module.fake_external.test.thing`, ), }, - WantError: ``, + WantError: `Cross-package move statement: This statement declares a move to an object declared in external module package "fake-external:///". Move statements can be only within a single module package.`, }, "move from module call in another module package": { Statements: []MoveStatement{ @@ -355,7 +354,7 @@ Each resource can have moved from only one source resource.`, `module.b`, ), }, - WantError: ``, + WantError: `Cross-package move statement: This statement declares a move from an object declared in external module package "fake-external:///". Move statements can be only within a single module package.`, }, "move to module call in another module package": { Statements: []MoveStatement{ @@ -365,7 +364,7 @@ Each resource can have moved from only one source resource.`, `module.fake_external.module.b`, ), }, - WantError: ``, + WantError: `Cross-package move statement: This statement declares a move to an object declared in external module package "fake-external:///". Move statements can be only within a single module package.`, }, "implied move from resource in another module package": { Statements: []MoveStatement{ @@ -703,5 +702,5 @@ func makeTestImpliedMoveStmt(t *testing.T, moduleStr, fromStr, toStr string) Mov } var fakeExternalModuleSource = addrs.ModuleSourceRemote{ - Package: addrs.ModulePackage("fake-external:///"), + PackageAddr: addrs.ModulePackage("fake-external:///"), } diff --git a/internal/registry/regsrc/module.go b/internal/registry/regsrc/module.go index 3ffa002bba07..9f9999d3474b 100644 --- a/internal/registry/regsrc/module.go +++ b/internal/registry/regsrc/module.go @@ -105,7 +105,7 @@ func NewModule(host, namespace, name, provider, submodule string) (*Module, erro // use addrs.ModuleSourceRegistry instead, and then package regsrc can be // removed altogether. func ModuleFromModuleSourceAddr(addr addrs.ModuleSourceRegistry) *Module { - ret := ModuleFromRegistryPackageAddr(addr.Package) + ret := ModuleFromRegistryPackageAddr(addr.PackageAddr) ret.RawSubmodule = addr.Subdir return ret } diff --git a/internal/states/remote/state.go b/internal/states/remote/state.go index ae123f8e3647..ca939a96a312 100644 --- a/internal/states/remote/state.go +++ b/internal/states/remote/state.go @@ -6,7 +6,6 @@ import ( "sync" uuid "github.com/hashicorp/go-uuid" - "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states/statefile" "github.com/hashicorp/terraform/internal/states/statemgr" @@ -47,19 +46,6 @@ func (s *State) State() *states.State { return s.state.DeepCopy() } -func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) { - if err := s.RefreshState(); err != nil { - return nil, fmt.Errorf("Failed to load state: %s", err) - } - - state := s.State() - if state == nil { - state = states.NewState() - } - - return state.RootModule().OutputValues, nil -} - // StateForMigration is part of our implementation of statemgr.Migrator. func (s *State) StateForMigration() *statefile.File { s.mu.Lock() diff --git a/internal/states/remote/state_test.go b/internal/states/remote/state_test.go index 1089ba1aaeed..c38e0bfec2e5 100644 --- a/internal/states/remote/state_test.go +++ b/internal/states/remote/state_test.go @@ -19,7 +19,6 @@ func TestState_impl(t *testing.T) { var _ statemgr.Writer = new(State) var _ statemgr.Persister = new(State) var _ statemgr.Refresher = new(State) - var _ statemgr.OutputReader = new(State) var _ statemgr.Locker = new(State) } @@ -246,30 +245,28 @@ func TestStatePersist(t *testing.T) { // Run tests in order. for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - s, cleanup := tc.mutationFunc(mgr) - - if err := mgr.WriteState(s); err != nil { - t.Fatalf("failed to WriteState for %q: %s", tc.name, err) - } - if err := mgr.PersistState(); err != nil { - t.Fatalf("failed to PersistState for %q: %s", tc.name, err) + s, cleanup := tc.mutationFunc(mgr) + + if err := mgr.WriteState(s); err != nil { + t.Fatalf("failed to WriteState for %q: %s", tc.name, err) + } + if err := mgr.PersistState(); err != nil { + t.Fatalf("failed to PersistState for %q: %s", tc.name, err) + } + + if tc.isRequested(t) { + // Get captured request from the mock client log + // based on the index of the current test + if logIdx >= len(mockClient.log) { + t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) } - - if tc.isRequested(t) { - // Get captured request from the mock client log - // based on the index of the current test - if logIdx >= len(mockClient.log) { - t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) - } - loggedRequest := mockClient.log[logIdx] - logIdx++ - if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { - t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) - } + loggedRequest := mockClient.log[logIdx] + logIdx++ + if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { + t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) } - cleanup() - }) + } + cleanup() } logCnt := len(mockClient.log) if logIdx != logCnt { @@ -277,33 +274,6 @@ func TestStatePersist(t *testing.T) { } } -func TestState_GetRootOutputValues(t *testing.T) { - // Initial setup of state with outputs already defined - mgr := &State{ - Client: &mockClient{ - current: []byte(` - { - "version": 4, - "lineage": "mock-lineage", - "serial": 1, - "terraform_version":"0.0.0", - "outputs": {"foo": {"value":"bar", "type": "string"}}, - "resources": [] - } - `), - }, - } - - outputs, err := mgr.GetRootOutputValues() - if err != nil { - t.Errorf("Expected GetRootOutputValues to not return an error, but it returned %v", err) - } - - if len(outputs) != 1 { - t.Errorf("Expected %d outputs, but received %d", 1, len(outputs)) - } -} - type migrationTestCase struct { name string // A function to generate a statefile @@ -425,39 +395,37 @@ func TestWriteStateForMigration(t *testing.T) { logIdx := 0 for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - sf := tc.stateFile(mgr) - err := mgr.WriteStateForMigration(sf, tc.force) - shouldError := tc.expectedError != "" - - // If we are expecting and error check it and move on - if shouldError { - if err == nil { - t.Fatalf("test case %q should have failed with error %q", tc.name, tc.expectedError) - } else if err.Error() != tc.expectedError { - t.Fatalf("test case %q expected error %q but got %q", tc.name, tc.expectedError, err) - } - return - } - - if err != nil { - t.Fatalf("test case %q failed: %v", tc.name, err) + sf := tc.stateFile(mgr) + err := mgr.WriteStateForMigration(sf, tc.force) + shouldError := tc.expectedError != "" + + // If we are expecting and error check it and move on + if shouldError { + if err == nil { + t.Fatalf("test case %q should have failed with error %q", tc.name, tc.expectedError) + } else if err.Error() != tc.expectedError { + t.Fatalf("test case %q expected error %q but got %q", tc.name, tc.expectedError, err) } - - // At this point we should just do a normal write and persist - // as would happen from the CLI - mgr.WriteState(mgr.State()) - mgr.PersistState() - - if logIdx >= len(mockClient.log) { - t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) - } - loggedRequest := mockClient.log[logIdx] - logIdx++ - if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { - t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) - } - }) + continue + } + + if err != nil { + t.Fatalf("test case %q failed: %v", tc.name, err) + } + + // At this point we should just do a normal write and persist + // as would happen from the CLI + mgr.WriteState(mgr.State()) + mgr.PersistState() + + if logIdx >= len(mockClient.log) { + t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) + } + loggedRequest := mockClient.log[logIdx] + logIdx++ + if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { + t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) + } } logCnt := len(mockClient.log) @@ -583,45 +551,43 @@ func TestWriteStateForMigrationWithForcePushClient(t *testing.T) { logIdx := 0 for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // Always reset client to not be force pushing - mockClient.force = false - sf := tc.stateFile(mgr) - err := mgr.WriteStateForMigration(sf, tc.force) - shouldError := tc.expectedError != "" - - // If we are expecting and error check it and move on - if shouldError { - if err == nil { - t.Fatalf("test case %q should have failed with error %q", tc.name, tc.expectedError) - } else if err.Error() != tc.expectedError { - t.Fatalf("test case %q expected error %q but got %q", tc.name, tc.expectedError, err) - } - return - } - - if err != nil { - t.Fatalf("test case %q failed: %v", tc.name, err) - } - - if tc.force && !mockClient.force { - t.Fatalf("test case %q should have enabled force push", tc.name) - } - - // At this point we should just do a normal write and persist - // as would happen from the CLI - mgr.WriteState(mgr.State()) - mgr.PersistState() - - if logIdx >= len(mockClient.log) { - t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) - } - loggedRequest := mockClient.log[logIdx] - logIdx++ - if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { - t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) + // Always reset client to not be force pushing + mockClient.force = false + sf := tc.stateFile(mgr) + err := mgr.WriteStateForMigration(sf, tc.force) + shouldError := tc.expectedError != "" + + // If we are expecting and error check it and move on + if shouldError { + if err == nil { + t.Fatalf("test case %q should have failed with error %q", tc.name, tc.expectedError) + } else if err.Error() != tc.expectedError { + t.Fatalf("test case %q expected error %q but got %q", tc.name, tc.expectedError, err) } - }) + continue + } + + if err != nil { + t.Fatalf("test case %q failed: %v", tc.name, err) + } + + if tc.force && !mockClient.force { + t.Fatalf("test case %q should have enabled force push", tc.name) + } + + // At this point we should just do a normal write and persist + // as would happen from the CLI + mgr.WriteState(mgr.State()) + mgr.PersistState() + + if logIdx >= len(mockClient.log) { + t.Fatalf("request lock and index are out of sync on %q: idx=%d len=%d", tc.name, logIdx, len(mockClient.log)) + } + loggedRequest := mockClient.log[logIdx] + logIdx++ + if diff := cmp.Diff(tc.expectedRequest, loggedRequest); len(diff) > 0 { + t.Fatalf("incorrect client requests for %q:\n%s", tc.name, diff) + } } logCnt := len(mockClient.log) diff --git a/internal/states/state.go b/internal/states/state.go index 90c73c11582e..28d962786d0c 100644 --- a/internal/states/state.go +++ b/internal/states/state.go @@ -19,7 +19,7 @@ import ( // so when accessing a State object concurrently it is the caller's // responsibility to ensure that only one write is in progress at a time // and that reads only occur when no write is in progress. The most common -// way to achieve this is to wrap the State in a SyncState and use the +// way to acheive this is to wrap the State in a SyncState and use the // higher-level atomic operations supported by that type. type State struct { // Modules contains the state for each module. The keys in this map are @@ -412,7 +412,7 @@ func (s *State) MoveAbsResource(src, dst addrs.AbsResource) { // MaybeMoveAbsResource moves the given src AbsResource's current state to the // new dst address. This function will succeed if both the src address does not // exist in state and the dst address does; the return value indicates whether -// or not the move occurred. This function will panic if either the src does not +// or not the move occured. This function will panic if either the src does not // exist or the dst does exist (but not both). func (s *State) MaybeMoveAbsResource(src, dst addrs.AbsResource) bool { // Get the source and destinatation addresses from state. diff --git a/internal/states/statemgr/filesystem.go b/internal/states/statemgr/filesystem.go index 1406f046563b..7cd19e8b0b52 100644 --- a/internal/states/statemgr/filesystem.go +++ b/internal/states/statemgr/filesystem.go @@ -233,20 +233,6 @@ func (s *Filesystem) RefreshState() error { return s.refreshState() } -func (s *Filesystem) GetRootOutputValues() (map[string]*states.OutputValue, error) { - err := s.RefreshState() - if err != nil { - return nil, err - } - - state := s.State() - if state == nil { - state = states.NewState() - } - - return state.RootModule().OutputValues, nil -} - func (s *Filesystem) refreshState() error { var reader io.Reader diff --git a/internal/states/statemgr/filesystem_test.go b/internal/states/statemgr/filesystem_test.go index 2f6285fbd1ee..1d319920f536 100644 --- a/internal/states/statemgr/filesystem_test.go +++ b/internal/states/statemgr/filesystem_test.go @@ -336,7 +336,6 @@ func TestFilesystem_impl(t *testing.T) { var _ Writer = new(Filesystem) var _ Persister = new(Filesystem) var _ Refresher = new(Filesystem) - var _ OutputReader = new(Filesystem) var _ Locker = new(Filesystem) } @@ -411,19 +410,6 @@ func TestFilesystem_refreshWhileLocked(t *testing.T) { } } -func TestFilesystem_GetRootOutputValues(t *testing.T) { - fs := testFilesystem(t) - - outputs, err := fs.GetRootOutputValues() - if err != nil { - t.Errorf("Expected GetRootOutputValues to not return an error, but it returned %v", err) - } - - if len(outputs) != 2 { - t.Errorf("Expected %d outputs, but received %d", 2, len(outputs)) - } -} - func testOverrideVersion(t *testing.T, v string) func() { oldVersionStr := tfversion.Version oldPrereleaseStr := tfversion.Prerelease diff --git a/internal/states/statemgr/lock.go b/internal/states/statemgr/lock.go index 79c149fe736e..190e06ea7de8 100644 --- a/internal/states/statemgr/lock.go +++ b/internal/states/statemgr/lock.go @@ -15,10 +15,6 @@ func (s *LockDisabled) State() *states.State { return s.Inner.State() } -func (s *LockDisabled) GetRootOutputValues() (map[string]*states.OutputValue, error) { - return s.Inner.GetRootOutputValues() -} - func (s *LockDisabled) WriteState(v *states.State) error { return s.Inner.WriteState(v) } diff --git a/internal/states/statemgr/persistent.go b/internal/states/statemgr/persistent.go index fde4a7f0f8ac..c15e84af2dc3 100644 --- a/internal/states/statemgr/persistent.go +++ b/internal/states/statemgr/persistent.go @@ -2,8 +2,6 @@ package statemgr import ( version "github.com/hashicorp/go-version" - - "github.com/hashicorp/terraform/internal/states" ) // Persistent is a union of the Refresher and Persistent interfaces, for types @@ -18,16 +16,6 @@ import ( type Persistent interface { Refresher Persister - OutputReader -} - -// OutputReader is the interface for managers that fetches output values from state -// or another source. This is a refinement of fetching the entire state and digging -// the output values from it because enhanced backends can apply special permissions -// to differentiate reading the state and reading the outputs within the state. -type OutputReader interface { - // GetRootOutputValues fetches the root module output values from state or another source - GetRootOutputValues() (map[string]*states.OutputValue, error) } // Refresher is the interface for managers that can read snapshots from diff --git a/internal/states/statemgr/statemgr_fake.go b/internal/states/statemgr/statemgr_fake.go index 8d88e4d24e7e..a547c1bc7c4f 100644 --- a/internal/states/statemgr/statemgr_fake.go +++ b/internal/states/statemgr/statemgr_fake.go @@ -65,10 +65,6 @@ func (m *fakeFull) PersistState() error { return m.fakeP.WriteState(m.t.State()) } -func (m *fakeFull) GetRootOutputValues() (map[string]*states.OutputValue, error) { - return m.State().RootModule().OutputValues, nil -} - func (m *fakeFull) Lock(info *LockInfo) (string, error) { m.lockLock.Lock() defer m.lockLock.Unlock() @@ -115,10 +111,6 @@ func (m *fakeErrorFull) State() *states.State { return nil } -func (m *fakeErrorFull) GetRootOutputValues() (map[string]*states.OutputValue, error) { - return nil, errors.New("fake state manager error") -} - func (m *fakeErrorFull) WriteState(s *states.State) error { return errors.New("fake state manager error") } diff --git a/internal/states/statemgr/testing.go b/internal/states/statemgr/testing.go index 171b21ad2ea0..82cecc0de55e 100644 --- a/internal/states/statemgr/testing.go +++ b/internal/states/statemgr/testing.go @@ -155,9 +155,5 @@ func TestFullInitialState() *states.State { Module: addrs.RootModule, } childMod.SetResourceProvider(rAddr, providerAddr) - - state.RootModule().SetOutputValue("sensitive_output", cty.StringVal("it's a secret"), true) - state.RootModule().SetOutputValue("nonsensitive_output", cty.StringVal("hello, world!"), false) - return state } diff --git a/internal/terraform/context_apply2_test.go b/internal/terraform/context_apply2_test.go index 9637a6951e34..f2cc27de3588 100644 --- a/internal/terraform/context_apply2_test.go +++ b/internal/terraform/context_apply2_test.go @@ -1,24 +1,20 @@ package terraform import ( - "bytes" "errors" "fmt" - "strings" "sync" "testing" "time" - "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" + "github.com/zclconf/go-cty/cty" ) // Test that the PreApply hook is called with the correct deposed key @@ -916,395 +912,3 @@ resource "test_resource" "c" { } }) } - -func TestContext2Apply_resourceConditionApplyTimeFail(t *testing.T) { - // This tests the less common situation where a condition fails due to - // a change in a resource other than the one the condition is attached to, - // and the condition result is unknown during planning. - // - // This edge case is a tricky one because it relies on Terraform still - // visiting test_resource.b (in the configuration below) to evaluate - // its conditions even though there aren't any changes directly planned - // for it, so that we can consider whether changes to test_resource.a - // have changed the outcome. - - m := testModuleInline(t, map[string]string{ - "main.tf": ` - variable "input" { - type = string - } - - resource "test_resource" "a" { - value = var.input - } - - resource "test_resource" "b" { - value = "beep" - - lifecycle { - postcondition { - condition = test_resource.a.output == self.output - error_message = "Outputs must match." - } - } - } - `, - }) - - p := testProvider("test") - p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - "test_resource": { - Attributes: map[string]*configschema.Attribute{ - "value": { - Type: cty.String, - Required: true, - }, - "output": { - Type: cty.String, - Computed: true, - }, - }, - }, - }, - }) - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - // Whenever "value" changes, "output" follows it during the apply step, - // but is initially unknown during the plan step. - - m := req.ProposedNewState.AsValueMap() - priorVal := cty.NullVal(cty.String) - if !req.PriorState.IsNull() { - priorVal = req.PriorState.GetAttr("value") - } - if m["output"].IsNull() || !priorVal.RawEquals(m["value"]) { - m["output"] = cty.UnknownVal(cty.String) - } - - resp.PlannedState = cty.ObjectVal(m) - resp.LegacyTypeSystem = true - return resp - } - p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { - m := req.PlannedState.AsValueMap() - m["output"] = m["value"] - resp.NewState = cty.ObjectVal(m) - return resp - } - ctx := testContext2(t, &ContextOpts{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), - }, - }) - instA := mustResourceInstanceAddr("test_resource.a") - instB := mustResourceInstanceAddr("test_resource.b") - - // Preparation: an initial plan and apply with a correct input variable - // should succeed and give us a valid and complete state to use for the - // subsequent plan and apply that we'll expect to fail. - var prevRunState *states.State - { - plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ - Mode: plans.NormalMode, - SetVariables: InputValues{ - "input": &InputValue{ - Value: cty.StringVal("beep"), - SourceType: ValueFromCLIArg, - }, - }, - }) - assertNoErrors(t, diags) - planA := plan.Changes.ResourceInstance(instA) - if planA == nil || planA.Action != plans.Create { - t.Fatalf("incorrect initial plan for instance A\nwant a 'create' change\ngot: %s", spew.Sdump(planA)) - } - planB := plan.Changes.ResourceInstance(instB) - if planB == nil || planB.Action != plans.Create { - t.Fatalf("incorrect initial plan for instance B\nwant a 'create' change\ngot: %s", spew.Sdump(planB)) - } - - state, diags := ctx.Apply(plan, m) - assertNoErrors(t, diags) - - stateA := state.ResourceInstance(instA) - if stateA == nil || stateA.Current == nil || !bytes.Contains(stateA.Current.AttrsJSON, []byte(`"beep"`)) { - t.Fatalf("incorrect initial state for instance A\ngot: %s", spew.Sdump(stateA)) - } - stateB := state.ResourceInstance(instB) - if stateB == nil || stateB.Current == nil || !bytes.Contains(stateB.Current.AttrsJSON, []byte(`"beep"`)) { - t.Fatalf("incorrect initial state for instance B\ngot: %s", spew.Sdump(stateB)) - } - prevRunState = state - } - - // Now we'll run another plan and apply with a different value for - // var.input that should cause the test_resource.b condition to be unknown - // during planning and then fail during apply. - { - plan, diags := ctx.Plan(m, prevRunState, &PlanOpts{ - Mode: plans.NormalMode, - SetVariables: InputValues{ - "input": &InputValue{ - Value: cty.StringVal("boop"), // NOTE: This has changed - SourceType: ValueFromCLIArg, - }, - }, - }) - assertNoErrors(t, diags) - planA := plan.Changes.ResourceInstance(instA) - if planA == nil || planA.Action != plans.Update { - t.Fatalf("incorrect initial plan for instance A\nwant an 'update' change\ngot: %s", spew.Sdump(planA)) - } - planB := plan.Changes.ResourceInstance(instB) - if planB == nil || planB.Action != plans.NoOp { - t.Fatalf("incorrect initial plan for instance B\nwant a 'no-op' change\ngot: %s", spew.Sdump(planB)) - } - - _, diags = ctx.Apply(plan, m) - if !diags.HasErrors() { - t.Fatal("final apply succeeded, but should've failed with a postcondition error") - } - if len(diags) != 1 { - t.Fatalf("expected exactly one diagnostic, but got: %s", diags.Err().Error()) - } - if got, want := diags[0].Description().Summary, "Resource postcondition failed"; got != want { - t.Fatalf("wrong diagnostic summary\ngot: %s\nwant: %s", got, want) - } - } -} - -// pass an input through some expanded values, and back to a provider to make -// sure we can fully evaluate a provider configuration during a destroy plan. -func TestContext2Apply_destroyWithConfiguredProvider(t *testing.T) { - m := testModuleInline(t, map[string]string{ - "main.tf": ` -variable "in" { - type = map(string) - default = { - "a" = "first" - "b" = "second" - } -} - -module "mod" { - source = "./mod" - for_each = var.in - in = each.value -} - -locals { - config = [for each in module.mod : each.out] -} - -provider "other" { - output = [for each in module.mod : each.out] - local = local.config - var = var.in -} - -resource "other_object" "other" { -} -`, - "./mod/main.tf": ` -variable "in" { - type = string -} - -data "test_object" "d" { - test_string = var.in -} - -resource "test_object" "a" { - test_string = var.in -} - -output "out" { - value = data.test_object.d.output -} -`}) - - testProvider := &MockProvider{ - GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ - Provider: providers.Schema{Block: simpleTestSchema()}, - ResourceTypes: map[string]providers.Schema{ - "test_object": providers.Schema{Block: simpleTestSchema()}, - }, - DataSources: map[string]providers.Schema{ - "test_object": providers.Schema{ - Block: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "test_string": { - Type: cty.String, - Optional: true, - }, - "output": { - Type: cty.String, - Computed: true, - }, - }, - }, - }, - }, - }, - } - - testProvider.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { - cfg := req.Config.AsValueMap() - s := cfg["test_string"].AsString() - if !strings.Contains("firstsecond", s) { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("expected 'first' or 'second', got %s", s)) - return resp - } - - cfg["output"] = cty.StringVal(s + "-ok") - resp.State = cty.ObjectVal(cfg) - return resp - } - - otherProvider := &MockProvider{ - GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ - Provider: providers.Schema{ - Block: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "output": { - Type: cty.List(cty.String), - Optional: true, - }, - "local": { - Type: cty.List(cty.String), - Optional: true, - }, - "var": { - Type: cty.Map(cty.String), - Optional: true, - }, - }, - }, - }, - ResourceTypes: map[string]providers.Schema{ - "other_object": providers.Schema{Block: simpleTestSchema()}, - }, - }, - } - - ctx := testContext2(t, &ContextOpts{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("test"): testProviderFuncFixed(testProvider), - addrs.NewDefaultProvider("other"): testProviderFuncFixed(otherProvider), - }, - }) - - opts := SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)) - plan, diags := ctx.Plan(m, states.NewState(), opts) - assertNoErrors(t, diags) - - state, diags := ctx.Apply(plan, m) - assertNoErrors(t, diags) - - otherProvider.ConfigureProviderCalled = false - otherProvider.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { - // check that our config is complete, even during a destroy plan - expected := cty.ObjectVal(map[string]cty.Value{ - "local": cty.ListVal([]cty.Value{cty.StringVal("first-ok"), cty.StringVal("second-ok")}), - "output": cty.ListVal([]cty.Value{cty.StringVal("first-ok"), cty.StringVal("second-ok")}), - "var": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("first"), - "b": cty.StringVal("second"), - }), - }) - - if !req.Config.RawEquals(expected) { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf( - `incorrect provider config: -expected: %#v -got: %#v`, - expected, req.Config)) - } - - return resp - } - - opts.Mode = plans.DestroyMode - // skip refresh so that we don't configure the provider before the destroy plan - opts.SkipRefresh = true - - // destroy only a single instance not included in the moved statements - _, diags = ctx.Plan(m, state, opts) - assertNoErrors(t, diags) - - if !otherProvider.ConfigureProviderCalled { - t.Fatal("failed to configure provider during destroy plan") - } -} - -// check that a provider can verify a planned destroy -func TestContext2Apply_plannedDestroy(t *testing.T) { - m := testModuleInline(t, map[string]string{ - "main.tf": ` -resource "test_object" "x" { - test_string = "ok" -}`, - }) - - p := simpleMockProvider() - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - if !req.ProposedNewState.IsNull() { - // we should only be destroying in this test - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unexpected plan with %#v", req.ProposedNewState)) - return resp - } - - resp.PlannedState = req.ProposedNewState - // we're going to verify the destroy plan by inserting private data required for destroy - resp.PlannedPrivate = append(resp.PlannedPrivate, []byte("planned")...) - return resp - } - - p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { - // if the value is nil, we return that directly to correspond to a delete - if !req.PlannedState.IsNull() { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unexpected apply with %#v", req.PlannedState)) - return resp - } - - resp.NewState = req.PlannedState - - // make sure we get our private data from the plan - private := string(req.PlannedPrivate) - if private != "planned" { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("missing private data from plan, got %q", private)) - } - return resp - } - - state := states.NewState() - root := state.EnsureModule(addrs.RootModuleInstance) - root.SetResourceInstanceCurrent( - mustResourceInstanceAddr("test_object.x").Resource, - &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{"test_string":"ok"}`), - }, - mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), - ) - - ctx := testContext2(t, &ContextOpts{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), - }, - }) - - plan, diags := ctx.Plan(m, state, &PlanOpts{ - Mode: plans.DestroyMode, - // we don't want to refresh, because that actually runs a normal plan - SkipRefresh: true, - }) - if diags.HasErrors() { - t.Fatalf("plan: %s", diags.Err()) - } - - _, diags = ctx.Apply(plan, m) - if diags.HasErrors() { - t.Fatalf("apply: %s", diags.Err()) - } -} diff --git a/internal/terraform/context_apply_test.go b/internal/terraform/context_apply_test.go index 4bdffe70c187..841ec7719d13 100644 --- a/internal/terraform/context_apply_test.go +++ b/internal/terraform/context_apply_test.go @@ -18,9 +18,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/go-test/deep" "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -31,6 +28,8 @@ import ( "github.com/hashicorp/terraform/internal/provisioners" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/gocty" ) func TestContext2Apply_basic(t *testing.T) { @@ -7026,12 +7025,6 @@ func TestContext2Apply_targetedDestroy(t *testing.T) { // test did not match actual terraform behavior: the output remains in // state. // - // The reason it remains in the state is that we prune out the root module - // output values from the destroy graph as part of pruning out the "update" - // nodes for the resources, because otherwise the root module output values - // force the resources to stay in the graph and can therefore cause - // unwanted dependency cycles. - // // TODO: Future refactoring may enable us to remove the output from state in // this case, and that would be Just Fine - this test can be modified to // expect 0 outputs. @@ -11048,13 +11041,6 @@ locals { p := testProvider("test") p.PlanResourceChangeFn = func(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - // this is a destroy plan - if r.ProposedNewState.IsNull() { - resp.PlannedState = r.ProposedNewState - resp.PlannedPrivate = r.PriorPrivate - return resp - } - n := r.ProposedNewState.AsValueMap() if r.PriorState.IsNull() { @@ -11467,20 +11453,13 @@ resource "test_resource" "a" { `}) p := testProvider("test") - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - // this is a destroy plan - if req.ProposedNewState.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { proposed := req.ProposedNewState.AsValueMap() proposed["id"] = cty.UnknownVal(cty.String) - - resp.PlannedState = cty.ObjectVal(proposed) - resp.RequiresReplace = []cty.Path{{cty.GetAttrStep{Name: "value"}}} - return resp + return providers.PlanResourceChangeResponse{ + PlannedState: cty.ObjectVal(proposed), + RequiresReplace: []cty.Path{{cty.GetAttrStep{Name: "value"}}}, + } } ctx := testContext2(t, &ContextOpts{ @@ -11999,19 +11978,14 @@ resource "test_resource" "foo" { func TestContext2Apply_moduleVariableOptionalAttributes(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` +terraform { + experiments = [module_variable_optional_attrs] +} + variable "in" { type = object({ - required = string - optional = optional(string) - default = optional(bool, true) - nested = optional( - map(object({ - a = optional(string, "foo") - b = optional(number, 5) - })), { - "boop": {} - } - ) + required = string + optional = optional(string) }) } @@ -12049,69 +12023,6 @@ output "out" { // Because "optional" was marked as optional, it got silently filled // in as a null value of string type rather than returning an error. "optional": cty.NullVal(cty.String), - - // Similarly, "default" was marked as optional with a default value, - // and since it was omitted should be filled in with that default. - "default": cty.True, - - // Nested is a complex structure which has fully described defaults, - // so again it should be filled with the default structure. - "nested": cty.MapVal(map[string]cty.Value{ - "boop": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.NumberIntVal(5), - }), - }), - }) - if !want.RawEquals(got) { - t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want) - } -} - -func TestContext2Apply_moduleVariableOptionalAttributesDefault(t *testing.T) { - m := testModuleInline(t, map[string]string{ - "main.tf": ` -variable "in" { - type = object({ - required = string - optional = optional(string) - default = optional(bool, true) - }) - default = { - required = "boop" - } -} - -output "out" { - value = var.in -} -`}) - - ctx := testContext2(t, &ContextOpts{}) - - // We don't specify a value for the variable here, relying on its defined - // default. - plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables))) - if diags.HasErrors() { - t.Fatal(diags.ErrWithWarnings()) - } - - state, diags := ctx.Apply(plan, m) - if diags.HasErrors() { - t.Fatal(diags.ErrWithWarnings()) - } - - got := state.RootModule().OutputValues["out"].Value - want := cty.ObjectVal(map[string]cty.Value{ - "required": cty.StringVal("boop"), - - // "optional" is not present in the variable default, so it is filled - // with null. - "optional": cty.NullVal(cty.String), - - // Similarly, "default" is not present in the variable default, so its - // value is replaced with the type's specified default. - "default": cty.True, }) if !want.RawEquals(got) { t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want) diff --git a/internal/terraform/context_import.go b/internal/terraform/context_import.go index ce5df08f5fb3..d809d6bb9dca 100644 --- a/internal/terraform/context_import.go +++ b/internal/terraform/context_import.go @@ -56,13 +56,11 @@ func (c *Context) Import(config *configs.Config, prevRunState *states.State, opt variables := opts.SetVariables // Initialize our graph builder - builder := &PlanGraphBuilder{ + builder := &ImportGraphBuilder{ ImportTargets: opts.Targets, Config: config, - State: state, RootVariableValues: variables, Plugins: c.plugins, - Operation: walkImport, } // Build the graph diff --git a/internal/terraform/context_import_test.go b/internal/terraform/context_import_test.go index af8ff4b856be..605010d17569 100644 --- a/internal/terraform/context_import_test.go +++ b/internal/terraform/context_import_test.go @@ -52,20 +52,9 @@ func TestContextImport_basic(t *testing.T) { } } -// import 1 of count instances in the configuration func TestContextImport_countIndex(t *testing.T) { p := testProvider("aws") - m := testModuleInline(t, map[string]string{ - "main.tf": ` -provider "aws" { - foo = "bar" -} - -resource "aws_instance" "foo" { - count = 2 -} -`}) - + m := testModule(t, "import-provider") ctx := testContext2(t, &ContextOpts{ Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), @@ -790,7 +779,7 @@ func TestContextImport_multiStateSame(t *testing.T) { } } -func TestContextImport_nestedModuleImport(t *testing.T) { +func TestContextImport_noConfigModuleImport(t *testing.T) { p := testProvider("aws") m := testModuleInline(t, map[string]string{ "main.tf": ` @@ -808,9 +797,6 @@ module "b" { source = "./b" y = module.a[each.key].y } - -resource "test_resource" "test" { -} `, "a/main.tf": ` output "y" { @@ -824,7 +810,6 @@ variable "y" { resource "test_resource" "unused" { value = var.y - // missing required, but should not error } `, }) @@ -838,8 +823,7 @@ resource "test_resource" "unused" { ResourceTypes: map[string]*configschema.Block{ "test_resource": { Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "required": {Type: cty.String, Required: true}, + "id": {Type: cty.String, Computed: true}, }, }, }, @@ -850,8 +834,17 @@ resource "test_resource" "unused" { { TypeName: "test_resource", State: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("test"), - "required": cty.StringVal("value"), + "id": cty.StringVal("test"), + }), + }, + }, + } + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "test_resource", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), }), }, }, @@ -878,7 +871,7 @@ resource "test_resource" "unused" { } ri := state.ResourceInstance(mustResourceInstanceAddr("test_resource.test")) - expected := `{"id":"test","required":"value"}` + expected := `{"id":"test"}` if ri == nil || ri.Current == nil { t.Fatal("no state is recorded for resource instance test_resource.test") } diff --git a/internal/terraform/context_plan.go b/internal/terraform/context_plan.go index 66e22d90bff1..12168901baab 100644 --- a/internal/terraform/context_plan.go +++ b/internal/terraform/context_plan.go @@ -410,7 +410,7 @@ func (c *Context) prePlanVerifyTargetedMoves(moveResults refactoring.MoveResults var diags tfdiags.Diagnostics var excluded []addrs.AbsResourceInstance - for _, result := range moveResults.Changes.Values() { + for _, result := range moveResults.Changes { fromMatchesTarget := false toMatchesTarget := false for _, targetAddr := range targets { @@ -522,7 +522,7 @@ func (c *Context) planWalk(config *configs.Config, prevRunState *states.State, o } diags = diags.Append(moveValidateDiags) // might just contain warnings - if moveResults.Blocked.Len() > 0 && !diags.HasErrors() { + if len(moveResults.Blocked) > 0 && !diags.HasErrors() { // If we had blocked moves and we're not going to be returning errors // then we'll report the blockers as a warning. We do this only in the // absense of errors because invalid move statements might well be @@ -560,7 +560,6 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State, Targets: opts.Targets, ForceReplace: opts.ForceReplace, skipRefresh: opts.SkipRefresh, - Operation: walkPlan, }).Build(addrs.RootModuleInstance) return graph, walkPlan, diags case plans.RefreshOnlyMode: @@ -572,18 +571,16 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State, Targets: opts.Targets, skipRefresh: opts.SkipRefresh, skipPlanChanges: true, // this activates "refresh only" mode. - Operation: walkPlan, }).Build(addrs.RootModuleInstance) return graph, walkPlan, diags case plans.DestroyMode: - graph, diags := (&PlanGraphBuilder{ + graph, diags := (&DestroyPlanGraphBuilder{ Config: config, State: prevRunState, RootVariableValues: opts.SetVariables, Plugins: c.plugins, Targets: opts.Targets, skipRefresh: opts.SkipRefresh, - Operation: walkPlanDestroy, }).Build(addrs.RootModuleInstance) return graph, walkPlanDestroy, diags default: @@ -595,7 +592,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State, func (c *Context) driftedResources(config *configs.Config, oldState, newState *states.State, moves refactoring.MoveResults) ([]*plans.ResourceInstanceChangeSrc, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - if newState.ManagedResourcesEqual(oldState) && moves.Changes.Len() == 0 { + if newState.ManagedResourcesEqual(oldState) && len(moves.Changes) == 0 { // Nothing to do, because we only detect and report drift for managed // resource instances. return nil, diags @@ -627,7 +624,7 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s // Previous run address defaults to the current address, but // can differ if the resource moved before refreshing prevRunAddr := addr - if move, ok := moves.Changes.GetOk(addr); ok { + if move, ok := moves.Changes[addr.UniqueKey()]; ok { prevRunAddr = move.From } @@ -752,13 +749,13 @@ func (c *Context) PlanGraphForUI(config *configs.Config, prevRunState *states.St } func blockedMovesWarningDiag(results refactoring.MoveResults) tfdiags.Diagnostic { - if results.Blocked.Len() < 1 { + if len(results.Blocked) < 1 { // Caller should check first panic("request to render blocked moves warning without any blocked moves") } var itemsBuf bytes.Buffer - for _, blocked := range results.Blocked.Values() { + for _, blocked := range results.Blocked { fmt.Fprintf(&itemsBuf, "\n - %s could not move to %s", blocked.Actual, blocked.Wanted) } diff --git a/internal/terraform/context_plan_test.go b/internal/terraform/context_plan_test.go index 2323dcc3b445..d05c2eb75c8d 100644 --- a/internal/terraform/context_plan_test.go +++ b/internal/terraform/context_plan_test.go @@ -3093,15 +3093,6 @@ func TestContext2Plan_forEachUnknownValue(t *testing.T) { if !strings.Contains(gotErrStr, wantErrStr) { t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) } - - // We should have a diagnostic that is marked as being caused by unknown - // values. - for _, diag := range diags { - if tfdiags.DiagnosticCausedByUnknown(diag) { - return // don't fall through to the error below - } - } - t.Fatalf("no diagnostic is marked as being caused by unknown\n%s", diags.Err().Error()) } func TestContext2Plan_destroy(t *testing.T) { diff --git a/internal/terraform/context_test.go b/internal/terraform/context_test.go index 12c376622b1c..52a254b0b1e1 100644 --- a/internal/terraform/context_test.go +++ b/internal/terraform/context_test.go @@ -276,14 +276,6 @@ func testApplyFn(req providers.ApplyResourceChangeRequest) (resp providers.Apply func testDiffFn(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { var planned map[string]cty.Value - - // this is a destroy plan - if req.ProposedNewState.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp - } - if !req.ProposedNewState.IsNull() { planned = req.ProposedNewState.AsValueMap() } diff --git a/internal/terraform/context_validate.go b/internal/terraform/context_validate.go index aad884442a39..070694920ca5 100644 --- a/internal/terraform/context_validate.go +++ b/internal/terraform/context_validate.go @@ -55,12 +55,11 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics { } } - graph, moreDiags := (&PlanGraphBuilder{ + graph, moreDiags := ValidateGraphBuilder(&PlanGraphBuilder{ Config: config, Plugins: c.plugins, State: states.NewState(), RootVariableValues: varValues, - Operation: walkValidate, }).Build(addrs.RootModuleInstance) diags = diags.Append(moreDiags) if moreDiags.HasErrors() { diff --git a/internal/terraform/context_walk.go b/internal/terraform/context_walk.go index cc61331cbb97..1bc7491d0661 100644 --- a/internal/terraform/context_walk.go +++ b/internal/terraform/context_walk.go @@ -73,7 +73,7 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con refreshState = states.NewState().SyncWrapper() prevRunState = states.NewState().SyncWrapper() - case walkPlan, walkPlanDestroy, walkImport: + case walkPlan, walkPlanDestroy: state = inputState.DeepCopy().SyncWrapper() refreshState = inputState.DeepCopy().SyncWrapper() prevRunState = inputState.DeepCopy().SyncWrapper() diff --git a/internal/terraform/diagnostics.go b/internal/terraform/diagnostics.go deleted file mode 100644 index 26f22f06ce6c..000000000000 --- a/internal/terraform/diagnostics.go +++ /dev/null @@ -1,42 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/internal/tfdiags" -) - -// This file contains some package-local helpers for working with diagnostics. -// For the main diagnostics API, see the separate "tfdiags" package. - -// diagnosticCausedByUnknown is an implementation of -// tfdiags.DiagnosticExtraBecauseUnknown which we can use in the "Extra" field -// of a diagnostic to indicate that the problem was caused by unknown values -// being involved in an expression evaluation. -// -// When using this, set the Extra to diagnosticCausedByUnknown(true) and also -// populate the EvalContext and Expression fields of the diagnostic so that -// the diagnostic renderer can use all of that information together to assist -// the user in understanding what was unknown. -type diagnosticCausedByUnknown bool - -var _ tfdiags.DiagnosticExtraBecauseUnknown = diagnosticCausedByUnknown(true) - -func (e diagnosticCausedByUnknown) DiagnosticCausedByUnknown() bool { - return bool(e) -} - -// diagnosticCausedBySensitive is an implementation of -// tfdiags.DiagnosticExtraBecauseSensitive which we can use in the "Extra" field -// of a diagnostic to indicate that the problem was caused by sensitive values -// being involved in an expression evaluation. -// -// When using this, set the Extra to diagnosticCausedBySensitive(true) and also -// populate the EvalContext and Expression fields of the diagnostic so that -// the diagnostic renderer can use all of that information together to assist -// the user in understanding what was sensitive. -type diagnosticCausedBySensitive bool - -var _ tfdiags.DiagnosticExtraBecauseSensitive = diagnosticCausedBySensitive(true) - -func (e diagnosticCausedBySensitive) DiagnosticCausedBySensitive() bool { - return bool(e) -} diff --git a/internal/terraform/eval_count.go b/internal/terraform/eval_count.go index d4ab1a998f60..c74a051b301f 100644 --- a/internal/terraform/eval_count.go +++ b/internal/terraform/eval_count.go @@ -31,12 +31,6 @@ func evaluateCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags Summary: "Invalid count argument", Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, Subject: expr.Range().Ptr(), - - // TODO: Also populate Expression and EvalContext in here, but - // we can't easily do that right now because the hcl.EvalContext - // (which is not the same as the ctx we have in scope here) is - // hidden away inside evaluateCountExpressionValue. - Extra: diagnosticCausedByUnknown(true), }) } @@ -97,7 +91,7 @@ func evaluateCountExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Val diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid count argument", - Detail: `The given "count" argument value is unsuitable: must be greater than or equal to zero.`, + Detail: `The given "count" argument value is unsuitable: negative numbers are not supported.`, Subject: expr.Range().Ptr(), }) return nullCount, diags diff --git a/internal/terraform/eval_for_each.go b/internal/terraform/eval_for_each.go index 3c80ebff01d3..ba7a8c9bca6c 100644 --- a/internal/terraform/eval_for_each.go +++ b/internal/terraform/eval_for_each.go @@ -70,7 +70,6 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU Subject: expr.Range().Ptr(), Expression: expr, EvalContext: hclCtx, - Extra: diagnosticCausedBySensitive(true), }) } @@ -110,7 +109,6 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU Subject: expr.Range().Ptr(), Expression: expr, EvalContext: hclCtx, - Extra: diagnosticCausedByUnknown(true), }) } // ensure that we have a map, and not a DynamicValue @@ -146,7 +144,6 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU Subject: expr.Range().Ptr(), Expression: expr, EvalContext: hclCtx, - Extra: diagnosticCausedByUnknown(true), }) } return cty.UnknownVal(ty), diags diff --git a/internal/terraform/eval_for_each_test.go b/internal/terraform/eval_for_each_test.go index 05dba9cabc46..be4b551cf039 100644 --- a/internal/terraform/eval_for_each_test.go +++ b/internal/terraform/eval_for_each_test.go @@ -88,45 +88,38 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { func TestEvaluateForEachExpression_errors(t *testing.T) { tests := map[string]struct { - Expr hcl.Expression - Summary, DetailSubstring string - CausedByUnknown, CausedBySensitive bool + Expr hcl.Expression + Summary, DetailSubstring string }{ "null set": { hcltest.MockExprLiteral(cty.NullVal(cty.Set(cty.String))), "Invalid for_each argument", `the given "for_each" argument value is null`, - false, false, }, "string": { hcltest.MockExprLiteral(cty.StringVal("i am definitely a set")), "Invalid for_each argument", "must be a map, or set of strings, and you have provided a value of type string", - false, false, }, "list": { hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")})), "Invalid for_each argument", "must be a map, or set of strings, and you have provided a value of type list", - false, false, }, "tuple": { hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")})), "Invalid for_each argument", "must be a map, or set of strings, and you have provided a value of type tuple", - false, false, }, "unknown string set": { hcltest.MockExprLiteral(cty.UnknownVal(cty.Set(cty.String))), "Invalid for_each argument", "set includes values derived from resource attributes that cannot be determined until apply", - true, false, }, "unknown map": { hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.Bool))), "Invalid for_each argument", "map includes keys derived from resource attributes that cannot be determined until apply", - true, false, }, "marked map": { hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ @@ -135,37 +128,31 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { }).Mark(marks.Sensitive)), "Invalid for_each argument", "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", - false, true, }, "set containing booleans": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.BoolVal(true)})), "Invalid for_each set argument", "supports maps and sets of strings, but you have provided a set containing type bool", - false, false, }, "set containing null": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.NullVal(cty.String)})), "Invalid for_each set argument", "must not contain null values", - false, false, }, "set containing unknown value": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)})), "Invalid for_each argument", "set includes values derived from resource attributes that cannot be determined until apply", - true, false, }, "set containing dynamic unknown value": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.DynamicPseudoType)})), "Invalid for_each argument", "set includes values derived from resource attributes that cannot be determined until apply", - true, false, }, "set containing marked values": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("beep").Mark(marks.Sensitive), cty.StringVal("boop")})), "Invalid for_each argument", "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", - false, true, }, } @@ -197,13 +184,6 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { } else { t.Errorf("diagnostic does not support FromExpr\ngot: %s", spew.Sdump(diags[0])) } - - if got, want := tfdiags.DiagnosticCausedByUnknown(diags[0]), test.CausedByUnknown; got != want { - t.Errorf("wrong result from tfdiags.DiagnosticCausedByUnknown\ngot: %#v\nwant: %#v", got, want) - } - if got, want := tfdiags.DiagnosticCausedBySensitive(diags[0]), test.CausedBySensitive; got != want { - t.Errorf("wrong result from tfdiags.DiagnosticCausedBySensitive\ngot: %#v\nwant: %#v", got, want) - } }) } } diff --git a/internal/terraform/eval_variable.go b/internal/terraform/eval_variable.go index 67a777430fb2..1d50b911b206 100644 --- a/internal/terraform/eval_variable.go +++ b/internal/terraform/eval_variable.go @@ -90,11 +90,6 @@ func prepareFinalInputVariableValue(addr addrs.AbsInputVariableInstance, raw *In given = defaultVal // must be set, because we checked above that the variable isn't required } - // Apply defaults from the variable's type constraint to the given value - if cfg.TypeDefaults != nil { - given = cfg.TypeDefaults.Apply(given) - } - val, err := convert.Convert(given, convertTy) if err != nil { log.Printf("[ERROR] prepareFinalInputVariableValue: %s has unsuitable type\n got: %s\n want: %s", addr, given.Type(), convertTy) diff --git a/internal/terraform/graph_builder_apply.go b/internal/terraform/graph_builder_apply.go index 619e2e14ff1e..eea3235cd1ab 100644 --- a/internal/terraform/graph_builder_apply.go +++ b/internal/terraform/graph_builder_apply.go @@ -136,7 +136,10 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { &ForcedCBDTransformer{}, // Destruction ordering - &DestroyEdgeTransformer{}, + &DestroyEdgeTransformer{ + Config: b.Config, + State: b.State, + }, &CBDEdgeTransformer{ Config: b.Config, State: b.State, diff --git a/internal/terraform/graph_builder_destroy_plan.go b/internal/terraform/graph_builder_destroy_plan.go new file mode 100644 index 000000000000..ade01c629bdc --- /dev/null +++ b/internal/terraform/graph_builder_destroy_plan.go @@ -0,0 +1,115 @@ +package terraform + +import ( + "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/dag" + "github.com/hashicorp/terraform/internal/states" + "github.com/hashicorp/terraform/internal/tfdiags" +) + +// DestroyPlanGraphBuilder implements GraphBuilder and is responsible for +// planning a pure-destroy. +// +// Planning a pure destroy operation is simple because we can ignore most +// ordering configuration and simply reverse the state. This graph mainly +// exists for targeting, because we need to walk the destroy dependencies to +// ensure we plan the required resources. Without the requirement for +// targeting, the plan could theoretically be created directly from the state. +type DestroyPlanGraphBuilder struct { + // Config is the configuration tree to build the plan from. + Config *configs.Config + + // State is the current state + State *states.State + + // RootVariableValues are the raw input values for root input variables + // given by the caller, which we'll resolve into final values as part + // of the plan walk. + RootVariableValues InputValues + + // Plugins is a library of plug-in components (providers and + // provisioners) available for use. + Plugins *contextPlugins + + // Targets are resources to target + Targets []addrs.Targetable + + // If set, skipRefresh will cause us stop skip refreshing any existing + // resource instances as part of our planning. This will cause us to fail + // to detect if an object has already been deleted outside of Terraform. + skipRefresh bool +} + +// See GraphBuilder +func (b *DestroyPlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { + return (&BasicGraphBuilder{ + Steps: b.Steps(), + Name: "DestroyPlanGraphBuilder", + }).Build(path) +} + +// See GraphBuilder +func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { + concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { + return &NodePlanDestroyableResourceInstance{ + NodeAbstractResourceInstance: a, + skipRefresh: b.skipRefresh, + } + } + concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { + return &NodePlanDeposedResourceInstanceObject{ + NodeAbstractResourceInstance: a, + DeposedKey: key, + skipRefresh: b.skipRefresh, + } + } + + concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { + return &NodeApplyableProvider{ + NodeAbstractProvider: a, + } + } + + steps := []GraphTransformer{ + // Creates nodes for the resource instances tracked in the state. + &StateTransformer{ + ConcreteCurrent: concreteResourceInstance, + ConcreteDeposed: concreteResourceInstanceDeposed, + State: b.State, + }, + + // Create the delete changes for root module outputs. + &OutputTransformer{ + Config: b.Config, + Destroy: true, + }, + + // Attach the state + &AttachStateTransformer{State: b.State}, + + // Attach the configuration to any resources + &AttachResourceConfigTransformer{Config: b.Config}, + + transformProviders(concreteProvider, b.Config), + + // Destruction ordering. We require this only so that + // targeting below will prune the correct things. + &DestroyEdgeTransformer{ + Config: b.Config, + State: b.State, + }, + + &TargetsTransformer{Targets: b.Targets}, + + // Close opened plugin connections + &CloseProviderTransformer{}, + + // Close the root module + &CloseRootModuleTransformer{}, + + &TransitiveReductionTransformer{}, + } + + return steps +} diff --git a/internal/terraform/graph_builder_import.go b/internal/terraform/graph_builder_import.go new file mode 100644 index 000000000000..79c0724f76d0 --- /dev/null +++ b/internal/terraform/graph_builder_import.go @@ -0,0 +1,101 @@ +package terraform + +import ( + "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/dag" + "github.com/hashicorp/terraform/internal/tfdiags" +) + +// ImportGraphBuilder implements GraphBuilder and is responsible for building +// a graph for importing resources into Terraform. This is a much, much +// simpler graph than a normal configuration graph. +type ImportGraphBuilder struct { + // ImportTargets are the list of resources to import. + ImportTargets []*ImportTarget + + // Module is a configuration to build the graph from. See ImportOpts.Config. + Config *configs.Config + + // RootVariableValues are the raw input values for root input variables + // given by the caller, which we'll resolve into final values as part + // of the plan walk. + RootVariableValues InputValues + + // Plugins is a library of plug-in components (providers and + // provisioners) available for use. + Plugins *contextPlugins +} + +// Build builds the graph according to the steps returned by Steps. +func (b *ImportGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { + return (&BasicGraphBuilder{ + Steps: b.Steps(), + Name: "ImportGraphBuilder", + }).Build(path) +} + +// Steps returns the ordered list of GraphTransformers that must be executed +// to build a complete graph. +func (b *ImportGraphBuilder) Steps() []GraphTransformer { + // Get the module. If we don't have one, we just use an empty tree + // so that the transform still works but does nothing. + config := b.Config + if config == nil { + config = configs.NewEmptyConfig() + } + + // Custom factory for creating providers. + concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { + return &NodeApplyableProvider{ + NodeAbstractProvider: a, + } + } + + steps := []GraphTransformer{ + // Create all our resources from the configuration and state + &ConfigTransformer{Config: config}, + + // Add dynamic values + &RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, + + // Attach the configuration to any resources + &AttachResourceConfigTransformer{Config: b.Config}, + + // Add the import steps + &ImportStateTransformer{Targets: b.ImportTargets, Config: b.Config}, + + transformProviders(concreteProvider, config), + + // Must attach schemas before ReferenceTransformer so that we can + // analyze the configuration to find references. + &AttachSchemaTransformer{Plugins: b.Plugins, Config: b.Config}, + + // Create expansion nodes for all of the module calls. This must + // come after all other transformers that create nodes representing + // objects that can belong to modules. + &ModuleExpansionTransformer{Config: b.Config}, + + // Connect so that the references are ready for targeting. We'll + // have to connect again later for providers and so on. + &ReferenceTransformer{}, + + // Make sure data sources are aware of any depends_on from the + // configuration + &attachDataResourceDependsOnTransformer{}, + + // Close opened plugin connections + &CloseProviderTransformer{}, + + // Close root module + &CloseRootModuleTransformer{}, + + // Optimize + &TransitiveReductionTransformer{}, + } + + return steps +} diff --git a/internal/terraform/graph_builder_plan.go b/internal/terraform/graph_builder_plan.go index b3a8bf5abde4..b5e080526e25 100644 --- a/internal/terraform/graph_builder_plan.go +++ b/internal/terraform/graph_builder_plan.go @@ -1,7 +1,7 @@ package terraform import ( - "log" + "sync" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" @@ -54,23 +54,20 @@ type PlanGraphBuilder struct { // where we _only_ do the refresh step.) skipPlanChanges bool - ConcreteProvider ConcreteProviderNodeFunc - ConcreteResource ConcreteResourceNodeFunc - ConcreteResourceInstance ConcreteResourceInstanceNodeFunc - ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc - ConcreteResourceInstanceDeposed ConcreteResourceInstanceDeposedNodeFunc - ConcreteModule ConcreteModuleNodeFunc + // CustomConcrete can be set to customize the node types created + // for various parts of the plan. This is useful in order to customize + // the plan behavior. + CustomConcrete bool + ConcreteProvider ConcreteProviderNodeFunc + ConcreteResource ConcreteResourceNodeFunc + ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc + ConcreteModule ConcreteModuleNodeFunc - // Plan Operation this graph will be used for. - Operation walkOperation - - // ImportTargets are the list of resources to import. - ImportTargets []*ImportTarget + once sync.Once } // See GraphBuilder func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - log.Printf("[TRACE] building graph for %s", b.Operation) return (&BasicGraphBuilder{ Steps: b.Steps(), Name: "PlanGraphBuilder", @@ -79,17 +76,16 @@ func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Dia // See GraphBuilder func (b *PlanGraphBuilder) Steps() []GraphTransformer { - switch b.Operation { - case walkPlan: - b.initPlan() - case walkPlanDestroy: - b.initDestroy() - case walkValidate: - b.initValidate() - case walkImport: - b.initImport() - default: - panic("invalid plan operation: " + b.Operation.String()) + b.once.Do(b.init) + + concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { + return &NodePlanDeposedResourceInstanceObject{ + NodeAbstractResourceInstance: a, + DeposedKey: key, + + skipRefresh: b.skipRefresh, + skipPlanChanges: b.skipPlanChanges, + } } steps := []GraphTransformer{ @@ -97,39 +93,27 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { &ConfigTransformer{ Concrete: b.ConcreteResource, Config: b.Config, - - // Resources are not added from the config on destroy. - skip: b.Operation == walkPlanDestroy, - - importTargets: b.ImportTargets, }, // Add dynamic values &RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues}, &ModuleVariableTransformer{Config: b.Config}, &LocalTransformer{Config: b.Config}, - &OutputTransformer{ - Config: b.Config, - RefreshOnly: b.skipPlanChanges, - removeRootOutputs: b.Operation == walkPlanDestroy, - }, + &OutputTransformer{Config: b.Config, RefreshOnly: b.skipPlanChanges}, // Add orphan resources &OrphanResourceInstanceTransformer{ Concrete: b.ConcreteResourceOrphan, State: b.State, Config: b.Config, - skip: b.Operation == walkPlanDestroy, }, // We also need nodes for any deposed instance objects present in the - // state, so we can plan to destroy them. (During plan this will - // intentionally skip creating nodes for _current_ objects, since - // ConfigTransformer created nodes that will do that during - // DynamicExpand.) + // state, so we can plan to destroy them. (This intentionally + // skips creating nodes for _current_ objects, since ConfigTransformer + // created nodes that will do that during DynamicExpand.) &StateTransformer{ - ConcreteCurrent: b.ConcreteResourceInstance, - ConcreteDeposed: b.ConcreteResourceInstanceDeposed, + ConcreteDeposed: concreteResourceInstanceDeposed, State: b.State, }, @@ -157,18 +141,15 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // objects that can belong to modules. &ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config}, + // Connect so that the references are ready for targeting. We'll + // have to connect again later for providers and so on. &ReferenceTransformer{}, - &AttachDependenciesTransformer{}, // Make sure data sources are aware of any depends_on from the // configuration &attachDataResourceDependsOnTransformer{}, - // DestroyEdgeTransformer is only required during a plan so that the - // TargetsTransformer can determine which nodes to keep in the graph. - &DestroyEdgeTransformer{}, - // Target &TargetsTransformer{Targets: b.Targets}, @@ -190,7 +171,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { return steps } -func (b *PlanGraphBuilder) initPlan() { +func (b *PlanGraphBuilder) init() { + // Do nothing if the user requests customizing the fields + if b.CustomConcrete { + return + } + b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { return &NodeApplyableProvider{ NodeAbstractProvider: a, @@ -213,71 +199,4 @@ func (b *PlanGraphBuilder) initPlan() { skipPlanChanges: b.skipPlanChanges, } } - - b.ConcreteResourceInstanceDeposed = func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { - return &NodePlanDeposedResourceInstanceObject{ - NodeAbstractResourceInstance: a, - DeposedKey: key, - - skipRefresh: b.skipRefresh, - skipPlanChanges: b.skipPlanChanges, - } - } -} - -func (b *PlanGraphBuilder) initDestroy() { - b.initPlan() - - b.ConcreteResourceInstance = func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodePlanDestroyableResourceInstance{ - NodeAbstractResourceInstance: a, - skipRefresh: b.skipRefresh, - } - } -} - -func (b *PlanGraphBuilder) initValidate() { - // Set the provider to the normal provider. This will ask for input. - b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { - return &NodeValidatableResource{ - NodeAbstractResource: a, - } - } - - b.ConcreteModule = func(n *nodeExpandModule) dag.Vertex { - return &nodeValidateModule{ - nodeExpandModule: *n, - } - } -} - -func (b *PlanGraphBuilder) initImport() { - b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { - return &nodeExpandPlannableResource{ - NodeAbstractResource: a, - - // For now we always skip planning changes for import, since we are - // not going to combine importing with other changes. This is - // temporary to try and maintain existing import behaviors, but - // planning will need to be allowed for more complex configurations. - skipPlanChanges: true, - - // We also skip refresh for now, since the plan output is written - // as the new state, and users are not expecting the import process - // to update any other instances in state. - skipRefresh: true, - } - } } diff --git a/internal/terraform/graph_builder_plan_test.go b/internal/terraform/graph_builder_plan_test.go index 8775e1f17518..9ec16c6ed79f 100644 --- a/internal/terraform/graph_builder_plan_test.go +++ b/internal/terraform/graph_builder_plan_test.go @@ -34,9 +34,8 @@ func TestPlanGraphBuilder(t *testing.T) { }, nil) b := &PlanGraphBuilder{ - Config: testModule(t, "graph-builder-plan-basic"), - Plugins: plugins, - Operation: walkPlan, + Config: testModule(t, "graph-builder-plan-basic"), + Plugins: plugins, } g, err := b.Build(addrs.RootModuleInstance) @@ -77,9 +76,8 @@ func TestPlanGraphBuilder_dynamicBlock(t *testing.T) { }, nil) b := &PlanGraphBuilder{ - Config: testModule(t, "graph-builder-plan-dynblock"), - Plugins: plugins, - Operation: walkPlan, + Config: testModule(t, "graph-builder-plan-dynblock"), + Plugins: plugins, } g, err := b.Build(addrs.RootModuleInstance) @@ -133,9 +131,8 @@ func TestPlanGraphBuilder_attrAsBlocks(t *testing.T) { }, nil) b := &PlanGraphBuilder{ - Config: testModule(t, "graph-builder-plan-attr-as-blocks"), - Plugins: plugins, - Operation: walkPlan, + Config: testModule(t, "graph-builder-plan-attr-as-blocks"), + Plugins: plugins, } g, err := b.Build(addrs.RootModuleInstance) @@ -176,7 +173,6 @@ func TestPlanGraphBuilder_targetModule(t *testing.T) { Targets: []addrs.Targetable{ addrs.RootModuleInstance.Child("child2", addrs.NoKey), }, - Operation: walkPlan, } g, err := b.Build(addrs.RootModuleInstance) @@ -198,9 +194,8 @@ func TestPlanGraphBuilder_forEach(t *testing.T) { }, nil) b := &PlanGraphBuilder{ - Config: testModule(t, "plan-for-each"), - Plugins: plugins, - Operation: walkPlan, + Config: testModule(t, "plan-for-each"), + Plugins: plugins, } g, err := b.Build(addrs.RootModuleInstance) @@ -233,7 +228,7 @@ local.instance_id (expand) aws_instance.web (expand) openstack_floating_ip.random (expand) provider["registry.terraform.io/hashicorp/openstack"] -output.instance_id (expand) +output.instance_id local.instance_id (expand) provider["registry.terraform.io/hashicorp/aws"] openstack_floating_ip.random (expand) @@ -243,7 +238,7 @@ provider["registry.terraform.io/hashicorp/openstack"] provider["registry.terraform.io/hashicorp/openstack"] (close) openstack_floating_ip.random (expand) root - output.instance_id (expand) + output.instance_id provider["registry.terraform.io/hashicorp/aws"] (close) provider["registry.terraform.io/hashicorp/openstack"] (close) var.foo diff --git a/internal/terraform/graph_builder_validate.go b/internal/terraform/graph_builder_validate.go new file mode 100644 index 000000000000..ff5148216c4f --- /dev/null +++ b/internal/terraform/graph_builder_validate.go @@ -0,0 +1,40 @@ +package terraform + +import ( + "github.com/hashicorp/terraform/internal/dag" +) + +// ValidateGraphBuilder creates the graph for the validate operation. +// +// ValidateGraphBuilder is based on the PlanGraphBuilder. We do this so that +// we only have to validate what we'd normally plan anyways. The +// PlanGraphBuilder given will be modified so it shouldn't be used for anything +// else after calling this function. +func ValidateGraphBuilder(p *PlanGraphBuilder) GraphBuilder { + // We're going to customize the concrete functions + p.CustomConcrete = true + + // Set the provider to the normal provider. This will ask for input. + p.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { + return &NodeApplyableProvider{ + NodeAbstractProvider: a, + } + } + + p.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { + return &NodeValidatableResource{ + NodeAbstractResource: a, + } + } + + p.ConcreteModule = func(n *nodeExpandModule) dag.Vertex { + return &nodeValidateModule{ + nodeExpandModule: *n, + } + } + + // We purposely don't set any other concrete types since they don't + // require validation. + + return p +} diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index 55b0717524bc..ebce8a872d60 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -5,8 +5,6 @@ import ( "log" "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" @@ -15,6 +13,7 @@ import ( "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" ) // nodeExpandOutput is the placeholder for a non-root module output that has @@ -60,14 +59,8 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) { // Find any recorded change for this output var change *plans.OutputChangeSrc - var outputChanges []*plans.OutputChangeSrc - if module.IsRoot() { - outputChanges = changes.GetRootOutputChanges() - } else { - parent, call := module.Call() - outputChanges = changes.GetOutputChanges(parent, call) - } - for _, c := range outputChanges { + parent, call := module.Call() + for _, c := range changes.GetOutputChanges(parent, call) { if c.Addr.String() == absAddr.String() { change = c break @@ -420,14 +413,12 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) tfdia before := cty.NullVal(cty.DynamicPseudoType) mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { - if o, ok := mod.OutputValues[n.Addr.OutputValue.Name]; ok { - sensitiveBefore = o.Sensitive - before = o.Value - } else { - // If the output was not in state, a delete change would - // be meaningless, so exit early. - return nil - + for name, o := range mod.OutputValues { + if name == n.Addr.OutputValue.Name { + sensitiveBefore = o.Sensitive + before = o.Value + break + } } } diff --git a/internal/terraform/node_output_test.go b/internal/terraform/node_output_test.go index 892427c77ce0..e2bbdec74535 100644 --- a/internal/terraform/node_output_test.go +++ b/internal/terraform/node_output_test.go @@ -5,12 +5,11 @@ import ( "testing" "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/states" + "github.com/zclconf/go-cty/cty" ) func TestNodeApplyableOutputExecute_knownValue(t *testing.T) { @@ -161,22 +160,3 @@ func TestNodeDestroyableOutputExecute(t *testing.T) { t.Fatal("Unexpected outputs in state after removal") } } - -func TestNodeDestroyableOutputExecute_notInState(t *testing.T) { - outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance) - - state := states.NewState() - - ctx := &MockEvalContext{ - StateState: state.SyncWrapper(), - } - node := NodeDestroyableOutput{Addr: outputAddr} - - diags := node.Execute(ctx, walkApply) - if diags.HasErrors() { - t.Fatalf("Unexpected error: %s", diags.Err()) - } - if state.OutputValue(outputAddr) != nil { - t.Fatal("Unexpected outputs in state after removal") - } -} diff --git a/internal/terraform/node_provider.go b/internal/terraform/node_provider.go index c5b09136cb28..2e611d5660e4 100644 --- a/internal/terraform/node_provider.go +++ b/internal/terraform/node_provider.go @@ -37,7 +37,10 @@ func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) (diag case walkValidate: log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr) return diags.Append(n.ValidateProvider(ctx, provider)) - case walkPlan, walkPlanDestroy, walkApply, walkDestroy: + case walkPlan, walkApply, walkDestroy: + // walkPlanDestroy is purposely skipped here, since the config is not + // evaluated, and the provider is not needed to create delete actions + // for all instances. log.Printf("[TRACE] NodeApplyableProvider: configuring %s", n.Addr) return diags.Append(n.ConfigureProvider(ctx, provider, false)) case walkImport: diff --git a/internal/terraform/node_resource_abstract.go b/internal/terraform/node_resource_abstract.go index 45941f13a166..1a5bd1ff5c21 100644 --- a/internal/terraform/node_resource_abstract.go +++ b/internal/terraform/node_resource_abstract.go @@ -68,9 +68,6 @@ type NodeAbstractResource struct { // The address of the provider this resource will use ResolvedProvider addrs.AbsProviderConfig - - // This resource may expand into instances which need to be imported. - importTargets []*ImportTarget } var ( @@ -127,10 +124,6 @@ func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable { return []addrs.Referenceable{n.Addr.Resource} } -func (n *NodeAbstractResource) Import(addr *ImportTarget) { - -} - // GraphNodeReferencer func (n *NodeAbstractResource) References() []*addrs.Reference { // If we have a config then we prefer to use that. diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index 7ffc310d42c0..d1e0928c00e6 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -6,8 +6,6 @@ import ( "strings" "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -18,6 +16,7 @@ import ( "github.com/hashicorp/terraform/internal/provisioners" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" ) // NodeAbstractResourceInstance represents a resource instance with no @@ -379,7 +378,6 @@ func (n *NodeAbstractResourceInstance) writeResourceInstanceStateImpl(ctx EvalCo // planDestroy returns a plain destroy diff. func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState *states.ResourceInstanceObject, deposedKey states.DeposedKey) (*plans.ResourceInstanceChange, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - var plan *plans.ResourceInstanceChange absAddr := n.Addr @@ -412,60 +410,9 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState return noop, nil } - unmarkedPriorVal, _ := currentState.Value.UnmarkDeep() - - // The config and new value are null to signify that this is a destroy - // operation. - nullVal := cty.NullVal(unmarkedPriorVal.Type()) - - provider, _, err := getProvider(ctx, n.ResolvedProvider) - if err != nil { - return plan, diags.Append(err) - } - - metaConfigVal, metaDiags := n.providerMetas(ctx) - diags = diags.Append(metaDiags) - if diags.HasErrors() { - return plan, diags - } - - // Allow the provider to check the destroy plan, and insert any necessary - // private data. - resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Resource.Type, - Config: nullVal, - PriorState: unmarkedPriorVal, - ProposedNewState: nullVal, - PriorPrivate: currentState.Private, - ProviderMeta: metaConfigVal, - }) - - // We may not have a config for all destroys, but we want to reference it in - // the diagnostics if we do. - if n.Config != nil { - resp.Diagnostics = resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()) - } - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return plan, diags - } - - // Check that the provider returned a null value here, since that is the - // only valid value for a destroy plan. - if !resp.PlannedState.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned a non-null destroy value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ResolvedProvider.Provider, n.Addr), - ), - ) - return plan, diags - } - - // Plan is always the same for a destroy. - plan = &plans.ResourceInstanceChange{ + // Plan is always the same for a destroy. We don't need the provider's + // help for this one. + plan := &plans.ResourceInstanceChange{ Addr: absAddr, PrevRunAddr: n.prevRunAddr(ctx), DeposedKey: deposedKey, @@ -474,7 +421,7 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState Before: currentState.Value, After: cty.NullVal(cty.DynamicPseudoType), }, - Private: resp.PlannedPrivate, + Private: currentState.Private, ProviderAddr: n.ResolvedProvider, } @@ -1711,7 +1658,7 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned return nil, keyData, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) } - if planned != nil && planned.Action != plans.Read && planned.Action != plans.NoOp { + if planned != nil && planned.Action != plans.Read { // If any other action gets in here then that's always a bug; this // EvalNode only deals with reading. diags = diags.Append(fmt.Errorf( @@ -1746,13 +1693,6 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned return nil, keyData, diags // failed preconditions prevent further evaluation } - if planned.Action == plans.NoOp { - // If we didn't actually plan to read this then we have nothing more - // to do; we're evaluating this only for incidentals like the - // precondition/postcondition checks. - return nil, keyData, diags - } - configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) diags = diags.Append(configDiags) if configDiags.HasErrors() { @@ -2050,19 +1990,6 @@ func (n *NodeAbstractResourceInstance) apply( state = &states.ResourceInstanceObject{} } - if applyConfig != nil { - forEach, _ := evaluateForEachExpression(applyConfig.ForEach, ctx) - keyData = EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) - } - - if change.Action == plans.NoOp { - // If this is a no-op change then we don't want to actually change - // anything, so we'll just echo back the state we were given and - // let our internal checks and updates proceed. - log.Printf("[TRACE] NodeAbstractResourceInstance.apply: skipping %s because it has no planned action", n.Addr) - return state, keyData, diags - } - provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) if err != nil { return nil, keyData, diags.Append(err) @@ -2079,6 +2006,8 @@ func (n *NodeAbstractResourceInstance) apply( configVal := cty.NullVal(cty.DynamicPseudoType) if applyConfig != nil { var configDiags tfdiags.Diagnostics + forEach, _ := evaluateForEachExpression(applyConfig.ForEach, ctx) + keyData = EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema, nil, keyData) diags = diags.Append(configDiags) if configDiags.HasErrors() { diff --git a/internal/terraform/node_resource_abstract_test.go b/internal/terraform/node_resource_abstract_test.go index 352cc1bb6ac1..42486d6134b8 100644 --- a/internal/terraform/node_resource_abstract_test.go +++ b/internal/terraform/node_resource_abstract_test.go @@ -121,9 +121,6 @@ func TestNodeAbstractResource_ReadResourceInstanceState(t *testing.T) { }, }, }) - // This test does not configure the provider, but the mock provider will - // check that this was called and report errors. - mockProvider.ConfigureProviderCalled = true tests := map[string]struct { State *states.State @@ -161,7 +158,6 @@ func TestNodeAbstractResource_ReadResourceInstanceState(t *testing.T) { ctx.StateState = test.State.SyncWrapper() ctx.PathPath = addrs.RootModuleInstance ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() - ctx.ProviderProvider = providers.Interface(mockProvider) got, readDiags := test.Node.readResourceInstanceState(ctx, test.Node.Addr.Resource.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) @@ -187,9 +183,6 @@ func TestNodeAbstractResource_ReadResourceInstanceStateDeposed(t *testing.T) { }, }, }) - // This test does not configure the provider, but the mock provider will - // check that this was called and report errors. - mockProvider.ConfigureProviderCalled = true tests := map[string]struct { State *states.State diff --git a/internal/terraform/node_resource_apply_instance.go b/internal/terraform/node_resource_apply_instance.go index 7fc6e6fc2e28..8940810d3f32 100644 --- a/internal/terraform/node_resource_apply_instance.go +++ b/internal/terraform/node_resource_apply_instance.go @@ -156,9 +156,6 @@ func (n *NodeApplyableResourceInstance) dataResourceExecute(ctx EvalContext) (di if change == nil { return diags } - if change.Action != plans.Read && change.Action != plans.NoOp { - diags = diags.Append(fmt.Errorf("nonsensical planned action %#v for %s; this is a bug in Terraform", change.Action, n.Addr)) - } // In this particular call to applyDataSource we include our planned // change, which signals that we expect this read to complete fully @@ -169,16 +166,9 @@ func (n *NodeApplyableResourceInstance) dataResourceExecute(ctx EvalContext) (di return diags } - if state != nil { - // If n.applyDataSource returned a nil state object with no accompanying - // errors then it determined that the given change doesn't require - // actually reading the data (e.g. because it was already read during - // the plan phase) and so we're only running through here to get the - // extra details like precondition/postcondition checks. - diags = diags.Append(n.writeResourceInstanceState(ctx, state, workingState)) - if diags.HasErrors() { - return diags - } + diags = diags.Append(n.writeResourceInstanceState(ctx, state, workingState)) + if diags.HasErrors() { + return diags } diags = diags.Append(n.writeChange(ctx, nil, "")) @@ -227,9 +217,6 @@ func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) if diffApply == nil || diffApply.Action == plans.Delete { return diags } - if diffApply.Action == plans.Read { - diags = diags.Append(fmt.Errorf("nonsensical planned action %#v for %s; this is a bug in Terraform", diffApply.Action, n.Addr)) - } destroy := (diffApply.Action == plans.Delete || diffApply.Action.IsReplace()) // Get the stored action for CBD if we have a plan already @@ -280,10 +267,10 @@ func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) diffApply = reducePlan(addr, diffApply, false) // reducePlan may have simplified our planned change // into a NoOp if it only requires destroying, since destroying - // is handled by NodeDestroyResourceInstance. If so, we'll - // still run through most of the logic here because we do still - // need to deal with other book-keeping such as marking the - // change as "complete", and running the author's postconditions. + // is handled by NodeDestroyResourceInstance. + if diffApply == nil || diffApply.Action == plans.NoOp { + return diags + } diags = diags.Append(n.preApplyHook(ctx, diffApply)) if diags.HasErrors() { diff --git a/internal/terraform/node_resource_plan.go b/internal/terraform/node_resource_plan.go index d478bc3ce0b9..1ee94c7435f0 100644 --- a/internal/terraform/node_resource_plan.go +++ b/internal/terraform/node_resource_plan.go @@ -322,17 +322,6 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { // The concrete resource factory we'll use concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { - // check if this node is being imported first - for _, importTarget := range n.importTargets { - if importTarget.Addr.Equal(a.Addr) { - return &graphNodeImportState{ - Addr: importTarget.Addr, - ID: importTarget.ID, - ResolvedProvider: n.ResolvedProvider, - } - } - } - // Add the config and state since we don't do that via transforms a.Config = n.Config a.ResolvedProvider = n.ResolvedProvider diff --git a/internal/terraform/provider_mock.go b/internal/terraform/provider_mock.go index 23ce9be5b5c7..b6988f6eb644 100644 --- a/internal/terraform/provider_mock.go +++ b/internal/terraform/provider_mock.go @@ -218,11 +218,6 @@ func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ p.Lock() defer p.Unlock() - if !p.ConfigureProviderCalled { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before UpgradeResourceState %q", r.TypeName)) - return resp - } - schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] if !ok { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) @@ -356,13 +351,6 @@ func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) return *p.PlanResourceChangeResponse } - // this is a destroy plan, - if r.ProposedNewState.IsNull() { - resp.PlannedState = r.ProposedNewState - resp.PlannedPrivate = r.PriorPrivate - return resp - } - schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] if !ok { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) diff --git a/internal/terraform/terraform_test.go b/internal/terraform/terraform_test.go index 419245958332..a3d81b7b6b3e 100644 --- a/internal/terraform/terraform_test.go +++ b/internal/terraform/terraform_test.go @@ -57,9 +57,6 @@ func testModuleWithSnapshot(t *testing.T, name string) (*configs.Config, *config // change its interface at this late stage. loader, _ := configload.NewLoaderForTests(t) - // We need to be able to exercise experimental features in our integration tests. - loader.AllowLanguageExperiments(true) - // Test modules usually do not refer to remote sources, and for local // sources only this ultimately just records all of the module paths // in a JSON file so that we can load them below. @@ -114,9 +111,6 @@ func testModuleInline(t *testing.T, sources map[string]string) *configs.Config { loader, cleanup := configload.NewLoaderForTests(t) defer cleanup() - // We need to be able to exercise experimental features in our integration tests. - loader.AllowLanguageExperiments(true) - // Test modules usually do not refer to remote sources, and for local // sources only this ultimately just records all of the module paths // in a JSON file so that we can load them below. diff --git a/internal/terraform/testdata/import-provider/main.tf b/internal/terraform/testdata/import-provider/main.tf index 5d41fb3e6162..ed8e3fe9fd40 100644 --- a/internal/terraform/testdata/import-provider/main.tf +++ b/internal/terraform/testdata/import-provider/main.tf @@ -3,4 +3,5 @@ provider "aws" { } resource "aws_instance" "foo" { + id = "bar" } diff --git a/internal/terraform/transform_config.go b/internal/terraform/transform_config.go index 59fa1eeea9f8..6cb42b620002 100644 --- a/internal/terraform/transform_config.go +++ b/internal/terraform/transform_config.go @@ -28,19 +28,9 @@ type ConfigTransformer struct { // Mode will only add resources that match the given mode ModeFilter bool Mode addrs.ResourceMode - - // Do not apply this transformer. - skip bool - - // configuration resources that are to be imported - importTargets []*ImportTarget } func (t *ConfigTransformer) Transform(g *Graph) error { - if t.skip { - return nil - } - // If no configuration is available, we don't do anything if t.Config == nil { return nil @@ -92,22 +82,11 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er continue } - // If any of the import targets can apply to this node's instances, - // filter them down to the applicable addresses. - var imports []*ImportTarget - configAddr := relAddr.InModule(path) - for _, i := range t.importTargets { - if target := i.Addr.ContainingResource().Config(); target.Equal(configAddr) { - imports = append(imports, i) - } - } - abstract := &NodeAbstractResource{ Addr: addrs.ConfigResource{ Resource: relAddr, Module: path, }, - importTargets: imports, } var node dag.Vertex = abstract diff --git a/internal/terraform/transform_destroy_edge.go b/internal/terraform/transform_destroy_edge.go index fc0f60b0b4c4..521acced02e7 100644 --- a/internal/terraform/transform_destroy_edge.go +++ b/internal/terraform/transform_destroy_edge.go @@ -4,6 +4,9 @@ import ( "log" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/states" + + "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" ) @@ -37,7 +40,12 @@ type GraphNodeCreator interface { // dependent resources will block parent resources from deleting. Concrete // example: VPC with subnets, the VPC can't be deleted while there are // still subnets. -type DestroyEdgeTransformer struct{} +type DestroyEdgeTransformer struct { + // These are needed to properly build the graph of dependencies + // to determine what a destroy node depends on. Any of these can be nil. + Config *configs.Config + State *states.State +} func (t *DestroyEdgeTransformer) Transform(g *Graph) error { // Build a map of what is being destroyed (by address string) to @@ -81,7 +89,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { return nil } - // Connect destroy dependencies as stored in the state + // Connect destroy despendencies as stored in the state for _, ds := range destroyers { for _, des := range ds { ri, ok := des.(GraphNodeResourceInstance) @@ -212,16 +220,6 @@ func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error { for _, v := range g.UpEdges(n) { switch v.(type) { case graphNodeExpandsInstances: - // Root module output values (which the following - // condition matches) are exempt because we know - // there is only ever exactly one instance of the - // root module, and so it's not actually important - // to expand it and so this lets us do a bit more - // pruning than we'd be able to do otherwise. - if tmp, ok := v.(graphNodeTemporaryValue); ok && !tmp.temporaryValue() { - continue - } - // expanders can always depend on module expansion // themselves return diff --git a/internal/terraform/transform_destroy_edge_test.go b/internal/terraform/transform_destroy_edge_test.go index a3690994018b..a5176a3cc7d3 100644 --- a/internal/terraform/transform_destroy_edge_test.go +++ b/internal/terraform/transform_destroy_edge_test.go @@ -5,12 +5,7 @@ import ( "strings" "testing" - "github.com/davecgh/go-spew/spew" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" - "github.com/hashicorp/terraform/internal/dag" - "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" ) @@ -42,7 +37,9 @@ func TestDestroyEdgeTransformer_basic(t *testing.T) { t.Fatal(err) } - tf := &DestroyEdgeTransformer{} + tf := &DestroyEdgeTransformer{ + Config: testModule(t, "transform-destroy-edge-basic"), + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -96,7 +93,9 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) { t.Fatal(err) } - tf := &DestroyEdgeTransformer{} + tf := &DestroyEdgeTransformer{ + Config: testModule(t, "transform-destroy-edge-multi"), + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -111,7 +110,9 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) { func TestDestroyEdgeTransformer_selfRef(t *testing.T) { g := Graph{Path: addrs.RootModuleInstance} g.Add(testDestroyNode("test_object.A")) - tf := &DestroyEdgeTransformer{} + tf := &DestroyEdgeTransformer{ + Config: testModule(t, "transform-destroy-edge-self-ref"), + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -152,7 +153,9 @@ func TestDestroyEdgeTransformer_module(t *testing.T) { t.Fatal(err) } - tf := &DestroyEdgeTransformer{} + tf := &DestroyEdgeTransformer{ + Config: testModule(t, "transform-destroy-edge-module"), + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -211,7 +214,9 @@ func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) { t.Fatal(err) } - tf := &DestroyEdgeTransformer{} + tf := &DestroyEdgeTransformer{ + Config: testModule(t, "transform-destroy-edge-module-only"), + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -279,7 +284,16 @@ func TestDestroyEdgeTransformer_destroyThenUpdate(t *testing.T) { t.Fatal(err) } - tf := &DestroyEdgeTransformer{} + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + test_string = "udpated" +} +`, + }) + tf := &DestroyEdgeTransformer{ + Config: m, + } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -296,151 +310,6 @@ test_object.B (destroy) } } -func TestPruneUnusedNodesTransformer_rootModuleOutputValues(t *testing.T) { - // This is a kinda-weird test case covering the very narrow situation - // where a root module output value depends on a resource, where we - // need to make sure that the output value doesn't block pruning of - // the resource from the graph. This special case exists because although - // root module objects are "expanders", they in practice always expand - // to exactly one instance and so don't have the usual requirement of - // needing to stick around in order to support downstream expanders - // when there are e.g. nested expanding modules. - - // In order to keep this test focused on the pruneUnusedNodesTransformer - // as much as possible we're using a minimal graph construction here which - // is just enough to get the nodes we need, but this does mean that this - // test might be invalidated by future changes to the apply graph builder, - // and so if something seems off here it might help to compare the - // following with the real apply graph transformer and verify whether - // this smaller construction is still realistic enough to be a valid test. - // It might be valid to change or remove this test to "make it work", as - // long as you verify that there is still _something_ upholding the - // invariant that a root module output value should not block a resource - // node from being pruned from the graph. - - concreteResource := func(a *NodeAbstractResource) dag.Vertex { - return &nodeExpandApplyableResource{ - NodeAbstractResource: a, - } - } - - concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodeApplyableResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - resourceInstAddr := mustResourceInstanceAddr("test.a") - providerCfgAddr := addrs.AbsProviderConfig{ - Module: addrs.RootModule, - Provider: addrs.MustParseProviderSourceString("foo/test"), - } - emptyObjDynamicVal, err := plans.NewDynamicValue(cty.EmptyObjectVal, cty.EmptyObject) - if err != nil { - t.Fatal(err) - } - nullObjDynamicVal, err := plans.NewDynamicValue(cty.NullVal(cty.EmptyObject), cty.EmptyObject) - if err != nil { - t.Fatal(err) - } - - config := testModuleInline(t, map[string]string{ - "main.tf": ` - resource "test" "a" { - } - - output "test" { - value = test.a.foo - } - `, - }) - state := states.BuildState(func(s *states.SyncState) { - s.SetResourceInstanceCurrent( - resourceInstAddr, - &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{}`), - }, - providerCfgAddr, - ) - }) - changes := plans.NewChanges() - changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{ - Addr: resourceInstAddr, - PrevRunAddr: resourceInstAddr, - ProviderAddr: providerCfgAddr, - ChangeSrc: plans.ChangeSrc{ - Action: plans.Delete, - Before: emptyObjDynamicVal, - After: nullObjDynamicVal, - }, - }) - - builder := &BasicGraphBuilder{ - Steps: []GraphTransformer{ - &ConfigTransformer{ - Concrete: concreteResource, - Config: config, - }, - &OutputTransformer{ - Config: config, - Changes: changes, - }, - &DiffTransformer{ - Concrete: concreteResourceInstance, - State: state, - Changes: changes, - }, - &ReferenceTransformer{}, - &AttachDependenciesTransformer{}, - &pruneUnusedNodesTransformer{}, - &CloseRootModuleTransformer{}, - }, - } - graph, diags := builder.Build(addrs.RootModuleInstance) - assertNoDiagnostics(t, diags) - - // At this point, thanks to pruneUnusedNodesTransformer, we should still - // have the node for the output value, but the "test.a (expand)" node - // should've been pruned in recognition of the fact that we're performing - // a destroy and therefore we only need the "test.a (destroy)" node. - - nodesByName := make(map[string]dag.Vertex) - nodesByResourceExpand := make(map[string]dag.Vertex) - for _, n := range graph.Vertices() { - name := dag.VertexName(n) - if _, exists := nodesByName[name]; exists { - t.Fatalf("multiple nodes have name %q", name) - } - nodesByName[name] = n - - if exp, ok := n.(*nodeExpandApplyableResource); ok { - addr := exp.Addr - if _, exists := nodesByResourceExpand[addr.String()]; exists { - t.Fatalf("multiple nodes are expanders for %s", addr) - } - nodesByResourceExpand[addr.String()] = exp - } - } - - // NOTE: The following is sensitive to the current name string formats we - // use for these particular node types. These names are not contractual - // so if this breaks in future it is fine to update these names to the new - // names as long as you verify first that the new names correspond to - // the same meaning as what we're assuming below. - if _, exists := nodesByName["test.a (destroy)"]; !exists { - t.Errorf("missing destroy node for resource instance test.a") - } - if _, exists := nodesByName["output.test (expand)"]; !exists { - t.Errorf("missing expand for output value 'test'") - } - - // We _must not_ have any node that expands a resource. - if len(nodesByResourceExpand) != 0 { - t.Errorf("resource expand nodes remain the graph after transform; should've been pruned\n%s", spew.Sdump(nodesByResourceExpand)) - } -} - func testDestroyNode(addrString string) GraphNodeDestroyer { instAddr := mustResourceInstanceAddr(addrString) inst := NewNodeAbstractResourceInstance(instAddr) diff --git a/internal/terraform/transform_diff.go b/internal/terraform/transform_diff.go index 894db4c51901..c65d5af96c6e 100644 --- a/internal/terraform/transform_diff.go +++ b/internal/terraform/transform_diff.go @@ -65,11 +65,7 @@ func (t *DiffTransformer) Transform(g *Graph) error { var update, delete, createBeforeDestroy bool switch rc.Action { case plans.NoOp: - // For a no-op change we don't take any action but we still - // run any condition checks associated with the object, to - // make sure that they still hold when considering the - // results of other changes. - update = true + continue case plans.Delete: delete = true case plans.DeleteThenCreate, plans.CreateThenDelete: diff --git a/internal/terraform/transform_diff_test.go b/internal/terraform/transform_diff_test.go index c7cddc452fb2..56608e500867 100644 --- a/internal/terraform/transform_diff_test.go +++ b/internal/terraform/transform_diff_test.go @@ -67,62 +67,6 @@ func TestDiffTransformer(t *testing.T) { } } -func TestDiffTransformer_noOpChange(t *testing.T) { - // "No-op" changes are how we record explicitly in a plan that we did - // indeed visit a particular resource instance during the planning phase - // and concluded that no changes were needed, as opposed to the resource - // instance not existing at all or having been excluded from planning - // entirely. - // - // We must include nodes for resource instances with no-op changes in the - // apply graph, even though they won't take any external actions, because - // there are some secondary effects such as precondition/postcondition - // checks that can refer to objects elsewhere and so might have their - // results changed even if the resource instance they are attached to - // didn't actually change directly itself. - - g := Graph{Path: addrs.RootModuleInstance} - - beforeVal, err := plans.NewDynamicValue(cty.StringVal(""), cty.String) - if err != nil { - t.Fatal(err) - } - - tf := &DiffTransformer{ - Changes: &plans.Changes{ - Resources: []*plans.ResourceInstanceChangeSrc{ - { - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), - ProviderAddr: addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModule, - }, - ChangeSrc: plans.ChangeSrc{ - // A "no-op" change has the no-op action and has the - // same object as both Before and After. - Action: plans.NoOp, - Before: beforeVal, - After: beforeVal, - }, - }, - }, - }, - } - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformDiffBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - const testTransformDiffBasicStr = ` aws_instance.foo ` diff --git a/internal/terraform/node_resource_import.go b/internal/terraform/transform_import_state.go similarity index 83% rename from internal/terraform/node_resource_import.go rename to internal/terraform/transform_import_state.go index ecf39a07e033..3aa53e22d79d 100644 --- a/internal/terraform/node_resource_import.go +++ b/internal/terraform/transform_import_state.go @@ -5,11 +5,62 @@ import ( "log" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" ) +// ImportStateTransformer is a GraphTransformer that adds nodes to the +// graph to represent the imports we want to do for resources. +type ImportStateTransformer struct { + Targets []*ImportTarget + Config *configs.Config +} + +func (t *ImportStateTransformer) Transform(g *Graph) error { + for _, target := range t.Targets { + + // This is only likely to happen in misconfigured tests + if t.Config == nil { + return fmt.Errorf("cannot import into an empty configuration") + } + + // Get the module config + modCfg := t.Config.Descendent(target.Addr.Module.Module()) + if modCfg == nil { + return fmt.Errorf("module %s not found", target.Addr.Module.Module()) + } + + providerAddr := addrs.AbsProviderConfig{ + Module: target.Addr.Module.Module(), + } + + // Try to find the resource config + rsCfg := modCfg.Module.ResourceByAddr(target.Addr.Resource.Resource) + if rsCfg != nil { + // Get the provider FQN for the resource from the resource configuration + providerAddr.Provider = rsCfg.Provider + + // Get the alias from the resource's provider local config + providerAddr.Alias = rsCfg.ProviderConfigAddr().Alias + } else { + // Resource has no matching config, so use an implied provider + // based on the resource type + rsProviderType := target.Addr.Resource.Resource.ImpliedProvider() + providerAddr.Provider = modCfg.Module.ImpliedProviderForUnqualifiedType(rsProviderType) + } + + node := &graphNodeImportState{ + Addr: target.Addr, + ID: target.ID, + ProviderAddr: providerAddr, + } + g.Add(node) + } + return nil +} + type graphNodeImportState struct { Addr addrs.AbsResourceInstance // Addr is the resource address to import into ID string // ID is the ID to import as diff --git a/internal/terraform/transform_orphan_resource.go b/internal/terraform/transform_orphan_resource.go index 974fdf0de3ed..af8302c4925c 100644 --- a/internal/terraform/transform_orphan_resource.go +++ b/internal/terraform/transform_orphan_resource.go @@ -26,16 +26,9 @@ type OrphanResourceInstanceTransformer struct { // Config is the root node in the configuration tree. We'll look up // the appropriate note in this tree using the path in each node. Config *configs.Config - - // Do not apply this transformer - skip bool } func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error { - if t.skip { - return nil - } - if t.State == nil { // If the entire state is nil, there can't be any orphans return nil diff --git a/internal/terraform/transform_output.go b/internal/terraform/transform_output.go index afc9ab09ecee..73419aea0b2e 100644 --- a/internal/terraform/transform_output.go +++ b/internal/terraform/transform_output.go @@ -21,7 +21,7 @@ type OutputTransformer struct { // If this is a planned destroy, root outputs are still in the configuration // so we need to record that we wish to remove them - removeRootOutputs bool + Destroy bool // Refresh-only mode means that any failing output preconditions are // reported as warnings rather than errors @@ -66,13 +66,13 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error { } } - destroy := t.removeRootOutputs + destroy := t.Destroy if rootChange != nil { destroy = rootChange.Action == plans.Delete } - // If this is a root output and we're destroying, we add the destroy - // node directly, as there is no need to expand. + // If this is a root output, we add the apply or destroy node directly, + // as the root modules does not expand. var node dag.Vertex switch { @@ -82,12 +82,20 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error { Config: o, } + case c.Path.IsRoot(): + node = &NodeApplyableOutput{ + Addr: addr.Absolute(addrs.RootModuleInstance), + Config: o, + Change: rootChange, + RefreshOnly: t.RefreshOnly, + } + default: node = &nodeExpandOutput{ Addr: addr, Module: c.Path, Config: o, - Destroy: t.removeRootOutputs, + Destroy: t.Destroy, RefreshOnly: t.RefreshOnly, } } diff --git a/internal/terraform/transform_provider_test.go b/internal/terraform/transform_provider_test.go index ff21685710d1..3580a548b300 100644 --- a/internal/terraform/transform_provider_test.go +++ b/internal/terraform/transform_provider_test.go @@ -49,6 +49,58 @@ func TestProviderTransformer(t *testing.T) { } } +func TestProviderTransformer_ImportModuleChild(t *testing.T) { + mod := testModule(t, "import-module") + + g := testProviderTransformerGraph(t, mod) + + { + tf := &ImportStateTransformer{ + Config: mod, + Targets: []*ImportTarget{ + &ImportTarget{ + Addr: addrs.RootModuleInstance. + Child("child", addrs.NoKey). + ResourceInstance( + addrs.ManagedResourceMode, + "aws_instance", + "foo", + addrs.NoKey, + ), + ID: "bar", + }, + }, + } + + if err := tf.Transform(g); err != nil { + t.Fatalf("err: %s", err) + } + t.Logf("graph after ImportStateTransformer:\n%s", g.String()) + } + + { + tf := &MissingProviderTransformer{} + if err := tf.Transform(g); err != nil { + t.Fatalf("err: %s", err) + } + t.Logf("graph after MissingProviderTransformer:\n%s", g.String()) + } + + { + tf := &ProviderTransformer{} + if err := tf.Transform(g); err != nil { + t.Fatalf("err: %s", err) + } + t.Logf("graph after ProviderTransformer:\n%s", g.String()) + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(testTransformImportModuleChildStr) + if actual != expected { + t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) + } +} + // Test providers with FQNs that do not match the typeName func TestProviderTransformer_fqns(t *testing.T) { for _, mod := range []string{"fqns", "fqns-module"} { @@ -489,3 +541,12 @@ module.child.module.grandchild.aws_instance.baz provider["registry.terraform.io/hashicorp/aws"].foo provider["registry.terraform.io/hashicorp/aws"].foo ` + +const testTransformImportModuleChildStr = ` +module.child.aws_instance.foo + provider["registry.terraform.io/hashicorp/aws"] +module.child.aws_instance.foo (import id "bar") + provider["registry.terraform.io/hashicorp/aws"] +module.child.module.nested.aws_instance.foo + provider["registry.terraform.io/hashicorp/aws"] +provider["registry.terraform.io/hashicorp/aws"]` diff --git a/internal/terraform/transform_reference.go b/internal/terraform/transform_reference.go index 7c45955b86f8..bd07161ca02e 100644 --- a/internal/terraform/transform_reference.go +++ b/internal/terraform/transform_reference.go @@ -114,7 +114,6 @@ func (t *ReferenceTransformer) Transform(g *Graph) error { // use their own state. continue } - parents := m.References(v) parentsDbg := make([]string, len(parents)) for i, v := range parents { @@ -125,14 +124,6 @@ func (t *ReferenceTransformer) Transform(g *Graph) error { dag.VertexName(v), parentsDbg) for _, parent := range parents { - // A destroy plan relies solely on the state, so we only need to - // ensure that temporary values are connected to get the evaluation - // order correct. Any references to destroy nodes will cause - // cycles, because they are connected in reverse order. - if _, ok := parent.(GraphNodeDestroyer); ok { - continue - } - if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(v, parent) { g.Connect(dag.BasicEdge(v, parent)) } else { diff --git a/internal/terraform/transform_targets_test.go b/internal/terraform/transform_targets_test.go index 0ed95c080978..0700bf4e8065 100644 --- a/internal/terraform/transform_targets_test.go +++ b/internal/terraform/transform_targets_test.go @@ -122,7 +122,7 @@ module.child.module.grandchild.output.id (expand) module.child.module.grandchild.aws_instance.foo module.child.output.grandchild_id (expand) module.child.module.grandchild.output.id (expand) -output.grandchild_id (expand) +output.grandchild_id module.child.output.grandchild_id (expand) `) if actual != expected { @@ -193,7 +193,7 @@ module.child.module.grandchild.output.id (expand) module.child.module.grandchild.aws_instance.foo module.child.output.grandchild_id (expand) module.child.module.grandchild.output.id (expand) -output.grandchild_id (expand) +output.grandchild_id module.child.output.grandchild_id (expand) `) if actual != expected { diff --git a/internal/tfdiags/consolidate_warnings.go b/internal/tfdiags/consolidate_warnings.go index 08d36d60b6c8..06f3d52cc022 100644 --- a/internal/tfdiags/consolidate_warnings.go +++ b/internal/tfdiags/consolidate_warnings.go @@ -119,10 +119,6 @@ func (wg *warningGroup) FromExpr() *FromExpr { return wg.Warnings[0].FromExpr() } -func (wg *warningGroup) ExtraInfo() interface{} { - return wg.Warnings[0].ExtraInfo() -} - func (wg *warningGroup) Append(diag Diagnostic) { if diag.Severity() != Warning { panic("can't append a non-warning diagnostic to a warningGroup") diff --git a/internal/tfdiags/diagnostic.go b/internal/tfdiags/diagnostic.go index f988f398b6c9..c241ab4220c4 100644 --- a/internal/tfdiags/diagnostic.go +++ b/internal/tfdiags/diagnostic.go @@ -15,13 +15,6 @@ type Diagnostic interface { // available. Returns nil if the diagnostic is not related to an // expression evaluation. FromExpr() *FromExpr - - // ExtraInfo returns the raw extra information value. This is a low-level - // API which requires some work on the part of the caller to properly - // access associated information, so in most cases it'll be more convienient - // to use the package-level ExtraInfo function to try to unpack a particular - // specialized interface from this value. - ExtraInfo() interface{} } type Severity rune diff --git a/internal/tfdiags/diagnostic_base.go b/internal/tfdiags/diagnostic_base.go index 88495290e7a8..04e56773deb2 100644 --- a/internal/tfdiags/diagnostic_base.go +++ b/internal/tfdiags/diagnostic_base.go @@ -31,7 +31,3 @@ func (d diagnosticBase) Source() Source { func (d diagnosticBase) FromExpr() *FromExpr { return nil } - -func (d diagnosticBase) ExtraInfo() interface{} { - return nil -} diff --git a/internal/tfdiags/diagnostic_extra.go b/internal/tfdiags/diagnostic_extra.go deleted file mode 100644 index 97a14746c462..000000000000 --- a/internal/tfdiags/diagnostic_extra.go +++ /dev/null @@ -1,171 +0,0 @@ -package tfdiags - -// This "Extra" idea is something we've inherited from HCL's diagnostic model, -// and so it's primarily to expose that functionality from wrapped HCL -// diagnostics but other diagnostic types could potentially implement this -// protocol too, if needed. - -// ExtraInfo tries to retrieve extra information of interface type T from -// the given diagnostic. -// -// "Extra information" is situation-specific additional contextual data which -// might allow for some special tailored reporting of particular -// diagnostics in the UI. Conventionally the extra information is provided -// as a hidden type that implements one or more interfaces which a caller -// can pass as type parameter T to retrieve a value of that type when the -// diagnostic has such an implementation. -// -// If the given diagnostic's extra value has an implementation of interface T -// then ExtraInfo returns a non-nil interface value. If there is no such -// implementation, ExtraInfo returns a nil T. -// -// Although the signature of this function does not constrain T to be an -// interface type, our convention is to only use interface types to access -// extra info in order to allow for alternative or wrapping implementations -// of the interface. -func ExtraInfo[T any](diag Diagnostic) T { - extra := diag.ExtraInfo() - if ret, ok := extra.(T); ok { - return ret - } - - // If "extra" doesn't implement T directly then we'll delegate to - // our ExtraInfoNext helper to try iteratively unwrapping it. - return ExtraInfoNext[T](extra) -} - -// ExtraInfoNext takes a value previously returned by ExtraInfo and attempts -// to find an implementation of interface T wrapped inside of it. The return -// value meaning is the same as for ExtraInfo. -// -// This is to help with the less common situation where a particular "extra" -// value might be wrapping another value implementing the same interface, -// and so callers can peel away one layer at a time until there are no more -// nested layers. -// -// Because this function is intended for searching for _nested_ implementations -// of T, ExtraInfoNext does not consider whether value "previous" directly -// implements interface T, on the assumption that the previous call to ExtraInfo -// with the same T caused "previous" to already be that result. -func ExtraInfoNext[T any](previous interface{}) T { - // As long as T is an interface type as documented, zero will always be - // a nil interface value for us to return in the non-matching case. - var zero T - - unwrapper, ok := previous.(DiagnosticExtraUnwrapper) - // If the given value isn't unwrappable then it can't possibly have - // any other info nested inside of it. - if !ok { - return zero - } - - extra := unwrapper.UnwrapDiagnosticExtra() - - // We'll keep unwrapping until we either find the interface we're - // looking for or we run out of layers of unwrapper. - for { - if ret, ok := extra.(T); ok { - return ret - } - - if unwrapper, ok := extra.(DiagnosticExtraUnwrapper); ok { - extra = unwrapper.UnwrapDiagnosticExtra() - } else { - return zero - } - } -} - -// DiagnosticExtraUnwrapper is an interface implemented by values in the -// Extra field of Diagnostic when they are wrapping another "Extra" value that -// was generated downstream. -// -// Diagnostic recipients which want to examine "Extra" values to sniff for -// particular types of extra data can either type-assert this interface -// directly and repeatedly unwrap until they recieve nil, or can use the -// helper function DiagnosticExtra. -// -// This interface intentionally matches hcl.DiagnosticExtraUnwrapper, so that -// wrapping extra values implemented using HCL's API will also work with the -// tfdiags API, but that non-HCL uses of this will not need to implement HCL -// just to get this interface. -type DiagnosticExtraUnwrapper interface { - // If the reciever is wrapping another "diagnostic extra" value, returns - // that value. Otherwise returns nil to indicate dynamically that nothing - // is wrapped. - // - // The "nothing is wrapped" condition can be signalled either by this - // method returning nil or by a type not implementing this interface at all. - // - // Implementers should never create unwrap "cycles" where a nested extra - // value returns a value that was also wrapping it. - UnwrapDiagnosticExtra() interface{} -} - -// DiagnosticExtraBecauseUnknown is an interface implemented by values in -// the Extra field of Diagnostic when the diagnostic is potentially caused by -// the presence of unknown values in an expression evaluation. -// -// Just implementing this interface is not sufficient signal, though. Callers -// must also call the DiagnosticCausedByUnknown method in order to confirm -// the result, or use the package-level function DiagnosticCausedByUnknown -// as a convenient wrapper. -type DiagnosticExtraBecauseUnknown interface { - // DiagnosticCausedByUnknown returns true if the associated diagnostic - // was caused by the presence of unknown values during an expression - // evaluation, or false otherwise. - // - // Callers might use this to tailor what contextual information they show - // alongside an error report in the UI, to avoid potential confusion - // caused by talking about the presence of unknown values if that was - // immaterial to the error. - DiagnosticCausedByUnknown() bool -} - -// DiagnosticCausedByUnknown returns true if the given diagnostic has an -// indication that it was caused by the presence of unknown values during -// an expression evaluation. -// -// This is a wrapper around checking if the diagnostic's extra info implements -// interface DiagnosticExtraBecauseUnknown and then calling its method if so. -func DiagnosticCausedByUnknown(diag Diagnostic) bool { - maybe := ExtraInfo[DiagnosticExtraBecauseUnknown](diag) - if maybe == nil { - return false - } - return maybe.DiagnosticCausedByUnknown() -} - -// DiagnosticExtraBecauseSensitive is an interface implemented by values in -// the Extra field of Diagnostic when the diagnostic is potentially caused by -// the presence of sensitive values in an expression evaluation. -// -// Just implementing this interface is not sufficient signal, though. Callers -// must also call the DiagnosticCausedBySensitive method in order to confirm -// the result, or use the package-level function DiagnosticCausedBySensitive -// as a convenient wrapper. -type DiagnosticExtraBecauseSensitive interface { - // DiagnosticCausedBySensitive returns true if the associated diagnostic - // was caused by the presence of sensitive values during an expression - // evaluation, or false otherwise. - // - // Callers might use this to tailor what contextual information they show - // alongside an error report in the UI, to avoid potential confusion - // caused by talking about the presence of sensitive values if that was - // immaterial to the error. - DiagnosticCausedBySensitive() bool -} - -// DiagnosticCausedBySensitive returns true if the given diagnostic has an -// indication that it was caused by the presence of sensitive values during -// an expression evaluation. -// -// This is a wrapper around checking if the diagnostic's extra info implements -// interface DiagnosticExtraBecauseSensitive and then calling its method if so. -func DiagnosticCausedBySensitive(diag Diagnostic) bool { - maybe := ExtraInfo[DiagnosticExtraBecauseSensitive](diag) - if maybe == nil { - return false - } - return maybe.DiagnosticCausedBySensitive() -} diff --git a/internal/tfdiags/error.go b/internal/tfdiags/error.go index 1e26bf96805e..13f7a714f42a 100644 --- a/internal/tfdiags/error.go +++ b/internal/tfdiags/error.go @@ -26,8 +26,3 @@ func (e nativeError) FromExpr() *FromExpr { // Native errors are not expression-related return nil } - -func (e nativeError) ExtraInfo() interface{} { - // Native errors don't carry any "extra information". - return nil -} diff --git a/internal/tfdiags/hcl.go b/internal/tfdiags/hcl.go index edf16b5b4d73..ad0d8220f996 100644 --- a/internal/tfdiags/hcl.go +++ b/internal/tfdiags/hcl.go @@ -50,10 +50,6 @@ func (d hclDiagnostic) FromExpr() *FromExpr { } } -func (d hclDiagnostic) ExtraInfo() interface{} { - return d.diag.Extra -} - // SourceRangeFromHCL constructs a SourceRange from the corresponding range // type within the HCL package. func SourceRangeFromHCL(hclRange hcl.Range) SourceRange { diff --git a/internal/tfdiags/rpc_friendly.go b/internal/tfdiags/rpc_friendly.go index 4c627bf98aac..485063b0c0e4 100644 --- a/internal/tfdiags/rpc_friendly.go +++ b/internal/tfdiags/rpc_friendly.go @@ -54,11 +54,6 @@ func (d rpcFriendlyDiag) FromExpr() *FromExpr { return nil } -func (d rpcFriendlyDiag) ExtraInfo() interface{} { - // RPC-friendly diagnostics always discard any "extra information". - return nil -} - func init() { gob.Register((*rpcFriendlyDiag)(nil)) } diff --git a/internal/tfdiags/simple_warning.go b/internal/tfdiags/simple_warning.go index 3c18f1924762..b0f1ecd46c60 100644 --- a/internal/tfdiags/simple_warning.go +++ b/internal/tfdiags/simple_warning.go @@ -28,8 +28,3 @@ func (e simpleWarning) FromExpr() *FromExpr { // Simple warnings are not expression-related return nil } - -func (e simpleWarning) ExtraInfo() interface{} { - // Simple warnings cannot carry extra information. - return nil -} diff --git a/internal/tfplugin5/tfplugin5.pb.go b/internal/tfplugin5/tfplugin5.pb.go index ba7fb7b47bfd..ae5ec5e9b9ae 100644 --- a/internal/tfplugin5/tfplugin5.pb.go +++ b/internal/tfplugin5/tfplugin5.pb.go @@ -1,6 +1,6 @@ -// Terraform Plugin RPC protocol version 5.3 +// Terraform Plugin RPC protocol version 5.2 // -// This file defines version 5.3 of the RPC protocol. To implement a plugin +// This file defines version 5.2 of the RPC protocol. To implement a plugin // against this protocol, copy this definition into your own codebase and // use protoc to generate stubs for your target language. // @@ -1562,12 +1562,11 @@ type GetProviderSchema_Response struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` - ServerCapabilities *GetProviderSchema_ServerCapabilities `protobuf:"bytes,6,opt,name=server_capabilities,json=serverCapabilities,proto3" json:"server_capabilities,omitempty"` + Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` } func (x *GetProviderSchema_Response) Reset() { @@ -1637,66 +1636,6 @@ func (x *GetProviderSchema_Response) GetProviderMeta() *Schema { return nil } -func (x *GetProviderSchema_Response) GetServerCapabilities() *GetProviderSchema_ServerCapabilities { - if x != nil { - return x.ServerCapabilities - } - return nil -} - -// ServerCapabilities allows providers to communicate extra information -// regarding supported protocol features. This is used to indicate -// availability of certain forward-compatible changes which may be optional -// in a major protocol version, but cannot be tested for directly. -type GetProviderSchema_ServerCapabilities struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The plan_destroy capability signals that a provider expects a call - // to PlanResourceChange when a resource is going to be destroyed. - PlanDestroy bool `protobuf:"varint,1,opt,name=plan_destroy,json=planDestroy,proto3" json:"plan_destroy,omitempty"` -} - -func (x *GetProviderSchema_ServerCapabilities) Reset() { - *x = GetProviderSchema_ServerCapabilities{} - if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetProviderSchema_ServerCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderSchema_ServerCapabilities) ProtoMessage() {} - -func (x *GetProviderSchema_ServerCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[29] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderSchema_ServerCapabilities.ProtoReflect.Descriptor instead. -func (*GetProviderSchema_ServerCapabilities) Descriptor() ([]byte, []int) { - return file_tfplugin5_proto_rawDescGZIP(), []int{6, 2} -} - -func (x *GetProviderSchema_ServerCapabilities) GetPlanDestroy() bool { - if x != nil { - return x.PlanDestroy - } - return false -} - type PrepareProviderConfig_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1708,7 +1647,7 @@ type PrepareProviderConfig_Request struct { func (x *PrepareProviderConfig_Request) Reset() { *x = PrepareProviderConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[32] + mi := &file_tfplugin5_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1721,7 +1660,7 @@ func (x *PrepareProviderConfig_Request) String() string { func (*PrepareProviderConfig_Request) ProtoMessage() {} func (x *PrepareProviderConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[32] + mi := &file_tfplugin5_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1756,7 +1695,7 @@ type PrepareProviderConfig_Response struct { func (x *PrepareProviderConfig_Response) Reset() { *x = PrepareProviderConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[33] + mi := &file_tfplugin5_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1769,7 +1708,7 @@ func (x *PrepareProviderConfig_Response) String() string { func (*PrepareProviderConfig_Response) ProtoMessage() {} func (x *PrepareProviderConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[33] + mi := &file_tfplugin5_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1818,7 +1757,7 @@ type UpgradeResourceState_Request struct { func (x *UpgradeResourceState_Request) Reset() { *x = UpgradeResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[34] + mi := &file_tfplugin5_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1831,7 +1770,7 @@ func (x *UpgradeResourceState_Request) String() string { func (*UpgradeResourceState_Request) ProtoMessage() {} func (x *UpgradeResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[34] + mi := &file_tfplugin5_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1886,7 +1825,7 @@ type UpgradeResourceState_Response struct { func (x *UpgradeResourceState_Response) Reset() { *x = UpgradeResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[35] + mi := &file_tfplugin5_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1899,7 +1838,7 @@ func (x *UpgradeResourceState_Response) String() string { func (*UpgradeResourceState_Response) ProtoMessage() {} func (x *UpgradeResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[35] + mi := &file_tfplugin5_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1941,7 +1880,7 @@ type ValidateResourceTypeConfig_Request struct { func (x *ValidateResourceTypeConfig_Request) Reset() { *x = ValidateResourceTypeConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[36] + mi := &file_tfplugin5_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1954,7 +1893,7 @@ func (x *ValidateResourceTypeConfig_Request) String() string { func (*ValidateResourceTypeConfig_Request) ProtoMessage() {} func (x *ValidateResourceTypeConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[36] + mi := &file_tfplugin5_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1995,7 +1934,7 @@ type ValidateResourceTypeConfig_Response struct { func (x *ValidateResourceTypeConfig_Response) Reset() { *x = ValidateResourceTypeConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[37] + mi := &file_tfplugin5_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2008,7 +1947,7 @@ func (x *ValidateResourceTypeConfig_Response) String() string { func (*ValidateResourceTypeConfig_Response) ProtoMessage() {} func (x *ValidateResourceTypeConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[37] + mi := &file_tfplugin5_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2043,7 +1982,7 @@ type ValidateDataSourceConfig_Request struct { func (x *ValidateDataSourceConfig_Request) Reset() { *x = ValidateDataSourceConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[38] + mi := &file_tfplugin5_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2056,7 +1995,7 @@ func (x *ValidateDataSourceConfig_Request) String() string { func (*ValidateDataSourceConfig_Request) ProtoMessage() {} func (x *ValidateDataSourceConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[38] + mi := &file_tfplugin5_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2097,7 +2036,7 @@ type ValidateDataSourceConfig_Response struct { func (x *ValidateDataSourceConfig_Response) Reset() { *x = ValidateDataSourceConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[39] + mi := &file_tfplugin5_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2110,7 +2049,7 @@ func (x *ValidateDataSourceConfig_Response) String() string { func (*ValidateDataSourceConfig_Response) ProtoMessage() {} func (x *ValidateDataSourceConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[39] + mi := &file_tfplugin5_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2145,7 +2084,7 @@ type Configure_Request struct { func (x *Configure_Request) Reset() { *x = Configure_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[40] + mi := &file_tfplugin5_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2158,7 +2097,7 @@ func (x *Configure_Request) String() string { func (*Configure_Request) ProtoMessage() {} func (x *Configure_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[40] + mi := &file_tfplugin5_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2199,7 +2138,7 @@ type Configure_Response struct { func (x *Configure_Response) Reset() { *x = Configure_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[41] + mi := &file_tfplugin5_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2212,7 +2151,7 @@ func (x *Configure_Response) String() string { func (*Configure_Response) ProtoMessage() {} func (x *Configure_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[41] + mi := &file_tfplugin5_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2249,7 +2188,7 @@ type ReadResource_Request struct { func (x *ReadResource_Request) Reset() { *x = ReadResource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[42] + mi := &file_tfplugin5_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2262,7 +2201,7 @@ func (x *ReadResource_Request) String() string { func (*ReadResource_Request) ProtoMessage() {} func (x *ReadResource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[42] + mi := &file_tfplugin5_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2319,7 +2258,7 @@ type ReadResource_Response struct { func (x *ReadResource_Response) Reset() { *x = ReadResource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[43] + mi := &file_tfplugin5_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2332,7 +2271,7 @@ func (x *ReadResource_Response) String() string { func (*ReadResource_Response) ProtoMessage() {} func (x *ReadResource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[43] + mi := &file_tfplugin5_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2385,7 +2324,7 @@ type PlanResourceChange_Request struct { func (x *PlanResourceChange_Request) Reset() { *x = PlanResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[44] + mi := &file_tfplugin5_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2398,7 +2337,7 @@ func (x *PlanResourceChange_Request) String() string { func (*PlanResourceChange_Request) ProtoMessage() {} func (x *PlanResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[44] + mi := &file_tfplugin5_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2482,7 +2421,7 @@ type PlanResourceChange_Response struct { func (x *PlanResourceChange_Response) Reset() { *x = PlanResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[45] + mi := &file_tfplugin5_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2495,7 +2434,7 @@ func (x *PlanResourceChange_Response) String() string { func (*PlanResourceChange_Response) ProtoMessage() {} func (x *PlanResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[45] + mi := &file_tfplugin5_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2562,7 +2501,7 @@ type ApplyResourceChange_Request struct { func (x *ApplyResourceChange_Request) Reset() { *x = ApplyResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[46] + mi := &file_tfplugin5_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2575,7 +2514,7 @@ func (x *ApplyResourceChange_Request) String() string { func (*ApplyResourceChange_Request) ProtoMessage() {} func (x *ApplyResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[46] + mi := &file_tfplugin5_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2658,7 +2597,7 @@ type ApplyResourceChange_Response struct { func (x *ApplyResourceChange_Response) Reset() { *x = ApplyResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[47] + mi := &file_tfplugin5_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2671,7 +2610,7 @@ func (x *ApplyResourceChange_Response) String() string { func (*ApplyResourceChange_Response) ProtoMessage() {} func (x *ApplyResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[47] + mi := &file_tfplugin5_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2727,7 +2666,7 @@ type ImportResourceState_Request struct { func (x *ImportResourceState_Request) Reset() { *x = ImportResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[48] + mi := &file_tfplugin5_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2740,7 +2679,7 @@ func (x *ImportResourceState_Request) String() string { func (*ImportResourceState_Request) ProtoMessage() {} func (x *ImportResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[48] + mi := &file_tfplugin5_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2783,7 +2722,7 @@ type ImportResourceState_ImportedResource struct { func (x *ImportResourceState_ImportedResource) Reset() { *x = ImportResourceState_ImportedResource{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[49] + mi := &file_tfplugin5_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2796,7 +2735,7 @@ func (x *ImportResourceState_ImportedResource) String() string { func (*ImportResourceState_ImportedResource) ProtoMessage() {} func (x *ImportResourceState_ImportedResource) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[49] + mi := &file_tfplugin5_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2845,7 +2784,7 @@ type ImportResourceState_Response struct { func (x *ImportResourceState_Response) Reset() { *x = ImportResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[50] + mi := &file_tfplugin5_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2858,7 +2797,7 @@ func (x *ImportResourceState_Response) String() string { func (*ImportResourceState_Response) ProtoMessage() {} func (x *ImportResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[50] + mi := &file_tfplugin5_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2901,7 +2840,7 @@ type ReadDataSource_Request struct { func (x *ReadDataSource_Request) Reset() { *x = ReadDataSource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[51] + mi := &file_tfplugin5_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2914,7 +2853,7 @@ func (x *ReadDataSource_Request) String() string { func (*ReadDataSource_Request) ProtoMessage() {} func (x *ReadDataSource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[51] + mi := &file_tfplugin5_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2963,7 +2902,7 @@ type ReadDataSource_Response struct { func (x *ReadDataSource_Response) Reset() { *x = ReadDataSource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[52] + mi := &file_tfplugin5_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2976,7 +2915,7 @@ func (x *ReadDataSource_Response) String() string { func (*ReadDataSource_Response) ProtoMessage() {} func (x *ReadDataSource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[52] + mi := &file_tfplugin5_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3015,7 +2954,7 @@ type GetProvisionerSchema_Request struct { func (x *GetProvisionerSchema_Request) Reset() { *x = GetProvisionerSchema_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[53] + mi := &file_tfplugin5_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3028,7 +2967,7 @@ func (x *GetProvisionerSchema_Request) String() string { func (*GetProvisionerSchema_Request) ProtoMessage() {} func (x *GetProvisionerSchema_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[53] + mi := &file_tfplugin5_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3056,7 +2995,7 @@ type GetProvisionerSchema_Response struct { func (x *GetProvisionerSchema_Response) Reset() { *x = GetProvisionerSchema_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[54] + mi := &file_tfplugin5_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3008,7 @@ func (x *GetProvisionerSchema_Response) String() string { func (*GetProvisionerSchema_Response) ProtoMessage() {} func (x *GetProvisionerSchema_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[54] + mi := &file_tfplugin5_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3110,7 +3049,7 @@ type ValidateProvisionerConfig_Request struct { func (x *ValidateProvisionerConfig_Request) Reset() { *x = ValidateProvisionerConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[55] + mi := &file_tfplugin5_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3123,7 +3062,7 @@ func (x *ValidateProvisionerConfig_Request) String() string { func (*ValidateProvisionerConfig_Request) ProtoMessage() {} func (x *ValidateProvisionerConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[55] + mi := &file_tfplugin5_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3157,7 +3096,7 @@ type ValidateProvisionerConfig_Response struct { func (x *ValidateProvisionerConfig_Response) Reset() { *x = ValidateProvisionerConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[56] + mi := &file_tfplugin5_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3170,7 +3109,7 @@ func (x *ValidateProvisionerConfig_Response) String() string { func (*ValidateProvisionerConfig_Response) ProtoMessage() {} func (x *ValidateProvisionerConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[56] + mi := &file_tfplugin5_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3205,7 +3144,7 @@ type ProvisionResource_Request struct { func (x *ProvisionResource_Request) Reset() { *x = ProvisionResource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[57] + mi := &file_tfplugin5_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3218,7 +3157,7 @@ func (x *ProvisionResource_Request) String() string { func (*ProvisionResource_Request) ProtoMessage() {} func (x *ProvisionResource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[57] + mi := &file_tfplugin5_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3260,7 +3199,7 @@ type ProvisionResource_Response struct { func (x *ProvisionResource_Response) Reset() { *x = ProvisionResource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin5_proto_msgTypes[58] + mi := &file_tfplugin5_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3273,7 +3212,7 @@ func (x *ProvisionResource_Response) String() string { func (*ProvisionResource_Response) ProtoMessage() {} func (x *ProvisionResource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin5_proto_msgTypes[58] + mi := &file_tfplugin5_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3413,9 +3352,9 @@ var file_tfplugin5_proto_rawDesc = []byte{ 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x50, 0x10, - 0x04, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x05, 0x22, 0xeb, 0x05, 0x0a, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x05, 0x22, 0xd0, 0x04, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x91, 0x05, + 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xaf, 0x04, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, @@ -3439,61 +3378,63 @@ var file_tfplugin5_proto_rawDesc = []byte{ 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, - 0x60, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x12, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x1a, 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x37, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x5f, - 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, - 0x6c, 0x61, 0x6e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x22, 0xdb, 0x01, 0x0a, 0x15, 0x50, - 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x61, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, + 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xdb, 0x01, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x85, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x90, 0x02, + 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x72, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x08, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x1a, 0x85, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, - 0x0f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, - 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x67, - 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x1a, 0x72, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x35, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x72, 0x61, 0x77, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, - 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xba, 0x01, 0x0a, 0x1a, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xb8, 0x01, + 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, @@ -3503,305 +3444,293 @@ var file_tfplugin5_proto_rawDesc = []byte{ 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xb8, 0x01, 0x0a, 0x18, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x1a, 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, + 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, + 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x65, 0x1a, 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, - 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, - 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, - 0xe3, 0x02, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x1a, 0xbc, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, + 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xf2, 0x04, 0x0a, 0x12, 0x50, + 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x1a, 0xbb, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x64, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, + 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, - 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, - 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, - 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xf2, 0x04, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xbb, 0x02, 0x0a, - 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x45, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x77, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4e, 0x65, - 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, - 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, - 0x70, 0x72, 0x69, 0x6f, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x9d, 0x02, 0x0a, 0x08, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, - 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, - 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, - 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x92, 0x04, 0x0a, 0x13, 0x41, - 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, - 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, + 0x9d, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, + 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, - 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, - 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, - 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0xc1, 0x01, 0x0a, 0x08, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, + 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, + 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, - 0xed, 0x02, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x1a, - 0x78, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x92, 0x04, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, - 0x9c, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x1a, 0x95, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x72, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x9b, - 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x78, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, - 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x01, 0x0a, - 0x19, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, - 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x11, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x1a, 0x73, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, - 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, + 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5b, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, + 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x1a, 0xc1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, + 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, + 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x22, 0xed, 0x02, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x1a, 0x78, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, 0xa3, + 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, 0x69, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x35, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x95, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, + 0x72, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, 0x6e, - 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, - 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, 0x97, 0x09, 0x0a, 0x08, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, - 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6c, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, - 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7b, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x2e, + 0x69, 0x63, 0x73, 0x22, 0x9b, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x78, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x19, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x22, 0xe5, 0x01, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x73, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x44, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x35, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5b, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, + 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, + 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, + 0x97, 0x09, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, + 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x65, 0x70, + 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x75, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x75, 0x0a, 0x18, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, - 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, - 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x50, - 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x6c, - 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x66, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x35, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x41, 0x70, 0x70, 0x6c, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x49, 0x6d, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x35, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x53, 0x74, 0x6f, - 0x70, 0x12, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, - 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x86, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, - 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x19, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x2c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, - 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x55, 0x70, 0x67, + 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, + 0x12, 0x1c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, + 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x30, 0x01, 0x12, 0x39, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x17, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, - 0x53, 0x74, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, - 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x35, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x63, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x35, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, + 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, + 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x86, 0x03, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x19, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x35, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x39, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, + 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x35, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, + 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x35, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3817,7 +3746,7 @@ func file_tfplugin5_proto_rawDescGZIP() []byte { } var file_tfplugin5_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_tfplugin5_proto_msgTypes = make([]protoimpl.MessageInfo, 59) +var file_tfplugin5_proto_msgTypes = make([]protoimpl.MessageInfo, 58) var file_tfplugin5_proto_goTypes = []interface{}{ (StringKind)(0), // 0: tfplugin5.StringKind (Diagnostic_Severity)(0), // 1: tfplugin5.Diagnostic.Severity @@ -3851,36 +3780,35 @@ var file_tfplugin5_proto_goTypes = []interface{}{ (*Schema_NestedBlock)(nil), // 29: tfplugin5.Schema.NestedBlock (*GetProviderSchema_Request)(nil), // 30: tfplugin5.GetProviderSchema.Request (*GetProviderSchema_Response)(nil), // 31: tfplugin5.GetProviderSchema.Response - (*GetProviderSchema_ServerCapabilities)(nil), // 32: tfplugin5.GetProviderSchema.ServerCapabilities - nil, // 33: tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry - nil, // 34: tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry - (*PrepareProviderConfig_Request)(nil), // 35: tfplugin5.PrepareProviderConfig.Request - (*PrepareProviderConfig_Response)(nil), // 36: tfplugin5.PrepareProviderConfig.Response - (*UpgradeResourceState_Request)(nil), // 37: tfplugin5.UpgradeResourceState.Request - (*UpgradeResourceState_Response)(nil), // 38: tfplugin5.UpgradeResourceState.Response - (*ValidateResourceTypeConfig_Request)(nil), // 39: tfplugin5.ValidateResourceTypeConfig.Request - (*ValidateResourceTypeConfig_Response)(nil), // 40: tfplugin5.ValidateResourceTypeConfig.Response - (*ValidateDataSourceConfig_Request)(nil), // 41: tfplugin5.ValidateDataSourceConfig.Request - (*ValidateDataSourceConfig_Response)(nil), // 42: tfplugin5.ValidateDataSourceConfig.Response - (*Configure_Request)(nil), // 43: tfplugin5.Configure.Request - (*Configure_Response)(nil), // 44: tfplugin5.Configure.Response - (*ReadResource_Request)(nil), // 45: tfplugin5.ReadResource.Request - (*ReadResource_Response)(nil), // 46: tfplugin5.ReadResource.Response - (*PlanResourceChange_Request)(nil), // 47: tfplugin5.PlanResourceChange.Request - (*PlanResourceChange_Response)(nil), // 48: tfplugin5.PlanResourceChange.Response - (*ApplyResourceChange_Request)(nil), // 49: tfplugin5.ApplyResourceChange.Request - (*ApplyResourceChange_Response)(nil), // 50: tfplugin5.ApplyResourceChange.Response - (*ImportResourceState_Request)(nil), // 51: tfplugin5.ImportResourceState.Request - (*ImportResourceState_ImportedResource)(nil), // 52: tfplugin5.ImportResourceState.ImportedResource - (*ImportResourceState_Response)(nil), // 53: tfplugin5.ImportResourceState.Response - (*ReadDataSource_Request)(nil), // 54: tfplugin5.ReadDataSource.Request - (*ReadDataSource_Response)(nil), // 55: tfplugin5.ReadDataSource.Response - (*GetProvisionerSchema_Request)(nil), // 56: tfplugin5.GetProvisionerSchema.Request - (*GetProvisionerSchema_Response)(nil), // 57: tfplugin5.GetProvisionerSchema.Response - (*ValidateProvisionerConfig_Request)(nil), // 58: tfplugin5.ValidateProvisionerConfig.Request - (*ValidateProvisionerConfig_Response)(nil), // 59: tfplugin5.ValidateProvisionerConfig.Response - (*ProvisionResource_Request)(nil), // 60: tfplugin5.ProvisionResource.Request - (*ProvisionResource_Response)(nil), // 61: tfplugin5.ProvisionResource.Response + nil, // 32: tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry + nil, // 33: tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry + (*PrepareProviderConfig_Request)(nil), // 34: tfplugin5.PrepareProviderConfig.Request + (*PrepareProviderConfig_Response)(nil), // 35: tfplugin5.PrepareProviderConfig.Response + (*UpgradeResourceState_Request)(nil), // 36: tfplugin5.UpgradeResourceState.Request + (*UpgradeResourceState_Response)(nil), // 37: tfplugin5.UpgradeResourceState.Response + (*ValidateResourceTypeConfig_Request)(nil), // 38: tfplugin5.ValidateResourceTypeConfig.Request + (*ValidateResourceTypeConfig_Response)(nil), // 39: tfplugin5.ValidateResourceTypeConfig.Response + (*ValidateDataSourceConfig_Request)(nil), // 40: tfplugin5.ValidateDataSourceConfig.Request + (*ValidateDataSourceConfig_Response)(nil), // 41: tfplugin5.ValidateDataSourceConfig.Response + (*Configure_Request)(nil), // 42: tfplugin5.Configure.Request + (*Configure_Response)(nil), // 43: tfplugin5.Configure.Response + (*ReadResource_Request)(nil), // 44: tfplugin5.ReadResource.Request + (*ReadResource_Response)(nil), // 45: tfplugin5.ReadResource.Response + (*PlanResourceChange_Request)(nil), // 46: tfplugin5.PlanResourceChange.Request + (*PlanResourceChange_Response)(nil), // 47: tfplugin5.PlanResourceChange.Response + (*ApplyResourceChange_Request)(nil), // 48: tfplugin5.ApplyResourceChange.Request + (*ApplyResourceChange_Response)(nil), // 49: tfplugin5.ApplyResourceChange.Response + (*ImportResourceState_Request)(nil), // 50: tfplugin5.ImportResourceState.Request + (*ImportResourceState_ImportedResource)(nil), // 51: tfplugin5.ImportResourceState.ImportedResource + (*ImportResourceState_Response)(nil), // 52: tfplugin5.ImportResourceState.Response + (*ReadDataSource_Request)(nil), // 53: tfplugin5.ReadDataSource.Request + (*ReadDataSource_Response)(nil), // 54: tfplugin5.ReadDataSource.Response + (*GetProvisionerSchema_Request)(nil), // 55: tfplugin5.GetProvisionerSchema.Request + (*GetProvisionerSchema_Response)(nil), // 56: tfplugin5.GetProvisionerSchema.Response + (*ValidateProvisionerConfig_Request)(nil), // 57: tfplugin5.ValidateProvisionerConfig.Request + (*ValidateProvisionerConfig_Response)(nil), // 58: tfplugin5.ValidateProvisionerConfig.Response + (*ProvisionResource_Request)(nil), // 59: tfplugin5.ProvisionResource.Request + (*ProvisionResource_Response)(nil), // 60: tfplugin5.ProvisionResource.Response } var file_tfplugin5_proto_depIdxs = []int32{ 1, // 0: tfplugin5.Diagnostic.severity:type_name -> tfplugin5.Diagnostic.Severity @@ -3895,93 +3823,92 @@ var file_tfplugin5_proto_depIdxs = []int32{ 27, // 9: tfplugin5.Schema.NestedBlock.block:type_name -> tfplugin5.Schema.Block 2, // 10: tfplugin5.Schema.NestedBlock.nesting:type_name -> tfplugin5.Schema.NestedBlock.NestingMode 8, // 11: tfplugin5.GetProviderSchema.Response.provider:type_name -> tfplugin5.Schema - 33, // 12: tfplugin5.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry - 34, // 13: tfplugin5.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry + 32, // 12: tfplugin5.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry + 33, // 13: tfplugin5.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry 4, // 14: tfplugin5.GetProviderSchema.Response.diagnostics:type_name -> tfplugin5.Diagnostic 8, // 15: tfplugin5.GetProviderSchema.Response.provider_meta:type_name -> tfplugin5.Schema - 32, // 16: tfplugin5.GetProviderSchema.Response.server_capabilities:type_name -> tfplugin5.GetProviderSchema.ServerCapabilities - 8, // 17: tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin5.Schema - 8, // 18: tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin5.Schema - 3, // 19: tfplugin5.PrepareProviderConfig.Request.config:type_name -> tfplugin5.DynamicValue - 3, // 20: tfplugin5.PrepareProviderConfig.Response.prepared_config:type_name -> tfplugin5.DynamicValue - 4, // 21: tfplugin5.PrepareProviderConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 7, // 22: tfplugin5.UpgradeResourceState.Request.raw_state:type_name -> tfplugin5.RawState - 3, // 23: tfplugin5.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin5.DynamicValue - 4, // 24: tfplugin5.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 25: tfplugin5.ValidateResourceTypeConfig.Request.config:type_name -> tfplugin5.DynamicValue - 4, // 26: tfplugin5.ValidateResourceTypeConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 27: tfplugin5.ValidateDataSourceConfig.Request.config:type_name -> tfplugin5.DynamicValue - 4, // 28: tfplugin5.ValidateDataSourceConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 29: tfplugin5.Configure.Request.config:type_name -> tfplugin5.DynamicValue - 4, // 30: tfplugin5.Configure.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 31: tfplugin5.ReadResource.Request.current_state:type_name -> tfplugin5.DynamicValue - 3, // 32: tfplugin5.ReadResource.Request.provider_meta:type_name -> tfplugin5.DynamicValue - 3, // 33: tfplugin5.ReadResource.Response.new_state:type_name -> tfplugin5.DynamicValue - 4, // 34: tfplugin5.ReadResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 35: tfplugin5.PlanResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue - 3, // 36: tfplugin5.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin5.DynamicValue - 3, // 37: tfplugin5.PlanResourceChange.Request.config:type_name -> tfplugin5.DynamicValue - 3, // 38: tfplugin5.PlanResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue - 3, // 39: tfplugin5.PlanResourceChange.Response.planned_state:type_name -> tfplugin5.DynamicValue - 5, // 40: tfplugin5.PlanResourceChange.Response.requires_replace:type_name -> tfplugin5.AttributePath - 4, // 41: tfplugin5.PlanResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 42: tfplugin5.ApplyResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue - 3, // 43: tfplugin5.ApplyResourceChange.Request.planned_state:type_name -> tfplugin5.DynamicValue - 3, // 44: tfplugin5.ApplyResourceChange.Request.config:type_name -> tfplugin5.DynamicValue - 3, // 45: tfplugin5.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue - 3, // 46: tfplugin5.ApplyResourceChange.Response.new_state:type_name -> tfplugin5.DynamicValue - 4, // 47: tfplugin5.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 48: tfplugin5.ImportResourceState.ImportedResource.state:type_name -> tfplugin5.DynamicValue - 52, // 49: tfplugin5.ImportResourceState.Response.imported_resources:type_name -> tfplugin5.ImportResourceState.ImportedResource - 4, // 50: tfplugin5.ImportResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 51: tfplugin5.ReadDataSource.Request.config:type_name -> tfplugin5.DynamicValue - 3, // 52: tfplugin5.ReadDataSource.Request.provider_meta:type_name -> tfplugin5.DynamicValue - 3, // 53: tfplugin5.ReadDataSource.Response.state:type_name -> tfplugin5.DynamicValue - 4, // 54: tfplugin5.ReadDataSource.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 8, // 55: tfplugin5.GetProvisionerSchema.Response.provisioner:type_name -> tfplugin5.Schema - 4, // 56: tfplugin5.GetProvisionerSchema.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 57: tfplugin5.ValidateProvisionerConfig.Request.config:type_name -> tfplugin5.DynamicValue - 4, // 58: tfplugin5.ValidateProvisionerConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 3, // 59: tfplugin5.ProvisionResource.Request.config:type_name -> tfplugin5.DynamicValue - 3, // 60: tfplugin5.ProvisionResource.Request.connection:type_name -> tfplugin5.DynamicValue - 4, // 61: tfplugin5.ProvisionResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic - 30, // 62: tfplugin5.Provider.GetSchema:input_type -> tfplugin5.GetProviderSchema.Request - 35, // 63: tfplugin5.Provider.PrepareProviderConfig:input_type -> tfplugin5.PrepareProviderConfig.Request - 39, // 64: tfplugin5.Provider.ValidateResourceTypeConfig:input_type -> tfplugin5.ValidateResourceTypeConfig.Request - 41, // 65: tfplugin5.Provider.ValidateDataSourceConfig:input_type -> tfplugin5.ValidateDataSourceConfig.Request - 37, // 66: tfplugin5.Provider.UpgradeResourceState:input_type -> tfplugin5.UpgradeResourceState.Request - 43, // 67: tfplugin5.Provider.Configure:input_type -> tfplugin5.Configure.Request - 45, // 68: tfplugin5.Provider.ReadResource:input_type -> tfplugin5.ReadResource.Request - 47, // 69: tfplugin5.Provider.PlanResourceChange:input_type -> tfplugin5.PlanResourceChange.Request - 49, // 70: tfplugin5.Provider.ApplyResourceChange:input_type -> tfplugin5.ApplyResourceChange.Request - 51, // 71: tfplugin5.Provider.ImportResourceState:input_type -> tfplugin5.ImportResourceState.Request - 54, // 72: tfplugin5.Provider.ReadDataSource:input_type -> tfplugin5.ReadDataSource.Request - 24, // 73: tfplugin5.Provider.Stop:input_type -> tfplugin5.Stop.Request - 56, // 74: tfplugin5.Provisioner.GetSchema:input_type -> tfplugin5.GetProvisionerSchema.Request - 58, // 75: tfplugin5.Provisioner.ValidateProvisionerConfig:input_type -> tfplugin5.ValidateProvisionerConfig.Request - 60, // 76: tfplugin5.Provisioner.ProvisionResource:input_type -> tfplugin5.ProvisionResource.Request - 24, // 77: tfplugin5.Provisioner.Stop:input_type -> tfplugin5.Stop.Request - 31, // 78: tfplugin5.Provider.GetSchema:output_type -> tfplugin5.GetProviderSchema.Response - 36, // 79: tfplugin5.Provider.PrepareProviderConfig:output_type -> tfplugin5.PrepareProviderConfig.Response - 40, // 80: tfplugin5.Provider.ValidateResourceTypeConfig:output_type -> tfplugin5.ValidateResourceTypeConfig.Response - 42, // 81: tfplugin5.Provider.ValidateDataSourceConfig:output_type -> tfplugin5.ValidateDataSourceConfig.Response - 38, // 82: tfplugin5.Provider.UpgradeResourceState:output_type -> tfplugin5.UpgradeResourceState.Response - 44, // 83: tfplugin5.Provider.Configure:output_type -> tfplugin5.Configure.Response - 46, // 84: tfplugin5.Provider.ReadResource:output_type -> tfplugin5.ReadResource.Response - 48, // 85: tfplugin5.Provider.PlanResourceChange:output_type -> tfplugin5.PlanResourceChange.Response - 50, // 86: tfplugin5.Provider.ApplyResourceChange:output_type -> tfplugin5.ApplyResourceChange.Response - 53, // 87: tfplugin5.Provider.ImportResourceState:output_type -> tfplugin5.ImportResourceState.Response - 55, // 88: tfplugin5.Provider.ReadDataSource:output_type -> tfplugin5.ReadDataSource.Response - 25, // 89: tfplugin5.Provider.Stop:output_type -> tfplugin5.Stop.Response - 57, // 90: tfplugin5.Provisioner.GetSchema:output_type -> tfplugin5.GetProvisionerSchema.Response - 59, // 91: tfplugin5.Provisioner.ValidateProvisionerConfig:output_type -> tfplugin5.ValidateProvisionerConfig.Response - 61, // 92: tfplugin5.Provisioner.ProvisionResource:output_type -> tfplugin5.ProvisionResource.Response - 25, // 93: tfplugin5.Provisioner.Stop:output_type -> tfplugin5.Stop.Response - 78, // [78:94] is the sub-list for method output_type - 62, // [62:78] is the sub-list for method input_type - 62, // [62:62] is the sub-list for extension type_name - 62, // [62:62] is the sub-list for extension extendee - 0, // [0:62] is the sub-list for field type_name + 8, // 16: tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin5.Schema + 8, // 17: tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin5.Schema + 3, // 18: tfplugin5.PrepareProviderConfig.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 19: tfplugin5.PrepareProviderConfig.Response.prepared_config:type_name -> tfplugin5.DynamicValue + 4, // 20: tfplugin5.PrepareProviderConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 7, // 21: tfplugin5.UpgradeResourceState.Request.raw_state:type_name -> tfplugin5.RawState + 3, // 22: tfplugin5.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin5.DynamicValue + 4, // 23: tfplugin5.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 24: tfplugin5.ValidateResourceTypeConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 25: tfplugin5.ValidateResourceTypeConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 26: tfplugin5.ValidateDataSourceConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 27: tfplugin5.ValidateDataSourceConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 28: tfplugin5.Configure.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 29: tfplugin5.Configure.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 30: tfplugin5.ReadResource.Request.current_state:type_name -> tfplugin5.DynamicValue + 3, // 31: tfplugin5.ReadResource.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 32: tfplugin5.ReadResource.Response.new_state:type_name -> tfplugin5.DynamicValue + 4, // 33: tfplugin5.ReadResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 34: tfplugin5.PlanResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue + 3, // 35: tfplugin5.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin5.DynamicValue + 3, // 36: tfplugin5.PlanResourceChange.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 37: tfplugin5.PlanResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 38: tfplugin5.PlanResourceChange.Response.planned_state:type_name -> tfplugin5.DynamicValue + 5, // 39: tfplugin5.PlanResourceChange.Response.requires_replace:type_name -> tfplugin5.AttributePath + 4, // 40: tfplugin5.PlanResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 41: tfplugin5.ApplyResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue + 3, // 42: tfplugin5.ApplyResourceChange.Request.planned_state:type_name -> tfplugin5.DynamicValue + 3, // 43: tfplugin5.ApplyResourceChange.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 44: tfplugin5.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 45: tfplugin5.ApplyResourceChange.Response.new_state:type_name -> tfplugin5.DynamicValue + 4, // 46: tfplugin5.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 47: tfplugin5.ImportResourceState.ImportedResource.state:type_name -> tfplugin5.DynamicValue + 51, // 48: tfplugin5.ImportResourceState.Response.imported_resources:type_name -> tfplugin5.ImportResourceState.ImportedResource + 4, // 49: tfplugin5.ImportResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 50: tfplugin5.ReadDataSource.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 51: tfplugin5.ReadDataSource.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 52: tfplugin5.ReadDataSource.Response.state:type_name -> tfplugin5.DynamicValue + 4, // 53: tfplugin5.ReadDataSource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 8, // 54: tfplugin5.GetProvisionerSchema.Response.provisioner:type_name -> tfplugin5.Schema + 4, // 55: tfplugin5.GetProvisionerSchema.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 56: tfplugin5.ValidateProvisionerConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 57: tfplugin5.ValidateProvisionerConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 58: tfplugin5.ProvisionResource.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 59: tfplugin5.ProvisionResource.Request.connection:type_name -> tfplugin5.DynamicValue + 4, // 60: tfplugin5.ProvisionResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 30, // 61: tfplugin5.Provider.GetSchema:input_type -> tfplugin5.GetProviderSchema.Request + 34, // 62: tfplugin5.Provider.PrepareProviderConfig:input_type -> tfplugin5.PrepareProviderConfig.Request + 38, // 63: tfplugin5.Provider.ValidateResourceTypeConfig:input_type -> tfplugin5.ValidateResourceTypeConfig.Request + 40, // 64: tfplugin5.Provider.ValidateDataSourceConfig:input_type -> tfplugin5.ValidateDataSourceConfig.Request + 36, // 65: tfplugin5.Provider.UpgradeResourceState:input_type -> tfplugin5.UpgradeResourceState.Request + 42, // 66: tfplugin5.Provider.Configure:input_type -> tfplugin5.Configure.Request + 44, // 67: tfplugin5.Provider.ReadResource:input_type -> tfplugin5.ReadResource.Request + 46, // 68: tfplugin5.Provider.PlanResourceChange:input_type -> tfplugin5.PlanResourceChange.Request + 48, // 69: tfplugin5.Provider.ApplyResourceChange:input_type -> tfplugin5.ApplyResourceChange.Request + 50, // 70: tfplugin5.Provider.ImportResourceState:input_type -> tfplugin5.ImportResourceState.Request + 53, // 71: tfplugin5.Provider.ReadDataSource:input_type -> tfplugin5.ReadDataSource.Request + 24, // 72: tfplugin5.Provider.Stop:input_type -> tfplugin5.Stop.Request + 55, // 73: tfplugin5.Provisioner.GetSchema:input_type -> tfplugin5.GetProvisionerSchema.Request + 57, // 74: tfplugin5.Provisioner.ValidateProvisionerConfig:input_type -> tfplugin5.ValidateProvisionerConfig.Request + 59, // 75: tfplugin5.Provisioner.ProvisionResource:input_type -> tfplugin5.ProvisionResource.Request + 24, // 76: tfplugin5.Provisioner.Stop:input_type -> tfplugin5.Stop.Request + 31, // 77: tfplugin5.Provider.GetSchema:output_type -> tfplugin5.GetProviderSchema.Response + 35, // 78: tfplugin5.Provider.PrepareProviderConfig:output_type -> tfplugin5.PrepareProviderConfig.Response + 39, // 79: tfplugin5.Provider.ValidateResourceTypeConfig:output_type -> tfplugin5.ValidateResourceTypeConfig.Response + 41, // 80: tfplugin5.Provider.ValidateDataSourceConfig:output_type -> tfplugin5.ValidateDataSourceConfig.Response + 37, // 81: tfplugin5.Provider.UpgradeResourceState:output_type -> tfplugin5.UpgradeResourceState.Response + 43, // 82: tfplugin5.Provider.Configure:output_type -> tfplugin5.Configure.Response + 45, // 83: tfplugin5.Provider.ReadResource:output_type -> tfplugin5.ReadResource.Response + 47, // 84: tfplugin5.Provider.PlanResourceChange:output_type -> tfplugin5.PlanResourceChange.Response + 49, // 85: tfplugin5.Provider.ApplyResourceChange:output_type -> tfplugin5.ApplyResourceChange.Response + 52, // 86: tfplugin5.Provider.ImportResourceState:output_type -> tfplugin5.ImportResourceState.Response + 54, // 87: tfplugin5.Provider.ReadDataSource:output_type -> tfplugin5.ReadDataSource.Response + 25, // 88: tfplugin5.Provider.Stop:output_type -> tfplugin5.Stop.Response + 56, // 89: tfplugin5.Provisioner.GetSchema:output_type -> tfplugin5.GetProvisionerSchema.Response + 58, // 90: tfplugin5.Provisioner.ValidateProvisionerConfig:output_type -> tfplugin5.ValidateProvisionerConfig.Response + 60, // 91: tfplugin5.Provisioner.ProvisionResource:output_type -> tfplugin5.ProvisionResource.Response + 25, // 92: tfplugin5.Provisioner.Stop:output_type -> tfplugin5.Stop.Response + 77, // [77:93] is the sub-list for method output_type + 61, // [61:77] is the sub-list for method input_type + 61, // [61:61] is the sub-list for extension type_name + 61, // [61:61] is the sub-list for extension extendee + 0, // [0:61] is the sub-list for field type_name } func init() { file_tfplugin5_proto_init() } @@ -4326,19 +4253,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProviderSchema_ServerCapabilities); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tfplugin5_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PrepareProviderConfig_Request); i { case 0: return &v.state @@ -4350,7 +4265,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PrepareProviderConfig_Response); i { case 0: return &v.state @@ -4362,7 +4277,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Request); i { case 0: return &v.state @@ -4374,7 +4289,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Response); i { case 0: return &v.state @@ -4386,7 +4301,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceTypeConfig_Request); i { case 0: return &v.state @@ -4398,7 +4313,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceTypeConfig_Response); i { case 0: return &v.state @@ -4410,7 +4325,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataSourceConfig_Request); i { case 0: return &v.state @@ -4422,7 +4337,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataSourceConfig_Response); i { case 0: return &v.state @@ -4434,7 +4349,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Configure_Request); i { case 0: return &v.state @@ -4446,7 +4361,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Configure_Response); i { case 0: return &v.state @@ -4458,7 +4373,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Request); i { case 0: return &v.state @@ -4470,7 +4385,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Response); i { case 0: return &v.state @@ -4482,7 +4397,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Request); i { case 0: return &v.state @@ -4494,7 +4409,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Response); i { case 0: return &v.state @@ -4506,7 +4421,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Request); i { case 0: return &v.state @@ -4518,7 +4433,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Response); i { case 0: return &v.state @@ -4530,7 +4445,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Request); i { case 0: return &v.state @@ -4542,7 +4457,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_ImportedResource); i { case 0: return &v.state @@ -4554,7 +4469,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Response); i { case 0: return &v.state @@ -4566,7 +4481,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Request); i { case 0: return &v.state @@ -4578,7 +4493,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Response); i { case 0: return &v.state @@ -4590,7 +4505,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetProvisionerSchema_Request); i { case 0: return &v.state @@ -4602,7 +4517,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetProvisionerSchema_Response); i { case 0: return &v.state @@ -4614,7 +4529,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProvisionerConfig_Request); i { case 0: return &v.state @@ -4626,7 +4541,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProvisionerConfig_Response); i { case 0: return &v.state @@ -4638,7 +4553,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProvisionResource_Request); i { case 0: return &v.state @@ -4650,7 +4565,7 @@ func file_tfplugin5_proto_init() { return nil } } - file_tfplugin5_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin5_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProvisionResource_Response); i { case 0: return &v.state @@ -4674,7 +4589,7 @@ func file_tfplugin5_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tfplugin5_proto_rawDesc, NumEnums: 3, - NumMessages: 59, + NumMessages: 58, NumExtensions: 0, NumServices: 2, }, diff --git a/internal/tfplugin5/tfplugin5.proto b/internal/tfplugin5/tfplugin5.proto index 0bdfe34554eb..2c3d0a477912 120000 --- a/internal/tfplugin5/tfplugin5.proto +++ b/internal/tfplugin5/tfplugin5.proto @@ -1 +1 @@ -../../docs/plugin-protocol/tfplugin5.3.proto \ No newline at end of file +../../docs/plugin-protocol/tfplugin5.2.proto \ No newline at end of file diff --git a/internal/tfplugin6/tfplugin6.pb.go b/internal/tfplugin6/tfplugin6.pb.go index 416e007b8996..6e87b98b2668 100644 --- a/internal/tfplugin6/tfplugin6.pb.go +++ b/internal/tfplugin6/tfplugin6.pb.go @@ -1,6 +1,6 @@ -// Terraform Plugin RPC protocol version 6.3 +// Terraform Plugin RPC protocol version 6.2 // -// This file defines version 6.3 of the RPC protocol. To implement a plugin +// This file defines version 6.2 of the RPC protocol. To implement a plugin // against this protocol, copy this definition into your own codebase and // use protoc to generate stubs for your target language. // @@ -1589,12 +1589,11 @@ type GetProviderSchema_Response struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` - ServerCapabilities *GetProviderSchema_ServerCapabilities `protobuf:"bytes,6,opt,name=server_capabilities,json=serverCapabilities,proto3" json:"server_capabilities,omitempty"` + Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` } func (x *GetProviderSchema_Response) Reset() { @@ -1664,66 +1663,6 @@ func (x *GetProviderSchema_Response) GetProviderMeta() *Schema { return nil } -func (x *GetProviderSchema_Response) GetServerCapabilities() *GetProviderSchema_ServerCapabilities { - if x != nil { - return x.ServerCapabilities - } - return nil -} - -// ServerCapabilities allows providers to communicate extra information -// regarding supported protocol features. This is used to indicate -// availability of certain forward-compatible changes which may be optional -// in a major protocol version, but cannot be tested for directly. -type GetProviderSchema_ServerCapabilities struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The plan_destroy capability signals that a provider expects a call - // to PlanResourceChange when a resource is going to be destroyed. - PlanDestroy bool `protobuf:"varint,1,opt,name=plan_destroy,json=planDestroy,proto3" json:"plan_destroy,omitempty"` -} - -func (x *GetProviderSchema_ServerCapabilities) Reset() { - *x = GetProviderSchema_ServerCapabilities{} - if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetProviderSchema_ServerCapabilities) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderSchema_ServerCapabilities) ProtoMessage() {} - -func (x *GetProviderSchema_ServerCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderSchema_ServerCapabilities.ProtoReflect.Descriptor instead. -func (*GetProviderSchema_ServerCapabilities) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{6, 2} -} - -func (x *GetProviderSchema_ServerCapabilities) GetPlanDestroy() bool { - if x != nil { - return x.PlanDestroy - } - return false -} - type ValidateProviderConfig_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1735,7 +1674,7 @@ type ValidateProviderConfig_Request struct { func (x *ValidateProviderConfig_Request) Reset() { *x = ValidateProviderConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[30] + mi := &file_tfplugin6_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1748,7 +1687,7 @@ func (x *ValidateProviderConfig_Request) String() string { func (*ValidateProviderConfig_Request) ProtoMessage() {} func (x *ValidateProviderConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[30] + mi := &file_tfplugin6_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1782,7 +1721,7 @@ type ValidateProviderConfig_Response struct { func (x *ValidateProviderConfig_Response) Reset() { *x = ValidateProviderConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[31] + mi := &file_tfplugin6_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1795,7 +1734,7 @@ func (x *ValidateProviderConfig_Response) String() string { func (*ValidateProviderConfig_Response) ProtoMessage() {} func (x *ValidateProviderConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[31] + mi := &file_tfplugin6_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1837,7 +1776,7 @@ type UpgradeResourceState_Request struct { func (x *UpgradeResourceState_Request) Reset() { *x = UpgradeResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[32] + mi := &file_tfplugin6_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1850,7 +1789,7 @@ func (x *UpgradeResourceState_Request) String() string { func (*UpgradeResourceState_Request) ProtoMessage() {} func (x *UpgradeResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[32] + mi := &file_tfplugin6_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1905,7 +1844,7 @@ type UpgradeResourceState_Response struct { func (x *UpgradeResourceState_Response) Reset() { *x = UpgradeResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[33] + mi := &file_tfplugin6_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1918,7 +1857,7 @@ func (x *UpgradeResourceState_Response) String() string { func (*UpgradeResourceState_Response) ProtoMessage() {} func (x *UpgradeResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[33] + mi := &file_tfplugin6_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1960,7 +1899,7 @@ type ValidateResourceConfig_Request struct { func (x *ValidateResourceConfig_Request) Reset() { *x = ValidateResourceConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[34] + mi := &file_tfplugin6_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1973,7 +1912,7 @@ func (x *ValidateResourceConfig_Request) String() string { func (*ValidateResourceConfig_Request) ProtoMessage() {} func (x *ValidateResourceConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[34] + mi := &file_tfplugin6_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2014,7 +1953,7 @@ type ValidateResourceConfig_Response struct { func (x *ValidateResourceConfig_Response) Reset() { *x = ValidateResourceConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[35] + mi := &file_tfplugin6_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2027,7 +1966,7 @@ func (x *ValidateResourceConfig_Response) String() string { func (*ValidateResourceConfig_Response) ProtoMessage() {} func (x *ValidateResourceConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[35] + mi := &file_tfplugin6_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2062,7 +2001,7 @@ type ValidateDataResourceConfig_Request struct { func (x *ValidateDataResourceConfig_Request) Reset() { *x = ValidateDataResourceConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[36] + mi := &file_tfplugin6_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2075,7 +2014,7 @@ func (x *ValidateDataResourceConfig_Request) String() string { func (*ValidateDataResourceConfig_Request) ProtoMessage() {} func (x *ValidateDataResourceConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[36] + mi := &file_tfplugin6_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2116,7 +2055,7 @@ type ValidateDataResourceConfig_Response struct { func (x *ValidateDataResourceConfig_Response) Reset() { *x = ValidateDataResourceConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[37] + mi := &file_tfplugin6_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2129,7 +2068,7 @@ func (x *ValidateDataResourceConfig_Response) String() string { func (*ValidateDataResourceConfig_Response) ProtoMessage() {} func (x *ValidateDataResourceConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[37] + mi := &file_tfplugin6_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2164,7 +2103,7 @@ type ConfigureProvider_Request struct { func (x *ConfigureProvider_Request) Reset() { *x = ConfigureProvider_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[38] + mi := &file_tfplugin6_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2177,7 +2116,7 @@ func (x *ConfigureProvider_Request) String() string { func (*ConfigureProvider_Request) ProtoMessage() {} func (x *ConfigureProvider_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[38] + mi := &file_tfplugin6_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2218,7 +2157,7 @@ type ConfigureProvider_Response struct { func (x *ConfigureProvider_Response) Reset() { *x = ConfigureProvider_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[39] + mi := &file_tfplugin6_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2231,7 +2170,7 @@ func (x *ConfigureProvider_Response) String() string { func (*ConfigureProvider_Response) ProtoMessage() {} func (x *ConfigureProvider_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[39] + mi := &file_tfplugin6_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2268,7 +2207,7 @@ type ReadResource_Request struct { func (x *ReadResource_Request) Reset() { *x = ReadResource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[40] + mi := &file_tfplugin6_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2281,7 +2220,7 @@ func (x *ReadResource_Request) String() string { func (*ReadResource_Request) ProtoMessage() {} func (x *ReadResource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[40] + mi := &file_tfplugin6_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2338,7 +2277,7 @@ type ReadResource_Response struct { func (x *ReadResource_Response) Reset() { *x = ReadResource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[41] + mi := &file_tfplugin6_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2351,7 +2290,7 @@ func (x *ReadResource_Response) String() string { func (*ReadResource_Response) ProtoMessage() {} func (x *ReadResource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[41] + mi := &file_tfplugin6_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2404,7 +2343,7 @@ type PlanResourceChange_Request struct { func (x *PlanResourceChange_Request) Reset() { *x = PlanResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[42] + mi := &file_tfplugin6_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2417,7 +2356,7 @@ func (x *PlanResourceChange_Request) String() string { func (*PlanResourceChange_Request) ProtoMessage() {} func (x *PlanResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[42] + mi := &file_tfplugin6_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2501,7 +2440,7 @@ type PlanResourceChange_Response struct { func (x *PlanResourceChange_Response) Reset() { *x = PlanResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[43] + mi := &file_tfplugin6_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2514,7 +2453,7 @@ func (x *PlanResourceChange_Response) String() string { func (*PlanResourceChange_Response) ProtoMessage() {} func (x *PlanResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[43] + mi := &file_tfplugin6_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2581,7 +2520,7 @@ type ApplyResourceChange_Request struct { func (x *ApplyResourceChange_Request) Reset() { *x = ApplyResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[44] + mi := &file_tfplugin6_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2594,7 +2533,7 @@ func (x *ApplyResourceChange_Request) String() string { func (*ApplyResourceChange_Request) ProtoMessage() {} func (x *ApplyResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[44] + mi := &file_tfplugin6_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2677,7 +2616,7 @@ type ApplyResourceChange_Response struct { func (x *ApplyResourceChange_Response) Reset() { *x = ApplyResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[45] + mi := &file_tfplugin6_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2690,7 +2629,7 @@ func (x *ApplyResourceChange_Response) String() string { func (*ApplyResourceChange_Response) ProtoMessage() {} func (x *ApplyResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[45] + mi := &file_tfplugin6_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2746,7 +2685,7 @@ type ImportResourceState_Request struct { func (x *ImportResourceState_Request) Reset() { *x = ImportResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[46] + mi := &file_tfplugin6_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2759,7 +2698,7 @@ func (x *ImportResourceState_Request) String() string { func (*ImportResourceState_Request) ProtoMessage() {} func (x *ImportResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[46] + mi := &file_tfplugin6_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2802,7 +2741,7 @@ type ImportResourceState_ImportedResource struct { func (x *ImportResourceState_ImportedResource) Reset() { *x = ImportResourceState_ImportedResource{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[47] + mi := &file_tfplugin6_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2815,7 +2754,7 @@ func (x *ImportResourceState_ImportedResource) String() string { func (*ImportResourceState_ImportedResource) ProtoMessage() {} func (x *ImportResourceState_ImportedResource) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[47] + mi := &file_tfplugin6_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2864,7 +2803,7 @@ type ImportResourceState_Response struct { func (x *ImportResourceState_Response) Reset() { *x = ImportResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[48] + mi := &file_tfplugin6_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2877,7 +2816,7 @@ func (x *ImportResourceState_Response) String() string { func (*ImportResourceState_Response) ProtoMessage() {} func (x *ImportResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[48] + mi := &file_tfplugin6_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2920,7 +2859,7 @@ type ReadDataSource_Request struct { func (x *ReadDataSource_Request) Reset() { *x = ReadDataSource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[49] + mi := &file_tfplugin6_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2933,7 +2872,7 @@ func (x *ReadDataSource_Request) String() string { func (*ReadDataSource_Request) ProtoMessage() {} func (x *ReadDataSource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[49] + mi := &file_tfplugin6_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2982,7 +2921,7 @@ type ReadDataSource_Response struct { func (x *ReadDataSource_Response) Reset() { *x = ReadDataSource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[50] + mi := &file_tfplugin6_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2995,7 +2934,7 @@ func (x *ReadDataSource_Response) String() string { func (*ReadDataSource_Response) ProtoMessage() {} func (x *ReadDataSource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[50] + mi := &file_tfplugin6_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3156,10 +3095,10 @@ var file_tfplugin6_proto_rawDesc = []byte{ 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x53, - 0x45, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x50, 0x10, 0x04, 0x22, 0xeb, 0x05, + 0x45, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x50, 0x10, 0x04, 0x22, 0xd0, 0x04, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x91, - 0x05, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, + 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xaf, + 0x04, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x10, 0x72, 0x65, @@ -3183,309 +3122,299 @@ var file_tfplugin6_proto_rawDesc = []byte{ 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x60, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x12, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x1a, 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6c, 0x61, 0x6e, - 0x5f, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, - 0x70, 0x6c, 0x61, 0x6e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x22, 0x99, 0x01, 0x0a, 0x16, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, - 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, - 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, - 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x1a, 0x72, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, - 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x72, 0x61, 0x77, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, - 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x99, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, + 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, + 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x90, 0x02, 0x0a, + 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x72, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, - 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, - 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, - 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x08, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, - 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, - 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, - 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, - 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, - 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, - 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xf2, 0x04, 0x0a, 0x12, 0x50, - 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x1a, 0xbb, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, - 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, + 0xb6, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, - 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x65, 0x64, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, - 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, - 0x9d, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, - 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, 0x67, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, + 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, - 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, - 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, - 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, - 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, - 0x92, 0x04, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, - 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, - 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, - 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, - 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x1a, 0xc1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, - 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, + 0xf2, 0x04, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xbb, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, + 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x12, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x50, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x9d, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x43, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, + 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, + 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x22, 0xed, 0x02, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, 0x07, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x1a, 0x78, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x74, 0x65, 0x6d, 0x22, 0x92, 0x04, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, 0xa3, - 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, 0x69, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, - 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x95, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, - 0x72, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, 0x6e, - 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, - 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, 0xcc, 0x09, 0x0a, 0x08, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, + 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, + 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0xc1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, + 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0xed, 0x02, 0x0a, 0x13, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x1a, 0x36, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x1a, 0x78, 0x0a, 0x10, 0x49, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x5e, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x49, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x11, 0x69, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x02, 0x0a, 0x0e, 0x52, 0x65, + 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x95, 0x01, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x72, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, + 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, + 0xcc, 0x09, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x60, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, + 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x1a, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, - 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, - 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, - 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, - 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, - 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7b, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, + 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, + 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x53, + 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, + 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3501,7 +3430,7 @@ func file_tfplugin6_proto_rawDescGZIP() []byte { } var file_tfplugin6_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_tfplugin6_proto_msgTypes = make([]protoimpl.MessageInfo, 51) +var file_tfplugin6_proto_msgTypes = make([]protoimpl.MessageInfo, 50) var file_tfplugin6_proto_goTypes = []interface{}{ (StringKind)(0), // 0: tfplugin6.StringKind (Diagnostic_Severity)(0), // 1: tfplugin6.Diagnostic.Severity @@ -3534,30 +3463,29 @@ var file_tfplugin6_proto_goTypes = []interface{}{ (*Schema_Object)(nil), // 28: tfplugin6.Schema.Object (*GetProviderSchema_Request)(nil), // 29: tfplugin6.GetProviderSchema.Request (*GetProviderSchema_Response)(nil), // 30: tfplugin6.GetProviderSchema.Response - (*GetProviderSchema_ServerCapabilities)(nil), // 31: tfplugin6.GetProviderSchema.ServerCapabilities - nil, // 32: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry - nil, // 33: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry - (*ValidateProviderConfig_Request)(nil), // 34: tfplugin6.ValidateProviderConfig.Request - (*ValidateProviderConfig_Response)(nil), // 35: tfplugin6.ValidateProviderConfig.Response - (*UpgradeResourceState_Request)(nil), // 36: tfplugin6.UpgradeResourceState.Request - (*UpgradeResourceState_Response)(nil), // 37: tfplugin6.UpgradeResourceState.Response - (*ValidateResourceConfig_Request)(nil), // 38: tfplugin6.ValidateResourceConfig.Request - (*ValidateResourceConfig_Response)(nil), // 39: tfplugin6.ValidateResourceConfig.Response - (*ValidateDataResourceConfig_Request)(nil), // 40: tfplugin6.ValidateDataResourceConfig.Request - (*ValidateDataResourceConfig_Response)(nil), // 41: tfplugin6.ValidateDataResourceConfig.Response - (*ConfigureProvider_Request)(nil), // 42: tfplugin6.ConfigureProvider.Request - (*ConfigureProvider_Response)(nil), // 43: tfplugin6.ConfigureProvider.Response - (*ReadResource_Request)(nil), // 44: tfplugin6.ReadResource.Request - (*ReadResource_Response)(nil), // 45: tfplugin6.ReadResource.Response - (*PlanResourceChange_Request)(nil), // 46: tfplugin6.PlanResourceChange.Request - (*PlanResourceChange_Response)(nil), // 47: tfplugin6.PlanResourceChange.Response - (*ApplyResourceChange_Request)(nil), // 48: tfplugin6.ApplyResourceChange.Request - (*ApplyResourceChange_Response)(nil), // 49: tfplugin6.ApplyResourceChange.Response - (*ImportResourceState_Request)(nil), // 50: tfplugin6.ImportResourceState.Request - (*ImportResourceState_ImportedResource)(nil), // 51: tfplugin6.ImportResourceState.ImportedResource - (*ImportResourceState_Response)(nil), // 52: tfplugin6.ImportResourceState.Response - (*ReadDataSource_Request)(nil), // 53: tfplugin6.ReadDataSource.Request - (*ReadDataSource_Response)(nil), // 54: tfplugin6.ReadDataSource.Response + nil, // 31: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + nil, // 32: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + (*ValidateProviderConfig_Request)(nil), // 33: tfplugin6.ValidateProviderConfig.Request + (*ValidateProviderConfig_Response)(nil), // 34: tfplugin6.ValidateProviderConfig.Response + (*UpgradeResourceState_Request)(nil), // 35: tfplugin6.UpgradeResourceState.Request + (*UpgradeResourceState_Response)(nil), // 36: tfplugin6.UpgradeResourceState.Response + (*ValidateResourceConfig_Request)(nil), // 37: tfplugin6.ValidateResourceConfig.Request + (*ValidateResourceConfig_Response)(nil), // 38: tfplugin6.ValidateResourceConfig.Response + (*ValidateDataResourceConfig_Request)(nil), // 39: tfplugin6.ValidateDataResourceConfig.Request + (*ValidateDataResourceConfig_Response)(nil), // 40: tfplugin6.ValidateDataResourceConfig.Response + (*ConfigureProvider_Request)(nil), // 41: tfplugin6.ConfigureProvider.Request + (*ConfigureProvider_Response)(nil), // 42: tfplugin6.ConfigureProvider.Response + (*ReadResource_Request)(nil), // 43: tfplugin6.ReadResource.Request + (*ReadResource_Response)(nil), // 44: tfplugin6.ReadResource.Response + (*PlanResourceChange_Request)(nil), // 45: tfplugin6.PlanResourceChange.Request + (*PlanResourceChange_Response)(nil), // 46: tfplugin6.PlanResourceChange.Response + (*ApplyResourceChange_Request)(nil), // 47: tfplugin6.ApplyResourceChange.Request + (*ApplyResourceChange_Response)(nil), // 48: tfplugin6.ApplyResourceChange.Response + (*ImportResourceState_Request)(nil), // 49: tfplugin6.ImportResourceState.Request + (*ImportResourceState_ImportedResource)(nil), // 50: tfplugin6.ImportResourceState.ImportedResource + (*ImportResourceState_Response)(nil), // 51: tfplugin6.ImportResourceState.Response + (*ReadDataSource_Request)(nil), // 52: tfplugin6.ReadDataSource.Request + (*ReadDataSource_Response)(nil), // 53: tfplugin6.ReadDataSource.Response } var file_tfplugin6_proto_depIdxs = []int32{ 1, // 0: tfplugin6.Diagnostic.severity:type_name -> tfplugin6.Diagnostic.Severity @@ -3575,77 +3503,76 @@ var file_tfplugin6_proto_depIdxs = []int32{ 26, // 12: tfplugin6.Schema.Object.attributes:type_name -> tfplugin6.Schema.Attribute 3, // 13: tfplugin6.Schema.Object.nesting:type_name -> tfplugin6.Schema.Object.NestingMode 9, // 14: tfplugin6.GetProviderSchema.Response.provider:type_name -> tfplugin6.Schema - 32, // 15: tfplugin6.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry - 33, // 16: tfplugin6.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + 31, // 15: tfplugin6.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + 32, // 16: tfplugin6.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry 5, // 17: tfplugin6.GetProviderSchema.Response.diagnostics:type_name -> tfplugin6.Diagnostic 9, // 18: tfplugin6.GetProviderSchema.Response.provider_meta:type_name -> tfplugin6.Schema - 31, // 19: tfplugin6.GetProviderSchema.Response.server_capabilities:type_name -> tfplugin6.GetProviderSchema.ServerCapabilities - 9, // 20: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin6.Schema - 9, // 21: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin6.Schema - 4, // 22: tfplugin6.ValidateProviderConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 23: tfplugin6.ValidateProviderConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 8, // 24: tfplugin6.UpgradeResourceState.Request.raw_state:type_name -> tfplugin6.RawState - 4, // 25: tfplugin6.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin6.DynamicValue - 5, // 26: tfplugin6.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 27: tfplugin6.ValidateResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 28: tfplugin6.ValidateResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 29: tfplugin6.ValidateDataResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 30: tfplugin6.ValidateDataResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 31: tfplugin6.ConfigureProvider.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 32: tfplugin6.ConfigureProvider.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 33: tfplugin6.ReadResource.Request.current_state:type_name -> tfplugin6.DynamicValue - 4, // 34: tfplugin6.ReadResource.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 35: tfplugin6.ReadResource.Response.new_state:type_name -> tfplugin6.DynamicValue - 5, // 36: tfplugin6.ReadResource.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 37: tfplugin6.PlanResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue - 4, // 38: tfplugin6.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin6.DynamicValue - 4, // 39: tfplugin6.PlanResourceChange.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 40: tfplugin6.PlanResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 41: tfplugin6.PlanResourceChange.Response.planned_state:type_name -> tfplugin6.DynamicValue - 6, // 42: tfplugin6.PlanResourceChange.Response.requires_replace:type_name -> tfplugin6.AttributePath - 5, // 43: tfplugin6.PlanResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 44: tfplugin6.ApplyResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue - 4, // 45: tfplugin6.ApplyResourceChange.Request.planned_state:type_name -> tfplugin6.DynamicValue - 4, // 46: tfplugin6.ApplyResourceChange.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 47: tfplugin6.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 48: tfplugin6.ApplyResourceChange.Response.new_state:type_name -> tfplugin6.DynamicValue - 5, // 49: tfplugin6.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 50: tfplugin6.ImportResourceState.ImportedResource.state:type_name -> tfplugin6.DynamicValue - 51, // 51: tfplugin6.ImportResourceState.Response.imported_resources:type_name -> tfplugin6.ImportResourceState.ImportedResource - 5, // 52: tfplugin6.ImportResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 53: tfplugin6.ReadDataSource.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 54: tfplugin6.ReadDataSource.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 55: tfplugin6.ReadDataSource.Response.state:type_name -> tfplugin6.DynamicValue - 5, // 56: tfplugin6.ReadDataSource.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 29, // 57: tfplugin6.Provider.GetProviderSchema:input_type -> tfplugin6.GetProviderSchema.Request - 34, // 58: tfplugin6.Provider.ValidateProviderConfig:input_type -> tfplugin6.ValidateProviderConfig.Request - 38, // 59: tfplugin6.Provider.ValidateResourceConfig:input_type -> tfplugin6.ValidateResourceConfig.Request - 40, // 60: tfplugin6.Provider.ValidateDataResourceConfig:input_type -> tfplugin6.ValidateDataResourceConfig.Request - 36, // 61: tfplugin6.Provider.UpgradeResourceState:input_type -> tfplugin6.UpgradeResourceState.Request - 42, // 62: tfplugin6.Provider.ConfigureProvider:input_type -> tfplugin6.ConfigureProvider.Request - 44, // 63: tfplugin6.Provider.ReadResource:input_type -> tfplugin6.ReadResource.Request - 46, // 64: tfplugin6.Provider.PlanResourceChange:input_type -> tfplugin6.PlanResourceChange.Request - 48, // 65: tfplugin6.Provider.ApplyResourceChange:input_type -> tfplugin6.ApplyResourceChange.Request - 50, // 66: tfplugin6.Provider.ImportResourceState:input_type -> tfplugin6.ImportResourceState.Request - 53, // 67: tfplugin6.Provider.ReadDataSource:input_type -> tfplugin6.ReadDataSource.Request - 22, // 68: tfplugin6.Provider.StopProvider:input_type -> tfplugin6.StopProvider.Request - 30, // 69: tfplugin6.Provider.GetProviderSchema:output_type -> tfplugin6.GetProviderSchema.Response - 35, // 70: tfplugin6.Provider.ValidateProviderConfig:output_type -> tfplugin6.ValidateProviderConfig.Response - 39, // 71: tfplugin6.Provider.ValidateResourceConfig:output_type -> tfplugin6.ValidateResourceConfig.Response - 41, // 72: tfplugin6.Provider.ValidateDataResourceConfig:output_type -> tfplugin6.ValidateDataResourceConfig.Response - 37, // 73: tfplugin6.Provider.UpgradeResourceState:output_type -> tfplugin6.UpgradeResourceState.Response - 43, // 74: tfplugin6.Provider.ConfigureProvider:output_type -> tfplugin6.ConfigureProvider.Response - 45, // 75: tfplugin6.Provider.ReadResource:output_type -> tfplugin6.ReadResource.Response - 47, // 76: tfplugin6.Provider.PlanResourceChange:output_type -> tfplugin6.PlanResourceChange.Response - 49, // 77: tfplugin6.Provider.ApplyResourceChange:output_type -> tfplugin6.ApplyResourceChange.Response - 52, // 78: tfplugin6.Provider.ImportResourceState:output_type -> tfplugin6.ImportResourceState.Response - 54, // 79: tfplugin6.Provider.ReadDataSource:output_type -> tfplugin6.ReadDataSource.Response - 23, // 80: tfplugin6.Provider.StopProvider:output_type -> tfplugin6.StopProvider.Response - 69, // [69:81] is the sub-list for method output_type - 57, // [57:69] is the sub-list for method input_type - 57, // [57:57] is the sub-list for extension type_name - 57, // [57:57] is the sub-list for extension extendee - 0, // [0:57] is the sub-list for field type_name + 9, // 19: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin6.Schema + 9, // 20: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin6.Schema + 4, // 21: tfplugin6.ValidateProviderConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 22: tfplugin6.ValidateProviderConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 8, // 23: tfplugin6.UpgradeResourceState.Request.raw_state:type_name -> tfplugin6.RawState + 4, // 24: tfplugin6.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin6.DynamicValue + 5, // 25: tfplugin6.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 26: tfplugin6.ValidateResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 27: tfplugin6.ValidateResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 28: tfplugin6.ValidateDataResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 29: tfplugin6.ValidateDataResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 30: tfplugin6.ConfigureProvider.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 31: tfplugin6.ConfigureProvider.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 32: tfplugin6.ReadResource.Request.current_state:type_name -> tfplugin6.DynamicValue + 4, // 33: tfplugin6.ReadResource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 34: tfplugin6.ReadResource.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 35: tfplugin6.ReadResource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 36: tfplugin6.PlanResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 37: tfplugin6.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin6.DynamicValue + 4, // 38: tfplugin6.PlanResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 39: tfplugin6.PlanResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 40: tfplugin6.PlanResourceChange.Response.planned_state:type_name -> tfplugin6.DynamicValue + 6, // 41: tfplugin6.PlanResourceChange.Response.requires_replace:type_name -> tfplugin6.AttributePath + 5, // 42: tfplugin6.PlanResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 43: tfplugin6.ApplyResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 44: tfplugin6.ApplyResourceChange.Request.planned_state:type_name -> tfplugin6.DynamicValue + 4, // 45: tfplugin6.ApplyResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 46: tfplugin6.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 47: tfplugin6.ApplyResourceChange.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 48: tfplugin6.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 49: tfplugin6.ImportResourceState.ImportedResource.state:type_name -> tfplugin6.DynamicValue + 50, // 50: tfplugin6.ImportResourceState.Response.imported_resources:type_name -> tfplugin6.ImportResourceState.ImportedResource + 5, // 51: tfplugin6.ImportResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 52: tfplugin6.ReadDataSource.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 53: tfplugin6.ReadDataSource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 54: tfplugin6.ReadDataSource.Response.state:type_name -> tfplugin6.DynamicValue + 5, // 55: tfplugin6.ReadDataSource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 29, // 56: tfplugin6.Provider.GetProviderSchema:input_type -> tfplugin6.GetProviderSchema.Request + 33, // 57: tfplugin6.Provider.ValidateProviderConfig:input_type -> tfplugin6.ValidateProviderConfig.Request + 37, // 58: tfplugin6.Provider.ValidateResourceConfig:input_type -> tfplugin6.ValidateResourceConfig.Request + 39, // 59: tfplugin6.Provider.ValidateDataResourceConfig:input_type -> tfplugin6.ValidateDataResourceConfig.Request + 35, // 60: tfplugin6.Provider.UpgradeResourceState:input_type -> tfplugin6.UpgradeResourceState.Request + 41, // 61: tfplugin6.Provider.ConfigureProvider:input_type -> tfplugin6.ConfigureProvider.Request + 43, // 62: tfplugin6.Provider.ReadResource:input_type -> tfplugin6.ReadResource.Request + 45, // 63: tfplugin6.Provider.PlanResourceChange:input_type -> tfplugin6.PlanResourceChange.Request + 47, // 64: tfplugin6.Provider.ApplyResourceChange:input_type -> tfplugin6.ApplyResourceChange.Request + 49, // 65: tfplugin6.Provider.ImportResourceState:input_type -> tfplugin6.ImportResourceState.Request + 52, // 66: tfplugin6.Provider.ReadDataSource:input_type -> tfplugin6.ReadDataSource.Request + 22, // 67: tfplugin6.Provider.StopProvider:input_type -> tfplugin6.StopProvider.Request + 30, // 68: tfplugin6.Provider.GetProviderSchema:output_type -> tfplugin6.GetProviderSchema.Response + 34, // 69: tfplugin6.Provider.ValidateProviderConfig:output_type -> tfplugin6.ValidateProviderConfig.Response + 38, // 70: tfplugin6.Provider.ValidateResourceConfig:output_type -> tfplugin6.ValidateResourceConfig.Response + 40, // 71: tfplugin6.Provider.ValidateDataResourceConfig:output_type -> tfplugin6.ValidateDataResourceConfig.Response + 36, // 72: tfplugin6.Provider.UpgradeResourceState:output_type -> tfplugin6.UpgradeResourceState.Response + 42, // 73: tfplugin6.Provider.ConfigureProvider:output_type -> tfplugin6.ConfigureProvider.Response + 44, // 74: tfplugin6.Provider.ReadResource:output_type -> tfplugin6.ReadResource.Response + 46, // 75: tfplugin6.Provider.PlanResourceChange:output_type -> tfplugin6.PlanResourceChange.Response + 48, // 76: tfplugin6.Provider.ApplyResourceChange:output_type -> tfplugin6.ApplyResourceChange.Response + 51, // 77: tfplugin6.Provider.ImportResourceState:output_type -> tfplugin6.ImportResourceState.Response + 53, // 78: tfplugin6.Provider.ReadDataSource:output_type -> tfplugin6.ReadDataSource.Response + 23, // 79: tfplugin6.Provider.StopProvider:output_type -> tfplugin6.StopProvider.Response + 68, // [68:80] is the sub-list for method output_type + 56, // [56:68] is the sub-list for method input_type + 56, // [56:56] is the sub-list for extension type_name + 56, // [56:56] is the sub-list for extension extendee + 0, // [0:56] is the sub-list for field type_name } func init() { file_tfplugin6_proto_init() } @@ -3966,19 +3893,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProviderSchema_ServerCapabilities); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_tfplugin6_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProviderConfig_Request); i { case 0: return &v.state @@ -3990,7 +3905,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProviderConfig_Response); i { case 0: return &v.state @@ -4002,7 +3917,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Request); i { case 0: return &v.state @@ -4014,7 +3929,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Response); i { case 0: return &v.state @@ -4026,7 +3941,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceConfig_Request); i { case 0: return &v.state @@ -4038,7 +3953,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceConfig_Response); i { case 0: return &v.state @@ -4050,7 +3965,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataResourceConfig_Request); i { case 0: return &v.state @@ -4062,7 +3977,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataResourceConfig_Response); i { case 0: return &v.state @@ -4074,7 +3989,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ConfigureProvider_Request); i { case 0: return &v.state @@ -4086,7 +4001,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ConfigureProvider_Response); i { case 0: return &v.state @@ -4098,7 +4013,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Request); i { case 0: return &v.state @@ -4110,7 +4025,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Response); i { case 0: return &v.state @@ -4122,7 +4037,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Request); i { case 0: return &v.state @@ -4134,7 +4049,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Response); i { case 0: return &v.state @@ -4146,7 +4061,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Request); i { case 0: return &v.state @@ -4158,7 +4073,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Response); i { case 0: return &v.state @@ -4170,7 +4085,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Request); i { case 0: return &v.state @@ -4182,7 +4097,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_ImportedResource); i { case 0: return &v.state @@ -4194,7 +4109,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Response); i { case 0: return &v.state @@ -4206,7 +4121,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Request); i { case 0: return &v.state @@ -4218,7 +4133,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Response); i { case 0: return &v.state @@ -4242,7 +4157,7 @@ func file_tfplugin6_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tfplugin6_proto_rawDesc, NumEnums: 4, - NumMessages: 51, + NumMessages: 50, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/tfplugin6/tfplugin6.proto b/internal/tfplugin6/tfplugin6.proto index a7cf631c59e4..6841b7f8f178 120000 --- a/internal/tfplugin6/tfplugin6.proto +++ b/internal/tfplugin6/tfplugin6.proto @@ -1 +1 @@ -../../docs/plugin-protocol/tfplugin6.3.proto \ No newline at end of file +../../docs/plugin-protocol/tfplugin6.2.proto \ No newline at end of file diff --git a/internal/typeexpr/defaults.go b/internal/typeexpr/defaults.go deleted file mode 100644 index 851c72fbfcab..000000000000 --- a/internal/typeexpr/defaults.go +++ /dev/null @@ -1,157 +0,0 @@ -package typeexpr - -import ( - "github.com/zclconf/go-cty/cty" -) - -// Defaults represents a type tree which may contain default values for -// optional object attributes at any level. This is used to apply nested -// defaults to an input value before converting it to the concrete type. -type Defaults struct { - // Type of the node for which these defaults apply. This is necessary in - // order to determine how to inspect the Defaults and Children collections. - Type cty.Type - - // DefaultValues contains the default values for each object attribute, - // indexed by attribute name. - DefaultValues map[string]cty.Value - - // Children is a map of Defaults for elements contained in this type. This - // only applies to structural and collection types. - // - // The map is indexed by string instead of cty.Value because cty.Number - // instances are non-comparable, due to embedding a *big.Float. - // - // Collections have a single element type, which is stored at key "". - Children map[string]*Defaults -} - -// Apply walks the given value, applying specified defaults wherever optional -// attributes are missing. The input and output values may have different -// types, and the result may still require type conversion to the final desired -// type. -// -// This function is permissive and does not report errors, assuming that the -// caller will have better context to report useful type conversion failure -// diagnostics. -func (d *Defaults) Apply(val cty.Value) cty.Value { - val, err := cty.TransformWithTransformer(val, &defaultsTransformer{defaults: d}) - - // The transformer should never return an error. - if err != nil { - panic(err) - } - - return val -} - -// defaultsTransformer implements cty.Transformer, as a pre-order traversal, -// applying defaults as it goes. The pre-order traversal allows us to specify -// defaults more loosely for structural types, as the defaults for the types -// will be applied to the default value later in the walk. -type defaultsTransformer struct { - defaults *Defaults -} - -var _ cty.Transformer = (*defaultsTransformer)(nil) - -func (t *defaultsTransformer) Enter(p cty.Path, v cty.Value) (cty.Value, error) { - // Cannot apply defaults to an unknown value - if !v.IsKnown() { - return v, nil - } - - // Look up the defaults for this path. - defaults := t.defaults.traverse(p) - - // If we have no defaults, nothing to do. - if len(defaults) == 0 { - return v, nil - } - - // Ensure we are working with an object or map. - vt := v.Type() - if !vt.IsObjectType() && !vt.IsMapType() { - // Cannot apply defaults because the value type is incompatible. - // We'll ignore this and let the later conversion stage display a - // more useful diagnostic. - return v, nil - } - - // Unmark the value and reapply the marks later. - v, valMarks := v.Unmark() - - // Convert the given value into an attribute map (if it's non-null and - // non-empty). - attrs := make(map[string]cty.Value) - if !v.IsNull() && v.LengthInt() > 0 { - attrs = v.AsValueMap() - } - - // Apply defaults where attributes are missing, constructing a new - // value with the same marks. - for attr, defaultValue := range defaults { - if attrValue, ok := attrs[attr]; !ok || attrValue.IsNull() { - attrs[attr] = defaultValue - } - } - - // We construct an object even if the input value was a map, as the - // type of an attribute's default value may be incompatible with the - // map element type. - return cty.ObjectVal(attrs).WithMarks(valMarks), nil -} - -func (t *defaultsTransformer) Exit(p cty.Path, v cty.Value) (cty.Value, error) { - return v, nil -} - -// traverse walks the abstract defaults structure for a given path, returning -// a set of default values (if any are present) or nil (if not). This operation -// differs from applying a path to a value because we need to customize the -// traversal steps for collection types, where a single set of defaults can be -// applied to an arbitrary number of elements. -func (d *Defaults) traverse(path cty.Path) map[string]cty.Value { - if len(path) == 0 { - return d.DefaultValues - } - - switch s := path[0].(type) { - case cty.GetAttrStep: - if d.Type.IsObjectType() { - // Attribute path steps are normally applied to objects, where each - // attribute may have different defaults. - return d.traverseChild(s.Name, path) - } else if d.Type.IsMapType() { - // Literal values for maps can result in attribute path steps, in which - // case we need to disregard the attribute name, as maps can have only - // one child. - return d.traverseChild("", path) - } - - return nil - case cty.IndexStep: - if d.Type.IsTupleType() { - // Tuples can have different types for each element, so we look - // up the defaults based on the index key. - return d.traverseChild(s.Key.AsBigFloat().String(), path) - } else if d.Type.IsCollectionType() { - // Defaults for collection element types are stored with a blank - // key, so we disregard the index key. - return d.traverseChild("", path) - } - return nil - default: - // At time of writing there are no other path step types. - return nil - } -} - -// traverseChild continues the traversal for a given child key, and mutually -// recurses with traverse. -func (d *Defaults) traverseChild(name string, path cty.Path) map[string]cty.Value { - if child, ok := d.Children[name]; ok { - return child.traverse(path[1:]) - } - return nil -} diff --git a/internal/typeexpr/defaults_test.go b/internal/typeexpr/defaults_test.go deleted file mode 100644 index a4da6bb6b2b0..000000000000 --- a/internal/typeexpr/defaults_test.go +++ /dev/null @@ -1,504 +0,0 @@ -package typeexpr - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty" -) - -var ( - valueComparer = cmp.Comparer(cty.Value.RawEquals) -) - -func TestDefaults_Apply(t *testing.T) { - simpleObject := cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Bool, - }, []string{"b"}) - nestedObject := cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "c": simpleObject, - "d": cty.Number, - }, []string{"c"}) - - testCases := map[string]struct { - defaults *Defaults - value cty.Value - want cty.Value - }{ - // Nothing happens when there are no default values and no children. - "no defaults": { - defaults: &Defaults{ - Type: cty.Map(cty.String), - }, - value: cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.StringVal("bar"), - }), - want: cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.StringVal("bar"), - }), - }, - // Passing a map which does not include one of the attributes with a - // default results in the default being applied to the output. Output - // is always an object. - "simple object with defaults applied": { - defaults: &Defaults{ - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - value: cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - want: cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - }, - // Unknown values may be assigned to root modules during validation, - // and we cannot apply defaults at that time. - "simple object with defaults but unknown value": { - defaults: &Defaults{ - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - value: cty.UnknownVal(cty.Map(cty.String)), - want: cty.UnknownVal(cty.Map(cty.String)), - }, - // Defaults do not override attributes which are present in the given - // value. - "simple object with optional attributes specified": { - defaults: &Defaults{ - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - value: cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.StringVal("false"), - }), - want: cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.StringVal("false"), - }), - }, - // Defaults will replace explicit nulls. - "object with explicit null for attribute with default": { - defaults: &Defaults{ - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - value: cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.NullVal(cty.String), - }), - want: cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - }, - // Defaults can be specified at any level of depth and will be applied - // so long as there is a parent value to populate. - "nested object with defaults applied": { - defaults: &Defaults{ - Type: nestedObject, - Children: map[string]*Defaults{ - "c": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.False, - }, - }, - }, - }, - value: cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "d": cty.NumberIntVal(5), - }), - want: cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.False, - }), - "d": cty.NumberIntVal(5), - }), - }, - // Testing traversal of collections. - "map of objects with defaults applied": { - defaults: &Defaults{ - Type: cty.Map(simpleObject), - Children: map[string]*Defaults{ - "": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - value: cty.MapVal(map[string]cty.Value{ - "f": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - }), - }), - want: cty.MapVal(map[string]cty.Value{ - "f": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - "b": cty.True, - }), - }), - }, - // A map variable value specified in a tfvars file will be an object, - // in which case we must still traverse the defaults structure - // correctly. - "map of objects with defaults applied, given object instead of map": { - defaults: &Defaults{ - Type: cty.Map(simpleObject), - Children: map[string]*Defaults{ - "": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - value: cty.ObjectVal(map[string]cty.Value{ - "f": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - }), - }), - want: cty.ObjectVal(map[string]cty.Value{ - "f": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - "b": cty.True, - }), - }), - }, - // Another example of a collection type, this time exercising the code - // processing a tuple input. - "list of objects with defaults applied": { - defaults: &Defaults{ - Type: cty.List(simpleObject), - Children: map[string]*Defaults{ - "": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("bar"), - "b": cty.True, - }), - }), - }, - // Unlike collections, tuple variable types can have defaults for - // multiple element types. - "tuple of objects with defaults applied": { - defaults: &Defaults{ - Type: cty.Tuple([]cty.Type{simpleObject, nestedObject}), - Children: map[string]*Defaults{ - "0": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.False, - }, - }, - "1": { - Type: nestedObject, - DefaultValues: map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("default"), - "b": cty.True, - }), - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - cty.ObjectVal(map[string]cty.Value{ - "d": cty.NumberIntVal(5), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.False, - }), - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("default"), - "b": cty.True, - }), - "d": cty.NumberIntVal(5), - }), - }), - }, - // More complex cases with deeply nested defaults, testing the "default - // within a default" edges. - "set of nested objects, no default sub-object": { - defaults: &Defaults{ - Type: cty.Set(nestedObject), - Children: map[string]*Defaults{ - "": { - Type: nestedObject, - Children: map[string]*Defaults{ - "c": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "d": cty.NumberIntVal(7), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - // No default value for "c" specified, so none applied. The - // convert stage will fill in a null. - "d": cty.NumberIntVal(7), - }), - }), - }, - "set of nested objects, empty default sub-object": { - defaults: &Defaults{ - Type: cty.Set(nestedObject), - Children: map[string]*Defaults{ - "": { - Type: nestedObject, - DefaultValues: map[string]cty.Value{ - // This is a convenient shorthand which causes a - // missing sub-object to be filled with an object - // with all of the default values specified in the - // sub-object's type. - "c": cty.EmptyObjectVal, - }, - Children: map[string]*Defaults{ - "c": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "d": cty.NumberIntVal(7), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - // Default value for "b" is applied to the empty object - // specified as the default for "c" - "b": cty.True, - }), - "d": cty.NumberIntVal(7), - }), - }), - }, - "set of nested objects, overriding default sub-object": { - defaults: &Defaults{ - Type: cty.Set(nestedObject), - Children: map[string]*Defaults{ - "": { - Type: nestedObject, - DefaultValues: map[string]cty.Value{ - // If no value is given for "c", we use this object - // of non-default values instead. These take - // precedence over the default values specified in - // the child type. - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("fallback"), - "b": cty.False, - }), - }, - Children: map[string]*Defaults{ - "c": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "d": cty.NumberIntVal(7), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - // The default value for "b" is not applied, as the - // default value for "c" includes a non-default value - // already. - "a": cty.StringVal("fallback"), - "b": cty.False, - }), - "d": cty.NumberIntVal(7), - }), - }), - }, - "set of nested objects, nulls in default sub-object overridden": { - defaults: &Defaults{ - Type: cty.Set(nestedObject), - Children: map[string]*Defaults{ - "": { - Type: nestedObject, - DefaultValues: map[string]cty.Value{ - // The default value for "c" is used to prepopulate - // the nested object's value if not specified, but - // the null default for its "b" attribute will be - // overridden by the default specified in the child - // type. - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("fallback"), - "b": cty.NullVal(cty.Bool), - }), - }, - Children: map[string]*Defaults{ - "c": { - Type: simpleObject, - DefaultValues: map[string]cty.Value{ - "b": cty.True, - }, - }, - }, - }, - }, - }, - value: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "d": cty.NumberIntVal(7), - }), - }), - want: cty.TupleVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("foo"), - "b": cty.True, - }), - "d": cty.NumberIntVal(5), - }), - cty.ObjectVal(map[string]cty.Value{ - "c": cty.ObjectVal(map[string]cty.Value{ - // The default value for "b" overrides the explicit - // null in the default value for "c". - "a": cty.StringVal("fallback"), - "b": cty.True, - }), - "d": cty.NumberIntVal(7), - }), - }), - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - got := tc.defaults.Apply(tc.value) - if !cmp.Equal(tc.want, got, valueComparer) { - t.Errorf("wrong result\n%s", cmp.Diff(tc.want, got, valueComparer)) - } - }) - } -} diff --git a/internal/typeexpr/get_type.go b/internal/typeexpr/get_type.go index 10ed611cb2c6..de5465b997ba 100644 --- a/internal/typeexpr/get_type.go +++ b/internal/typeexpr/get_type.go @@ -5,52 +5,49 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" ) const invalidTypeSummary = "Invalid type specification" -// getType is the internal implementation of Type, TypeConstraint, and -// TypeConstraintWithDefaults, using the passed flags to distinguish. When -// `constraint` is true, the "any" keyword can be used in place of a concrete -// type. When `withDefaults` is true, the "optional" call expression supports -// an additional argument describing a default value. -func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Defaults, hcl.Diagnostics) { +// getType is the internal implementation of both Type and TypeConstraint, +// using the passed flag to distinguish. When constraint is false, the "any" +// keyword will produce an error. +func getType(expr hcl.Expression, constraint bool) (cty.Type, hcl.Diagnostics) { // First we'll try for one of our keywords kw := hcl.ExprAsKeyword(expr) switch kw { case "bool": - return cty.Bool, nil, nil + return cty.Bool, nil case "string": - return cty.String, nil, nil + return cty.String, nil case "number": - return cty.Number, nil, nil + return cty.Number, nil case "any": if constraint { - return cty.DynamicPseudoType, nil, nil + return cty.DynamicPseudoType, nil } - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The keyword %q cannot be used in this type specification: an exact type is required.", kw), Subject: expr.Range().Ptr(), }} case "list", "map", "set": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", kw), Subject: expr.Range().Ptr(), }} case "object": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.", Subject: expr.Range().Ptr(), }} case "tuple": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The tuple type constructor requires one argument specifying the element types as a list.", @@ -59,7 +56,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def case "": // okay! we'll fall through and try processing as a call, then. default: - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The keyword %q is not a valid type specification.", kw), @@ -71,7 +68,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def // try to process it as a call instead. call, diags := hcl.ExprCall(expr) if diags.HasErrors() { - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).", @@ -80,20 +77,13 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def } switch call.Name { - case "bool", "string", "number": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + case "bool", "string", "number", "any": + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("Primitive type keyword %q does not expect arguments.", call.Name), Subject: &call.ArgsRange, }} - case "any": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: invalidTypeSummary, - Detail: fmt.Sprintf("Type constraint keyword %q does not expect arguments.", call.Name), - Subject: &call.ArgsRange, - }} } if len(call.Arguments) != 1 { @@ -108,7 +98,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def switch call.Name { case "list", "set", "map": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", call.Name), @@ -116,7 +106,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def Context: &contextRange, }} case "object": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.", @@ -124,7 +114,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def Context: &contextRange, }} case "tuple": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The tuple type constructor requires one argument specifying the element types as a list.", @@ -137,21 +127,18 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def switch call.Name { case "list": - ety, defaults, diags := getType(call.Arguments[0], constraint, withDefaults) - ty := cty.List(ety) - return ty, collectionDefaults(ty, defaults), diags + ety, diags := getType(call.Arguments[0], constraint) + return cty.List(ety), diags case "set": - ety, defaults, diags := getType(call.Arguments[0], constraint, withDefaults) - ty := cty.Set(ety) - return ty, collectionDefaults(ty, defaults), diags + ety, diags := getType(call.Arguments[0], constraint) + return cty.Set(ety), diags case "map": - ety, defaults, diags := getType(call.Arguments[0], constraint, withDefaults) - ty := cty.Map(ety) - return ty, collectionDefaults(ty, defaults), diags + ety, diags := getType(call.Arguments[0], constraint) + return cty.Map(ety), diags case "object": attrDefs, diags := hcl.ExprMap(call.Arguments[0]) if diags.HasErrors() { - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "Object type constructor requires a map whose keys are attribute names and whose values are the corresponding attribute types.", @@ -161,8 +148,6 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def } atys := make(map[string]cty.Type) - defaultValues := make(map[string]cty.Value) - children := make(map[string]*Defaults) var optAttrs []string for _, attrDef := range attrDefs { attrName := hcl.ExprAsKeyword(attrDef.Key) @@ -182,7 +167,6 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def // modifier optional(...) to indicate an optional attribute. If // so, we'll unwrap that first and make a note about it being // optional for when we construct the type below. - var defaultExpr hcl.Expression if call, callDiags := hcl.ExprCall(atyExpr); !callDiags.HasErrors() { if call.Name == "optional" { if len(call.Arguments) < 1 { @@ -196,40 +180,16 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def continue } if constraint { - if withDefaults { - switch len(call.Arguments) { - case 2: - defaultExpr = call.Arguments[1] - defaultVal, defaultDiags := defaultExpr.Value(nil) - diags = append(diags, defaultDiags...) - if !defaultDiags.HasErrors() { - optAttrs = append(optAttrs, attrName) - defaultValues[attrName] = defaultVal - } - case 1: - optAttrs = append(optAttrs, attrName) - default: - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: invalidTypeSummary, - Detail: "Optional attribute modifier expects at most two arguments: the attribute type, and a default value.", - Subject: call.ArgsRange.Ptr(), - Context: atyExpr.Range().Ptr(), - }) - } - } else { - if len(call.Arguments) == 1 { - optAttrs = append(optAttrs, attrName) - } else { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: invalidTypeSummary, - Detail: "Optional attribute modifier expects only one argument: the attribute type.", - Subject: call.ArgsRange.Ptr(), - Context: atyExpr.Range().Ptr(), - }) - } + if len(call.Arguments) > 1 { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: invalidTypeSummary, + Detail: "Optional attribute modifier expects only one argument: the attribute type.", + Subject: call.ArgsRange.Ptr(), + Context: atyExpr.Range().Ptr(), + }) } + optAttrs = append(optAttrs, attrName) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -243,39 +203,19 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def } } - aty, aDefaults, attrDiags := getType(atyExpr, constraint, withDefaults) + aty, attrDiags := getType(atyExpr, constraint) diags = append(diags, attrDiags...) - - // If a default is set for an optional attribute, verify that it is - // convertible to the attribute type. - if defaultVal, ok := defaultValues[attrName]; ok { - _, err := convert.Convert(defaultVal, aty) - if err != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid default value for optional attribute", - Detail: fmt.Sprintf("This default value is not compatible with the attribute's type constraint: %s.", err), - Subject: defaultExpr.Range().Ptr(), - }) - delete(defaultValues, attrName) - } - } - atys[attrName] = aty - if aDefaults != nil { - children[attrName] = aDefaults - } } // NOTE: ObjectWithOptionalAttrs is experimental in cty at the // time of writing, so this interface might change even in future // minor versions of cty. We're accepting that because Terraform // itself is considering optional attributes as experimental right now. - ty := cty.ObjectWithOptionalAttrs(atys, optAttrs) - return ty, structuredDefaults(ty, defaultValues, children), diags + return cty.ObjectWithOptionalAttrs(atys, optAttrs), diags case "tuple": elemDefs, diags := hcl.ExprList(call.Arguments[0]) if diags.HasErrors() { - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "Tuple type constructor requires a list of element types.", @@ -284,19 +224,14 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def }} } etys := make([]cty.Type, len(elemDefs)) - children := make(map[string]*Defaults, len(elemDefs)) for i, defExpr := range elemDefs { - ety, elemDefaults, elemDiags := getType(defExpr, constraint, withDefaults) + ety, elemDiags := getType(defExpr, constraint) diags = append(diags, elemDiags...) etys[i] = ety - if elemDefaults != nil { - children[fmt.Sprintf("%d", i)] = elemDefaults - } } - ty := cty.Tuple(etys) - return ty, structuredDefaults(ty, nil, children), diags + return cty.Tuple(etys), diags case "optional": - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("Keyword %q is valid only as a modifier for object type attributes.", call.Name), @@ -305,7 +240,7 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def default: // Can't access call.Arguments in this path because we've not validated // that it contains exactly one expression here. - return cty.DynamicPseudoType, nil, hcl.Diagnostics{{ + return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("Keyword %q is not a valid type constructor.", call.Name), @@ -313,33 +248,3 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def }} } } - -func collectionDefaults(ty cty.Type, defaults *Defaults) *Defaults { - if defaults == nil { - return nil - } - return &Defaults{ - Type: ty, - Children: map[string]*Defaults{ - "": defaults, - }, - } -} - -func structuredDefaults(ty cty.Type, defaultValues map[string]cty.Value, children map[string]*Defaults) *Defaults { - if len(defaultValues) == 0 && len(children) == 0 { - return nil - } - - defaults := &Defaults{ - Type: ty, - } - if len(defaultValues) > 0 { - defaults.DefaultValues = defaultValues - } - if len(children) > 0 { - defaults.Children = children - } - - return defaults -} diff --git a/internal/typeexpr/get_type_test.go b/internal/typeexpr/get_type_test.go deleted file mode 100644 index 2dca23d27e41..000000000000 --- a/internal/typeexpr/get_type_test.go +++ /dev/null @@ -1,669 +0,0 @@ -package typeexpr - -import ( - "fmt" - "testing" - - "github.com/hashicorp/hcl/v2/gohcl" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/hcl/v2/json" - "github.com/zclconf/go-cty/cty" -) - -var ( - typeComparer = cmp.Comparer(cty.Type.Equals) -) - -func TestGetType(t *testing.T) { - tests := []struct { - Source string - Constraint bool - Want cty.Type - WantError string - }{ - // keywords - { - `bool`, - false, - cty.Bool, - "", - }, - { - `number`, - false, - cty.Number, - "", - }, - { - `string`, - false, - cty.String, - "", - }, - { - `any`, - false, - cty.DynamicPseudoType, - `The keyword "any" cannot be used in this type specification: an exact type is required.`, - }, - { - `any`, - true, - cty.DynamicPseudoType, - "", - }, - { - `list`, - false, - cty.DynamicPseudoType, - "The list type constructor requires one argument specifying the element type.", - }, - { - `map`, - false, - cty.DynamicPseudoType, - "The map type constructor requires one argument specifying the element type.", - }, - { - `set`, - false, - cty.DynamicPseudoType, - "The set type constructor requires one argument specifying the element type.", - }, - { - `object`, - false, - cty.DynamicPseudoType, - "The object type constructor requires one argument specifying the attribute types and values as a map.", - }, - { - `tuple`, - false, - cty.DynamicPseudoType, - "The tuple type constructor requires one argument specifying the element types as a list.", - }, - - // constructors - { - `bool()`, - false, - cty.DynamicPseudoType, - `Primitive type keyword "bool" does not expect arguments.`, - }, - { - `number()`, - false, - cty.DynamicPseudoType, - `Primitive type keyword "number" does not expect arguments.`, - }, - { - `string()`, - false, - cty.DynamicPseudoType, - `Primitive type keyword "string" does not expect arguments.`, - }, - { - `any()`, - false, - cty.DynamicPseudoType, - `Type constraint keyword "any" does not expect arguments.`, - }, - { - `any()`, - true, - cty.DynamicPseudoType, - `Type constraint keyword "any" does not expect arguments.`, - }, - { - `list(string)`, - false, - cty.List(cty.String), - ``, - }, - { - `set(string)`, - false, - cty.Set(cty.String), - ``, - }, - { - `map(string)`, - false, - cty.Map(cty.String), - ``, - }, - { - `list()`, - false, - cty.DynamicPseudoType, - `The list type constructor requires one argument specifying the element type.`, - }, - { - `list(string, string)`, - false, - cty.DynamicPseudoType, - `The list type constructor requires one argument specifying the element type.`, - }, - { - `list(any)`, - false, - cty.List(cty.DynamicPseudoType), - `The keyword "any" cannot be used in this type specification: an exact type is required.`, - }, - { - `list(any)`, - true, - cty.List(cty.DynamicPseudoType), - ``, - }, - { - `object({})`, - false, - cty.EmptyObject, - ``, - }, - { - `object({name=string})`, - false, - cty.Object(map[string]cty.Type{"name": cty.String}), - ``, - }, - { - `object({"name"=string})`, - false, - cty.EmptyObject, - `Object constructor map keys must be attribute names.`, - }, - { - `object({name=nope})`, - false, - cty.Object(map[string]cty.Type{"name": cty.DynamicPseudoType}), - `The keyword "nope" is not a valid type specification.`, - }, - { - `object()`, - false, - cty.DynamicPseudoType, - `The object type constructor requires one argument specifying the attribute types and values as a map.`, - }, - { - `object(string)`, - false, - cty.DynamicPseudoType, - `Object type constructor requires a map whose keys are attribute names and whose values are the corresponding attribute types.`, - }, - { - `tuple([])`, - false, - cty.EmptyTuple, - ``, - }, - { - `tuple([string, bool])`, - false, - cty.Tuple([]cty.Type{cty.String, cty.Bool}), - ``, - }, - { - `tuple([nope])`, - false, - cty.Tuple([]cty.Type{cty.DynamicPseudoType}), - `The keyword "nope" is not a valid type specification.`, - }, - { - `tuple()`, - false, - cty.DynamicPseudoType, - `The tuple type constructor requires one argument specifying the element types as a list.`, - }, - { - `tuple(string)`, - false, - cty.DynamicPseudoType, - `Tuple type constructor requires a list of element types.`, - }, - { - `shwoop(string)`, - false, - cty.DynamicPseudoType, - `Keyword "shwoop" is not a valid type constructor.`, - }, - { - `list("string")`, - false, - cty.List(cty.DynamicPseudoType), - `A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).`, - }, - - // More interesting combinations - { - `list(object({}))`, - false, - cty.List(cty.EmptyObject), - ``, - }, - { - `list(map(tuple([])))`, - false, - cty.List(cty.Map(cty.EmptyTuple)), - ``, - }, - - // Optional modifier - { - `object({name=string,age=optional(number)})`, - true, - cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "name": cty.String, - "age": cty.Number, - }, []string{"age"}), - ``, - }, - { - `object({name=string,meta=optional(any)})`, - true, - cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "name": cty.String, - "meta": cty.DynamicPseudoType, - }, []string{"meta"}), - ``, - }, - { - `object({name=string,age=optional(number)})`, - false, - cty.Object(map[string]cty.Type{ - "name": cty.String, - "age": cty.Number, - }), - `Optional attribute modifier is only for type constraints, not for exact types.`, - }, - { - `object({name=string,meta=optional(any)})`, - false, - cty.Object(map[string]cty.Type{ - "name": cty.String, - "meta": cty.DynamicPseudoType, - }), - `Optional attribute modifier is only for type constraints, not for exact types.`, - }, - { - `object({name=string,meta=optional()})`, - true, - cty.Object(map[string]cty.Type{ - "name": cty.String, - }), - `Optional attribute modifier requires the attribute type as its argument.`, - }, - { - `object({name=string,meta=optional(string, "hello")})`, - true, - cty.Object(map[string]cty.Type{ - "name": cty.String, - "meta": cty.String, - }), - `Optional attribute modifier expects only one argument: the attribute type.`, - }, - { - `optional(string)`, - false, - cty.DynamicPseudoType, - `Keyword "optional" is valid only as a modifier for object type attributes.`, - }, - { - `optional`, - false, - cty.DynamicPseudoType, - `The keyword "optional" is not a valid type specification.`, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%s (constraint=%v)", test.Source, test.Constraint), func(t *testing.T) { - expr, diags := hclsyntax.ParseExpression([]byte(test.Source), "", hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - t.Fatalf("failed to parse: %s", diags) - } - - got, _, diags := getType(expr, test.Constraint, false) - if test.WantError == "" { - for _, diag := range diags { - t.Error(diag) - } - } else { - found := false - for _, diag := range diags { - t.Log(diag) - if diag.Severity == hcl.DiagError && diag.Detail == test.WantError { - found = true - } - } - if !found { - t.Errorf("missing expected error detail message: %s", test.WantError) - } - } - - if !got.Equals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - -func TestGetTypeJSON(t *testing.T) { - // We have fewer test cases here because we're mainly exercising the - // extra indirection in the JSON syntax package, which ultimately calls - // into the native syntax parser (which we tested extensively in - // TestGetType). - tests := []struct { - Source string - Constraint bool - Want cty.Type - WantError string - }{ - { - `{"expr":"bool"}`, - false, - cty.Bool, - "", - }, - { - `{"expr":"list(bool)"}`, - false, - cty.List(cty.Bool), - "", - }, - { - `{"expr":"list"}`, - false, - cty.DynamicPseudoType, - "The list type constructor requires one argument specifying the element type.", - }, - } - - for _, test := range tests { - t.Run(test.Source, func(t *testing.T) { - file, diags := json.Parse([]byte(test.Source), "") - if diags.HasErrors() { - t.Fatalf("failed to parse: %s", diags) - } - - type TestContent struct { - Expr hcl.Expression `hcl:"expr"` - } - var content TestContent - diags = gohcl.DecodeBody(file.Body, nil, &content) - if diags.HasErrors() { - t.Fatalf("failed to decode: %s", diags) - } - - got, _, diags := getType(content.Expr, test.Constraint, false) - if test.WantError == "" { - for _, diag := range diags { - t.Error(diag) - } - } else { - found := false - for _, diag := range diags { - t.Log(diag) - if diag.Severity == hcl.DiagError && diag.Detail == test.WantError { - found = true - } - } - if !found { - t.Errorf("missing expected error detail message: %s", test.WantError) - } - } - - if !got.Equals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - -func TestGetTypeDefaults(t *testing.T) { - tests := []struct { - Source string - Want *Defaults - WantError string - }{ - // primitive types have nil defaults - { - `bool`, - nil, - "", - }, - { - `number`, - nil, - "", - }, - { - `string`, - nil, - "", - }, - { - `any`, - nil, - "", - }, - - // complex structures with no defaults have nil defaults - { - `map(string)`, - nil, - "", - }, - { - `set(number)`, - nil, - "", - }, - { - `tuple([number, string])`, - nil, - "", - }, - { - `object({ a = string, b = number })`, - nil, - "", - }, - { - `map(list(object({ a = string, b = optional(number) })))`, - nil, - "", - }, - - // object optional attribute with defaults - { - `object({ a = string, b = optional(number, 5) })`, - &Defaults{ - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - "", - }, - - // nested defaults - { - `object({ a = optional(object({ b = optional(number, 5) }), {}) })`, - &Defaults{ - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "b": cty.Number, - }, []string{"b"}), - }, []string{"a"}), - DefaultValues: map[string]cty.Value{ - "a": cty.EmptyObjectVal, - }, - Children: map[string]*Defaults{ - "a": { - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - }, - }, - "", - }, - - // collections of objects with defaults - { - `map(object({ a = string, b = optional(number, 5) }))`, - &Defaults{ - Type: cty.Map(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"})), - Children: map[string]*Defaults{ - "": { - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - }, - }, - "", - }, - { - `list(object({ a = string, b = optional(number, 5) }))`, - &Defaults{ - Type: cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"})), - Children: map[string]*Defaults{ - "": { - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - }, - }, - "", - }, - { - `set(object({ a = string, b = optional(number, 5) }))`, - &Defaults{ - Type: cty.Set(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"})), - Children: map[string]*Defaults{ - "": { - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - }, - }, - "", - }, - - // tuples containing objects with defaults work differently from - // collections - { - `tuple([string, bool, object({ a = string, b = optional(number, 5) })])`, - &Defaults{ - Type: cty.Tuple([]cty.Type{ - cty.String, - cty.Bool, - cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - }), - Children: map[string]*Defaults{ - "2": { - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"b"}), - DefaultValues: map[string]cty.Value{ - "b": cty.NumberIntVal(5), - }, - }, - }, - }, - "", - }, - - // incompatible default value causes an error - { - `object({ a = optional(string, "hello"), b = optional(number, true) })`, - &Defaults{ - Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ - "a": cty.String, - "b": cty.Number, - }, []string{"a", "b"}), - DefaultValues: map[string]cty.Value{ - "a": cty.StringVal("hello"), - }, - }, - "This default value is not compatible with the attribute's type constraint: number required.", - }, - - // Too many arguments - { - `object({name=string,meta=optional(string, "hello", "world")})`, - nil, - `Optional attribute modifier expects at most two arguments: the attribute type, and a default value.`, - }, - } - - for _, test := range tests { - t.Run(test.Source, func(t *testing.T) { - expr, diags := hclsyntax.ParseExpression([]byte(test.Source), "", hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - t.Fatalf("failed to parse: %s", diags) - } - - _, got, diags := getType(expr, true, true) - if test.WantError == "" { - for _, diag := range diags { - t.Error(diag) - } - } else { - found := false - for _, diag := range diags { - t.Log(diag) - if diag.Severity == hcl.DiagError && diag.Detail == test.WantError { - found = true - } - } - if !found { - t.Errorf("missing expected error detail message: %s", test.WantError) - } - } - - if !cmp.Equal(test.Want, got, valueComparer, typeComparer) { - t.Errorf("wrong result\n%s", cmp.Diff(test.Want, got, valueComparer, typeComparer)) - } - }) - } -} diff --git a/internal/typeexpr/public.go b/internal/typeexpr/public.go index 82f215c0978f..3b8f618fbcd1 100644 --- a/internal/typeexpr/public.go +++ b/internal/typeexpr/public.go @@ -15,8 +15,7 @@ import ( // successful, returns the resulting type. If unsuccessful, error diagnostics // are returned. func Type(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { - ty, _, diags := getType(expr, false, false) - return ty, diags + return getType(expr, false) } // TypeConstraint attempts to parse the given expression as a type constraint @@ -27,20 +26,7 @@ func Type(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { // allows the keyword "any" to represent cty.DynamicPseudoType, which is often // used as a wildcard in type checking and type conversion operations. func TypeConstraint(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { - ty, _, diags := getType(expr, true, false) - return ty, diags -} - -// TypeConstraintWithDefaults attempts to parse the given expression as a type -// constraint which may include default values for object attributes. If -// successful both the resulting type and corresponding defaults are returned. -// If unsuccessful, error diagnostics are returned. -// -// When using this function, defaults should be applied to the input value -// before type conversion, to ensure that objects with missing attributes have -// default values populated. -func TypeConstraintWithDefaults(expr hcl.Expression) (cty.Type, *Defaults, hcl.Diagnostics) { - return getType(expr, true, true) + return getType(expr, true) } // TypeString returns a string rendering of the given type as it would be diff --git a/internal/typeexpr/type_string_test.go b/internal/typeexpr/type_string_test.go deleted file mode 100644 index fbdf3f481d70..000000000000 --- a/internal/typeexpr/type_string_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package typeexpr - -import ( - "testing" - - "github.com/zclconf/go-cty/cty" -) - -func TestTypeString(t *testing.T) { - tests := []struct { - Type cty.Type - Want string - }{ - { - cty.DynamicPseudoType, - "any", - }, - { - cty.String, - "string", - }, - { - cty.Number, - "number", - }, - { - cty.Bool, - "bool", - }, - { - cty.List(cty.Number), - "list(number)", - }, - { - cty.Set(cty.Bool), - "set(bool)", - }, - { - cty.Map(cty.String), - "map(string)", - }, - { - cty.EmptyObject, - "object({})", - }, - { - cty.Object(map[string]cty.Type{"foo": cty.Bool}), - "object({foo=bool})", - }, - { - cty.Object(map[string]cty.Type{"foo": cty.Bool, "bar": cty.String}), - "object({bar=string,foo=bool})", - }, - { - cty.EmptyTuple, - "tuple([])", - }, - { - cty.Tuple([]cty.Type{cty.Bool}), - "tuple([bool])", - }, - { - cty.Tuple([]cty.Type{cty.Bool, cty.String}), - "tuple([bool,string])", - }, - { - cty.List(cty.DynamicPseudoType), - "list(any)", - }, - { - cty.Tuple([]cty.Type{cty.DynamicPseudoType}), - "tuple([any])", - }, - { - cty.Object(map[string]cty.Type{"foo": cty.DynamicPseudoType}), - "object({foo=any})", - }, - { - // We don't expect to find attributes that aren't valid identifiers - // because we only promise to support types that this package - // would've created, but we allow this situation during rendering - // just because it's convenient for applications trying to produce - // error messages about mismatched types. Note that the quoted - // attribute name is not actually accepted by our Type and - // TypeConstraint functions, so this is one situation where the - // TypeString result cannot be re-parsed by those functions. - cty.Object(map[string]cty.Type{"foo bar baz": cty.String}), - `object({"foo bar baz"=string})`, - }, - } - - for _, test := range tests { - t.Run(test.Type.GoString(), func(t *testing.T) { - got := TypeString(test.Type) - if got != test.Want { - t.Errorf("wrong result\ntype: %#v\ngot: %s\nwant: %s", test.Type, got, test.Want) - } - }) - } -} diff --git a/internal/typeexpr/type_type_test.go b/internal/typeexpr/type_type_test.go deleted file mode 100644 index 2286a2e1a532..000000000000 --- a/internal/typeexpr/type_type_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package typeexpr - -import ( - "fmt" - "testing" - - "github.com/zclconf/go-cty/cty" -) - -func TestTypeConstraintType(t *testing.T) { - tyVal1 := TypeConstraintVal(cty.String) - tyVal2 := TypeConstraintVal(cty.String) - tyVal3 := TypeConstraintVal(cty.Number) - - if !tyVal1.RawEquals(tyVal2) { - t.Errorf("tyVal1 not equal to tyVal2\ntyVal1: %#v\ntyVal2: %#v", tyVal1, tyVal2) - } - if tyVal1.RawEquals(tyVal3) { - t.Errorf("tyVal1 equal to tyVal2, but should not be\ntyVal1: %#v\ntyVal3: %#v", tyVal1, tyVal3) - } - - if got, want := TypeConstraintFromVal(tyVal1), cty.String; !got.Equals(want) { - t.Errorf("wrong type extracted from tyVal1\ngot: %#v\nwant: %#v", got, want) - } - if got, want := TypeConstraintFromVal(tyVal3), cty.Number; !got.Equals(want) { - t.Errorf("wrong type extracted from tyVal3\ngot: %#v\nwant: %#v", got, want) - } -} - -func TestConvertFunc(t *testing.T) { - // This is testing the convert function directly, skipping over the HCL - // parsing and evaluation steps that would normally lead there. There is - // another test in the "integrationtest" package called TestTypeConvertFunc - // that exercises the full path to this function via the hclsyntax parser. - - tests := []struct { - val, ty cty.Value - want cty.Value - wantErr string - }{ - // The goal here is not an exhaustive set of conversions, since that's - // already covered in cty/convert, but rather exercising different - // permutations of success and failure to make sure the function - // handles all of the results in a reasonable way. - { - cty.StringVal("hello"), - TypeConstraintVal(cty.String), - cty.StringVal("hello"), - ``, - }, - { - cty.True, - TypeConstraintVal(cty.String), - cty.StringVal("true"), - ``, - }, - { - cty.StringVal("hello"), - TypeConstraintVal(cty.Bool), - cty.NilVal, - `a bool is required`, - }, - { - cty.UnknownVal(cty.Bool), - TypeConstraintVal(cty.Bool), - cty.UnknownVal(cty.Bool), - ``, - }, - { - cty.DynamicVal, - TypeConstraintVal(cty.Bool), - cty.UnknownVal(cty.Bool), - ``, - }, - { - cty.NullVal(cty.Bool), - TypeConstraintVal(cty.Bool), - cty.NullVal(cty.Bool), - ``, - }, - { - cty.NullVal(cty.DynamicPseudoType), - TypeConstraintVal(cty.Bool), - cty.NullVal(cty.Bool), - ``, - }, - { - cty.StringVal("hello").Mark(1), - TypeConstraintVal(cty.String), - cty.StringVal("hello").Mark(1), - ``, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%#v to %#v", test.val, test.ty), func(t *testing.T) { - got, err := ConvertFunc.Call([]cty.Value{test.val, test.ty}) - - if err != nil { - if test.wantErr != "" { - if got, want := err.Error(), test.wantErr; got != want { - t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) - } - } else { - t.Errorf("unexpected error\ngot: %s\nwant: ", err) - } - return - } - if test.wantErr != "" { - t.Errorf("wrong error\ngot: \nwant: %s", test.wantErr) - } - - if !test.want.RawEquals(got) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) - } - }) - } -} diff --git a/main.go b/main.go index c807bb5a40da..1ffd8a343ade 100644 --- a/main.go +++ b/main.go @@ -84,9 +84,6 @@ func realMain() int { } log.Printf("[INFO] Go runtime version: %s", runtime.Version()) log.Printf("[INFO] CLI args: %#v", os.Args) - if ExperimentsAllowed() { - log.Printf("[INFO] This build of Terraform allows using experimental features") - } streams, err := terminal.Init() if err != nil { diff --git a/scripts/goimportscheck.sh b/scripts/goimportscheck.sh deleted file mode 100755 index f80e4a68fe7e..000000000000 --- a/scripts/goimportscheck.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Check goimports -echo "==> Checking the code complies with goimports requirements..." -target_files=$(git diff --name-only origin/main --diff-filter=MA | grep "\.go") - -if [[ -z ${target_files} ]]; then - exit 0 -fi - -goimports_files=$(goimports -w -l "${target_files}") -if [[ -n ${goimports_files} ]]; then - echo 'goimports needs running on the following files:' - echo "${goimports_files}" - echo "You can use the command and flags \`goimports -w -l\` to reformat the code" - exit 1 -fi - -exit 0 diff --git a/tools/tools.go b/tools/tools.go index bd54b0e5ec20..f575e0189f40 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -5,6 +5,7 @@ package tools import ( _ "github.com/golang/mock/mockgen" + _ "github.com/golang/protobuf/protoc-gen-go" _ "github.com/mitchellh/gox" _ "github.com/nishanths/exhaustive" _ "golang.org/x/tools/cmd/cover" diff --git a/version/version.go b/version/version.go index aca727e93bee..1dc5e222094a 100644 --- a/version/version.go +++ b/version/version.go @@ -11,7 +11,7 @@ import ( ) // The main version number that is being run at the moment. -var Version = "1.3.0" +var Version = "1.2.8" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/website/README.md b/website/README.md index eb9aead4144b..2ddc75b997be 100644 --- a/website/README.md +++ b/website/README.md @@ -1,30 +1,16 @@ # Terraform Documentation -This directory contains the portions of [the Terraform website](https://www.terraform.io/) that pertain to the core functionality, excluding providers and the overall configuration. +This directory contains the portions of [the Terraform website](https://www.terraform.io/) that pertain to the +core functionality, excluding providers and the overall configuration. -The website uses the files in this directory in conjunction with -[the `terraform-website` repository](https://github.com/hashicorp/terraform-website). The `terraform-website` repository brings all of the documentation together and contains the scripts for testing and building the entire site. - -## Suggesting Changes - -You can [submit an issue](https://github.com/hashicorp/terraform/issues/new/choose) with documentation requests or submit a pull request with suggested changes. - -Click **Edit this page** at the bottom of any Terraform website page to go directly to the associated markdown file in GitHub. +The files in this directory are intended to be used in conjunction with +[the `terraform-website` repository](https://github.com/hashicorp/terraform-website), which brings all of the +different documentation sources together and contains the scripts for testing and building the site as +a whole. ## Modifying Sidebar Navigation -You must update the the sidebar navigation when you add or delete documentation .mdx files. If you do not update the navigation, the website deploy preview fails. - -To update the sidebar navigation, you must edit the appropriate `nav-data.json` file. This repository contains the sidebar navigation files for the following documentation sets: -- Terraform Language: [`language-nav-data.json`](https://github.com/hashicorp/terraform/blob/main/website/data/language-nav-data.json) -- Terraform CLI: [`cli-nav-data.json`](https://github.com/hashicorp/terraform/blob/main/website/data/cli-nav-data.json) -- Introduction to Terraform: [`intro-nav-data.json`](https://github.com/hashicorp/terraform/blob/update-readme/website/data/intro-nav-data.json) - -For more details about how to update the sidebar navigation, refer to [Editing Navigation Sidebars](https://github.com/hashicorp/terraform-website#editing-navigation-sidebars) in the `terraform-website` repository. - -## Adding Redirects - -You must add a redirect when you move, rename, or delete documentation pages. Refer to https://github.com/hashicorp/terraform-website#redirects for details. +Updates to the sidebar navigation of Terraform docs need to be made in the [`terraform-website`](https://github.com/hashicorp/terraform-website/) repository (preferrably in a PR also updating the submodule commit). You can read more about how to make modifications to the navigation in the [README for `terraform-website`](https://github.com/hashicorp/terraform-website#editing-navigation-sidebars). ## Previewing Changes @@ -33,45 +19,25 @@ You should preview all of your changes locally before creating a pull request. T **Set Up Local Environment** 1. [Install Docker](https://docs.docker.com/get-docker/). -2. [Install Go](https://golang.org/doc/install) or create a `~/go` directory manually. -3. Open terminal and set `GOPATH` as an environment variable: - - Bash: `export $GOPATH=~/go`(bash) - - Zsh: `echo -n 'export GOPATH=~/go' >> ~/.zshrc` - -4. Restart your terminal or command line session. +1. Restart your terminal or command line session. **Launch Site Locally** 1. Navigate into your local `terraform` top-level directory and run `make website`. -1. Open `http://localhost:3000` in your web browser. While the preview is running, you can edit pages and Next.js automatically rebuilds them. -1. Press `ctrl-C` in your terminal to stop the server and end the preview. - +1. Open `http://localhost:3000` in your web browser. While the preview is running, you can edit pages and Next.js will automatically rebuild them. +1. When you're done with the preview, press `ctrl-C` in your terminal to stop the server. ## Deploying Changes -Merging a PR to `main` queues up documentation changes for the next minor product release. Your changes are not immediately available on the website. - -The website generates versioned documentation by pointing to the HEAD of the release branch for that version. For example, the `v1.2.x` documentation on the website points to the HEAD of the `v1.2` release branch in the `terraform` repository. To update existing documentation versions, you must also backport your changes to that release branch. Backported changes become live on the site within one hour. - -### Backporting - -**Important:** Editing old versions (not latest) should be rare. We backport to old versions when there is an egregious error. Egregious errors include inaccuracies that could cause security vulnerabilities or extreme inconvenience for users. - -Backporting involves cherry-picking commits to one or more release branches within a docs repository. You can backport (cherry-pick) commits to a version branch by adding the associated backport label to your pull request. For example, if you need to add a security warning to the v1.1 documentation, you must add the `1.1-backport` label. When you merge a pull request with one or more backport labels, GitHub Actions opens a backport PR to cherry-pick your changes to the associated release branches. You must manually merge the backport PR to finish backporting the changes. - -To make your changes available on the latest docs version: - -1. Add the backport label for the latest version. +Merge the PR to main. The changes will appear in the next major Terraform release. - Screen Shot 2022-08-09 at 11 06 17 AM - -1. Merge the pull request. GitHub Actions autogenerates a backport pull request, linked to the original. +If you need your changes to be deployed sooner, cherry-pick them to: - Screen Shot 2022-08-09 at 11 08 52 AM +- the current release branch (e.g. `v1.1`) and push. They will be deployed in the next minor version release (once every two weeks). +- the `stable-website` branch and push. They will be included in the next site deploy (see below). Note that the release process resets `stable-website` to match the release tag, removing any additional commits. So, we recommend always cherry-picking to the version branch first and then to `stable-website` when needed. +Once your PR to `stable-website` is merged, open a PR bumping the submodule commit in [`terraform-website`](https://github.com/hashicorp/terraform-website). -1. Merge the auto-generated backport pull request. +### Deployment - You can review and merge your own backport pull request without waiting for another review if the changes in the backport pull request are effectively equivalent to the original. You can make minor adjustments to resolve merge conflicts, but you should not merge a backport PR that contains major content or functionality changes from the original, approved pull request. If you are not sure whether it is okay to merge a backport pull request, post a comment on the original pull request to discuss with the team. +New commits in `hashicorp/terraform` and `hashicorp/terraform-cdk` don't automatically deploy the site. To use the latest upstream content, you'll need to open a PR bumping the submodule commit. If your changes aren't being deployed, it's very likely that you need to open a PR to update the submodule commit. diff --git a/website/data/language-nav-data.json b/website/data/language-nav-data.json index dd7003207744..1c4600e65b7e 100644 --- a/website/data/language-nav-data.json +++ b/website/data/language-nav-data.json @@ -319,10 +319,6 @@ "title": "chomp", "href": "/language/functions/chomp" }, - { - "title": "endswith", - "href": "/language/functions/endswith" - }, { "title": "format", "href": "/language/functions/format" @@ -356,10 +352,6 @@ "title": "split", "href": "/language/functions/split" }, - { - "title": "startswith", - "href": "/language/functions/startswith" - }, { "title": "strrev", "href": "/language/functions/strrev" @@ -692,6 +684,10 @@ "title": "Type Conversion Functions", "routes": [ { "title": "can", "href": "/language/functions/can" }, + { + "title": "defaults", + "href": "/language/functions/defaults" + }, { "title": "nonsensitive", "href": "/language/functions/nonsensitive" @@ -781,10 +777,10 @@ { "title": "concat", "path": "functions/concat", "hidden": true }, { "title": "contains", "path": "functions/contains", "hidden": true }, { "title": "csvdecode", "path": "functions/csvdecode", "hidden": true }, + { "title": "defaults", "path": "functions/defaults", "hidden": true }, { "title": "dirname", "path": "functions/dirname", "hidden": true }, { "title": "distinct", "path": "functions/distinct", "hidden": true }, { "title": "element", "path": "functions/element", "hidden": true }, - { "title": "endswith", "path": "functions/endswith", "hidden": true }, { "title": "file", "path": "functions/file", "hidden": true }, { "title": "filebase64", "path": "functions/filebase64", "hidden": true }, { @@ -860,7 +856,6 @@ { "title": "slice", "path": "functions/slice", "hidden": true }, { "title": "sort", "path": "functions/sort", "hidden": true }, { "title": "split", "path": "functions/split", "hidden": true }, - { "title": "startswith", "path": "functions/startswith", "hidden": true }, { "title": "strrev", "path": "functions/strrev", "hidden": true }, { "title": "substr", "path": "functions/substr", "hidden": true }, { "title": "sum", "path": "functions/sum", "hidden": true }, diff --git a/website/docs/cli/commands/fmt.mdx b/website/docs/cli/commands/fmt.mdx index ef03d401935c..ebdfaac5669f 100644 --- a/website/docs/cli/commands/fmt.mdx +++ b/website/docs/cli/commands/fmt.mdx @@ -45,18 +45,16 @@ and the generated files. ## Usage -Usage: `terraform fmt [options] [target...]` +Usage: `terraform fmt [options] [TARGET]` -By default, `fmt` scans the current directory for configuration files. If you -provide a directory for the `target` argument, then `fmt` will scan that -directory instead. If you provide a file, then `fmt` will process just that -file. If you provide a single dash (`-`), then `fmt` will read from standard -input (STDIN). +By default, `fmt` scans the current directory for configuration files. If +you provide a directory for the `target` argument, then `fmt` will scan that directory instead. If you provide a file, then `fmt` will process just that file. If you provide a single dash (`-`), then `fmt` will read from standard input (STDIN). The command-line flags are all optional. The following flags are available: * `-list=false` - Don't list the files containing formatting inconsistencies. * `-write=false` - Don't overwrite the input files. (This is implied by `-check` or when the input is STDIN.) * `-diff` - Display diffs of formatting changes -* `-check` - Check if the input is formatted. Exit status will be 0 if all input is properly formatted. If not, exit status will be non-zero and the command will output a list of filenames whose files are not properly formatted. +* `-check` - Check if the input is formatted. Exit status will be 0 if + all input is properly formatted and non-zero otherwise. * `-recursive` - Also process files in subdirectories. By default, only the given directory (or current directory) is processed. diff --git a/website/docs/cli/commands/init.mdx b/website/docs/cli/commands/init.mdx index 05e679b2ef53..3c980f941950 100644 --- a/website/docs/cli/commands/init.mdx +++ b/website/docs/cli/commands/init.mdx @@ -166,7 +166,7 @@ You can modify `terraform init`'s plugin behavior with the following options: The valid values for the lockfile mode are as follows: -* `readonly`: suppress the lockfile changes, but verify checksums against the +* readonly: suppress the lockfile changes, but verify checksums against the information already recorded. It conflicts with the `-upgrade` flag. If you update the lockfile with third-party dependency management tools, it would be useful to control when it changes explicitly. diff --git a/website/docs/language/expressions/references.mdx b/website/docs/language/expressions/references.mdx index 75b3bc41bbb0..947013a36f16 100644 --- a/website/docs/language/expressions/references.mdx +++ b/website/docs/language/expressions/references.mdx @@ -120,19 +120,12 @@ to mark the reference as for a data resource. The following values are available: -- `path.module` is the filesystem path of the module where the expression is placed. - We do not recommend using `path.module` in write operations because it can produce - different behavior depending on whether you use remote or local module sources. - Multiple invocations of local modules use the same source directory, overwriting - the data in `path.module` during each call. This can lead to race conditions and - unexpected results. +- `path.module` is the filesystem path of the module where the expression is placed. We do not recommend using `path.module` in write operations because it can produce different behavior depending on whether you use remote or local module sources. Multiple invocations of local modules use the same source directory, overwriting the data in `path.module` during each call. This can lead to race conditions and unexpected results. - `path.root` is the filesystem path of the root module of the configuration. -- `path.cwd` is the filesystem path of the original working directory from where you - ran Terraform before applying any `-chdir` argument. This path is an absolute path - that includes details about the filesystem structure. It is also useful in some - advanced cases where Terraform is run from a directory other than the root module - directory. We recommend using `path.root` or `path.module` over `path.cwd` where - possible. +- `path.cwd` is the filesystem path of the current working directory. In + normal use of Terraform this is the same as `path.root`, but some advanced + uses of Terraform run it from a directory other than the root module + directory, causing these paths to be different. - `terraform.workspace` is the name of the currently selected [workspace](/language/state/workspaces). diff --git a/website/docs/language/expressions/type-constraints.mdx b/website/docs/language/expressions/type-constraints.mdx index 2f5d5b495f13..9ff803b81bb3 100644 --- a/website/docs/language/expressions/type-constraints.mdx +++ b/website/docs/language/expressions/type-constraints.mdx @@ -258,160 +258,39 @@ variable "no_type_constraint" { In this case, Terraform will replace `any` with the exact type of the given value and thus perform no type conversion whatsoever. -## Optional Object Type Attributes +## Experimental: Optional Object Type Attributes --> **Note:** Optional object type attributes are supported only in Terraform v1.3 and later. +From Terraform v0.14 there is _experimental_ support for marking particular +attributes as optional in an object type constraint. -Terraform typically returns an error when it does not receive a value for specified object attributes. When you mark an attribute as optional, Terraform instead inserts a default value for the missing attribute. This allows the receiving module to describe an appropriate fallback behavior. - -To mark attributes as optional, use the `optional` modifier in the object type constraint. The following example creates optional attribute `b` and optional attribute with a default value `c`. +To mark an attribute as optional, use the additional `optional(...)` modifier +around its type declaration: ```hcl variable "with_optional_attribute" { type = object({ - a = string # a required attribute - b = optional(string) # an optional attribute - c = optional(number, 127) # an optional attribute with default value + a = string # a required attribute + b = optional(string) # an optional attribute }) } ``` -The `optional` modifier takes one or two arguments. -- **Type:** (Required) The first argument -specifies the type of the attribute. -- **Default:** (Optional) The second argument defines the default value that Terraform should use if the attribute is not present. This must be compatible with the attribute type. If not specified, Terraform uses a `null` value of the appropriate type as the default. - -An optional attribute with a non-`null` default value is guaranteed to never have the value `null` within the receiving module. Terraform will substitute the default value both when a caller omits the attribute altogether and when a caller explicitly sets it to `null`, thereby avoiding the need for additional checks to handle a possible null value. - -Terraform applies object attribute defaults top-down in nested variable types. This means that Terraform applies the default value you specify in the `optional` modifier first and then later applies any nested default values to that attribute. - -### Example: Nested Structures with Optional Attributes and Defaults - -The following example defines a variable for storage buckets that host a website. This variable type uses several optional attributes, including `website`, which is itself an optional `object` type that has optional attributes and defaults. - -```hcl -variable "buckets" { - type = list(object({ - name = string - enabled = optional(bool, true) - website = optional(object({ - index_document = optional(string, "index.html") - error_document = optional(string, "error.html") - routing_rules = optional(string) - }), {}) - })) -} -``` - -The following example `terraform.tfvars` file specifies three bucket configurations for `var.buckets`. - -- `production` sets the routing rules to add a redirect -- `archived` uses default configuration, but is disabled -- `docs` overrides the index and error documents to use text files - -The `production` bucket does not specify the index and error documents, and the `archived` bucket omits the website configuration entirely. Terraform will use the default values specified in the `bucket` type constraint. - -```hcl -buckets = [ - { - name = "production" - website = { - routing_rules = <<-EOT - [ - { - "Condition" = { "KeyPrefixEquals": "img/" }, - "Redirect" = { "ReplaceKeyPrefixWith": "images/" } - } - ] - EOT - } - }, - { - name = "archived" - enabled = false - }, - { - name = "docs" - website = { - index_document = "index.txt" - error_document = "error.txt" - } - }, -] -``` - -This configuration produces the following variable values. - -- For the `production` and `docs` buckets, Terraform sets `enabled` to `true`. Terraform also supplies default values for `website`, and then the values specified in `docs` override those defaults. -- For the `archived` and `docs` buckets, Terraform sets `routing_rules` to a `null` value. When Terraform does not receive optional attributes and there are no specified defaults, Terraform populates those attributes with a `null` value. -- For the `archived` bucket, Terraform populates the `website` attribute with the default values specified in the `buckets` type constraint. - -```hcl -tolist([ - { - "enabled" = true - "name" = "production" - "website" = { - "error_document" = "error.html" - "index_document" = "index.html" - "routing_rules" = <<-EOT - [ - { - "Condition" = { "KeyPrefixEquals": "img/" }, - "Redirect" = { "ReplaceKeyPrefixWith": "images/" } - } - ] - - EOT - } - }, - { - "enabled" = false - "name" = "archived" - "website" = { - "error_document" = "error.html" - "index_document" = "index.html" - "routing_rules" = tostring(null) - } - }, - { - "enabled" = true - "name" = "docs" - "website" = { - "error_document" = "error.txt" - "index_document" = "index.txt" - "routing_rules" = tostring(null) - } - }, -]) -``` - -### Example: Conditionally setting an optional attribute +By default, for required attributes, Terraform will return an error if the +source value has no matching attribute. Marking an attribute as optional +changes the behavior in that situation: Terraform will instead just silently +insert `null` as the value of the attribute, allowing the receiving module +to describe an appropriate fallback behavior. -Sometimes the decision about whether or not to set a value for an optional argument needs to be made dynamically based on some other data. In that case, the calling `module` block can use a conditional expression with `null` as one of its result arms to represent dynamically leaving the argument unset. - -With the `variable "buckets"` declaration shown in the previous section, the following example conditionally overrides the `index_document` and `error_document` settings in the `website` object based on a new variable `var.legacy_filenames`: +Because this feature is currently experimental, it requires an explicit +opt-in on a per-module basis. To use it, write a `terraform` block with the +`experiments` argument set as follows: ```hcl -variable "legacy_filenames" { - type = bool - default = false - nullable = false -} - -module "buckets" { - source = "./modules/buckets" - - buckets = [ - { - name = "maybe_legacy" - website = { - error_document = var.legacy_filenames ? "ERROR.HTM" : null - index_document = var.legacy_filenames ? "INDEX.HTM" : null - } - }, - ] +terraform { + experiments = [module_variable_optional_attrs] } ``` -When `var.legacy_filenames` is set to `true`, the call will override the document filenames. When it is `false`, the call will leave the two filenames unspecified, thereby allowing the module to use its specified default values. +Until the experiment is concluded, the behavior of this feature may see +breaking changes even in minor releases. We recommend using this feature +only in prerelease versions of modules as long as it remains experimental. diff --git a/website/docs/language/functions/defaults.mdx b/website/docs/language/functions/defaults.mdx new file mode 100644 index 000000000000..74c535280b01 --- /dev/null +++ b/website/docs/language/functions/defaults.mdx @@ -0,0 +1,198 @@ +--- +page_title: defaults - Functions - Configuration Language +description: The defaults function can fill in default values in place of null values. +--- + +# `defaults` Function + +-> **Note:** This function is available only in Terraform 0.15 and later. + +~> **Experimental:** This function is part of +[the optional attributes experiment](/language/expressions/type-constraints#experimental-optional-object-type-attributes) +and is only available in modules where the `module_variable_optional_attrs` +experiment is explicitly enabled. + +The `defaults` function is a specialized function intended for use with +input variables whose type constraints are object types or collections of +object types that include optional attributes. + +When you define an attribute as optional and the caller doesn't provide an +explicit value for it, Terraform will set the attribute to `null` to represent +that it was omitted. If you want to use a placeholder value other than `null` +when an attribute isn't set, you can use the `defaults` function to concisely +assign default values only where an attribute value was set to `null`. + +``` +defaults(input_value, defaults) +``` + +The `defaults` function expects that the `input_value` argument will be the +value of an input variable with an exact [type constraint](/language/expressions/types) +(not containing `any`). The function will then visit every attribute in +the data structure, including attributes of nested objects, and apply the +default values given in the defaults object. + +The interpretation of attributes in the `defaults` argument depends on what +type an attribute has in the `input_value`: + +* **Primitive types** (`string`, `number`, `bool`): if a default value is given + then it will be used only if the `input_value`'s attribute of the same + name has the value `null`. The default value's type must match the input + value's type. +* **Structural types** (`object` and `tuple` types): Terraform will recursively + visit all of the attributes or elements of the nested value and repeat the + same defaults-merging logic one level deeper. The default value's type must + be of the same kind as the input value's type, and a default value for an + object type must only contain attribute names that appear in the input + value's type. +* **Collection types** (`list`, `map`, and `set` types): Terraform will visit + each of the collection elements in turn and apply defaults to them. In this + case the default value is only a single value to be applied to _all_ elements + of the collection, so it must have a type compatible with the collection's + element type rather than with the collection type itself. + +The above rules may be easier to follow with an example. Consider the following +Terraform configuration: + +```hcl +terraform { + # Optional attributes and the defaults function are + # both experimental, so we must opt in to the experiment. + experiments = [module_variable_optional_attrs] +} + +variable "storage" { + type = object({ + name = string + enabled = optional(bool) + website = object({ + index_document = optional(string) + error_document = optional(string) + }) + documents = map( + object({ + source_file = string + content_type = optional(string) + }) + ) + }) +} + +locals { + storage = defaults(var.storage, { + # If "enabled" isn't set then it will default + # to true. + enabled = true + + # The "website" attribute is required, but + # it's here to provide defaults for the + # optional attributes inside. + website = { + index_document = "index.html" + error_document = "error.html" + } + + # The "documents" attribute has a map type, + # so the default value represents defaults + # to be applied to all of the elements in + # the map, not for the map itself. Therefore + # it's a single object matching the map + # element type, not a map itself. + documents = { + # If _any_ of the map elements omit + # content_type then this default will be + # used instead. + content_type = "application/octet-stream" + } + }) +} + +output "storage" { + value = local.storage +} +``` + +To test this out, we can create a file `terraform.tfvars` to provide an example +value for `var.storage`: + +```hcl +storage = { + name = "example" + + website = { + error_document = "error.txt" + } + documents = { + "index.html" = { + source_file = "index.html.tmpl" + content_type = "text/html" + } + "error.txt" = { + source_file = "error.txt.tmpl" + content_type = "text/plain" + } + "terraform.exe" = { + source_file = "terraform.exe" + } + } +} +``` + +The above value conforms to the variable's type constraint because it only +omits attributes that are declared as optional. Terraform will automatically +populate those attributes with the value `null` before evaluating anything +else, and then the `defaults` function in `local.storage` will substitute +default values for each of them. + +The result of this `defaults` call would therefore be the following object: + +``` +storage = { + "documents" = tomap({ + "error.txt" = { + "content_type" = "text/plain" + "source_file" = "error.txt.tmpl" + } + "index.html" = { + "content_type" = "text/html" + "source_file" = "index.html.tmpl" + } + "terraform.exe" = { + "content_type" = "application/octet-stream" + "source_file" = "terraform.exe" + } + }) + "enabled" = true + "name" = "example" + "website" = { + "error_document" = "error.txt" + "index_document" = "index.html" + } +} +``` + +Notice that `enabled` and `website.index_document` were both populated directly +from the defaults. Notice also that the `"terraform.exe"` element of +`documents` had its `content_type` attribute populated from the `documents` +default, but the default value didn't need to predict that there would be an +element key `"terraform.exe"` because the default values apply equally to +all elements of the map where the optional attributes are `null`. + +## Using `defaults` elsewhere + +The design of the `defaults` function depends on input values having +well-specified type constraints, so it can reliably recognize the difference +between similar types: maps vs. objects, lists vs. tuples. The type constraint +causes Terraform to convert the caller's value to conform to the constraint +and thus `defaults` can rely on the input to conform. + +Elsewhere in the Terraform language it's typical to be less precise about +types, for example using the object construction syntax `{ ... }` to construct +values that will be used as if they are maps. Because `defaults` uses the +type information of `input_value`, an `input_value` that _doesn't_ originate +in an input variable will tend not to have an appropriate value type and will +thus not be interpreted as expected by `defaults`. + +We recommend using `defaults` only with fully-constrained input variable values +in the first argument, so you can use the variable's type constraint to +explicitly distinguish between collection and structural types. diff --git a/website/docs/language/functions/endswith.mdx b/website/docs/language/functions/endswith.mdx deleted file mode 100644 index d96cc88aad8c..000000000000 --- a/website/docs/language/functions/endswith.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -page_title: endswith - Functions - Configuration Language -description: |- - The endswith function takes two values: a string to check and a suffix string. It returns true if the first string ends with that exact suffix. ---- - -# `endswith` Function - -`endswith` takes two values: a string to check and a suffix string. The function returns true if the first string ends with that exact suffix. - -```hcl -endswith(string, suffix) -``` - -## Examples - -``` -> endswith("hello world", "world") -true - -> endswith("hello world", "hello") -false -``` - -## Related Functions - -- [`startswith`](/language/functions/startswith) takes two values: a string to check and a prefix string. The function returns true if the string begins with that exact prefix. diff --git a/website/docs/language/functions/startswith.mdx b/website/docs/language/functions/startswith.mdx deleted file mode 100644 index d49f7aa0ba13..000000000000 --- a/website/docs/language/functions/startswith.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -page_title: startsswith - Functions - Configuration Language -description: |- - The startswith function takes two values: a string to check and a prefix string. It returns true if the string begins with that exact prefix. ---- - -# `startswith` Function - -`startswith` takes two values: a string to check and a prefix string. The function returns true if the string begins with that exact prefix. - -```hcl -startswith(string, prefix) -``` - -## Examples - -``` -> startswith("hello world", "hello") -true - -> startswith("hello world", "world") -false -``` - -## Related Functions - -- [`endswith`](/language/functions/endswith) takes two values: a string to check and a suffix string. The function returns true if the first string ends with that exact suffix. \ No newline at end of file diff --git a/website/docs/language/modules/develop/refactoring.mdx b/website/docs/language/modules/develop/refactoring.mdx index 28314041613c..210d025ce033 100644 --- a/website/docs/language/modules/develop/refactoring.mdx +++ b/website/docs/language/modules/develop/refactoring.mdx @@ -400,6 +400,12 @@ assumes that all three of these modules are maintained by the same people and distributed together in a single [module package](/language/modules/sources#modules-in-package-sub-directories). +To reduce [coupling](https://en.wikipedia.org/wiki/Coupling_\(computer_programming\)) +between separately-packaged modules, Terraform only allows declarations of +moves between modules in the same package. In other words, Terraform would +not have allowed moving into `module.x` above if the `source` address of +that call had not been a [local path](/language/modules/sources#local-paths). + Terraform resolves module references in `moved` blocks relative to the module instance they are defined in. For example, if the original module above were already a child module named `module.original`, the reference to diff --git a/website/docs/language/resources/syntax.mdx b/website/docs/language/resources/syntax.mdx index cabb1ef9aab5..7ed899f971fe 100644 --- a/website/docs/language/resources/syntax.mdx +++ b/website/docs/language/resources/syntax.mdx @@ -133,7 +133,7 @@ The following meta-arguments are documented on separate pages: ## Custom Condition Checks -You can use `precondition` and `postcondition` blocks to specify assumptions and guarantees about how the resource operates. The following example creates a precondition that checks whether the AMI is properly configured. +You can use `precondition` and `postcondition` blocks to specify assumptions and guarantees about how the resource operates. The following examples creates a precondition that checks whether the AMI is properly configured. ```hcl resource "aws_instance" "example" { diff --git a/website/docs/language/settings/backends/azurerm.mdx b/website/docs/language/settings/backends/azurerm.mdx index a9676b7756a3..3ef63612291d 100644 --- a/website/docs/language/settings/backends/azurerm.mdx +++ b/website/docs/language/settings/backends/azurerm.mdx @@ -9,7 +9,7 @@ Stores the state as a Blob with the given Key within the Blob Container within [ This backend supports state locking and consistency checking with Azure Blob Storage native capabilities. -~> **Terraform 1.1 and 1.2 supported a feature-flag to allow enabling/disabling the use of Microsoft Graph (and MSAL) rather than Azure Active Directory Graph (and ADAL) - however this flag has since been removed in Terraform 1.3. Microsoft Graph (and MSAL) are now enabled by default and Azure Active Directory Graph (and ADAL) can no longer be used. +-> **Note:** In Terraform 1.2 the Azure Backend uses MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication by default - you can disable this by setting `use_microsoft_graph` to `false`. **This setting will be removed in Terraform 1.3, due to Microsoft's deprecation of ADAL**. ## Example Configuration @@ -263,6 +263,10 @@ When authenticating using the Managed Service Identity (MSI) - the following fie * `tenant_id` - (Optional) The Tenant ID in which the Subscription exists. This can also be sourced from the `ARM_TENANT_ID` environment variable. +* `use_microsoft_graph` - (Optional) Should MSAL be used for authentication instead of ADAL, and should Microsoft Graph be used instead of Azure Active Directory Graph? Defaults to `true`. + +-> **Note:** In Terraform 1.2 the Azure Backend uses MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication by default - you can disable this by setting `use_microsoft_graph` to `false`. **This setting will be removed in Terraform 1.3, due to Microsoft's deprecation of ADAL**. + * `use_msi` - (Optional) Should Managed Service Identity authentication be used? This can also be sourced from the `ARM_USE_MSI` environment variable. *** @@ -275,6 +279,8 @@ When authenticating using a Service Principal with OpenID Connect (OIDC) - the f * `use_oidc` - (Optional) Should OIDC authentication be used? This can also be sourced from the `ARM_USE_OIDC` environment variable. +~> **Note:** When using OIDC for authentication, `use_microsoft_graph` must be set to `true` (which is the default). + *** When authenticating using a SAS Token associated with the Storage Account - the following fields are also supported: @@ -295,6 +301,10 @@ When authenticating using AzureAD Authentication - the following fields are also -> **Note:** When using AzureAD for Authentication to Storage you also need to ensure the `Storage Blob Data Owner` role is assigned. +* `use_microsoft_graph` - (Optional) Should MSAL be used for authentication instead of ADAL, and should Microsoft Graph be used instead of Azure Active Directory Graph? Defaults to `true`. + +-> **Note:** In Terraform 1.2 the Azure Backend uses MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication by default - you can disable this by setting `use_microsoft_graph` to `false`. **This setting will be removed in Terraform 1.3, due to Microsoft's deprecation of ADAL**. + *** When authenticating using a Service Principal with a Client Certificate - the following fields are also supported: @@ -311,6 +321,10 @@ When authenticating using a Service Principal with a Client Certificate - the fo * `tenant_id` - (Optional) The Tenant ID in which the Subscription exists. This can also be sourced from the `ARM_TENANT_ID` environment variable. +* `use_microsoft_graph` - (Optional) Should MSAL be used for authentication instead of ADAL, and should Microsoft Graph be used instead of Azure Active Directory Graph? Defaults to `true`. + +-> **Note:** In Terraform 1.2 the Azure Backend uses MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication by default - you can disable this by setting `use_microsoft_graph` to `false`. **This setting will be removed in Terraform 1.3, due to Microsoft's deprecation of ADAL**. + *** When authenticating using a Service Principal with a Client Secret - the following fields are also supported: @@ -324,3 +338,7 @@ When authenticating using a Service Principal with a Client Secret - the followi * `subscription_id` - (Optional) The Subscription ID in which the Storage Account exists. This can also be sourced from the `ARM_SUBSCRIPTION_ID` environment variable. * `tenant_id` - (Optional) The Tenant ID in which the Subscription exists. This can also be sourced from the `ARM_TENANT_ID` environment variable. + +* `use_microsoft_graph` - (Optional) Should MSAL be used for authentication instead of ADAL, and should Microsoft Graph be used instead of Azure Active Directory Graph? Defaults to `true`. + +-> **Note:** In Terraform 1.2 the Azure Backend uses MSAL (and Microsoft Graph) rather than ADAL (and Azure Active Directory Graph) for authentication by default - you can disable this by setting `use_microsoft_graph` to `false`. **This setting will be removed in Terraform 1.3, due to Microsoft's deprecation of ADAL**. diff --git a/website/layouts/language.erb b/website/layouts/language.erb index f2bf83dede52..b2233530f218 100644 --- a/website/layouts/language.erb +++ b/website/layouts/language.erb @@ -370,10 +370,6 @@ chomp -