diff --git a/.github/workflows/terraform_provider.yml b/.github/workflows/terraform_provider.yml index c5c1b03b..0c0f970b 100644 --- a/.github/workflows/terraform_provider.yml +++ b/.github/workflows/terraform_provider.yml @@ -159,7 +159,7 @@ jobs: REDISCLOUD_ACCESS_KEY: ${{ secrets.REDISCLOUD_ACCESS_KEY_QA }} REDISCLOUD_SECRET_KEY: ${{ secrets.REDISCLOUD_SECRET_KEY_QA }} REDISCLOUD_URL: https://api-cloudapi.qa.redislabs.com/v1 - AWS_TEST_CLOUD_ACCOUNT_NAME: oc + AWS_TEST_CLOUD_ACCOUNT_NAME: EXTERNAL-CA-2023 # REDISCLOUD_ACCESS_KEY: ${{ secrets.REDISCLOUD_ACCESS_KEY_PROD }} # REDISCLOUD_SECRET_KEY: ${{ secrets.REDISCLOUD_SECRET_KEY_PROD }} # REDISCLOUD_URL: https://api.redislabs.com/v1/ diff --git a/GNUmakefile b/GNUmakefile index 41bd4fe5..af2c78f2 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -22,8 +22,8 @@ $(BIN)/%: build: @echo "Building local provider binary" - @mkdir -p ./$(BIN) - go build -o ./$(BIN)/terraform-provider-rediscloud_v$(PROVIDER_VERSION) + @mkdir -p $(BIN) + go build -o $(BIN)/terraform-provider-rediscloud_v$(PROVIDER_VERSION) @sh -c "'$(CURDIR)/scripts/generate-dev-overrides.sh'" clean: @@ -31,7 +31,7 @@ clean: rm -rf $(BIN) testacc: - TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m -parallel=$(TEST_PARALLELISM) + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 360m -parallel=$(TEST_PARALLELISM) install_local: build @echo "Installing local provider binary to plugins mirror path $(PLUGINS_PATH)/$(PLUGINS_PROVIDER_PATH)" diff --git a/docs/resources/rediscloud_active_active_regions.md b/docs/resources/rediscloud_active_active_regions.md new file mode 100644 index 00000000..edec8539 --- /dev/null +++ b/docs/resources/rediscloud_active_active_regions.md @@ -0,0 +1,82 @@ +--- +layout: "rediscloud" +page_title: "Redis Cloud: rediscloud_active_active_regions" +description: |- + Regions resource in the Terraform provider Redis Cloud. +--- + +# Resource: rediscloud_active_active_regions + +Creates an Active Active Regions within your Redis Enterprise Cloud subscription. +This resource is responsible for creating and managing regions within that subscription. This allows Redis Enterprise Cloud to efficiently provision your cluster within each defined region in a separate block. + +## Example Usage + +```hcl +resource "rediscloud_active_active_subscription_regions" "regions-resource" { + subscription_id = rediscloud_active_active_subscription.subscription-resource.id + delete_regions = false + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + database { + database_id = rediscloud_active_active_subscription_database.database-resource.db_id + database_name = rediscloud_active_active_subscription_database.database-resource.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + database { + database_id = rediscloud_active_active_subscription_database.database-resource.db_id + database_name = rediscloud_active_active_subscription_database.database-resource.name + local_write_operations_per_second = 2000 + local_read_operations_per_second = 4000 + } + } + } +``` + +## Argument Reference + +The following arguments are supported: + +* `subscription_id` - (Required) ID of the subscription that the regions belong to +* `delete_regions` - (Optional) Flag required to be set when one or more regions is to be deleted, if the flag is not set an error will be thrown +* `region` - (Required) Cloud networking details, per region, documented below + +The `region` block supports: + +* `region_id` - (Computed) The ID of the region, as created by the API +* `region` - (Required) Region name +* `vpc_id` - (Computed) Identifier of the VPC to be peered, set by the API +* `networking_deployment_cidr` - (Required) Deployment CIDR mask. The total number of bits must be 24 (x.x.x.x/24) +* `recreate_region` - (Optional) Protection flag, needs to be set if a region has to be re-created. A region will need to be re-created in the case of a change on the `networking_deployment_cidr` field. During re-create, the region will be deleted (so the `delete_regions` flag also needs to be set) and then created again. Default: 'false' +* `database` - (Required) A block defining the write and read operations in the region, per database, documented below + +The `database` block supports: + +* `database_id` - (Required) Database ID belonging to the subscription +* `database_name` - (Required) Database name belonging to the subscription +* `local_write_operations_per_second` - (Required) Local write operations per second for this active-active region +* `local_read_operations_per_second` - (Required) Local read operations per second for this active-active region + + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 60 mins) Used when creating the regions +* `update` - (Defaults to 60 mins) Used when updating the regions +* `delete` - (Defaults to 10 mins) Used when destroying the regions + +## Import + +`rediscloud_active_active_regions` can be imported using the ID of the subscription, e.g. + +``` +$ terraform import rediscloud_active_active_regions.regions-resource 12345678 +``` + diff --git a/docs/resources/rediscloud_active_active_subscription.md b/docs/resources/rediscloud_active_active_subscription.md new file mode 100644 index 00000000..a83e7a60 --- /dev/null +++ b/docs/resources/rediscloud_active_active_subscription.md @@ -0,0 +1,92 @@ +--- +layout: "rediscloud" +page_title: "Redis Cloud: rediscloud_active_active_subscription" +description: |- + Subscription resource in the Terraform provider Redis Cloud. +--- + +# Resource: rediscloud_active_active_subscription + +Creates an Active-Active Subscription within your Redis Enterprise Cloud Account. +This resource is responsible for creating and managing subscriptions. + +~> **Note:** The creation_plan block allows the API server to create a well-optimised infrastructure for your databases in the cluster. +The attributes inside the block are used by the provider to create initial +databases. Those databases will be deleted after provisioning a new +subscription, then the databases defined as separate resources will be attached to +the subscription. The creation_plan block can ONLY be used for provisioning new +subscriptions, the block will be ignored if you make any further changes or try importing the resource (e.g. `terraform import` ...). + +## Example Usage + +```hcl +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_active_active_subscription" "subscription-resource" { + name = "subscription-name" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A meaningful name to identify the subscription +* `payment_method` (Optional) The payment method for the requested subscription, (either `credit-card` or `marketplace`). If `credit-card` is specified, `payment_method_id` must be defined. Default: 'credit-card' +* `payment_method_id` - (Optional) A valid payment method pre-defined in the current account. This value is __Optional__ for AWS/GCP Marketplace accounts, but __Required__ for all other account types +* `cloud_provider` - (Optional) The cloud provider to use with the subscription, (either `AWS` or `GCP`). Default: ‘AWS’ +* `creation_plan` - (Required) A creation plan object, documented below + +The `creation_plan` block supports: + +* `memory_limit_in_gb` - (Required) Maximum memory usage that will be used for your largest planned database, including replication and other overhead +* `quantity` - (Required) The planned number of databases in the subscription. + +The creation_plan `region` block supports: + +* `region` - (Required) Deployment region as defined by cloud provider +* `networking_deployment_cidr` - (Required) Deployment CIDR mask. The total number of bits must be 24 (x.x.x.x/24) +* `write_operations_per_second` - (Required) Throughput measurement for an active-active subscription +* `read_operations_per_second` - (Required) Throughput measurement for an active-active subscription + +~> **Note:** If changes are made to attributes in the subscription which require the subscription to be recreated (such as `cloud_provider` or `payment_method`), the creation_plan will need to be defined in order to change these attributes. This is because the creation_plan is always required when a subscription is created. + + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 mins) Used when creating the subscription +* `update` - (Defaults to 30 mins) Used when updating the subscription +* `delete` - (Defaults to 10 mins) Used when destroying the subscription + +## Import + +`rediscloud_active_active_subscription` can be imported using the ID of the subscription, e.g. + +``` +$ terraform import rediscloud_active_active_subscription.subscription-resource 12345678 +``` + +~> **Note:** the creation_plan block will be ignored during imports. diff --git a/docs/resources/rediscloud_active_active_subscription_database.md b/docs/resources/rediscloud_active_active_subscription_database.md new file mode 100644 index 00000000..4b19daf0 --- /dev/null +++ b/docs/resources/rediscloud_active_active_subscription_database.md @@ -0,0 +1,133 @@ +--- +layout: "rediscloud" +page_title: "Redis Cloud: rediscloud_active_active_subscription_database" +description: |- +Database resource for Active-Active Subscriptions in the Terraform provider Redis Cloud. +--- + +# Resource: rediscloud_active_active_subscription_database + +Creates a Database within a specified Active-Active Subscription in your Redis Enterprise Cloud Account. + +## Example Usage + +```hcl +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_active_active_subscription" "subscription-resource" { + name = "subscription-name" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 2000 + } + } +} + +resource "rediscloud_active_active_subscription_database" "database-resource" { + subscription_id = rediscloud_active_active_subscription.subscription-resource.id + name = "database-name" + memory_limit_in_gb = 1 + global_data_persistence = "aof-every-1-second" + global_password = "some-random-pass-2" + global_source_ips = ["192.168.0.0/16"] + global_alert { + name = "dataset-size" + value = 40 + } + + override_region { + name = "us-east-2" + override_global_source_ips = ["192.10.0.0/16"] + } + + override_region { + name = "us-east-1" + override_global_data_persistence = "none" + override_global_password = "region-specific-password" + override_global_alert { + name = "dataset-size" + value = 60 + } + } +} + +output "us-east-1-public-endpoints" { + value = rediscloud_active_active_subscription_database.database-resource.public_endpoint.us-east-1 +} + +output "us-east-2-private-endpoints" { + value = rediscloud_active_active_subscription_database.database-resource.private_endpoint.us-east-1 +} +``` + +## Argument Reference + +The following arguments are supported: +* `subscription_id`: (Required) The ID of the Active-Active subscription to create the database in +* `name` - (Required) A meaningful name to identify the database +* `memory_limit_in_gb` - (Required) Maximum memory usage for this specific database, including replication and other overhead +* `support_oss_cluster_api` - (Optional) Support Redis open-source (OSS) Cluster API. Default: ‘false’ +* `external_endpoint_for_oss_cluster_api` - (Optional) Should use the external endpoint for open-source (OSS) Cluster API. + Can only be enabled if OSS Cluster API support is enabled. Default: 'false' +* `enable_tls` - (Optional) Use TLS for authentication. Default: ‘false’ +* `client_ssl_certificate` - (Optional) SSL certificate to authenticate user connections. +* `data_eviction` - (Optional) The data items eviction policy (either: 'allkeys-lru', 'allkeys-lfu', 'allkeys-random', 'volatile-lru', 'volatile-lfu', 'volatile-random', 'volatile-ttl' or 'noeviction'. Default: 'volatile-lru') +* `global_data_persistence` - (Optional) Global rate of database data persistence (in persistent storage) of regions that dont override global settings. Default: 'none' +* `global_password` - (Optional) Password to access the database of regions that dont override global settings. If left empty, the password will be generated automatically +* `global_alert` - (Optional) A block defining Redis database alert of regions that dont override global settings, documented below, can be specified multiple times +* `global_source_ips` - (Optional) List of source IP addresses or subnet masks of regions that dont override global settings. If specified, Redis clients will be able to connect to this database only from within the specified source IP addresses ranges (example: ['192.168.10.0/32', '192.168.12.0/24']) +* `override_region` - (Optional) Override region specific configuration, documented below + + +The `override_region` block supports: + +* `name` - (Required) Region name. +* `override_global_alert` - (Optional) A block defining Redis regional instance of an Active-Active database alert, documented below, can be specified multiple times +* `override_global_password` - (Optional) If specified, this regional instance of an Active-Active database password will be used to access the database +* `override_global_source_ips` - (Optional) List of regional instance of an Active-Active database source IP addresses or subnet masks. If specified, Redis clients will be able to connect to this database only from within the specified source IP addresses ranges (example: ['192.168.10.0/32', '192.168.12.0/24'] ) +* `override_global_data_persistence` - (Optional) Regional instance of an Active-Active database data persistence rate (in persistent storage) + +The `override_global_alert` block supports: + +* `name` - (Required) Alert name +* `value` - (Required) Alert value + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 mins) Used when creating the database +* `update` - (Defaults to 30 mins) Used when updating the database +* `delete` - (Defaults to 10 mins) Used when destroying the database + +## Attribute reference + +* `db_id` - Identifier of the database created +* `public_endpoint` - A map of which public endpoints can to access the database per region, uses region name as key. +* `private_endpoint` - A map of which private endpoints can to access the database per region, uses region name as key. + +## Import +`rediscloud_active_active_subscription_database` can be imported using the ID of the Active-Active subscription and the ID of the database in the format {subscription ID}/{database ID}, e.g. + +``` +$ terraform import rediscloud_active_active_subscription_database.database-resource 123456/12345678 +``` + +Note: Due to constraints in the Redis Cloud API, the import process will not import global attributes or override region attributes. If you wish to use these attributes in your Terraform configuraton, you will need to manually add them to your Terraform configuration and run `terraform apply` to update the database. + diff --git a/docs/resources/rediscloud_active_active_subscription_peering.md b/docs/resources/rediscloud_active_active_subscription_peering.md new file mode 100644 index 00000000..430d93e0 --- /dev/null +++ b/docs/resources/rediscloud_active_active_subscription_peering.md @@ -0,0 +1,113 @@ +--- +layout: "rediscloud" +page_title: "Redis Cloud: rediscloud_active_active_subscription_peering" +description: |- + Active-Active subscription VPC peering resource in the Terraform provider Redis Cloud. +--- + +# Resource: rediscloud_active_active_subscription_peering + +Creates an AWS or GCP VPC peering for an existing Redis Enterprise Cloud Active-Active Subscription, allowing access to your subscription databases as if they were on the same network. + +For AWS, peering should be accepted by the other side. +For GCP, the opposite peering request should be submitted. + +## Example Usage - AWS + +The following example shows how an Active-Active subscription can be peered with an AWS VPC using the rediscloud and AWS providers. + +```hcl +resource "rediscloud_active_active_subscription" "subscription-resource" { + // ... +} + +resource "rediscloud_active_active_subscription_peering" "peering-resource" { + subscription_id = rediscloud_active_active_subscription.subscription-resource.id + source_region = "us-east-1" + destination_region = "eu-west-2" + aws_account_id = "123456789012" + vpc_id = "vpc-01234567890" + vpc_cidr = "10.0.10.0/24" +} + +resource "aws_vpc_peering_connection_accepter" "aws-peering-resource" { + vpc_peering_connection_id = rediscloud_active_active_subscription_peering.peering-resource.aws_peering_id + auto_accept = true +} +``` + +## Example Usage - GCP + +The following example shows how an Active-Active subscription can be peered with a GCP project network using the rediscloud and google providers. +The example HCL locates the network details and creates/accepts the vpc peering connection through the Google provider. + +```hcl +resource "rediscloud_active_active_subscription" "subscription-resource" { + // ... +} + +data "google_compute_network" "network" { + project = "my-gcp-project" + name = "my-gcp-vpc" +} + +resource "rediscloud_active_active_subscription_peering" "peering-resource" { + subscription_id = rediscloud_active_active_subscription.subscription-resource.id + provider_name = "GCP" + gcp_project_id = data.google_compute_network.network.project + gcp_network_name = data.google_compute_network.network.name +} + +resource "google_compute_network_peering" "gcp-peering-resource" { + name = "peering-gcp-example" + network = data.google_compute_network.network.self_link + peer_network = "https://www.googleapis.com/compute/v1/projects/${rediscloud_active_active_subscription_peering.peering-resource.gcp_redis_project_id}/global/networks/${rediscloud_active_active_subscription_peering.example.gcp_redis_network_name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `provider_name` - (Optional) The cloud provider to use with the vpc peering, (either `AWS` or `GCP`). Default: ‘AWS’ +* `subscription_id` - (Required) A valid Active-Active subscription predefined in the current account + +**AWS ONLY:** +* `aws_account_id` - (Required) AWS account ID that the VPC to be peered lives in +* `source_region` - (Required) Name of the region to create the VPC peering from +* `destination_region` - (Required) Name of the region to create the VPC peering to +* `vpc_id` - (Required) Identifier of the VPC to be peered +* `vpc_cidr` - (Required) CIDR range of the VPC to be peered + +**GCP ONLY:** +* `gcp_project_id` - (Required) GCP project ID that the VPC to be peered lives in +* `gcp_network_name` - (Required) The name of the network to be peered + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 10 mins) Used when creating the peering connection +* `delete` - (Defaults to 10 mins) Used when deleting the peering connection + +## Attribute reference + +* `status` is set to the current status of the peering - `initiating-request`, `pending-acceptance`, `active`, `inactive` or `failed`. + +**AWS ONLY:** + +* `aws_peering_id` Identifier of the AWS cloud peering + +**GCP ONLY:** + +* `gcp_redis_project_id` Identifier of the Redis Enterprise Cloud GCP project to be peered +* `gcp_redis_network_name` The name of the Redis Enterprise Cloud network to be peered +* `gcp_peering_id` Identifier of the cloud peering + +## Import + +`rediscloud_active_active_subscription_peering` can be imported using the ID of the Active-Active subscription and the ID of the peering connection, e.g. + +``` +$ terraform import rediscloud_active_active_subscription_peering.peering-resource 12345678/1234 +``` diff --git a/docs/resources/rediscloud_subscription_peering.md b/docs/resources/rediscloud_subscription_peering.md index b6cecdd1..59b675c3 100644 --- a/docs/resources/rediscloud_subscription_peering.md +++ b/docs/resources/rediscloud_subscription_peering.md @@ -45,13 +45,6 @@ resource "rediscloud_subscription" "example" { // ... } -resource "rediscloud_subscription_peering" "example" { - subscription_id = rediscloud_subscription.example.id - provider_name = "GCP" - gcp_project_id = "cloud-api-123456" - gcp_network_name = "cloud-api-vpc-peering-example" -} - data "google_compute_network" "network" { project = "my-gcp-project" name = "my-gcp-vpc" diff --git a/go.mod b/go.mod index 9b67e81e..3edfa79a 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/RedisLabs/terraform-provider-rediscloud go 1.17 require ( - github.com/RedisLabs/rediscloud-go-api v0.1.9 + github.com/RedisLabs/rediscloud-go-api v0.2.0 github.com/bflad/tfproviderlint v0.28.1 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0 - github.com/stretchr/testify v1.8.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1 + github.com/stretchr/testify v1.8.1 ) require ( - github.com/agext/levenshtein v1.2.2 // indirect + github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/avast/retry-go v3.0.0+incompatible // indirect @@ -19,48 +19,47 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.1 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.4 // indirect + github.com/hashicorp/go-plugin v1.4.8 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.13.0 // indirect + github.com/hashicorp/hcl/v2 v2.15.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.17.2 // indirect + github.com/hashicorp/terraform-exec v0.17.3 // indirect github.com/hashicorp/terraform-json v0.14.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.12.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.14.2 // indirect github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect - github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect + github.com/hashicorp/terraform-registry-address v0.1.0 // indirect github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.1 // indirect - github.com/zclconf/go-cty v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect - golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.8 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d // indirect - google.golang.org/grpc v1.48.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + github.com/vmihailenco/tagparser v0.1.2 // indirect + github.com/zclconf/go-cty v1.12.1 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + golang.org/x/tools v0.4.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f51f2a8b..ee6f8f1a 100644 --- a/go.sum +++ b/go.sum @@ -14,38 +14,386 @@ cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZ 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= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/RedisLabs/rediscloud-go-api v0.1.9 h1:JNwtsULJvCEYvXDxyThP1xKV2P8YQBzsxvEBc9Uc0x0= -github.com/RedisLabs/rediscloud-go-api v0.1.9/go.mod h1:8xgaxWT/l5KWwC9eqj4lMpMW7UhaQ+PXnaI/g1Z+76c= +github.com/RedisLabs/rediscloud-go-api v0.2.0 h1:tkS2AIMLLlI3yeW22dT5DP2BvaYO+K7xvytJvFo8z04= +github.com/RedisLabs/rediscloud-go-api v0.2.0/go.mod h1:EJXWMnC2ZSM5m7k2TCnmxenYv57o6yGlZXo0ZVnMgIs= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= 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 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= 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/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= @@ -77,6 +425,8 @@ github.com/bflad/tfproviderlint v0.28.1/go.mod h1:7Z9Pyl1Z1UWJcPBuyjN89D2NaJGpjR github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -84,8 +434,11 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 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= @@ -98,7 +451,11 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3 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.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -134,6 +491,8 @@ 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/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -150,9 +509,11 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -163,12 +524,18 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.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/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -176,14 +543,35 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -199,15 +587,17 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= -github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= -github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= +github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= 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= @@ -228,39 +618,42 @@ github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8= github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= -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.15.0 h1:CPDXO6+uORPjKflkWCCwoWc9uRp+zSIPcCQ+BrxV7m8= +github.com/hashicorp/hcl/v2 v2.15.0/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A= github.com/hashicorp/terraform-exec v0.10.0/go.mod h1:tOT8j1J8rP05bZBGWXfMyU3HkLi1LWyqL3Bzsc3CJjo= github.com/hashicorp/terraform-exec v0.13.0/go.mod h1:SGhto91bVRlgXQWcJ5znSz+29UZIa8kpBbkGwQ+g9E8= -github.com/hashicorp/terraform-exec v0.17.2 h1:EU7i3Fh7vDUI9nNRdMATCEfnm9axzTnad8zszYZ73Go= -github.com/hashicorp/terraform-exec v0.17.2/go.mod h1:tuIbsL2l4MlwwIZx9HPM+LOV9vVyEfBYu2GsO1uH3/8= +github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= +github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= github.com/hashicorp/terraform-json v0.5.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU= github.com/hashicorp/terraform-json v0.8.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= github.com/hashicorp/terraform-plugin-go v0.2.1/go.mod h1:10V6F3taeDWVAoLlkmArKttR3IULlRWFAGtQIQTIDr4= -github.com/hashicorp/terraform-plugin-go v0.12.0 h1:6wW9mT1dSs0Xq4LR6HXj1heQ5ovr5GxXNJwkErZzpJw= -github.com/hashicorp/terraform-plugin-go v0.12.0/go.mod h1:kwhmaWHNDvT1B3QiSJdAtrB/D4RaKSY/v3r2BuoWK4M= -github.com/hashicorp/terraform-plugin-log v0.6.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= +github.com/hashicorp/terraform-plugin-go v0.14.1/go.mod h1:Bc/K6K26BQ2FHqIELPbpKtt2CzzbQou+0UQF3/0NsCQ= +github.com/hashicorp/terraform-plugin-go v0.14.2 h1:rhsVEOGCnY04msNymSvbUsXfRLKh9znXZmHlf5e8mhE= +github.com/hashicorp/terraform-plugin-go v0.14.2/go.mod h1:Q12UjumPNGiFsZffxOsA40Tlz1WVXt2Evh865Zj0+UA= github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= github.com/hashicorp/terraform-plugin-sdk v1.16.1 h1:G2iK7MBT4LuNcVASPXWS1ciBUuIm8oIY0zRfCmi3xy4= github.com/hashicorp/terraform-plugin-sdk v1.16.1/go.mod h1:KSsGcuZ1JRqnmYzz+sWIiUwNvJkzXbGRIdefwFfOdyY= github.com/hashicorp/terraform-plugin-sdk/v2 v2.5.0/go.mod h1:z+cMZ0iswzZOahBJ3XmNWgWkVnAd2bl8g+FhyyuPDH4= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0 h1:+KxZULPsbjpAVoP0WNj/8aVW6EqpcX5JcUcQ5wl7Da4= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.20.0/go.mod h1:DwGJG3KNxIPluVk6hexvDfYR/MS/eKGpiztJoT3Bbbw= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1 h1:zHcMbxY0+rFO9gY99elV/XC/UnQVg7FhRCbj1i5b7vM= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1/go.mod h1:+tNlb0wkfdsDJ7JEiERLz4HzM19HyiuIoGzTsM7rPpw= github.com/hashicorp/terraform-plugin-test/v2 v2.1.3/go.mod h1:pmaUHiUtDL/8Mz3FuyZ/vRDb0LpaOWQjVRW9ORF7FHs= -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-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= +github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= 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= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -298,16 +691,18 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.1/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= @@ -323,8 +718,9 @@ github.com/mitchellh/go-testing-interface v1.0.4/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -333,10 +729,10 @@ github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -353,13 +749,15 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX 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/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -368,8 +766,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -377,8 +776,9 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU 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/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= @@ -386,15 +786,17 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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.7.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o= -github.com/zclconf/go-cty v1.8.0/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 v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -402,6 +804,9 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -414,8 +819,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh 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-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -438,6 +845,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/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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= @@ -446,8 +855,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +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/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -478,19 +891,56 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -500,7 +950,13 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/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= @@ -537,31 +993,68 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.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/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= @@ -606,13 +1099,27 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.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.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -630,13 +1137,49 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -669,8 +1212,85 @@ google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= 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= @@ -687,9 +1307,30 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +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.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -704,8 +1345,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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= diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 07edf9e6..baae990f 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -51,10 +51,14 @@ func New(version string) func() *schema.Provider { "rediscloud_subscription_peerings": dataSourceRedisCloudSubscriptionPeerings(), }, ResourcesMap: map[string]*schema.Resource{ - "rediscloud_cloud_account": resourceRedisCloudCloudAccount(), - "rediscloud_subscription": resourceRedisCloudSubscription(), - "rediscloud_subscription_database": resourceRedisCloudSubscriptionDatabase(), - "rediscloud_subscription_peering": resourceRedisCloudSubscriptionPeering(), + "rediscloud_cloud_account": resourceRedisCloudCloudAccount(), + "rediscloud_subscription": resourceRedisCloudSubscription(), + "rediscloud_subscription_database": resourceRedisCloudSubscriptionDatabase(), + "rediscloud_subscription_peering": resourceRedisCloudSubscriptionPeering(), + "rediscloud_active_active_subscription_database": resourceRedisCloudActiveActiveSubscriptionDatabase(), + "rediscloud_active_active_subscription": resourceRedisCloudActiveActiveSubscription(), + "rediscloud_active_active_subscription_regions": resourceRedisCloudActiveActiveRegion(), + "rediscloud_active_active_subscription_peering": resourceRedisCloudActiveActiveSubscriptionPeering(), }, } diff --git a/internal/provider/resource_rediscloud_active_active_region.go b/internal/provider/resource_rediscloud_active_active_region.go new file mode 100644 index 00000000..b4533f42 --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_region.go @@ -0,0 +1,468 @@ +package provider + +import ( + "context" + "reflect" + "regexp" + "strconv" + "time" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/RedisLabs/rediscloud-go-api/service/regions" + "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRedisCloudActiveActiveRegion() *schema.Resource { + return &schema.Resource{ + Description: "Creates an Active Active Region and within your Redis Enterprise Cloud Account.", + CreateContext: resourceRedisCloudActiveActiveRegionCreate, + ReadContext: resourceRedisCloudActiveActiveRegionRead, + UpdateContext: resourceRedisCloudActiveActiveRegionUpdate, + DeleteContext: resourceRedisCloudActiveActiveRegionDelete, + Importer: &schema.ResourceImporter{ + // Let the READ operation do the heavy lifting for importing values from the API. + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "subscription_id": { + Description: "ID of the subscription that the regions belong to", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.StringMatch(regexp.MustCompile("^\\d+$"), "must be a number")), + }, + "delete_regions": { + Description: "Delete regions flag has to be set for re-creating and deleting regions", + Type: schema.TypeBool, + Optional: true, + }, + "region": { + Description: "Cloud networking details, per region (multiple regions for Active-Active cluster)", + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "region_id": { + Description: "The region id", + Type: schema.TypeInt, + Computed: true, + }, + "region": { + Description: "Deployment region as defined by cloud provider", + Type: schema.TypeString, + Required: true, + }, + "vpc_id": { + Description: "Identifier of the VPC to be peered", + Type: schema.TypeString, + Computed: true, + }, + "recreate_region": { + Description: "Defines whether the regions should be re-created", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "networking_deployment_cidr": { + Description: "Deployment CIDR mask", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.IsCIDR), + }, + + "database": { + Description: "The database resource", + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "database_id": { + Description: "A numeric id for the database", + Type: schema.TypeInt, + Required: true, + }, + "database_name": { + Description: "A meaningful name to identify the database", + Type: schema.TypeString, + Required: true, + }, + "local_write_operations_per_second": { + Description: "Write operations per second for creation plan databases", + Type: schema.TypeInt, + Required: true, + }, + "local_read_operations_per_second": { + Description: "Write operations per second for creation plan databases", + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceRedisCloudActiveActiveRegionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + subId, err := strconv.Atoi(d.Get("subscription_id").(string)) + if err != nil { + return diag.FromErr(err) + } + d.SetId(strconv.Itoa(subId)) + + return resourceRedisCloudActiveActiveRegionUpdate(ctx, d, meta) +} + +func resourceRedisCloudActiveActiveRegionUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + subId, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + deleteRegionsFlag := d.Get("delete_regions").(bool) + + // Get existing regions, so we can do a manual diff + // Query API for existing Regions for a given Subscription + existingRegions, err := api.client.Regions.List(ctx, subId) + if err != nil { + return diag.FromErr(err) + } + + // Create an existingRegionMap + existingRegionMap := make(map[string]*regions.Region) + for _, existingRegion := range existingRegions.Regions { + existingRegionMap[*existingRegion.Region] = existingRegion + } + + desiredRegions := buildRegionsFromResourceData(d.Get("region").(*schema.Set)) + + // Determine which regions currently exist but aren't in the config + // These will need to be deleted + regionsToDelete := make([]*regions.Region, 0) + for _, r := range existingRegions.Regions { + if _, ok := desiredRegions[*r.Region]; !ok { + regionsToDelete = append(regionsToDelete, r) + } + } + + // Of the regions that are in the config, determine which are brand new and should be created, which already exist + // but have changed and require recreating (if update not supported), and which have changed and require updates + // (updating a region's DBs is supported) + regionsToCreate := make([]*regions.Region, 0) + regionsToRecreate := make([]*regions.Region, 0) + regionsToUpdateDatabases := make([]*regions.Region, 0) + for _, r := range desiredRegions { + existingRegion, ok := existingRegionMap[*r.Region] + if !ok { + regionsToCreate = append(regionsToCreate, r) + } else { + if shouldRecreateRegion(r, existingRegion) { + if !*r.RecreateRegion || !deleteRegionsFlag { + return diag.Errorf("Region %s needs to be recreated but recreate_region flag was not set!", *r.Region) + } + regionsToRecreate = append(regionsToRecreate, r) + } else if shouldUpdateRegionDatabases(r, existingRegion) { + regionsToUpdateDatabases = append(regionsToUpdateDatabases, r) + } + } + } + + if len(regionsToCreate) > 0 { + err := regionsCreate(ctx, subId, regionsToCreate, api) + if err != nil { + return diag.FromErr(err) + } + } + + if len(regionsToRecreate) > 0 { + err := regionsDelete(ctx, subId, regionsToRecreate, api) + if err != nil { + return diag.FromErr(err) + } + err = regionsCreate(ctx, subId, regionsToRecreate, api) + if err != nil { + return diag.FromErr(err) + } + } + + if len(regionsToUpdateDatabases) > 0 { + err = regionsUpdateDatabases(ctx, subId, api, regionsToUpdateDatabases, existingRegionMap) + if err != nil { + return diag.FromErr(err) + } + } + + if len(regionsToDelete) > 0 { + if !deleteRegionsFlag { + return diag.Errorf("Region has been removed, but delete_regions flag was not set!") + } + + err := regionsDelete(ctx, subId, regionsToDelete, api) + if err != nil { + return diag.FromErr(err) + } + } + + return resourceRedisCloudActiveActiveRegionRead(ctx, d, meta) +} + +func resourceRedisCloudActiveActiveRegionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + subId, err := strconv.Atoi(d.Id()) + existingRegions, err := api.client.Regions.List(ctx, subId) + if err != nil { + if _, ok := err.(*subscriptions.NotFound); ok { + d.SetId("") + return nil + } + return diag.FromErr(err) + } + + if err := d.Set("subscription_id", strconv.Itoa(*existingRegions.SubscriptionId)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("region", buildResourceDataFromAPIRegions(existingRegions.Regions, d.Get("region").(*schema.Set))); err != nil { + return diag.FromErr(err) + } + + return nil +} + +// Does nothing +func resourceRedisCloudActiveActiveRegionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return resourceRedisCloudActiveActiveRegionRead(ctx, d, meta) +} + +func regionsCreate(ctx context.Context, subId int, regionsToCreate []*regions.Region, api *apiClient) error { + // If no new regions were defined return + if len(regionsToCreate) == 0 { + return nil + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + // Call GO API createRegion for all non-existing regions + for _, currentRegion := range regionsToCreate { + createDatabases := make([]*regions.CreateDatabase, 0) + for _, database := range currentRegion.Databases { + localThroughputMeasurement := regions.CreateLocalThroughput{ + Region: currentRegion.Region, + ReadOperationsPerSecond: database.ReadOperationsPerSecond, + WriteOperationsPerSecond: database.WriteOperationsPerSecond, + } + createDatabase := regions.CreateDatabase{ + Name: database.DatabaseName, + LocalThroughputMeasurement: &localThroughputMeasurement, + } + createDatabases = append(createDatabases, &createDatabase) + } + + createRegion := regions.CreateRegion{ + Region: currentRegion.Region, + DeploymentCIDR: currentRegion.DeploymentCIDR, + Databases: createDatabases, + } + + _, err := api.client.Regions.Create(ctx, subId, createRegion) + + if err != nil { + return err + } + + // Wait for the subscription to be active before deleting it. + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + + // There is a timing issue where the subscription is marked as active before the creation-plan databases are deleted. + // This additional wait ensures that the databases are deleted before the subscription is deleted. + time.Sleep(10 * time.Second) //lintignore:R018 + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + } + + return nil +} + +func regionsUpdateDatabases(ctx context.Context, subId int, api *apiClient, regionsToUpdateDatabases []*regions.Region, existingRegionMap map[string]*regions.Region) error { + databaseUpdates := make(map[int][]*databases.LocalRegionProperties) + for _, desiredRegion := range regionsToUpdateDatabases { + // Collect existing databases to a map + existingDBMap := make(map[int]*regions.Database) + for _, db := range existingRegionMap[*desiredRegion.Region].Databases { + existingDBMap[*db.DatabaseId] = db + } + + for _, db := range desiredRegion.Databases { + if !reflect.DeepEqual(db, existingDBMap[*db.DatabaseId]) { + localThroughput := databases.LocalThroughput{ + Region: desiredRegion.Region, + WriteOperationsPerSecond: db.WriteOperationsPerSecond, + ReadOperationsPerSecond: db.ReadOperationsPerSecond, + } + localRegionProperty := databases.LocalRegionProperties{ + Region: desiredRegion.Region, + LocalThroughputMeasurement: &localThroughput, + } + databaseUpdates[*db.DatabaseId] = append(databaseUpdates[*db.DatabaseId], &localRegionProperty) + } + } + } + + if len(databaseUpdates) > 0 { + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + for dbId, localRegionProperties := range databaseUpdates { + dbUpdate := databases.UpdateActiveActiveDatabase{ + Regions: localRegionProperties, + } + err := api.client.Database.ActiveActiveUpdate(ctx, subId, dbId, dbUpdate) + if err != nil { + return err + } + + // Wait for the subscription to be active before deleting it. + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + + // There is a timing issue where the subscription is marked as active before the creation-plan databases are deleted. + // This additional wait ensures that the databases are deleted before the subscription is deleted. + time.Sleep(10 * time.Second) //lintignore:R018 + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + } + } + + return nil +} + +func regionsDelete(ctx context.Context, subId int, regionsToDelete []*regions.Region, api *apiClient) error { + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + deleteRegions := regions.DeleteRegions{} + for _, region := range regionsToDelete { + deleteRegion := regions.DeleteRegion{ + Region: region.Region, + } + deleteRegions.Regions = append(deleteRegions.Regions, &deleteRegion) + } + + err := api.client.Regions.DeleteWithQuery(ctx, subId, deleteRegions) + if err != nil { + return err + } + + // Wait for the subscription to be active before deleting it. + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + + // There is a timing issue where the subscription is marked as active before the creation-plan databases are deleted. + // This additional wait ensures that the databases are deleted before the subscription is deleted. + time.Sleep(10 * time.Second) //lintignore:R018 + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return err + } + + return nil +} + +func buildResourceDataFromAPIRegions(regionsFromAPI []*regions.Region, regionsFromConfig *schema.Set) []map[string]interface{} { + var result []map[string]interface{} + + recreateRegions := make(map[string]bool) + for _, element := range regionsFromConfig.List() { + r := element.(map[string]interface{}) + recreateRegions[r["region"].(string)] = r["recreate_region"].(bool) + } + + for _, region := range regionsFromAPI { + var dbs []interface{} + for _, database := range region.Databases { + databaseMapString := map[string]interface{}{ + "database_id": database.DatabaseId, + "database_name": database.DatabaseName, + "local_read_operations_per_second": database.ReadOperationsPerSecond, + "local_write_operations_per_second": database.WriteOperationsPerSecond, + } + dbs = append(dbs, databaseMapString) + } + + regionMapString := map[string]interface{}{ + "region_id": region.RegionId, + "region": region.Region, + "recreate_region": recreateRegions[*region.Region], + "networking_deployment_cidr": region.DeploymentCIDR, + "vpc_id": region.VpcId, + "database": dbs, + } + result = append(result, regionMapString) + } + + return result +} + +func buildRegionsFromResourceData(rd *schema.Set) map[string]*regions.Region { + result := make(map[string]*regions.Region) + for _, r := range rd.List() { + regionMap := r.(map[string]interface{}) + + dbs := make([]*regions.Database, 0) + for _, database := range regionMap["database"].(*schema.Set).List() { + databaseMap := database.(map[string]interface{}) + db := regions.Database{ + DatabaseId: redis.Int(databaseMap["database_id"].(int)), + DatabaseName: redis.String(databaseMap["database_name"].(string)), + ReadOperationsPerSecond: redis.Int(databaseMap["local_read_operations_per_second"].(int)), + WriteOperationsPerSecond: redis.Int(databaseMap["local_write_operations_per_second"].(int)), + } + dbs = append(dbs, &db) + } + + region := regions.Region{ + Region: redis.String(regionMap["region"].(string)), + RecreateRegion: redis.Bool(regionMap["recreate_region"].(bool)), + DeploymentCIDR: redis.String(regionMap["networking_deployment_cidr"].(string)), + VpcId: redis.String(regionMap["vpc_id"].(string)), + Databases: dbs, + } + + result[*region.Region] = ®ion + } + + return result +} + +func shouldRecreateRegion(existingRegion *regions.Region, desiredRegion *regions.Region) bool { + return *existingRegion.DeploymentCIDR != *desiredRegion.DeploymentCIDR +} + +func shouldUpdateRegionDatabases(existingRegion *regions.Region, desiredRegion *regions.Region) bool { + return !shouldRecreateRegion(existingRegion, + desiredRegion) && !reflect.DeepEqual(existingRegion.Databases, desiredRegion.Databases) +} diff --git a/internal/provider/resource_rediscloud_active_active_region_test.go b/internal/provider/resource_rediscloud_active_active_region_test.go new file mode 100644 index 00000000..596db59a --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_region_test.go @@ -0,0 +1,354 @@ +package provider + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccResourceRedisCloudActiveActiveRegion_CRUDI(t *testing.T) { + subName := acctest.RandomWithPrefix(testResourcePrefix) + "-regions-test" + dbName := acctest.RandomWithPrefix(testResourcePrefix) + "-regions" + "-db" + dbPass := acctest.RandString(20) + resourceName := "rediscloud_active_active_subscription_regions.example" + + var subId int + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccResourceRedisCloudCreateActiveActiveRegion, subName, dbName, dbPass), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "region.#", "3"), + resource.TestCheckResourceAttr(resourceName, "region.2.region", "eu-west-2"), + resource.TestCheckResourceAttr(resourceName, "region.2.networking_deployment_cidr", "10.2.0.0/24"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.#", "1"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.database_name", dbName), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_write_operations_per_second", "1500"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_read_operations_per_second", "1500"), + + func(s *terraform.State) error { + r := s.RootModule().Resources[resourceName] + + var err error + subId, err = strconv.Atoi(r.Primary.ID) + if err != nil { + return err + } + + client := testProvider.Meta().(*apiClient) + sub, err := client.client.Subscription.Get(context.TODO(), subId) + if err != nil { + return err + } + + if redis.StringValue(sub.Name) != subName { + return fmt.Errorf("unexpected name value: %s", redis.StringValue(sub.Name)) + } + return nil + }, + ), + }, + { + // Checks region re-created correctly + Config: fmt.Sprintf(testAccResourceRedisCloudReCreateActiveActiveRegion, subName, dbName, dbPass), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "region.#", "3"), + resource.TestCheckResourceAttr(resourceName, "region.2.region", "eu-west-2"), + resource.TestCheckResourceAttr(resourceName, "region.2.networking_deployment_cidr", "10.3.0.0/24"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.#", "1"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.database_name", dbName), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_write_operations_per_second", "1500"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_read_operations_per_second", "1500"), + ), + }, + { + // Checks region DB updated correctly + Config: fmt.Sprintf(testAccResourceRedisCloudUpdateDBActiveActiveRegion, subName, dbName, dbPass), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "region.#", "3"), + resource.TestCheckResourceAttr(resourceName, "region.2.region", "eu-west-2"), + resource.TestCheckResourceAttr(resourceName, "region.2.networking_deployment_cidr", "10.3.0.0/24"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.#", "1"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.database_name", dbName), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "region.2.database.0.local_read_operations_per_second", "1000"), + ), + }, + { + // Checks region deleted correctly + Config: fmt.Sprintf(testAccResourceRedisCloudDeleteActiveActiveRegion, subName, dbName, dbPass), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "region.0.region", "us-east-1"), + resource.TestCheckResourceAttr(resourceName, "region.1.region", "us-east-2"), + ), + }, + { + // Checks region re-created correctly + Config: fmt.Sprintf(testAccResourceRedisCloudRemoveAndCreateSameTimeActiveActiveRegion, subName, dbName, dbPass), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "region.0.region", "us-east-1"), + resource.TestCheckResourceAttr(resourceName, "region.0.networking_deployment_cidr", "10.0.0.0/24"), + resource.TestCheckResourceAttr(resourceName, "region.1.region", "eu-west-1"), + resource.TestCheckResourceAttr(resourceName, "region.1.networking_deployment_cidr", "10.2.0.0/24"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"delete_regions"}, + }, + }, + }) +} + +const testAARegionsBoilerplate = ` +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.1.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} + +resource "rediscloud_active_active_subscription_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = "%s" + memory_limit_in_gb = 3 + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + + // OPTIONAL + global_data_persistence = "none" + global_password = "%s" + global_alert { + name = "dataset-size" + value = 40 + } +} + +` + +// TF config for provisioning a new region. +const testAccResourceRedisCloudCreateActiveActiveRegion = testAARegionsBoilerplate + ` + +resource "rediscloud_active_active_subscription_regions" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + delete_regions = false + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.1.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "eu-west-2" + networking_deployment_cidr = "10.2.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1500 + local_read_operations_per_second = 1500 + } + } + } + +` + +// TF config for re-creating a region +const testAccResourceRedisCloudReCreateActiveActiveRegion = testAARegionsBoilerplate + ` + +resource "rediscloud_active_active_subscription_regions" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + delete_regions = true + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.1.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "eu-west-2" + networking_deployment_cidr = "10.3.0.0/24" + recreate_region = true + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1500 + local_read_operations_per_second = 1500 + } + } + } + +` + +// TF config for updating DB of a region +const testAccResourceRedisCloudUpdateDBActiveActiveRegion = testAARegionsBoilerplate + ` + +resource "rediscloud_active_active_subscription_regions" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + delete_regions = false + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.1.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "eu-west-2" + networking_deployment_cidr = "10.3.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + } + +` + +// TF config for deleting a region +const testAccResourceRedisCloudDeleteActiveActiveRegion = testAARegionsBoilerplate + ` + +resource "rediscloud_active_active_subscription_regions" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + delete_regions = true + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.1.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + } + +` + +// TF config for deleting a region +const testAccResourceRedisCloudRemoveAndCreateSameTimeActiveActiveRegion = testAARegionsBoilerplate + ` + +resource "rediscloud_active_active_subscription_regions" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + delete_regions = true + region { + region = "us-east-1" + networking_deployment_cidr = "10.0.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + region { + region = "eu-west-1" + networking_deployment_cidr = "10.2.0.0/24" + recreate_region = false + database { + database_id = rediscloud_active_active_subscription_database.example.db_id + database_name = rediscloud_active_active_subscription_database.example.name + local_write_operations_per_second = 1000 + local_read_operations_per_second = 1000 + } + } + } + +` diff --git a/internal/provider/resource_rediscloud_active_active_subscription.go b/internal/provider/resource_rediscloud_active_active_subscription.go new file mode 100644 index 00000000..458efa33 --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription.go @@ -0,0 +1,443 @@ +package provider + +import ( + "bytes" + "context" + "fmt" + "regexp" + "strconv" + "time" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRedisCloudActiveActiveSubscription() *schema.Resource { + return &schema.Resource{ + Description: "Creates a Subscription and database resources within your Redis Enterprise Cloud Account.", + CreateContext: resourceRedisCloudActiveActiveSubscriptionCreate, + ReadContext: resourceRedisCloudActiveActiveSubscriptionRead, + UpdateContext: resourceRedisCloudActiveActiveSubscriptionUpdate, + DeleteContext: resourceRedisCloudActiveActiveSubscriptionDelete, + CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error { + _, cPlanExists := diff.GetOk("creation_plan") + if cPlanExists { + return nil + } + + // The resource hasn't been created yet, but the creation plan is missing. + if diff.Id() == "" { + return fmt.Errorf(`the "creation_plan" block is required`) + } + return nil + }, + + Importer: &schema.ResourceImporter{ + // Let the READ operation do the heavy lifting for importing values from the API. + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Description: "A meaningful name to identify the subscription", + Type: schema.TypeString, + Optional: true, + }, + "payment_method": { + Description: "Payment method for the requested subscription. If credit card is specified, the payment method Id must be defined.", + Type: schema.TypeString, + ForceNew: true, + ValidateDiagFunc: validateDiagFunc(validation.StringMatch(regexp.MustCompile("^(credit-card|marketplace)$"), "must be 'credit-card' or 'marketplace'")), + Optional: true, + Default: "credit-card", + }, + "payment_method_id": { + Computed: true, + Description: "A valid payment method pre-defined in the current account", + Type: schema.TypeString, + ValidateDiagFunc: validateDiagFunc(validation.StringMatch(regexp.MustCompile("^\\d+$"), "must be a number")), + Optional: true, + }, + "cloud_provider": { + Description: "A cloud provider string either GCP or AWS", + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Default: "AWS", + ValidateDiagFunc: validateDiagFunc(validation.StringMatch(regexp.MustCompile("^(GCP|AWS)$"), "must be 'GCP' or 'AWS'")), + }, + "creation_plan": { + Description: "Information about the planned databases used to optimise the database infrastructure. This information is only used when creating a new subscription and any changes will be ignored after this.", + Type: schema.TypeList, + MaxItems: 1, + // The block is required when the user provisions a new subscription. + // The block is ignored in the UPDATE operation or after IMPORTing the resource. + // Custom validation is handled in CustomizeDiff. + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if d.Id() == "" { + // We don't want to ignore the block if the resource is about to be created. + return false + } + return true + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "memory_limit_in_gb": { + Description: "Maximum memory usage for each database", + Type: schema.TypeFloat, + Required: true, + }, + "quantity": { + Description: "The planned number of databases", + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "region": { + Description: "Cloud networking details, per region (multiple regions for Active-Active cluster)", + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["region"].(string))) + return schema.HashString(buf.String()) + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "region": { + Description: "Deployment region as defined by cloud provider", + Type: schema.TypeString, + Required: true, + }, + "networking_deployment_cidr": { + Description: "Deployment CIDR mask", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.IsCIDR), + }, + "write_operations_per_second": { + Description: "Write operations per second for creation plan databases", + Type: schema.TypeInt, + Required: true, + }, + "read_operations_per_second": { + Description: "Write operations per second for creation plan databases", + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceRedisCloudActiveActiveSubscriptionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + plan := d.Get("creation_plan").([]interface{}) + + // Create creation-plan databases + planMap := plan[0].(map[string]interface{}) + + // Create CloudProviders + providers, err := buildCreateActiveActiveCloudProviders(d.Get("cloud_provider").(string), planMap) + if err != nil { + return diag.FromErr(err) + } + + // Create Subscription + name := d.Get("name").(string) + + paymentMethod := d.Get("payment_method").(string) + paymentMethodID, err := readPaymentMethodID(d) + if err != nil { + return diag.FromErr(err) + } + + // Create databases + var dbs []*subscriptions.CreateDatabase + + dbs = buildSubscriptionCreatePlanAADatabases(planMap) + + createSubscriptionRequest := subscriptions.CreateSubscription{ + DeploymentType: redis.String("active-active"), + Name: redis.String(name), + DryRun: redis.Bool(false), + PaymentMethodID: paymentMethodID, + PaymentMethod: redis.String(paymentMethod), + CloudProviders: providers, + Databases: dbs, + } + + subId, err := api.client.Subscription.Create(ctx, createSubscriptionRequest) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(strconv.Itoa(subId)) + + // Confirm Subscription Active status + err = waitForSubscriptionToBeActive(ctx, subId, api) + if err != nil { + return diag.FromErr(err) + } + + // There is a timing issue where the subscription is marked as active before the creation-plan databases are listed . + // This additional wait ensures that the databases will be listed before calling api.client.Database.List() + time.Sleep(10 * time.Second) //lintignore:R018 + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + + // Locate Databases to confirm Active status + dbList := api.client.Database.List(ctx, subId) + + for dbList.Next() { + dbId := *dbList.Value().ID + + if err := waitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil { + return diag.FromErr(err) + } + // Delete each creation-plan database + dbErr := api.client.Database.Delete(ctx, subId, dbId) + if dbErr != nil { + diag.FromErr(dbErr) + } + } + if dbList.Err() != nil { + return diag.FromErr(dbList.Err()) + } + + // Check that the subscription is in an active state before calling the read function + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + + return resourceRedisCloudActiveActiveSubscriptionRead(ctx, d, meta) +} + +func resourceRedisCloudActiveActiveSubscriptionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + var diags diag.Diagnostics + + subId, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subscription, err := api.client.Subscription.Get(ctx, subId) + if err != nil { + if _, ok := err.(*subscriptions.NotFound); ok { + d.SetId("") + return diags + } + return diag.FromErr(err) + } + + if err := d.Set("name", redis.StringValue(subscription.Name)); err != nil { + return diag.FromErr(err) + } + + if subscription.PaymentMethodID != nil && redis.IntValue(subscription.PaymentMethodID) != 0 { + paymentMethodID := strconv.Itoa(redis.IntValue(subscription.PaymentMethodID)) + if err := d.Set("payment_method_id", paymentMethodID); err != nil { + return diag.FromErr(err) + } + } + if err := d.Set("payment_method", redis.StringValue(subscription.PaymentMethod)); err != nil { + return diag.FromErr(err) + } + + cloudDetails := subscription.CloudDetails + if len(cloudDetails) == 0 { + return diag.FromErr(fmt.Errorf("Cloud details is empty. Subscription status: %s", redis.StringValue(subscription.Status))) + } + cloudProvider := cloudDetails[0].Provider + if err := d.Set("cloud_provider", cloudProvider); err != nil { + return diag.FromErr(err) + } + + return diags +} + +func resourceRedisCloudActiveActiveSubscriptionUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + subId, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + if d.HasChanges("name", "payment_method_id") { + updateSubscriptionRequest := subscriptions.UpdateSubscription{} + + if d.HasChange("name") { + name := d.Get("name").(string) + updateSubscriptionRequest.Name = &name + } + + if d.HasChange("payment_method_id") { + paymentMethodID, err := readPaymentMethodID(d) + if err != nil { + return diag.FromErr(err) + } + + updateSubscriptionRequest.PaymentMethodID = paymentMethodID + } + + err = api.client.Subscription.Update(ctx, subId, updateSubscriptionRequest) + if err != nil { + return diag.FromErr(err) + } + } + + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + + return resourceRedisCloudSubscriptionRead(ctx, d, meta) +} + +func resourceRedisCloudActiveActiveSubscriptionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // use the meta value to retrieve your client from the provider configure method + api := meta.(*apiClient) + + var diags diag.Diagnostics + + subId, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + // Wait for the subscription to be active before deleting it. + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + + // There is a timing issue where the subscription is marked as active before the creation-plan databases are deleted. + // This additional wait ensures that the databases are deleted before the subscription is deleted. + time.Sleep(10 * time.Second) //lintignore:R018 + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + // Delete subscription once all databases are deleted + err = api.client.Subscription.Delete(ctx, subId) + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + err = waitForSubscriptionToBeDeleted(ctx, subId, api) + if err != nil { + return diag.FromErr(err) + } + + return diags +} + +func buildCreateActiveActiveCloudProviders(provider string, creationPlan map[string]interface{}) ([]*subscriptions.CreateCloudProvider, error) { + createRegions := make([]*subscriptions.CreateRegion, 0) + if regions := creationPlan["region"].(*schema.Set).List(); len(regions) != 0 { + + for _, region := range regions { + regionMap := region.(map[string]interface{}) + + regionStr := regionMap["region"].(string) + + createRegion := subscriptions.CreateRegion{ + Region: redis.String(regionStr), + } + + if v, ok := regionMap["networking_deployment_cidr"]; ok && v != "" { + createRegion.Networking = &subscriptions.CreateNetworking{ + DeploymentCIDR: redis.String(v.(string)), + } + } + + if v, ok := regionMap["networking_vpc_id"]; ok && v != "" { + if createRegion.Networking == nil { + createRegion.Networking = &subscriptions.CreateNetworking{} + } + createRegion.Networking.VPCId = redis.String(v.(string)) + } + + createRegions = append(createRegions, &createRegion) + } + } + + createCloudProviders := make([]*subscriptions.CreateCloudProvider, 0) + createCloudProvider := &subscriptions.CreateCloudProvider{ + Provider: redis.String(provider), + CloudAccountID: redis.Int(1), // Active-Active subscriptions are created with Redis internal resources + Regions: createRegions, + } + + createCloudProviders = append(createCloudProviders, createCloudProvider) + + return createCloudProviders, nil +} + +func buildSubscriptionCreatePlanAADatabases(planMap map[string]interface{}) []*subscriptions.CreateDatabase { + createDatabases := make([]*subscriptions.CreateDatabase, 0) + + dbName := "creation-plan-db-" + idx := 1 + numDatabases := planMap["quantity"].(int) + memoryLimitInGB := planMap["memory_limit_in_gb"].(float64) + regions := planMap["region"] + var localThroughputs []*subscriptions.CreateLocalThroughput + for _, v := range regions.(*schema.Set).List() { + region := v.(map[string]interface{}) + localThroughputs = append(localThroughputs, &subscriptions.CreateLocalThroughput{ + Region: redis.String(region["region"].(string)), + WriteOperationsPerSecond: redis.Int(region["write_operations_per_second"].(int)), + ReadOperationsPerSecond: redis.Int(region["read_operations_per_second"].(int)), + }) + } + // create the remaining DBs with all other modules + createDatabases = append(createDatabases, createAADatabase(dbName, &idx, localThroughputs, numDatabases, memoryLimitInGB)...) + + return createDatabases +} + +// createDatabase returns a CreateDatabase struct with the given parameters +func createAADatabase(dbName string, idx *int, localThroughputs []*subscriptions.CreateLocalThroughput, numDatabases int, memoryLimitInGB float64) []*subscriptions.CreateDatabase { + var databases []*subscriptions.CreateDatabase + for i := 0; i < numDatabases; i++ { + createDatabase := subscriptions.CreateDatabase{ + Name: redis.String(dbName + strconv.Itoa(*idx)), + Protocol: redis.String("redis"), + MemoryLimitInGB: redis.Float64(memoryLimitInGB), + LocalThroughputMeasurement: localThroughputs, + Quantity: redis.Int(1), + } + *idx++ + databases = append(databases, &createDatabase) + } + return databases +} diff --git a/internal/provider/resource_rediscloud_active_active_subscription_database.go b/internal/provider/resource_rediscloud_active_active_subscription_database.go new file mode 100644 index 00000000..08d311b6 --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription_database.go @@ -0,0 +1,655 @@ +package provider + +import ( + "context" + "log" + "time" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/databases" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRedisCloudActiveActiveSubscriptionDatabase() *schema.Resource { + return &schema.Resource{ + Description: "Creates database resource within an active-active subscription in your Redis Enterprise Cloud Account.", + CreateContext: resourceRedisCloudActiveActiveSubscriptionDatabaseCreate, + ReadContext: resourceRedisCloudActiveActiveSubscriptionDatabaseRead, + UpdateContext: resourceRedisCloudActiveActiveSubscriptionDatabaseUpdate, + DeleteContext: resourceRedisCloudActiveActiveSubscriptionDatabaseDelete, + + Importer: &schema.ResourceImporter{ + StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + subId, dbId, err := toDatabaseId(d.Id()) + if err != nil { + return nil, err + } + if err := d.Set("subscription_id", subId); err != nil { + return nil, err + } + if err := d.Set("db_id", dbId); err != nil { + return nil, err + } + d.SetId(buildResourceId(subId, dbId)) + return []*schema.ResourceData{d}, nil + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "subscription_id": { + Description: "Identifier of the subscription", + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "db_id": { + Description: "Identifier of the database created", + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Description: "A meaningful name to identify the database", + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringLenBetween(0, 40)), + }, + "memory_limit_in_gb": { + Description: "Maximum memory usage for this specific database", + Type: schema.TypeFloat, + Required: true, + }, + "support_oss_cluster_api": { + Description: "Support Redis open-source (OSS) Cluster API", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "external_endpoint_for_oss_cluster_api": { + Description: "Should use the external endpoint for open-source (OSS) Cluster API", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "enable_tls": { + Description: "Use TLS for authentication.", + Type: schema.TypeBool, + Optional: true, + }, + "client_ssl_certificate": { + Description: "SSL certificate to authenticate user connections.", + Type: schema.TypeString, + Optional: true, + }, + "data_eviction": { + Description: "Data eviction items policy", + Type: schema.TypeString, + Optional: true, + Default: "volatile-lru", + }, + "global_data_persistence": { + Description: "Rate of database data persistence (in persistent storage)", + Type: schema.TypeString, + Optional: true, + }, + "global_password": { + Description: "Password used to access the database. If left empty, the password will be generated automatically", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Computed: true, + }, + "global_alert": { + Description: "Set of alerts to enable on the database", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Description: "Alert name", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.StringInSlice(databases.AlertNameValues(), false)), + }, + "value": { + Description: "Alert value", + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "global_source_ips": { + Description: "Set of CIDR addresses to allow access to the database", + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: validateDiagFunc(validation.IsCIDR), + }, + }, + "override_region": { + Description: "Region-specific configuration parameters to override the global configuration", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Description: "Region name", + Type: schema.TypeString, + Required: true, + }, + "override_global_alert": { + Description: "Set of alerts to enable on the database", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Description: "Alert name", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.StringInSlice(databases.AlertNameValues(), false)), + }, + "value": { + Description: "Alert value", + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "override_global_password": { + Description: "Password used to access the database. If left empty, the password will be generated automatically", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "override_global_source_ips": { + Description: "Set of CIDR addresses to allow access to the database", + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: validateDiagFunc(validation.IsCIDR), + }, + }, + "override_global_data_persistence": { + Description: "Rate of database data persistence (in persistent storage)", + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "public_endpoint": { + Description: "Region public and private endpoints to access the database", + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "private_endpoint": { + Description: "Region public and private endpoints to access the database", + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceRedisCloudActiveActiveSubscriptionDatabaseCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + subId := d.Get("subscription_id").(int) + subscriptionMutex.Lock(subId) + + name := d.Get("name").(string) + memoryLimitInGB := d.Get("memory_limit_in_gb").(float64) + supportOSSClusterAPI := d.Get("support_oss_cluster_api").(bool) + useExternalEndpointForOSSClusterAPI := d.Get("external_endpoint_for_oss_cluster_api").(bool) + dataEviction := d.Get("data_eviction").(string) + globalDataPersistence := d.Get("global_data_persistence").(string) + globalPassword := d.Get("global_password").(string) + globalSourceIp := setToStringSlice(d.Get("global_source_ips").(*schema.Set)) + + createAlerts := make([]*databases.CreateAlert, 0) + alerts := d.Get("global_alert").(*schema.Set) + for _, alert := range alerts.List() { + alertMap := alert.(map[string]interface{}) + + alertName := alertMap["name"].(string) + alertValue := alertMap["value"].(int) + + createAlert := &databases.CreateAlert{ + Name: redis.String(alertName), + Value: redis.Int(alertValue), + } + + createAlerts = append(createAlerts, createAlert) + } + + // Get regions from /subscriptions/{subscriptionId}/regions, this will use the Regions API + regions, err := api.client.Regions.List(ctx, subId) + if err != nil { + subscriptionMutex.Unlock(subId) + return diag.FromErr(err) + } + + localThroughputs := make([]*databases.LocalThroughput, 0) + for _, region := range regions.Regions { + createLocalThroughput := &databases.LocalThroughput{ + Region: redis.String(*region.Region), + WriteOperationsPerSecond: redis.Int(1000), + ReadOperationsPerSecond: redis.Int(1000), + } + + localThroughputs = append(localThroughputs, createLocalThroughput) + } + + createDatabase := databases.CreateActiveActiveDatabase{ + DryRun: redis.Bool(false), + Name: redis.String(name), + MemoryLimitInGB: redis.Float64(memoryLimitInGB), + SupportOSSClusterAPI: redis.Bool(supportOSSClusterAPI), + UseExternalEndpointForOSSClusterAPI: redis.Bool(useExternalEndpointForOSSClusterAPI), + GlobalSourceIP: globalSourceIp, + GlobalAlerts: createAlerts, + LocalThroughputMeasurement: localThroughputs, + } + + if dataEviction != "" { + createDatabase.DataEvictionPolicy = redis.String(dataEviction) + } + + if globalDataPersistence != "" { + createDatabase.GlobalDataPersistence = redis.String(globalDataPersistence) + } + + if globalPassword != "" { + createDatabase.GlobalPassword = redis.String(globalPassword) + } + + // Confirm Subscription Active status before creating database + err = waitForSubscriptionToBeActive(ctx, subId, api) + if err != nil { + subscriptionMutex.Unlock(subId) + return diag.FromErr(err) + } + + dbId, err := api.client.Database.ActiveActiveCreate(ctx, subId, createDatabase) + if err != nil { + subscriptionMutex.Unlock(subId) + return diag.FromErr(err) + } + + d.SetId(buildResourceId(subId, dbId)) + + // Confirm Database Active status + err = waitForDatabaseToBeActive(ctx, subId, dbId, api) + if err != nil { + subscriptionMutex.Unlock(subId) + return diag.FromErr(err) + } + + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + subscriptionMutex.Unlock(subId) + return diag.FromErr(err) + } + + // Some attributes on a database are not accessible by the subscription creation API. + // Run the subscription update function to apply any additional changes to the databases, such as password and so on. + subscriptionMutex.Unlock(subId) + return resourceRedisCloudActiveActiveSubscriptionDatabaseUpdate(ctx, d, meta) +} + +func resourceRedisCloudActiveActiveSubscriptionDatabaseRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + var diags diag.Diagnostics + + subId, dbId, err := toDatabaseId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + // We are not import this resource, so we can read the subscription_id defined in this resource. + if subId == 0 { + subId = d.Get("subscription_id").(int) + } + + db, err := api.client.Database.GetActiveActive(ctx, subId, dbId) + if err != nil { + if _, ok := err.(*databases.NotFound); ok { + d.SetId("") + return diags + } + return diag.FromErr(err) + } + + if err := d.Set("db_id", redis.IntValue(db.ID)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("name", redis.StringValue(db.Name)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("data_eviction", redis.StringValue(db.DataEvictionPolicy)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("support_oss_cluster_api", redis.BoolValue(db.SupportOSSClusterAPI)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("external_endpoint_for_oss_cluster_api", redis.BoolValue(db.UseExternalEndpointForOSSClusterAPI)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("memory_limit_in_gb", redis.Float64(*db.CrdbDatabases[0].MemoryLimitInGB)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("enable_tls", redis.BoolValue(db.CrdbDatabases[0].Security.EnableTls)); err != nil { + return diag.FromErr(err) + } + + var regionDbConfigs []map[string]interface{} + publicEndpointConfig := make(map[string]interface{}) + privateEndpointConfig := make(map[string]interface{}) + for _, regionDb := range db.CrdbDatabases { + // Set the endpoints for the region + publicEndpointConfig[redis.StringValue(regionDb.Region)] = redis.StringValue(regionDb.PublicEndpoint) + privateEndpointConfig[redis.StringValue(regionDb.Region)] = redis.StringValue(regionDb.PrivateEndpoint) + // Check if the region is in the state as an override + stateOverrideRegion := getStateOverrideRegion(d, redis.StringValue(regionDb.Region)) + if stateOverrideRegion == nil { + continue + } + regionDbConfig := map[string]interface{}{ + "name": redis.StringValue(regionDb.Region), + } + var sourceIPs []string + if !(len(regionDb.Security.SourceIPs) == 1 && redis.StringValue(regionDb.Security.SourceIPs[0]) == "0.0.0.0/0") { + // The API handles an empty list as ["0.0.0.0/0"] but need to be careful to match the input to avoid Terraform detecting drift + sourceIPs = redis.StringSliceValue(regionDb.Security.SourceIPs...) + } + if stateSourceIPs := getStateOverrideRegion(d, redis.StringValue(regionDb.Region))["override_global_source_ips"]; stateSourceIPs != nil { + if len(stateSourceIPs.(*schema.Set).List()) > 0 { + regionDbConfig["override_global_source_ips"] = sourceIPs + } + } + + if stateDataPersistence := getStateOverrideRegion(d, redis.StringValue(regionDb.Region))["override_global_data_persistence"]; stateDataPersistence != nil { + if stateDataPersistence.(string) != "" { + regionDbConfig["override_global_data_persistence"] = regionDb.DataPersistence + } + } + + if stateOverridePassword := getStateOverrideRegion(d, redis.StringValue(regionDb.Region))["override_global_password"]; stateOverridePassword != "" { + if *regionDb.Security.Password == d.Get("global_password").(string) { + regionDbConfig["override_global_password"] = "" + } else { + regionDbConfig["override_global_password"] = redis.StringValue(regionDb.Security.Password) + } + } + + stateOverrideAlerts := getStateAlertsFromDbRegion(getStateOverrideRegion(d, redis.StringValue(regionDb.Region))) + if len(stateOverrideAlerts) > 0 { + regionDbConfig["override_global_alert"] = flattenAlerts(regionDb.Alerts) + } + + regionDbConfigs = append(regionDbConfigs, regionDbConfig) + } + + // Only set override_region if it is defined in the config + if len(d.Get("override_region").(*schema.Set).List()) > 0 { + if err := d.Set("override_region", regionDbConfigs); err != nil { + return diag.FromErr(err) + } + } + + if err := d.Set("public_endpoint", publicEndpointConfig); err != nil { + return diag.FromErr(err) + } + if err := d.Set("private_endpoint", privateEndpointConfig); err != nil { + return diag.FromErr(err) + } + + return diags +} + +func resourceRedisCloudActiveActiveSubscriptionDatabaseDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // use the meta value to retrieve your client from the provider configure method + api := meta.(*apiClient) + + var diags diag.Diagnostics + subId := d.Get("subscription_id").(int) + + _, dbId, err := toDatabaseId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + if err := waitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil { + return diag.FromErr(err) + } + + dbErr := api.client.Database.Delete(ctx, subId, dbId) + if dbErr != nil { + diag.FromErr(dbErr) + } + + err = waitForDatabaseToBeDeleted(ctx, subId, dbId, api) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceRedisCloudActiveActiveSubscriptionDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + _, dbId, err := toDatabaseId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subId := d.Get("subscription_id").(int) + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + var globalAlerts []*databases.UpdateAlert + for _, alert := range d.Get("global_alert").(*schema.Set).List() { + dbAlert := alert.(map[string]interface{}) + + globalAlerts = append(globalAlerts, &databases.UpdateAlert{ + Name: redis.String(dbAlert["name"].(string)), + Value: redis.Int(dbAlert["value"].(int)), + }) + } + + globalSourceIps := setToStringSlice(d.Get("global_source_ips").(*schema.Set)) + + // Make a list of region-specific configurations + var regions []*databases.LocalRegionProperties + for _, region := range d.Get("override_region").(*schema.Set).List() { + dbRegion := region.(map[string]interface{}) + + overrideAlerts := getStateAlertsFromDbRegion(getStateOverrideRegion(d, dbRegion["name"].(string))) + + // Make a list of region-specific source IPs for use in the regions list below + var overrideSourceIps []*string + for _, source_ip := range dbRegion["override_global_source_ips"].(*schema.Set).List() { + overrideSourceIps = append(overrideSourceIps, redis.String(source_ip.(string))) + } + + regionProps := &databases.LocalRegionProperties{ + Region: redis.String(dbRegion["name"].(string)), + } + + if len(overrideAlerts) > 0 { + regionProps.Alerts = overrideAlerts + } else if len(globalAlerts) > 0 { + regionProps.Alerts = globalAlerts + } + if len(overrideSourceIps) > 0 { + regionProps.SourceIP = overrideSourceIps + } else if len(globalSourceIps) > 0 { + regionProps.SourceIP = globalSourceIps + } + dataPersistence := dbRegion["override_global_data_persistence"].(string) + if dataPersistence != "" { + regionProps.DataPersistence = redis.String(dataPersistence) + } else if d.Get("global_data_persistence").(string) != "" { + regionProps.DataPersistence = redis.String(d.Get("global_data_persistence").(string)) + } + password := dbRegion["override_global_password"].(string) + // If the password is not set, check if the global password is set and use that + if password != "" { + regionProps.Password = redis.String(password) + } else { + if d.Get("global_password").(string) != "" { + regionProps.Password = redis.String(d.Get("global_password").(string)) + } + } + regions = append(regions, regionProps) + } + + // Populate the database update request with the required fields + update := databases.UpdateActiveActiveDatabase{ + MemoryLimitInGB: redis.Float64(d.Get("memory_limit_in_gb").(float64)), + SupportOSSClusterAPI: redis.Bool(d.Get("support_oss_cluster_api").(bool)), + UseExternalEndpointForOSSClusterAPI: redis.Bool(d.Get("external_endpoint_for_oss_cluster_api").(bool)), + DataEvictionPolicy: redis.String(d.Get("data_eviction").(string)), + GlobalAlerts: globalAlerts, + GlobalSourceIP: globalSourceIps, + Regions: regions, + } + + // The below fields are optional and will only be sent in the request if they are present in the Terraform configuration + if len(globalSourceIps) == 0 { + update.GlobalSourceIP = []*string{redis.String("0.0.0.0/0")} + } + + if d.Get("global_password").(string) != "" { + update.GlobalPassword = redis.String(d.Get("global_password").(string)) + } + + if d.Get("global_data_persistence").(string) != "" { + update.GlobalDataPersistence = redis.String(d.Get("global_data_persistence").(string)) + } + + //The cert validation is done by the API (HTTP 400 is returned if it's invalid). + clientSSLCertificate := d.Get("client_ssl_certificate").(string) + enableTLS := d.Get("enable_tls").(bool) + if enableTLS { + //TLS only: enable_tls=true, client_ssl_certificate="". + update.EnableTls = redis.Bool(enableTLS) + //mTLS: enableTls=true, non-empty client_ssl_certificate. + if clientSSLCertificate != "" { + update.ClientSSLCertificate = redis.String(clientSSLCertificate) + } + } else { + // mTLS (backward compatibility): enable_tls=false, non-empty client_ssl_certificate. + if clientSSLCertificate != "" { + update.ClientSSLCertificate = redis.String(clientSSLCertificate) + } else { + // Default: enable_tls=false, client_ssl_certificate="" + update.EnableTls = redis.Bool(enableTLS) + } + } + + update.UseExternalEndpointForOSSClusterAPI = redis.Bool(d.Get("external_endpoint_for_oss_cluster_api").(bool)) + + err = api.client.Database.ActiveActiveUpdate(ctx, subId, dbId, update) + if err != nil { + return diag.FromErr(err) + } + + if err := waitForDatabaseToBeActive(ctx, subId, dbId, api); err != nil { + return diag.FromErr(err) + } + + if err := waitForSubscriptionToBeActive(ctx, subId, api); err != nil { + return diag.FromErr(err) + } + + return resourceRedisCloudActiveActiveSubscriptionDatabaseRead(ctx, d, meta) +} + +func getStateOverrideRegion(d *schema.ResourceData, regionName string) map[string]interface{} { + for _, region := range d.Get("override_region").(*schema.Set).List() { + dbRegion := region.(map[string]interface{}) + if dbRegion["name"].(string) == regionName { + return dbRegion + } + } + return nil +} + +func getStateAlertsFromDbRegion(dbRegion map[string]interface{}) []*databases.UpdateAlert { + // Make a list of region-specific alert configurations for use in the regions list below + if dbRegion == nil { + return nil + } else if dbRegion["override_global_alert"] == nil { + return nil + } + var overrideAlerts []*databases.UpdateAlert + for _, alert := range dbRegion["override_global_alert"].(*schema.Set).List() { + dbAlert := alert.(map[string]interface{}) + overrideAlerts = append(overrideAlerts, &databases.UpdateAlert{ + Name: redis.String(dbAlert["name"].(string)), + Value: redis.Int(dbAlert["value"].(int)), + }) + } + return overrideAlerts +} + +func waitForDatabaseToBeDeleted(ctx context.Context, subId int, dbId int, api *apiClient) error { + wait := &resource.StateChangeConf{ + Delay: 10 * time.Second, + Pending: []string{"pending"}, + Target: []string{"deleted"}, + Timeout: 10 * time.Minute, + + Refresh: func() (result interface{}, state string, err error) { + log.Printf("[DEBUG] Waiting for datbase %d to be deleted", dbId) + + _, err = api.client.Database.Get(ctx, subId, dbId) + if err != nil { + if _, ok := err.(*databases.NotFound); ok { + return "deleted", "deleted", nil + } + return nil, "", err + } + + return "pending", "pending", nil + }, + } + if _, err := wait.WaitForStateContext(ctx); err != nil { + return err + } + + return nil +} diff --git a/internal/provider/resource_rediscloud_active_active_subscription_database_test.go b/internal/provider/resource_rediscloud_active_active_subscription_database_test.go new file mode 100644 index 00000000..ed796cbc --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription_database_test.go @@ -0,0 +1,241 @@ +package provider + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// Checks CRUDI (CREATE,READ,UPDATE,IMPORT) operations on the database resource. +func TestAccResourceRedisCloudActiveActiveSubscriptionDatabase_CRUDI(t *testing.T) { + + subscriptionName := acctest.RandomWithPrefix(testResourcePrefix) + "-subscription" + name := acctest.RandomWithPrefix(testResourcePrefix) + "-database" + password := acctest.RandString(20) + resourceName := "rediscloud_active_active_subscription_database.example" + subscriptionResourceName := "rediscloud_active_active_subscription.example" + + var subId int + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + // Test database creation + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionDatabase, subscriptionName, name, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "memory_limit_in_gb", "3"), + resource.TestCheckResourceAttr(resourceName, "support_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "global_data_persistence", "none"), + resource.TestCheckResourceAttr(resourceName, "external_endpoint_for_oss_cluster_api", "false"), + resource.TestCheckResourceAttr(resourceName, "global_password", password), + resource.TestCheckResourceAttr(resourceName, "enable_tls", "false"), + resource.TestCheckResourceAttr(resourceName, "data_eviction", "volatile-lru"), + resource.TestCheckResourceAttr(resourceName, "global_alert.#", "1"), + resource.TestCheckResourceAttr(resourceName, "global_alert.0.name", "dataset-size"), + resource.TestCheckResourceAttr(resourceName, "global_alert.0.value", "40"), + resource.TestCheckResourceAttr(resourceName, "global_source_ips.#", "2"), + + resource.TestCheckResourceAttr(resourceName, "override_region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.name", "us-east-1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_data_persistence", "aof-every-write"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_password", "region-specific-password"), + // check override region alert block + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.#", "1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.0.name", "dataset-size"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.0.value", "42"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_source_ips.#", "1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_source_ips.0", "192.175.0.0/16"), + + // Check that global values are used for the second region where no override is set + resource.TestCheckResourceAttr(resourceName, "override_region.1.name", "us-east-2"), + resource.TestCheckResourceAttr(resourceName, "override_region.1.override_global_data_persistence", ""), + resource.TestCheckResourceAttr(resourceName, "override_region.1.override_global_password", ""), + resource.TestCheckResourceAttr(resourceName, "override_region.1.override_global_alert.#", "0"), + resource.TestCheckResourceAttr(resourceName, "override_region.1.override_source_ips.#", "0"), + + // Test databases exist + func(s *terraform.State) error { + r := s.RootModule().Resources[subscriptionResourceName] + + var err error + subId, err = strconv.Atoi(r.Primary.ID) + if err != nil { + return fmt.Errorf("couldn't parse the subscription ID: %s", redis.StringValue(&r.Primary.ID)) + } + + client := testProvider.Meta().(*apiClient) + sub, err := client.client.Subscription.Get(context.TODO(), subId) + if err != nil { + return err + } + + if redis.StringValue(sub.Name) != subscriptionName { + return fmt.Errorf("unexpected name value: %s", redis.StringValue(sub.Name)) + } + + listDb := client.client.Database.List(context.TODO(), subId) + if listDb.Next() != true { + return fmt.Errorf("no database found: %s", listDb.Err()) + } + if listDb.Err() != nil { + return listDb.Err() + } + + return nil + }, + ), + }, + // Test database is updated successfully + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionDatabaseUpdate, subscriptionName, name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "memory_limit_in_gb", "1"), + resource.TestCheckResourceAttr(resourceName, "support_oss_cluster_api", "true"), + resource.TestCheckResourceAttr(resourceName, "external_endpoint_for_oss_cluster_api", "true"), + resource.TestCheckResourceAttr(resourceName, "global_data_persistence", "aof-every-1-second"), + resource.TestCheckResourceAttr(resourceName, "global_password", "updated-password"), + + resource.TestCheckResourceAttr(resourceName, "override_region.#", "1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.name", "us-east-1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_data_persistence", "none"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_password", "password-updated"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.#", "1"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.0.name", "dataset-size"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_alert.0.value", "41"), + resource.TestCheckResourceAttr(resourceName, "override_region.0.override_global_source_ips.#", "0"), + ), + }, + // Test that that database is imported successfully + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionDatabaseImport, subscriptionName, name), + ResourceName: "rediscloud_active_active_subscription_database.example", + ImportState: true, + ImportStateVerify: true, + // global and override attributes not supported as part of import + ImportStateVerifyIgnore: []string{ + "global_data_persistence", + "global_password", + "global_source_ips.#", + "global_source_ips.0", + "override_region.#", + "override_region.0.%", + "override_region.0.name", + "override_region.0.override_global_alert.#", + "override_region.0.override_global_alert.0.%", + "override_region.0.override_global_alert.0.name", + "override_region.0.override_global_alert.0.value", + "override_region.0.override_global_data_persistence", + "override_region.0.override_global_password", + }, + }, + }, + }) +} + +const activeActiveSubscriptionBoilerplate = ` + data "rediscloud_payment_method" "card" { + card_type = "Visa" + } + + resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } + } +` + +// Create and Read tests +// TF config for provisioning a new database +const testAccResourceRedisCloudActiveActiveSubscriptionDatabase = activeActiveSubscriptionBoilerplate + ` +resource "rediscloud_active_active_subscription_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = "%s" + memory_limit_in_gb = 3 + support_oss_cluster_api = false + external_endpoint_for_oss_cluster_api = false + enable_tls = false + + global_data_persistence = "none" + global_password = "%s" + global_source_ips = ["192.168.0.0/16", "192.170.0.0/16"] + global_alert { + name = "dataset-size" + value = 40 + } + override_region { + name = "us-east-1" + override_global_data_persistence = "aof-every-write" + override_global_source_ips = ["192.175.0.0/16"] + override_global_password = "region-specific-password" + override_global_alert { + name = "dataset-size" + value = 42 + } + } + override_region { + name = "us-east-2" + } + +} +` + +// TF config for updating a database +const testAccResourceRedisCloudActiveActiveSubscriptionDatabaseUpdate = activeActiveSubscriptionBoilerplate + ` +resource "rediscloud_active_active_subscription_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = "%s" + memory_limit_in_gb = 1 + support_oss_cluster_api = true + external_endpoint_for_oss_cluster_api = true + + global_data_persistence = "aof-every-1-second" + global_password = "updated-password" + global_source_ips = ["192.170.0.0/16"] + + override_region { + name = "us-east-1" + override_global_data_persistence = "none" + override_global_password = "password-updated" + override_global_alert { + name = "dataset-size" + value = 41 + } + } + } + ` + +// TF config for updating a database +const testAccResourceRedisCloudActiveActiveSubscriptionDatabaseImport = activeActiveSubscriptionBoilerplate + ` +resource "rediscloud_active_active_subscription_database" "example" { + subscription_id = rediscloud_active_active_subscription.example.id + name = "%s" + memory_limit_in_gb = 1 + } + ` diff --git a/internal/provider/resource_rediscloud_active_active_subscription_peering.go b/internal/provider/resource_rediscloud_active_active_subscription_peering.go new file mode 100644 index 00000000..436b86f4 --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription_peering.go @@ -0,0 +1,383 @@ +package provider + +import ( + "context" + "log" + "regexp" + "strconv" + "strings" + "time" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" + "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRedisCloudActiveActiveSubscriptionPeering() *schema.Resource { + return &schema.Resource{ + Description: "Creates a VPC peering for an existing Redis Enterprise Cloud Active-Active Subscription, allowing access to your subscription databases as if they were on the same network.", + CreateContext: resourceRedisCloudSubscriptionActiveActivePeeringCreate, + ReadContext: resourceRedisCloudSubscriptionActiveActivePeeringRead, + DeleteContext: resourceRedisCloudSubscriptionActiveActivePeeringDelete, + // UpdateContext - not set as all attributes are not updatable or computed + + Importer: &schema.ResourceImporter{ + StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + _, _, err := toVpcPeeringId(d.Id()) + if err != nil { + return nil, err + } + return []*schema.ResourceData{d}, nil + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "subscription_id": { + Description: "A valid subscription predefined in the current account", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateDiagFunc(validation.StringMatch(regexp.MustCompile("^\\d+$"), "must be a number")), + ForceNew: true, + }, + "provider_name": { + Type: schema.TypeString, + Description: "The cloud provider to use with the vpc peering, (either `AWS` or `GCP`)", + ValidateDiagFunc: validateDiagFunc(validation.StringInSlice(cloud_accounts.ProviderValues(), false)), + Optional: true, + ForceNew: true, + Default: "AWS", + }, + "source_region": { + Description: "AWS or GCP Region that the VPC to be peered lives in", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "destination_region": { + Description: "AWS Region that the VPC to be peered lives in", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "aws_account_id": { + Description: "AWS account id that the VPC to be peered lives in", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "vpc_id": { + Description: "Identifier of the VPC to be peered", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "vpc_cidr": { + Description: "CIDR range of the VPC to be peered", + Type: schema.TypeString, + ForceNew: true, + Optional: true, + ValidateDiagFunc: validateDiagFunc(validation.IsCIDR), + }, + "gcp_project_id": { + Description: "GCP project ID that the VPC to be peered lives in", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "gcp_network_name": { + Description: "The name of the network to be peered", + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "status": { + Description: "Current status of the account - `initiating-request`, `pending-acceptance`, `active`, `inactive` or `failed`", + Type: schema.TypeString, + Computed: true, + }, + "aws_peering_id": { + Description: "Identifier of the AWS cloud peering", + Type: schema.TypeString, + Computed: true, + }, + "gcp_redis_project_id": { + Description: "Identifier of the Redis Enterprise Cloud GCP project to be peered", + Type: schema.TypeString, + Computed: true, + }, + "gcp_redis_network_name": { + Description: "The name of the Redis Enterprise Cloud network to be peered", + Type: schema.TypeString, + Computed: true, + }, + "gcp_peering_id": { + Description: "Identifier of the cloud peering", + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceRedisCloudSubscriptionActiveActivePeeringCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + + subId, err := strconv.Atoi(d.Get("subscription_id").(string)) + if err != nil { + return diag.FromErr(err) + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + providerName := d.Get("provider_name").(string) + + peeringRequest := subscriptions.CreateActiveActiveVPCPeering{} + + if providerName == "AWS" { + + sourceRegion, ok := d.GetOk("source_region") + if !ok { + return diag.Errorf("`region` must be set when `provider_name` is `AWS`") + } + + destinationRegion, ok := d.GetOk("destination_region") + if !ok { + return diag.Errorf("`region` must be set when `provider_name` is `AWS`") + } + + awsAccountID, ok := d.GetOk("aws_account_id") + if !ok { + return diag.Errorf("`aws_account_id` must be set when `provider_name` is `AWS`") + } + + vpcID, ok := d.GetOk("vpc_id") + if !ok { + return diag.Errorf("`vpc_id` must be set when `provider_name` is `AWS`") + } + + vpcCIDR, ok := d.GetOk("vpc_cidr") + if !ok { + return diag.Errorf("`vpc_cidrs` must be set when `provider_name` is `AWS`") + } + + peeringRequest.SourceRegion = redis.String(sourceRegion.(string)) + peeringRequest.DestinationRegion = redis.String(destinationRegion.(string)) + peeringRequest.AWSAccountID = redis.String(awsAccountID.(string)) + peeringRequest.VPCId = redis.String(vpcID.(string)) + peeringRequest.VPCCidr = redis.String(vpcCIDR.(string)) + } + + if providerName == "GCP" { + + gcpProjectID, ok := d.GetOk("gcp_project_id") + if !ok { + return diag.Errorf("`gcp_project_id` must be set when `provider_name` is `GCP`") + } + + gcpNetworkName, ok := d.GetOk("gcp_network_name") + if !ok { + return diag.Errorf("`network_name` must be set when `provider_name` is `GCP`") + } + + sourceRegion, ok := d.GetOk("source_region") + if !ok { + return diag.Errorf("`region` must be set when `provider_name` is `GCP`") + } + + peeringRequest.Provider = redis.String(strings.ToLower(providerName)) + peeringRequest.SourceRegion = redis.String(sourceRegion.(string)) + peeringRequest.VPCProjectUID = redis.String(gcpProjectID.(string)) + peeringRequest.VPCNetworkName = redis.String(gcpNetworkName.(string)) + } + + peering, err := api.client.Subscription.CreateActiveActiveVPCPeering(ctx, subId, peeringRequest) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(buildResourceId(subId, peering)) + + err = waitForActiveActivePeeringToBeInitiated(ctx, subId, peering, api) + if err != nil { + return diag.FromErr(err) + } + + return resourceRedisCloudSubscriptionActiveActivePeeringRead(ctx, d, meta) +} + +func resourceRedisCloudSubscriptionActiveActivePeeringRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + var diags diag.Diagnostics + + subId, id, err := toVpcPeeringId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := d.Set("subscription_id", strconv.Itoa(subId)); err != nil { + return diag.FromErr(err) + } + + peerings, err := api.client.Subscription.ListActiveActiveVPCPeering(ctx, subId) + if err != nil { + if _, ok := err.(*subscriptions.NotFound); ok { + d.SetId("") + return diags + } + return diag.FromErr(err) + } + + peering, sourceRegion := findActiveActiveVpcPeering(id, peerings) + if peering == nil { + d.SetId("") + return diags + } + + if err := d.Set("status", redis.StringValue(peering.Status)); err != nil { + return diag.FromErr(err) + } + + providerName := d.Get("provider_name").(string) + + if redis.StringValue(peering.GCPProjectUID) != "" { + providerName = "GCP" + } + + if err := d.Set("provider_name", providerName); err != nil { + return diag.FromErr(err) + } + + if providerName == "AWS" { + if err := d.Set("aws_account_id", redis.StringValue(peering.AWSAccountID)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("aws_peering_id", redis.StringValue(peering.AWSPeeringID)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("vpc_id", redis.StringValue(peering.VPCId)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("vpc_cidr", redis.StringValue(peering.VPCCidr)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("source_region", redis.StringValue(sourceRegion)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("destination_region", redis.StringValue(peering.RegionName)); err != nil { + return diag.FromErr(err) + } + } + if providerName == "GCP" { + if err := d.Set("gcp_project_id", redis.StringValue(peering.GCPProjectUID)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("gcp_network_name", redis.StringValue(peering.NetworkName)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("source_region", redis.StringValue(peering.SourceRegion)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("gcp_redis_project_id", redis.StringValue(peering.RedisProjectUID)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("gcp_redis_network_name", redis.StringValue(peering.RedisNetworkName)); err != nil { + return diag.FromErr(err) + } + if err := d.Set("gcp_peering_id", redis.StringValue(peering.CloudPeeringID)); err != nil { + return diag.FromErr(err) + } + + } + return diags + +} + +func resourceRedisCloudSubscriptionActiveActivePeeringDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + api := meta.(*apiClient) + var diags diag.Diagnostics + + subId, id, err := toVpcPeeringId(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + subscriptionMutex.Lock(subId) + defer subscriptionMutex.Unlock(subId) + + err = api.client.Subscription.DeleteActiveActiveVPCPeering(ctx, subId, id) + if err != nil { + if _, ok := err.(*subscriptions.NotFound); ok { + d.SetId("") + return diags + } + return diag.FromErr(err) + } + + d.SetId("") + + return diags +} + +func findActiveActiveVpcPeering(id int, regions []*subscriptions.ActiveActiveVpcRegion) (*subscriptions.ActiveActiveVPCPeering, *string) { + for _, region := range regions { + peerings := region.VPCPeerings + for _, peering := range peerings { + if redis.IntValue(peering.ID) == id { + return peering, region.SourceRegion + } + } + } + return nil, nil +} + +func waitForActiveActivePeeringToBeInitiated(ctx context.Context, subId, id int, api *apiClient) error { + wait := &resource.StateChangeConf{ + Delay: 10 * time.Second, + Pending: []string{ + subscriptions.VPCPeeringStatusInitiatingRequest, + }, + Target: []string{ + subscriptions.VPCPeeringStatusActive, + subscriptions.VPCPeeringStatusInactive, + subscriptions.VPCPeeringStatusPendingAcceptance, + }, + Timeout: 10 * time.Minute, + + Refresh: func() (result interface{}, state string, err error) { + log.Printf("[DEBUG] Waiting for vpc peering %d to be initiated. Status: %s", id, state) + + list, err := api.client.Subscription.ListActiveActiveVPCPeering(ctx, subId) + if err != nil { + return nil, "", err + } + + peering, _ := findActiveActiveVpcPeering(id, list) + if peering == nil { + log.Printf("Peering %d/%d not present yet", subId, id) + return nil, "", nil + } + + return redis.StringValue(peering.Status), redis.StringValue(peering.Status), nil + }, + } + if _, err := wait.WaitForStateContext(ctx); err != nil { + return err + } + + return nil +} diff --git a/internal/provider/resource_rediscloud_active_active_subscription_peering_test.go b/internal/provider/resource_rediscloud_active_active_subscription_peering_test.go new file mode 100644 index 00000000..73b3cb43 --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription_peering_test.go @@ -0,0 +1,185 @@ +package provider + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccResourceRedisCloudActiveActiveSubscriptionPeering_aws(t *testing.T) { + + name := acctest.RandomWithPrefix(testResourcePrefix) + + cidrRange := os.Getenv("AWS_VPC_CIDR") + // Choose a CIDR range for the subscription that's unlikely to overlap with any VPC CIDR + subCidrRange := "10.0.0.0/24" + + overlap, err := cidrRangesOverlap(subCidrRange, cidrRange) + if err != nil { + t.Fatalf("AWS_VPC_CIDR is not a valid CIDR range %s: %s", cidrRange, err) + } + if overlap { + subCidrRange = "172.16.0.0/24" + } + + peeringRegion := os.Getenv("AWS_PEERING_REGION") + matchesRegex(t, peeringRegion, "^[a-z]+-[a-z]+-\\d+$") + + accountId := os.Getenv("AWS_ACCOUNT_ID") + matchesRegex(t, accountId, "^\\d+$") + + vpcId := os.Getenv("AWS_VPC_ID") + matchesRegex(t, vpcId, "^vpc-[a-z\\d]+$") + + tf := fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionPeeringAWS, + name, + subCidrRange, + peeringRegion, + accountId, + vpcId, + cidrRange, + ) + resourceName := "rediscloud_active_active_subscription_peering.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPeeringPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: tf, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr(resourceName, "id", regexp.MustCompile("^\\d*/\\d*$")), + resource.TestCheckResourceAttrSet(resourceName, "status"), + resource.TestCheckResourceAttrSet(resourceName, "provider_name"), + resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, "vpc_id"), + resource.TestCheckResourceAttr(resourceName, "vpc_cidr", cidrRange), + resource.TestCheckResourceAttrSet(resourceName, "source_region"), + resource.TestCheckResourceAttrSet(resourceName, "destination_region"), + resource.TestCheckResourceAttrSet(resourceName, "aws_peering_id"), + ), + }, + }, + }) +} + +func TestAccResourceRedisCloudActiveActiveSubscriptionPeering_gcp(t *testing.T) { + + if testing.Short() { + t.Skip("Required environment variables currently not available under CI") + } + + name := acctest.RandomWithPrefix(testResourcePrefix) + + tf := fmt.Sprintf(testAccResourceRedisCloudSubscriptionPeeringGCP, + name, + os.Getenv("GCP_VPC_PROJECT"), + os.Getenv("GCP_VPC_ID"), + ) + resourceName := "rediscloud_active_active_subscription_peering.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: tf, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr(resourceName, "id", regexp.MustCompile("^\\d*/\\d*$")), + resource.TestCheckResourceAttr(resourceName, "provider_name", "GCP"), + resource.TestCheckResourceAttrSet(resourceName, "status"), + resource.TestCheckResourceAttrSet(resourceName, "gcp_project_id"), + resource.TestCheckResourceAttrSet(resourceName, "gcp_network_name"), + resource.TestCheckResourceAttrSet(resourceName, "gcp_redis_project_id"), + resource.TestCheckResourceAttrSet(resourceName, "gcp_redis_network_name"), + resource.TestCheckResourceAttrSet(resourceName, "gcp_peering_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +const testAccResourceRedisCloudActiveActiveSubscriptionPeeringAWS = ` +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "%s" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} + +resource "rediscloud_active_active_subscription_peering" "test" { + subscription_id = rediscloud_active_active_subscription.example.id + provider_name = "AWS" + source_region = "us-east-2" + destination_region = "%s" + aws_account_id = "%s" + vpc_id = "%s" + vpc_cidr = "%s" +} +` + +const testAccResourceRedisCloudActiveActiveSubscriptionPeeringGCP = ` +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "GCP" + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} + +resource "rediscloud_active_active_subscription_peering" "test" { + subscription_id = rediscloud_subscription.example.id + provider_name = "GCP" + gcp_project_id = "%s" + gcp_network_name = "%s" +} +` diff --git a/internal/provider/resource_rediscloud_active_active_subscription_test.go b/internal/provider/resource_rediscloud_active_active_subscription_test.go new file mode 100644 index 00000000..ca91133d --- /dev/null +++ b/internal/provider/resource_rediscloud_active_active_subscription_test.go @@ -0,0 +1,307 @@ +package provider + +import ( + "context" + "flag" + "fmt" + "regexp" + "strconv" + "testing" + + "github.com/RedisLabs/rediscloud-go-api/redis" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var activeActiveContractFlag = flag.Bool("activeActiveContract", false, + "Add this flag '-activeActiveContract' to run tests for contract associated accounts") + +var activeActiveMarketplaceFlag = flag.Bool("activeActiveMarketplace", false, + "Add this flag '-activeActiveMarketplace' to run tests for marketplace associated accounts") + +// Checks CRUDI (CREATE,READ,UPDATE,IMPORT) operations on the subscription resource. +func TestAccResourceRedisCloudActiveActiveSubscription_CRUDI(t *testing.T) { + + name := acctest.RandomWithPrefix(testResourcePrefix) + resourceName := "rediscloud_active_active_subscription.example" + + var subId int + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscription, name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.#", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.memory_limit_in_gb", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.quantity", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.read_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.read_operations_per_second", "1000"), + + func(s *terraform.State) error { + r := s.RootModule().Resources[resourceName] + + var err error + subId, err = strconv.Atoi(r.Primary.ID) + if err != nil { + return err + } + + client := testProvider.Meta().(*apiClient) + sub, err := client.client.Subscription.Get(context.TODO(), subId) + if err != nil { + return err + } + + if redis.StringValue(sub.Name) != name { + return fmt.Errorf("unexpected name value: %s", redis.StringValue(sub.Name)) + } + return nil + }, + ), + }, + { + // Checks if the changes in the creation plan are ignored. + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionNoCreationPlan, name, "AWS"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.#", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.memory_limit_in_gb", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.quantity", "1"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.read_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.read_operations_per_second", "1000"), + ), + }, + { + // Checks if the creation_plan block is ignored after the IMPORT operation. + ResourceName: resourceName, + ImportState: true, + ImportStateCheck: func(states []*terraform.InstanceState) error { + creationPlan, ok := states[0].Attributes["creation_plan.#"] + if ok && creationPlan != "0" { + return fmt.Errorf("Unexpected creation_plan block. Should be 0, instead of %s", creationPlan) + } + return nil + }, + }, + { + // Checks if an error is raised when a ForceNew attribute is changed and the creation_plan block is not defined. + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionNoCreationPlan, name, "GCP"), + ResourceName: resourceName, + ExpectError: regexp.MustCompile(`Error: the "creation_plan" block is required`), + }, + }, + }) +} + +func TestAccResourceRedisCloudActiveActiveSubscription_createUpdateContractPayment(t *testing.T) { + + if !*activeActiveContractFlag { + t.Skip("The '-activeActiveContract' parameter wasn't provided in the test command.") + } + + name := acctest.RandomWithPrefix(testResourcePrefix) + updatedName := fmt.Sprintf("%v-updatedName", name) + resourceName := "rediscloud_active_active_subscription.example" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionContractPayment, name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider.0.provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.read_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.read_operations_per_second", "1000"), + ), + }, + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionContractPayment, updatedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "payment_method_id"), + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + ), + }, + }, + }) +} + +func TestAccResourceRedisCloudActiveActiveSubscription_createUpdateMarketplacePayment(t *testing.T) { + + if !*activeActiveMarketplaceFlag { + t.Skip("The '-activeActiveMarketplace' parameter wasn't provided in the test command.") + } + + name := acctest.RandomWithPrefix(testResourcePrefix) + updatedName := fmt.Sprintf("%v-updatedName", name) + resourceName := "rediscloud_active_active_subscription.example" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAwsPreExistingCloudAccountPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckActiveActiveSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionMarketplacePayment, name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider.0.provider", "AWS"), + resource.TestCheckResourceAttrSet(resourceName, "payment_method_id"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.#", "2"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.0.read_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.write_operations_per_second", "1000"), + resource.TestCheckResourceAttr(resourceName, "creation_plan.0.region.1.read_operations_per_second", "1000"), + ), + }, + { + Config: fmt.Sprintf(testAccResourceRedisCloudActiveActiveSubscriptionMarketplacePayment, updatedName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + ), + }, + }, + }) +} + +func testAccCheckActiveActiveSubscriptionDestroy(s *terraform.State) error { + client := testProvider.Meta().(*apiClient) + + for _, r := range s.RootModule().Resources { + if r.Type != "rediscloud_active_active_subscription" { + continue + } + + subId, err := strconv.Atoi(r.Primary.ID) + if err != nil { + return err + } + + subs, err := client.client.Subscription.List(context.TODO()) + if err != nil { + return err + } + + for _, sub := range subs { + if redis.IntValue(sub.ID) == subId { + return fmt.Errorf("subscription %d still exists", subId) + } + } + } + + return nil +} + +// TF config for provisioning a new subscription. +const testAccResourceRedisCloudActiveActiveSubscription = ` +data "rediscloud_payment_method" "card" { + card_type = "Visa" +} + +resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 1 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} +` + +const testAccResourceRedisCloudActiveActiveSubscriptionNoCreationPlan = ` + + data "rediscloud_payment_method" "card" { + card_type = "Visa" + } + + resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method_id = data.rediscloud_payment_method.card.id + cloud_provider = "%s" + + } +` + +const testAccResourceRedisCloudActiveActiveSubscriptionContractPayment = ` + +resource "rediscloud_active_active_subscription" "example" { + name = "%s" + cloud_provider = "AWS" + + creation_plan { + memory_limit_in_gb = 2 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } +} +` + +const testAccResourceRedisCloudActiveActiveSubscriptionMarketplacePayment = ` + +resource "rediscloud_active_active_subscription" "example" { + name = "%s" + payment_method = "marketplace" + + cloud_provider = "AWS" + creation_plan { + memory_limit_in_gb = 2 + quantity = 1 + region { + region = "us-east-1" + networking_deployment_cidr = "192.168.0.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + region { + region = "us-east-2" + networking_deployment_cidr = "10.0.1.0/24" + write_operations_per_second = 1000 + read_operations_per_second = 1000 + } + } + } +` diff --git a/internal/provider/resource_rediscloud_subscription.go b/internal/provider/resource_rediscloud_subscription.go index 32a694a8..682f7b53 100644 --- a/internal/provider/resource_rediscloud_subscription.go +++ b/internal/provider/resource_rediscloud_subscription.go @@ -267,7 +267,8 @@ func resourceRedisCloudSubscription() *schema.Resource { "support_oss_cluster_api": { Description: "Support Redis open-source (OSS) Cluster API", Type: schema.TypeBool, - Required: true, + Optional: true, + Default: false, }, "replication": { Description: "Databases replication", diff --git a/internal/provider/resource_rediscloud_subscription_peering.go b/internal/provider/resource_rediscloud_subscription_peering.go index f2b41af0..d0c3a012 100644 --- a/internal/provider/resource_rediscloud_subscription_peering.go +++ b/internal/provider/resource_rediscloud_subscription_peering.go @@ -3,6 +3,12 @@ package provider import ( "context" "fmt" + "log" + "regexp" + "strconv" + "strings" + "time" + "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" @@ -10,16 +16,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "log" - "regexp" - "strconv" - "strings" - "time" ) func resourceRedisCloudSubscriptionPeering() *schema.Resource { return &schema.Resource{ - Description: "Creates an AWS VPC peering for an existing Redis Enterprise Cloud Subscription, allowing access to your subscription databases as if they were on the same network.", + Description: "Creates a VPC peering for an existing Redis Enterprise Cloud Subscription, allowing access to your subscription databases as if they were on the same network.", CreateContext: resourceRedisCloudSubscriptionPeeringCreate, ReadContext: resourceRedisCloudSubscriptionPeeringRead, DeleteContext: resourceRedisCloudSubscriptionPeeringDelete, diff --git a/internal/provider/resource_rediscloud_subscription_peering_test.go b/internal/provider/resource_rediscloud_subscription_peering_test.go index 069a84d6..bbc88b8d 100644 --- a/internal/provider/resource_rediscloud_subscription_peering_test.go +++ b/internal/provider/resource_rediscloud_subscription_peering_test.go @@ -194,7 +194,7 @@ resource "rediscloud_subscription" "example" { cloud_provider { provider = "GCP" - cloud_account_id = 1 + cloud_account_id = data.rediscloud_cloud_account.account.id region { region = "europe-west1" networking_deployment_cidr = "192.168.0.0/24" diff --git a/internal/provider/resource_rediscloud_subscription_test.go b/internal/provider/resource_rediscloud_subscription_test.go index 3f64cafb..3fa97b7d 100644 --- a/internal/provider/resource_rediscloud_subscription_test.go +++ b/internal/provider/resource_rediscloud_subscription_test.go @@ -95,8 +95,8 @@ func TestAccResourceRedisCloudSubscription_CRUDI(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { - creationPlan := states[0].Attributes["creation_plan.#"] - if creationPlan != "0" { + creationPlan, ok := states[0].Attributes["creation_plan.#"] + if ok && creationPlan != "0" { return fmt.Errorf("Unexpected creation_plan block. Should be 0, instead of %s", creationPlan) } return nil diff --git a/internal/provider/sweeper_test.go b/internal/provider/sweeper_test.go index 566094ad..bedd76cf 100644 --- a/internal/provider/sweeper_test.go +++ b/internal/provider/sweeper_test.go @@ -3,17 +3,18 @@ package provider import ( "context" "fmt" + "log" + "os" + "strings" + "testing" + "time" + rediscloud_api "github.com/RedisLabs/rediscloud-go-api" "github.com/RedisLabs/rediscloud-go-api/redis" "github.com/RedisLabs/rediscloud-go-api/service/cloud_accounts" "github.com/RedisLabs/rediscloud-go-api/service/databases" "github.com/RedisLabs/rediscloud-go-api/service/subscriptions" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "log" - "os" - "strings" - "testing" - "time" ) const testResourcePrefix = "tf-test" @@ -49,9 +50,13 @@ func init() { Name: "rediscloud_subscription", F: testSweepSubscriptions, }) + resource.AddTestSweepers("rediscloud_active_active_subscription", &resource.Sweeper{ + Name: "rediscloud_active_active_subscription", + F: testSweepActiveActiveSubscriptions, + }) resource.AddTestSweepers("rediscloud_cloud_account", &resource.Sweeper{ Name: "rediscloud_cloud_account", - Dependencies: []string{"rediscloud_subscription"}, // in case a subscription depends on an account + Dependencies: []string{"rediscloud_subscription", "rediscloud_active_active_subscription"}, // in case a subscription depends on an account F: testSweepCloudAccounts, }) } @@ -105,6 +110,10 @@ func testSweepSubscriptions(region string) error { continue } + if redis.StringValue(sub.DeploymentType) != subscriptions.SubscriptionDeploymentTypeSingleRegion { + continue + } + subId := redis.IntValue(sub.ID) sweepSub, dbIds, err := testSweepReadDatabases(client, subId) if err != nil { @@ -163,3 +172,53 @@ func testSweepReadDatabases(client *rediscloud_api.Client, subId int) (bool, []i return true, dbIds, nil } + +func testSweepActiveActiveSubscriptions(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return err + } + + list, err := client.Subscription.List(context.TODO()) + if err != nil { + return err + } + + for _, sub := range list { + if redis.StringValue(sub.Status) != subscriptions.SubscriptionStatusActive { + continue + } + + if !strings.HasPrefix(redis.StringValue(sub.Name), "tf-test") { + continue + } + + if redis.StringValue(sub.DeploymentType) != subscriptions.SubscriptionDeploymentTypeActiveActive { + continue + } + + subId := redis.IntValue(sub.ID) + sweepSub, dbIds, err := testSweepReadDatabases(client, subId) + if err != nil { + return err + } + + if !sweepSub { + continue + } + + for _, db := range dbIds { + err := client.Database.Delete(context.TODO(), subId, db) + if err != nil { + return err + } + } + + err = client.Subscription.Delete(context.TODO(), subId) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/provider/utils.go b/internal/provider/utils.go index 0cd7b40a..8292be46 100644 --- a/internal/provider/utils.go +++ b/internal/provider/utils.go @@ -77,7 +77,6 @@ func (m *perIdLock) get(id int) *sync.Mutex { return mutex } - // IDs of any resources dependent on a subscription need to be divided by a slash. In this format: /. func buildResourceId(subId int, id int) string { return fmt.Sprintf("%d/%d", subId, id)