From 9d94f223e69a4d17e30835b4e9ef6b59a407e291 Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 30 Oct 2024 11:03:07 -0700 Subject: [PATCH] Promoting Memorystore to GA (#12112) (#20108) [upstream:61370a87fcc2c9f8f9040b692b728c6ba20077e4] Signed-off-by: Modular Magician --- .changelog/12112.txt | 3 + google/fwmodels/provider_model.go | 1 + google/fwprovider/framework_provider.go | 6 + google/fwtransport/framework_config.go | 10 + google/provider/provider.go | 6 + google/provider/provider_mmv1_resources.go | 6 +- .../memorystore/memorystore_operation.go | 92 + .../resource_memorystore_instance.go | 1645 +++++++++++++++++ ...rce_memorystore_instance_generated_test.go | 320 ++++ .../resource_memorystore_instance_sweeper.go | 143 ++ .../resource_memorystore_instance_test.go | 317 ++++ google/sweeper/gcp_sweeper_test.go | 1 + google/transport/config.go | 9 + .../docs/r/memorystore_instance.html.markdown | 17 - 14 files changed, 2557 insertions(+), 19 deletions(-) create mode 100644 .changelog/12112.txt create mode 100644 google/services/memorystore/memorystore_operation.go create mode 100644 google/services/memorystore/resource_memorystore_instance.go create mode 100644 google/services/memorystore/resource_memorystore_instance_generated_test.go create mode 100644 google/services/memorystore/resource_memorystore_instance_sweeper.go diff --git a/.changelog/12112.txt b/.changelog/12112.txt new file mode 100644 index 00000000000..7e719d9832e --- /dev/null +++ b/.changelog/12112.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_memorystore_instance` (ga) +``` \ No newline at end of file diff --git a/google/fwmodels/provider_model.go b/google/fwmodels/provider_model.go index 40c8d1d0e3d..1a8d540deaa 100644 --- a/google/fwmodels/provider_model.go +++ b/google/fwmodels/provider_model.go @@ -108,6 +108,7 @@ type ProviderModel struct { LoggingCustomEndpoint types.String `tfsdk:"logging_custom_endpoint"` LookerCustomEndpoint types.String `tfsdk:"looker_custom_endpoint"` MemcacheCustomEndpoint types.String `tfsdk:"memcache_custom_endpoint"` + MemorystoreCustomEndpoint types.String `tfsdk:"memorystore_custom_endpoint"` MigrationCenterCustomEndpoint types.String `tfsdk:"migration_center_custom_endpoint"` MLEngineCustomEndpoint types.String `tfsdk:"ml_engine_custom_endpoint"` MonitoringCustomEndpoint types.String `tfsdk:"monitoring_custom_endpoint"` diff --git a/google/fwprovider/framework_provider.go b/google/fwprovider/framework_provider.go index 14635ab88b6..866ec1e2581 100644 --- a/google/fwprovider/framework_provider.go +++ b/google/fwprovider/framework_provider.go @@ -616,6 +616,12 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest, transport_tpg.CustomEndpointValidator(), }, }, + "memorystore_custom_endpoint": &schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + transport_tpg.CustomEndpointValidator(), + }, + }, "migration_center_custom_endpoint": &schema.StringAttribute{ Optional: true, Validators: []validator.String{ diff --git a/google/fwtransport/framework_config.go b/google/fwtransport/framework_config.go index 012e6e7735e..a45745f8de0 100644 --- a/google/fwtransport/framework_config.go +++ b/google/fwtransport/framework_config.go @@ -142,6 +142,7 @@ type FrameworkProviderConfig struct { LoggingBasePath string LookerBasePath string MemcacheBasePath string + MemorystoreBasePath string MigrationCenterBasePath string MLEngineBasePath string MonitoringBasePath string @@ -304,6 +305,7 @@ func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context, p.LoggingBasePath = data.LoggingCustomEndpoint.ValueString() p.LookerBasePath = data.LookerCustomEndpoint.ValueString() p.MemcacheBasePath = data.MemcacheCustomEndpoint.ValueString() + p.MemorystoreBasePath = data.MemorystoreCustomEndpoint.ValueString() p.MigrationCenterBasePath = data.MigrationCenterCustomEndpoint.ValueString() p.MLEngineBasePath = data.MLEngineCustomEndpoint.ValueString() p.MonitoringBasePath = data.MonitoringCustomEndpoint.ValueString() @@ -1113,6 +1115,14 @@ func (p *FrameworkProviderConfig) HandleDefaults(ctx context.Context, data *fwmo data.MemcacheCustomEndpoint = types.StringValue(customEndpoint.(string)) } } + if data.MemorystoreCustomEndpoint.IsNull() { + customEndpoint := transport_tpg.MultiEnvDefault([]string{ + "GOOGLE_MEMORYSTORE_CUSTOM_ENDPOINT", + }, transport_tpg.DefaultBasePaths[transport_tpg.MemorystoreBasePathKey]) + if customEndpoint != nil { + data.MemorystoreCustomEndpoint = types.StringValue(customEndpoint.(string)) + } + } if data.MigrationCenterCustomEndpoint.IsNull() { customEndpoint := transport_tpg.MultiEnvDefault([]string{ "GOOGLE_MIGRATION_CENTER_CUSTOM_ENDPOINT", diff --git a/google/provider/provider.go b/google/provider/provider.go index 9d361ed473e..2746afb3f43 100644 --- a/google/provider/provider.go +++ b/google/provider/provider.go @@ -535,6 +535,11 @@ func Provider() *schema.Provider { Optional: true, ValidateFunc: transport_tpg.ValidateCustomEndpoint, }, + "memorystore_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: transport_tpg.ValidateCustomEndpoint, + }, "migration_center_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -1029,6 +1034,7 @@ func ProviderConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.LoggingBasePath = d.Get("logging_custom_endpoint").(string) config.LookerBasePath = d.Get("looker_custom_endpoint").(string) config.MemcacheBasePath = d.Get("memcache_custom_endpoint").(string) + config.MemorystoreBasePath = d.Get("memorystore_custom_endpoint").(string) config.MigrationCenterBasePath = d.Get("migration_center_custom_endpoint").(string) config.MLEngineBasePath = d.Get("ml_engine_custom_endpoint").(string) config.MonitoringBasePath = d.Get("monitoring_custom_endpoint").(string) diff --git a/google/provider/provider_mmv1_resources.go b/google/provider/provider_mmv1_resources.go index 58b54298c34..d14c5ad6dee 100644 --- a/google/provider/provider_mmv1_resources.go +++ b/google/provider/provider_mmv1_resources.go @@ -81,6 +81,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/services/logging" "github.com/hashicorp/terraform-provider-google/google/services/looker" "github.com/hashicorp/terraform-provider-google/google/services/memcache" + "github.com/hashicorp/terraform-provider-google/google/services/memorystore" "github.com/hashicorp/terraform-provider-google/google/services/migrationcenter" "github.com/hashicorp/terraform-provider-google/google/services/mlengine" "github.com/hashicorp/terraform-provider-google/google/services/monitoring" @@ -449,9 +450,9 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{ } // Resources -// Generated resources: 482 +// Generated resources: 483 // Generated IAM resources: 261 -// Total generated resources: 743 +// Total generated resources: 744 var generatedResources = map[string]*schema.Resource{ "google_folder_access_approval_settings": accessapproval.ResourceAccessApprovalFolderSettings(), "google_organization_access_approval_settings": accessapproval.ResourceAccessApprovalOrganizationSettings(), @@ -963,6 +964,7 @@ var generatedResources = map[string]*schema.Resource{ "google_logging_organization_settings": logging.ResourceLoggingOrganizationSettings(), "google_looker_instance": looker.ResourceLookerInstance(), "google_memcache_instance": memcache.ResourceMemcacheInstance(), + "google_memorystore_instance": memorystore.ResourceMemorystoreInstance(), "google_migration_center_group": migrationcenter.ResourceMigrationCenterGroup(), "google_migration_center_preference_set": migrationcenter.ResourceMigrationCenterPreferenceSet(), "google_ml_engine_model": mlengine.ResourceMLEngineModel(), diff --git a/google/services/memorystore/memorystore_operation.go b/google/services/memorystore/memorystore_operation.go new file mode 100644 index 00000000000..08eb8395b01 --- /dev/null +++ b/google/services/memorystore/memorystore_operation.go @@ -0,0 +1,92 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package memorystore + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +type MemorystoreOperationWaiter struct { + Config *transport_tpg.Config + UserAgent string + Project string + tpgresource.CommonOperationWaiter +} + +func (w *MemorystoreOperationWaiter) QueryOp() (interface{}, error) { + if w == nil { + return nil, fmt.Errorf("Cannot query operation, it's unset or nil.") + } + // Returns the proper get. + url := fmt.Sprintf("%s%s", w.Config.MemorystoreBasePath, w.CommonOperationWaiter.Op.Name) + + return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: w.Config, + Method: "GET", + Project: w.Project, + RawURL: url, + UserAgent: w.UserAgent, + }) +} + +func createMemorystoreWaiter(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string) (*MemorystoreOperationWaiter, error) { + w := &MemorystoreOperationWaiter{ + Config: config, + UserAgent: userAgent, + Project: project, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +// nolint: deadcode,unused +func MemorystoreOperationWaitTimeWithResponse(config *transport_tpg.Config, op map[string]interface{}, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + w, err := createMemorystoreWaiter(config, op, project, activity, userAgent) + if err != nil { + return err + } + if err := tpgresource.OperationWait(w, activity, timeout, config.PollInterval); err != nil { + return err + } + rawResponse := []byte(w.CommonOperationWaiter.Op.Response) + if len(rawResponse) == 0 { + return errors.New("`resource` not set in operation response") + } + return json.Unmarshal(rawResponse, response) +} + +func MemorystoreOperationWaitTime(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + if val, ok := op["name"]; !ok || val == "" { + // This was a synchronous call - there is no operation to wait for. + return nil + } + w, err := createMemorystoreWaiter(config, op, project, activity, userAgent) + if err != nil { + // If w is nil, the op was synchronous. + return err + } + return tpgresource.OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google/services/memorystore/resource_memorystore_instance.go b/google/services/memorystore/resource_memorystore_instance.go new file mode 100644 index 00000000000..a04ef29c7ea --- /dev/null +++ b/google/services/memorystore/resource_memorystore_instance.go @@ -0,0 +1,1645 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package memorystore + +import ( + "fmt" + "log" + "net/http" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "github.com/hashicorp/terraform-provider-google/google/verify" +) + +func ResourceMemorystoreInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceMemorystoreInstanceCreate, + Read: resourceMemorystoreInstanceRead, + Update: resourceMemorystoreInstanceUpdate, + Delete: resourceMemorystoreInstanceDelete, + + Importer: &schema.ResourceImporter{ + State: resourceMemorystoreInstanceImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(120 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + CustomizeDiff: customdiff.All( + tpgresource.SetLabelsDiff, + tpgresource.DefaultProviderProject, + ), + + Schema: map[string]*schema.Schema{ + "instance_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Required. The ID to use for the instance, which will become the final component of +the instance's resource name. + +This value is subject to the following restrictions: + +* Must be 4-63 characters in length +* Must begin with a letter or digit +* Must contain only lowercase letters, digits, and hyphens +* Must not end with a hyphen +* Must be unique within a location`, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Resource ID segment making up resource 'name'. It identifies the resource within its parent collection as described in https://google.aip.dev/122. See documentation for resource type 'memorystore.googleapis.com/CertificateAuthority'.`, + }, + "shard_count": { + Type: schema.TypeInt, + Required: true, + Description: `Required. Number of shards for the instance.`, + }, + "authorization_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Optional. Immutable. Authorization mode of the instance. Possible values: + AUTH_DISABLED +IAM_AUTH`, + }, + "deletion_protection_enabled": { + Type: schema.TypeBool, + Optional: true, + Description: `Optional. If set to true deletion of the instance will fail.`, + Default: true, + }, + "engine_configs": { + Type: schema.TypeMap, + Optional: true, + Description: `Optional. User-provided engine configurations for the instance.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "engine_version": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Optional. Immutable. Engine version of the instance.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Optional. Labels to represent user-provided metadata. + +**Note**: This field is non-authoritative, and will only manage the labels present in your configuration. +Please refer to the field 'effective_labels' for all of the labels present on the resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + ValidateFunc: verify.ValidateEnum([]string{"CLUSTER", "STANDALONE", ""}), + Description: `Optional. Standalone or cluster. + Possible values: + CLUSTER +STANDALONE Possible values: ["CLUSTER", "STANDALONE"]`, + }, + "node_type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Optional. Immutable. Machine type for individual nodes of the instance. + Possible values: + SHARED_CORE_NANO +HIGHMEM_MEDIUM +HIGHMEM_XLARGE +STANDARD_SMALL`, + }, + "persistence_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Represents persistence configuration for a instance.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aof_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Configuration for AOF based persistence.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "append_fsync": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Optional. The fsync mode. + Possible values: + NEVER +EVERY_SEC +ALWAYS`, + }, + }, + }, + }, + "mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: verify.ValidateEnum([]string{"DISABLED", "RDB", "AOF", ""}), + Description: `Optional. Current persistence mode. + Possible values: +DISABLED +RDB +AOF Possible values: ["DISABLED", "RDB", "AOF"]`, + }, + "rdb_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Configuration for RDB based persistence.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rdb_snapshot_period": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Optional. Period between RDB snapshots. + Possible values: + ONE_HOUR +SIX_HOURS +TWELVE_HOURS +TWENTY_FOUR_HOURS`, + }, + "rdb_snapshot_start_time": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Optional. Time that the first snapshot was/will be attempted, and to which future +snapshots will be aligned. If not provided, the current time will be +used.`, + }, + }, + }, + }, + }, + }, + }, + "replica_count": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: `Optional. Number of replica nodes per shard. If omitted the default is 0 replicas.`, + }, + "transit_encryption_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Optional. Immutable. In-transit encryption mode of the instance. + Possible values: + TRANSIT_ENCRYPTION_DISABLED +SERVER_AUTHENTICATION`, + }, + "zone_distribution_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Zone distribution configuration for allocation of instance resources.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: verify.ValidateEnum([]string{"MULTI_ZONE", "SINGLE_ZONE", ""}), + Description: `Optional. Current zone distribution mode. Defaults to MULTI_ZONE. + Possible values: + MULTI_ZONE +SINGLE_ZONE Possible values: ["MULTI_ZONE", "SINGLE_ZONE"]`, + }, + "zone": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Optional. Defines zone where all resources will be allocated with SINGLE_ZONE mode. +Ignored for MULTI_ZONE mode.`, + }, + }, + }, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Creation timestamp of the instance.`, + }, + "discovery_endpoints": { + Type: schema.TypeList, + Computed: true, + Description: `Output only. Endpoints clients can connect to the instance through. Currently only one +discovery endpoint is supported.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "address": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. IP address of the exposed endpoint clients connect to.`, + }, + "network": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The network where the IP address of the discovery endpoint will be +reserved, in the form of +projects/{network_project}/global/networks/{network_id}.`, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + Description: `Output only. The port number of the exposed endpoint.`, + }, + }, + }, + }, + "effective_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `All of labels (key/value pairs) present on the resource in GCP, including the labels configured through Terraform, other clients and services.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "endpoints": { + Type: schema.TypeList, + Computed: true, + Description: `Endpoints for the instance.`, + Elem: &schema.Schema{ + Type: schema.TypeList, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `Identifier. Unique name of the instance. +Format: projects/{project}/locations/{location}/instances/{instance}`, + }, + "node_config": { + Type: schema.TypeList, + Computed: true, + Description: `Represents configuration for nodes of the instance.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "size_gb": { + Type: schema.TypeFloat, + Computed: true, + Description: `Output only. Memory size in GB of the node.`, + }, + }, + }, + }, + "psc_auto_connections": { + Type: schema.TypeList, + Computed: true, + Description: `Output only. User inputs and resource details of the auto-created PSC connections.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connection_type": { + Type: schema.TypeString, + Computed: true, + Description: `Output Only. Type of a PSC Connection. + Possible values: + CONNECTION_TYPE_DISCOVERY + CONNECTION_TYPE_PRIMARY + CONNECTION_TYPE_READER`, + }, + "forwarding_rule": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The URI of the consumer side forwarding rule. +Format: +projects/{project}/regions/{region}/forwardingRules/{forwarding_rule}`, + }, + "ip_address": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The IP allocated on the consumer network for the PSC forwarding rule.`, + }, + "network": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The consumer network where the IP address resides, in the form of +projects/{project_id}/global/networks/{network_id}.`, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + Description: `Output only. Ports of the exposed endpoint.`, + }, + "project_id": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The consumer project_id where the forwarding rule is created from.`, + }, + "psc_connection_id": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The PSC connection id of the forwarding rule connected to the +service attachment.`, + }, + "psc_connection_status": { + Type: schema.TypeString, + Computed: true, + Description: `Output Only. The status of the PSC connection: whether a connection exists and ACTIVE or it no longer exists. + Possible values: + ACTIVE + NOT_FOUND`, + }, + "service_attachment": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The service attachment which is the target of the PSC connection, in the form of projects/{project-id}/regions/{region}/serviceAttachments/{service-attachment-id}.`, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Current state of the instance. + Possible values: + CREATING +ACTIVE +UPDATING +DELETING`, + }, + "state_info": { + Type: schema.TypeList, + Computed: true, + Description: `Additional information about the state of the instance.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "update_info": { + Type: schema.TypeList, + Computed: true, + Description: `Represents information about instance with state UPDATING.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "target_replica_count": { + Type: schema.TypeInt, + Computed: true, + Description: `Output only. Target number of replica nodes per shard for the instance.`, + }, + "target_shard_count": { + Type: schema.TypeInt, + Computed: true, + Description: `Output only. Target number of shards for the instance.`, + }, + }, + }, + }, + }, + }, + }, + "terraform_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `The combination of labels configured directly on the resource + and default labels configured on the provider.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "uid": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. System assigned, unique identifier for the instance.`, + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Latest update timestamp of the instance.`, + }, + "desired_psc_auto_connections": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Description: `Required. Immutable. User inputs for the auto-created PSC connections.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "network": { + Type: schema.TypeString, + Required: true, + Description: `Required. The consumer network where the IP address resides, in the form of +projects/{project_id}/global/networks/{network_id}.`, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + Description: `Required. The consumer project_id where the forwarding rule is created from.`, + }, + }, + }, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceMemorystoreInstanceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + replicaCountProp, err := expandMemorystoreInstanceReplicaCount(d.Get("replica_count"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("replica_count"); !tpgresource.IsEmptyValue(reflect.ValueOf(replicaCountProp)) && (ok || !reflect.DeepEqual(v, replicaCountProp)) { + obj["replicaCount"] = replicaCountProp + } + authorizationModeProp, err := expandMemorystoreInstanceAuthorizationMode(d.Get("authorization_mode"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("authorization_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(authorizationModeProp)) && (ok || !reflect.DeepEqual(v, authorizationModeProp)) { + obj["authorizationMode"] = authorizationModeProp + } + transitEncryptionModeProp, err := expandMemorystoreInstanceTransitEncryptionMode(d.Get("transit_encryption_mode"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("transit_encryption_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(transitEncryptionModeProp)) && (ok || !reflect.DeepEqual(v, transitEncryptionModeProp)) { + obj["transitEncryptionMode"] = transitEncryptionModeProp + } + shardCountProp, err := expandMemorystoreInstanceShardCount(d.Get("shard_count"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("shard_count"); !tpgresource.IsEmptyValue(reflect.ValueOf(shardCountProp)) && (ok || !reflect.DeepEqual(v, shardCountProp)) { + obj["shardCount"] = shardCountProp + } + nodeTypeProp, err := expandMemorystoreInstanceNodeType(d.Get("node_type"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("node_type"); !tpgresource.IsEmptyValue(reflect.ValueOf(nodeTypeProp)) && (ok || !reflect.DeepEqual(v, nodeTypeProp)) { + obj["nodeType"] = nodeTypeProp + } + persistenceConfigProp, err := expandMemorystoreInstancePersistenceConfig(d.Get("persistence_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("persistence_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(persistenceConfigProp)) && (ok || !reflect.DeepEqual(v, persistenceConfigProp)) { + obj["persistenceConfig"] = persistenceConfigProp + } + engineVersionProp, err := expandMemorystoreInstanceEngineVersion(d.Get("engine_version"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("engine_version"); !tpgresource.IsEmptyValue(reflect.ValueOf(engineVersionProp)) && (ok || !reflect.DeepEqual(v, engineVersionProp)) { + obj["engineVersion"] = engineVersionProp + } + engineConfigsProp, err := expandMemorystoreInstanceEngineConfigs(d.Get("engine_configs"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("engine_configs"); !tpgresource.IsEmptyValue(reflect.ValueOf(engineConfigsProp)) && (ok || !reflect.DeepEqual(v, engineConfigsProp)) { + obj["engineConfigs"] = engineConfigsProp + } + zoneDistributionConfigProp, err := expandMemorystoreInstanceZoneDistributionConfig(d.Get("zone_distribution_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("zone_distribution_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(zoneDistributionConfigProp)) && (ok || !reflect.DeepEqual(v, zoneDistributionConfigProp)) { + obj["zoneDistributionConfig"] = zoneDistributionConfigProp + } + deletionProtectionEnabledProp, err := expandMemorystoreInstanceDeletionProtectionEnabled(d.Get("deletion_protection_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("deletion_protection_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(deletionProtectionEnabledProp)) && (ok || !reflect.DeepEqual(v, deletionProtectionEnabledProp)) { + obj["deletionProtectionEnabled"] = deletionProtectionEnabledProp + } + modeProp, err := expandMemorystoreInstanceMode(d.Get("mode"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(modeProp)) && (ok || !reflect.DeepEqual(v, modeProp)) { + obj["mode"] = modeProp + } + labelsProp, err := expandMemorystoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("effective_labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + obj, err = resourceMemorystoreInstanceEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{MemorystoreBasePath}}projects/{{project}}/locations/{{location}}/instances?instanceId={{instance_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Instance: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + Headers: headers, + }) + if err != nil { + return fmt.Errorf("Error creating Instance: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = MemorystoreOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Instance", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create Instance: %s", err) + } + + opRes, err = resourceMemorystoreInstanceDecoder(d, meta, opRes) + if err != nil { + return fmt.Errorf("Error decoding response from operation: %s", err) + } + if opRes == nil { + return fmt.Errorf("Error decoding response from operation, could not find object") + } + + if err := d.Set("name", flattenMemorystoreInstanceName(opRes["name"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Instance %q: %#v", d.Id(), res) + + return resourceMemorystoreInstanceRead(d, meta) +} + +func resourceMemorystoreInstanceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{MemorystoreBasePath}}projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Headers: headers, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("MemorystoreInstance %q", d.Id())) + } + + res, err = resourceMemorystoreInstanceDecoder(d, meta, res) + if err != nil { + return err + } + + if res == nil { + // Decoding the object has resulted in it being gone. It may be marked deleted + log.Printf("[DEBUG] Removing MemorystoreInstance because it no longer exists.") + d.SetId("") + return nil + } + + // Explicitly set virtual fields to default values if unset + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + + if err := d.Set("name", flattenMemorystoreInstanceName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("create_time", flattenMemorystoreInstanceCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("update_time", flattenMemorystoreInstanceUpdateTime(res["updateTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("labels", flattenMemorystoreInstanceLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("state", flattenMemorystoreInstanceState(res["state"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("state_info", flattenMemorystoreInstanceStateInfo(res["stateInfo"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("uid", flattenMemorystoreInstanceUid(res["uid"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("replica_count", flattenMemorystoreInstanceReplicaCount(res["replicaCount"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("authorization_mode", flattenMemorystoreInstanceAuthorizationMode(res["authorizationMode"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("transit_encryption_mode", flattenMemorystoreInstanceTransitEncryptionMode(res["transitEncryptionMode"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("shard_count", flattenMemorystoreInstanceShardCount(res["shardCount"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("discovery_endpoints", flattenMemorystoreInstanceDiscoveryEndpoints(res["discoveryEndpoints"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("node_type", flattenMemorystoreInstanceNodeType(res["nodeType"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("persistence_config", flattenMemorystoreInstancePersistenceConfig(res["persistenceConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("engine_version", flattenMemorystoreInstanceEngineVersion(res["engineVersion"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("engine_configs", flattenMemorystoreInstanceEngineConfigs(res["engineConfigs"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("node_config", flattenMemorystoreInstanceNodeConfig(res["nodeConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("zone_distribution_config", flattenMemorystoreInstanceZoneDistributionConfig(res["zoneDistributionConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("deletion_protection_enabled", flattenMemorystoreInstanceDeletionProtectionEnabled(res["deletionProtectionEnabled"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("endpoints", flattenMemorystoreInstanceEndpoints(res["endpoints"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("mode", flattenMemorystoreInstanceMode(res["mode"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("psc_auto_connections", flattenMemorystoreInstancePscAutoConnections(res["pscAutoConnections"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("terraform_labels", flattenMemorystoreInstanceTerraformLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("effective_labels", flattenMemorystoreInstanceEffectiveLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + + return nil +} + +func resourceMemorystoreInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + replicaCountProp, err := expandMemorystoreInstanceReplicaCount(d.Get("replica_count"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("replica_count"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, replicaCountProp)) { + obj["replicaCount"] = replicaCountProp + } + shardCountProp, err := expandMemorystoreInstanceShardCount(d.Get("shard_count"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("shard_count"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, shardCountProp)) { + obj["shardCount"] = shardCountProp + } + persistenceConfigProp, err := expandMemorystoreInstancePersistenceConfig(d.Get("persistence_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("persistence_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, persistenceConfigProp)) { + obj["persistenceConfig"] = persistenceConfigProp + } + engineConfigsProp, err := expandMemorystoreInstanceEngineConfigs(d.Get("engine_configs"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("engine_configs"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, engineConfigsProp)) { + obj["engineConfigs"] = engineConfigsProp + } + deletionProtectionEnabledProp, err := expandMemorystoreInstanceDeletionProtectionEnabled(d.Get("deletion_protection_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("deletion_protection_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, deletionProtectionEnabledProp)) { + obj["deletionProtectionEnabled"] = deletionProtectionEnabledProp + } + labelsProp, err := expandMemorystoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("effective_labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + obj, err = resourceMemorystoreInstanceEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{MemorystoreBasePath}}projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Instance %q: %#v", d.Id(), obj) + headers := make(http.Header) + updateMask := []string{} + + if d.HasChange("replica_count") { + updateMask = append(updateMask, "replicaCount") + } + + if d.HasChange("shard_count") { + updateMask = append(updateMask, "shardCount") + } + + if d.HasChange("persistence_config") { + updateMask = append(updateMask, "persistenceConfig") + } + + if d.HasChange("engine_configs") { + updateMask = append(updateMask, "engineConfigs") + } + + if d.HasChange("deletion_protection_enabled") { + updateMask = append(updateMask, "deletionProtectionEnabled") + } + + if d.HasChange("effective_labels") { + updateMask = append(updateMask, "labels") + } + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + // if updateMask is empty we are not updating anything so skip the post + if len(updateMask) > 0 { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + Headers: headers, + }) + + if err != nil { + return fmt.Errorf("Error updating Instance %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Instance %q: %#v", d.Id(), res) + } + + err = MemorystoreOperationWaitTime( + config, res, project, "Updating Instance", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + } + + return resourceMemorystoreInstanceRead(d, meta) +} + +func resourceMemorystoreInstanceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{MemorystoreBasePath}}projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + headers := make(http.Header) + + log.Printf("[DEBUG] Deleting Instance %q", d.Id()) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + Headers: headers, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Instance") + } + + err = MemorystoreOperationWaitTime( + config, res, project, "Deleting Instance", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Instance %q: %#v", d.Id(), res) + return nil +} + +func resourceMemorystoreInstanceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^projects/(?P[^/]+)/locations/(?P[^/]+)/instances/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Explicitly set virtual fields to default values on import + + return []*schema.ResourceData{d}, nil +} + +func flattenMemorystoreInstanceName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceUpdateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + + transformed := make(map[string]interface{}) + if l, ok := d.GetOkExists("labels"); ok { + for k := range l.(map[string]interface{}) { + transformed[k] = v.(map[string]interface{})[k] + } + } + + return transformed +} + +func flattenMemorystoreInstanceState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceStateInfo(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["update_info"] = + flattenMemorystoreInstanceStateInfoUpdateInfo(original["updateInfo"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstanceStateInfoUpdateInfo(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["target_shard_count"] = + flattenMemorystoreInstanceStateInfoUpdateInfoTargetShardCount(original["targetShardCount"], d, config) + transformed["target_replica_count"] = + flattenMemorystoreInstanceStateInfoUpdateInfoTargetReplicaCount(original["targetReplicaCount"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstanceStateInfoUpdateInfoTargetShardCount(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceStateInfoUpdateInfoTargetReplicaCount(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceUid(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceReplicaCount(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceAuthorizationMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceTransitEncryptionMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceShardCount(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceDiscoveryEndpoints(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "address": flattenMemorystoreInstanceDiscoveryEndpointsAddress(original["address"], d, config), + "port": flattenMemorystoreInstanceDiscoveryEndpointsPort(original["port"], d, config), + "network": flattenMemorystoreInstanceDiscoveryEndpointsNetwork(original["network"], d, config), + }) + } + return transformed +} +func flattenMemorystoreInstanceDiscoveryEndpointsAddress(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceDiscoveryEndpointsPort(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceDiscoveryEndpointsNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceNodeType(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePersistenceConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["mode"] = + flattenMemorystoreInstancePersistenceConfigMode(original["mode"], d, config) + transformed["rdb_config"] = + flattenMemorystoreInstancePersistenceConfigRdbConfig(original["rdbConfig"], d, config) + transformed["aof_config"] = + flattenMemorystoreInstancePersistenceConfigAofConfig(original["aofConfig"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstancePersistenceConfigMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePersistenceConfigRdbConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["rdb_snapshot_period"] = + flattenMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotPeriod(original["rdbSnapshotPeriod"], d, config) + transformed["rdb_snapshot_start_time"] = + flattenMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotStartTime(original["rdbSnapshotStartTime"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotPeriod(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotStartTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePersistenceConfigAofConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["append_fsync"] = + flattenMemorystoreInstancePersistenceConfigAofConfigAppendFsync(original["appendFsync"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstancePersistenceConfigAofConfigAppendFsync(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceEngineVersion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceEngineConfigs(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceNodeConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["size_gb"] = + flattenMemorystoreInstanceNodeConfigSizeGb(original["sizeGb"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstanceNodeConfigSizeGb(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceZoneDistributionConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["zone"] = + flattenMemorystoreInstanceZoneDistributionConfigZone(original["zone"], d, config) + transformed["mode"] = + flattenMemorystoreInstanceZoneDistributionConfigMode(original["mode"], d, config) + return []interface{}{transformed} +} +func flattenMemorystoreInstanceZoneDistributionConfigZone(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceZoneDistributionConfigMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceDeletionProtectionEnabled(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceEndpoints(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstanceMode(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnections(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "psc_connection_id": flattenMemorystoreInstancePscAutoConnectionsPscConnectionId(original["pscConnectionId"], d, config), + "ip_address": flattenMemorystoreInstancePscAutoConnectionsIpAddress(original["ipAddress"], d, config), + "forwarding_rule": flattenMemorystoreInstancePscAutoConnectionsForwardingRule(original["forwardingRule"], d, config), + "project_id": flattenMemorystoreInstancePscAutoConnectionsProjectId(original["projectId"], d, config), + "network": flattenMemorystoreInstancePscAutoConnectionsNetwork(original["network"], d, config), + "service_attachment": flattenMemorystoreInstancePscAutoConnectionsServiceAttachment(original["serviceAttachment"], d, config), + "psc_connection_status": flattenMemorystoreInstancePscAutoConnectionsPscConnectionStatus(original["pscConnectionStatus"], d, config), + "connection_type": flattenMemorystoreInstancePscAutoConnectionsConnectionType(original["connectionType"], d, config), + "port": flattenMemorystoreInstancePscAutoConnectionsPort(original["port"], d, config), + }) + } + return transformed +} +func flattenMemorystoreInstancePscAutoConnectionsPscConnectionId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsIpAddress(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsForwardingRule(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsProjectId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsServiceAttachment(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsPscConnectionStatus(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsConnectionType(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenMemorystoreInstancePscAutoConnectionsPort(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenMemorystoreInstanceTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + + transformed := make(map[string]interface{}) + if l, ok := d.GetOkExists("terraform_labels"); ok { + for k := range l.(map[string]interface{}) { + transformed[k] = v.(map[string]interface{})[k] + } + } + + return transformed +} + +func flattenMemorystoreInstanceEffectiveLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandMemorystoreInstanceReplicaCount(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceAuthorizationMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceTransitEncryptionMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceShardCount(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceNodeType(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstancePersistenceConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedMode, err := expandMemorystoreInstancePersistenceConfigMode(original["mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMode); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["mode"] = transformedMode + } + + transformedRdbConfig, err := expandMemorystoreInstancePersistenceConfigRdbConfig(original["rdb_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRdbConfig); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["rdbConfig"] = transformedRdbConfig + } + + transformedAofConfig, err := expandMemorystoreInstancePersistenceConfigAofConfig(original["aof_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAofConfig); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["aofConfig"] = transformedAofConfig + } + + return transformed, nil +} + +func expandMemorystoreInstancePersistenceConfigMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstancePersistenceConfigRdbConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedRdbSnapshotPeriod, err := expandMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotPeriod(original["rdb_snapshot_period"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRdbSnapshotPeriod); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["rdbSnapshotPeriod"] = transformedRdbSnapshotPeriod + } + + transformedRdbSnapshotStartTime, err := expandMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotStartTime(original["rdb_snapshot_start_time"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRdbSnapshotStartTime); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["rdbSnapshotStartTime"] = transformedRdbSnapshotStartTime + } + + return transformed, nil +} + +func expandMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotPeriod(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstancePersistenceConfigRdbConfigRdbSnapshotStartTime(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstancePersistenceConfigAofConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAppendFsync, err := expandMemorystoreInstancePersistenceConfigAofConfigAppendFsync(original["append_fsync"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAppendFsync); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["appendFsync"] = transformedAppendFsync + } + + return transformed, nil +} + +func expandMemorystoreInstancePersistenceConfigAofConfigAppendFsync(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceEngineVersion(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceEngineConfigs(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandMemorystoreInstanceZoneDistributionConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedZone, err := expandMemorystoreInstanceZoneDistributionConfigZone(original["zone"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedZone); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["zone"] = transformedZone + } + + transformedMode, err := expandMemorystoreInstanceZoneDistributionConfigMode(original["mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMode); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["mode"] = transformedMode + } + + return transformed, nil +} + +func expandMemorystoreInstanceZoneDistributionConfigZone(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceZoneDistributionConfigMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceDeletionProtectionEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceMode(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandMemorystoreInstanceEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func resourceMemorystoreInstanceEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + v, ok := d.GetOk("desired_psc_auto_connections") + if !ok { + return obj, nil // No desired connections, nothing to update + } + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + desiredConnection := raw.(map[string]interface{}) + connectionReq := make(map[string]interface{}) + + projectId := desiredConnection["project_id"] + if val := reflect.ValueOf(projectId); val.IsValid() && !tpgresource.IsEmptyValue(val) { + connectionReq["projectId"] = projectId + } + + network := desiredConnection["network"] + if val := reflect.ValueOf(network); val.IsValid() && !tpgresource.IsEmptyValue(val) { + connectionReq["network"] = network + } + + req = append(req, connectionReq) + } + obj["pscAutoConnections"] = req + return obj, nil +} + +func resourceMemorystoreInstanceDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { + // Retrieve pscAutoConnections from API response + v, ok := res["pscAutoConnections"] + if !ok { + return nil, fmt.Errorf("pscAutoConnections field not found in API response") + } + + connections, ok := v.([]interface{}) + if !ok { + return nil, fmt.Errorf("pscAutoConnections is not an array") + } + + transformed := make([]interface{}, 0, len(connections)) + uniqueConnections := make(map[string]bool) // Track unique project+network combos + + for _, raw := range connections { + connectionData, ok := raw.(map[string]interface{}) + if !ok || len(connectionData) < 1 { + return nil, fmt.Errorf("Invalid or empty psc connection data: %v", raw) + } + + projectID, ok := connectionData["projectId"].(string) + if !ok { + return nil, fmt.Errorf("invalid project ID in psc connection: %v", connectionData) + } + + networkID, ok := connectionData["network"].(string) + if !ok { + return nil, fmt.Errorf("invalid network ID in psc connection: %v", connectionData) + } + + uniqueKey := projectID + networkID + if !uniqueConnections[uniqueKey] { // Check for uniqueness + uniqueConnections[uniqueKey] = true + transformed = append(transformed, map[string]interface{}{ + "project_id": projectID, + "network": networkID, + }) + } + } + + d.Set("desired_psc_auto_connections", transformed) + return res, nil +} diff --git a/google/services/memorystore/resource_memorystore_instance_generated_test.go b/google/services/memorystore/resource_memorystore_instance_generated_test.go new file mode 100644 index 00000000000..61e82ae41a0 --- /dev/null +++ b/google/services/memorystore/resource_memorystore_instance_generated_test.go @@ -0,0 +1,320 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package memorystore_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func TestAccMemorystoreInstance_memorystoreInstanceBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "prevent_destroy": false, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccMemorystoreInstance_memorystoreInstanceBasicExample(context), + }, + { + ResourceName: "google_memorystore_instance.instance-basic", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"instance_id", "labels", "location", "terraform_labels"}, + }, + }, + }) +} + +func testAccMemorystoreInstance_memorystoreInstanceBasicExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_memorystore_instance" "instance-basic" { + instance_id = "tf-test-basic-instance%{random_suffix}" + shard_count = 3 + desired_psc_auto_connections { + network = google_compute_network.producer_net.id + project_id = data.google_project.project.project_id + } + location = "us-central1" + deletion_protection_enabled = false + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + + lifecycle { + prevent_destroy = "%{prevent_destroy}" + } +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "tf-test-my-policy%{random_suffix}" + location = "us-central1" + service_class = "gcp-memorystore" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "tf-test-my-subnet%{random_suffix}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "tf-test-my-network%{random_suffix}" + auto_create_subnetworks = false +} + +data "google_project" "project" { +} +`, context) +} + +func TestAccMemorystoreInstance_memorystoreInstanceFullExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "prevent_destroy": false, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccMemorystoreInstance_memorystoreInstanceFullExample(context), + }, + { + ResourceName: "google_memorystore_instance.instance-full", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"instance_id", "labels", "location", "terraform_labels"}, + }, + }, + }) +} + +func testAccMemorystoreInstance_memorystoreInstanceFullExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_memorystore_instance" "instance-full" { + instance_id = "tf-test-full-instance%{random_suffix}" + shard_count = 3 + desired_psc_auto_connections { + network = google_compute_network.producer_net.id + project_id = data.google_project.project.project_id + } + location = "us-central1" + replica_count = 2 + node_type = "SHARED_CORE_NANO" + transit_encryption_mode = "TRANSIT_ENCRYPTION_DISABLED" + authorization_mode = "AUTH_DISABLED" + engine_configs = { + maxmemory-policy = "volatile-ttl" + } + zone_distribution_config { + mode = "SINGLE_ZONE" + zone = "us-central1-b" + } + engine_version = "VALKEY_7_2" + deletion_protection_enabled = false + mode = "CLUSTER" + persistence_config { + mode = "RDB" + rdb_config { + rdb_snapshot_period = "ONE_HOUR" + rdb_snapshot_start_time = "2024-10-02T15:01:23Z" + } + } + labels = { + "abc" : "xyz" + } + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + + lifecycle { + prevent_destroy = "%{prevent_destroy}" + } +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "tf-test-my-policy%{random_suffix}" + location = "us-central1" + service_class = "gcp-memorystore" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "tf-test-my-subnet%{random_suffix}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "tf-test-my-network%{random_suffix}" + auto_create_subnetworks = false +} + +data "google_project" "project" { +} +`, context) +} + +func TestAccMemorystoreInstance_memorystoreInstancePersistenceAofExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "prevent_destroy": false, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccMemorystoreInstance_memorystoreInstancePersistenceAofExample(context), + }, + { + ResourceName: "google_memorystore_instance.instance-persistence-aof", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"instance_id", "labels", "location", "terraform_labels"}, + }, + }, + }) +} + +func testAccMemorystoreInstance_memorystoreInstancePersistenceAofExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_memorystore_instance" "instance-persistence-aof" { + instance_id = "tf-test-aof-instance%{random_suffix}" + shard_count = 3 + desired_psc_auto_connections { + network = google_compute_network.producer_net.id + project_id = data.google_project.project.project_id + } + location = "us-central1" + persistence_config { + mode = "AOF" + aof_config { + append_fsync = "EVERY_SEC" + } + } + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + deletion_protection_enabled = false + lifecycle { + prevent_destroy = "%{prevent_destroy}" + } +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "tf-test-my-policy%{random_suffix}" + location = "us-central1" + service_class = "gcp-memorystore" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "tf-test-my-subnet%{random_suffix}" + ip_cidr_range = "10.0.0.248/29" + region = "us-central1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "tf-test-my-network%{random_suffix}" + auto_create_subnetworks = false +} + +data "google_project" "project" { +} +`, context) +} + +func testAccCheckMemorystoreInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_memorystore_instance" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{MemorystoreBasePath}}projects/{{project}}/locations/{{location}}/instances/{{instance_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: config.UserAgent, + }) + if err == nil { + return fmt.Errorf("MemorystoreInstance still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/services/memorystore/resource_memorystore_instance_sweeper.go b/google/services/memorystore/resource_memorystore_instance_sweeper.go new file mode 100644 index 00000000000..22b16b2a0c5 --- /dev/null +++ b/google/services/memorystore/resource_memorystore_instance_sweeper.go @@ -0,0 +1,143 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package memorystore + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/sweeper" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func init() { + sweeper.AddTestSweepers("MemorystoreInstance", testSweepMemorystoreInstance) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepMemorystoreInstance(region string) error { + resourceName := "MemorystoreInstance" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sweeper.SharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := envvar.GetTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &tpgresource.ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://memorystore.googleapis.com/v1/projects/{{project}}/locations/{{location}}/instances", "?")[0] + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["instances"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + var name string + // Id detected in the delete URL, attempt to use id. + if obj["id"] != nil { + name = tpgresource.GetResourceNameFromSelfLink(obj["id"].(string)) + } else if obj["name"] != nil { + name = tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) + } else { + log.Printf("[INFO][SWEEPER_LOG] %s resource name and id were nil", resourceName) + return nil + } + // Skip resources that shouldn't be sweeped + if !sweeper.IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://memorystore.googleapis.com/v1/projects/{{project}}/locations/{{location}}/instances/{{instance_id}}" + deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: config.Project, + RawURL: deleteUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/services/memorystore/resource_memorystore_instance_test.go b/google/services/memorystore/resource_memorystore_instance_test.go index 61d164b4963..0b7112d81e7 100644 --- a/google/services/memorystore/resource_memorystore_instance_test.go +++ b/google/services/memorystore/resource_memorystore_instance_test.go @@ -1,3 +1,320 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package memorystore_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +// Validate that replica count is updated for the instance +func TestAccMemorystoreInstance_updateReplicaCount(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create instance with replica count 1 + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 1, shardCount: 3, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // update replica count to 2 + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 2, shardCount: 3, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // clean up the resource + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 2, shardCount: 3, preventDestroy: false, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + }, + }) +} + +// Validate that shard count is updated for the cluster +func TestAccMemorystoreInstance_updateShardCount(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create cluster with shard count 3 + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 1, shardCount: 3, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // update shard count to 5 + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 1, shardCount: 5, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // clean up the resource + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 1, shardCount: 5, preventDestroy: false, zoneDistributionMode: "MULTI_ZONE", deletionProtectionEnabled: false}), + }, + }, + }) +} + +// Validate that engineConfigs is updated for the cluster +func TestAccMemorystoreInstance_updateRedisConfigs(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create cluster + Config: createOrUpdateMemorystoreInstance(&InstanceParams{ + name: name, + shardCount: 3, + zoneDistributionMode: "MULTI_ZONE", + engineConfigs: map[string]string{ + "maxmemory-policy": "volatile-ttl", + }, + deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // add a new redis config key-value pair and update existing redis config + Config: createOrUpdateMemorystoreInstance(&InstanceParams{ + name: name, + shardCount: 3, + zoneDistributionMode: "MULTI_ZONE", + engineConfigs: map[string]string{ + "maxmemory-policy": "allkeys-lru", + "maxmemory-clients": "90%", + }, + deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // remove all redis configs + Config: createOrUpdateMemorystoreInstance(&InstanceParams{ + name: name, + shardCount: 3, + zoneDistributionMode: "MULTI_ZONE", + engineConfigs: map[string]string{ + "maxmemory-policy": "allkeys-lru", + "maxmemory-clients": "90%", + }, + deletionProtectionEnabled: false}), + }, + }, + }) +} + +// Validate that deletion protection is updated for the cluster +func TestAccMemorystoreInstance_updateDeletionProtection(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create cluster with deletion protection true + Config: createOrUpdateMemorystoreInstance(&InstanceParams{ + name: name, + shardCount: 3, + zoneDistributionMode: "MULTI_ZONE", + deletionProtectionEnabled: true, + }), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // update cluster with deletion protection false + Config: createOrUpdateMemorystoreInstance(&InstanceParams{ + name: name, + shardCount: 3, + zoneDistributionMode: "MULTI_ZONE", + deletionProtectionEnabled: false, + }), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Validate that persistence config is updated for the cluster +func TestAccMemorystoreInstance_updatePersistence(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckMemorystoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + // create instance with AOF enabled + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 0, shardCount: 3, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", persistenceMode: "AOF", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // update persitence to RDB + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 0, shardCount: 3, preventDestroy: true, zoneDistributionMode: "MULTI_ZONE", persistenceMode: "RDB", deletionProtectionEnabled: false}), + }, + { + ResourceName: "google_memorystore_instance.test", + ImportState: true, + ImportStateVerify: true, + }, + { + // clean up the resource + Config: createOrUpdateMemorystoreInstance(&InstanceParams{name: name, replicaCount: 0, shardCount: 3, preventDestroy: false, zoneDistributionMode: "MULTI_ZONE", persistenceMode: "RDB", deletionProtectionEnabled: false}), + }, + }, + }) +} + +type InstanceParams struct { + name string + replicaCount int + shardCount int + preventDestroy bool + nodeType string + engineConfigs map[string]string + zoneDistributionMode string + zone string + deletionProtectionEnabled bool + persistenceMode string +} + +func createOrUpdateMemorystoreInstance(params *InstanceParams) string { + lifecycleBlock := "" + if params.preventDestroy { + lifecycleBlock = ` + lifecycle { + prevent_destroy = true + }` + } + var strBuilder strings.Builder + for key, value := range params.engineConfigs { + strBuilder.WriteString(fmt.Sprintf("%s = \"%s\"\n", key, value)) + } + + zoneDistributionConfigBlock := `` + if params.zoneDistributionMode != "" { + zoneDistributionConfigBlock = fmt.Sprintf(` + zone_distribution_config { + mode = "%s" + zone = "%s" + } + `, params.zoneDistributionMode, params.zone) + } + persistenceBlock := `` + if params.persistenceMode != "" { + persistenceBlock = fmt.Sprintf(` + persistence_config { + mode = "%s" + } + `, params.persistenceMode) + } + return fmt.Sprintf(` +resource "google_memorystore_instance" "test" { + instance_id = "%s" + replica_count = %d + shard_count = %d + node_type = "%s" + location = "europe-west1" + desired_psc_auto_connections { + network = google_compute_network.producer_net.id + project_id = data.google_project.project.project_id + } + deletion_protection_enabled = %t + engine_configs = { + %s + } + %s + %s + depends_on = [ + google_network_connectivity_service_connection_policy.default + ] + %s +} + +resource "google_network_connectivity_service_connection_policy" "default" { + name = "%s" + location = "europe-west1" + service_class = "gcp-memorystore" + description = "my basic service connection policy" + network = google_compute_network.producer_net.id + psc_config { + subnetworks = [google_compute_subnetwork.producer_subnet.id] + } +} + +resource "google_compute_subnetwork" "producer_subnet" { + name = "%s" + ip_cidr_range = "10.0.0.248/29" + region = "europe-west1" + network = google_compute_network.producer_net.id +} + +resource "google_compute_network" "producer_net" { + name = "%s" + auto_create_subnetworks = false +} + +data "google_project" "project" { +} +`, params.name, params.replicaCount, params.shardCount, params.nodeType, params.deletionProtectionEnabled, strBuilder.String(), zoneDistributionConfigBlock, persistenceBlock, lifecycleBlock, params.name, params.name, params.name) +} diff --git a/google/sweeper/gcp_sweeper_test.go b/google/sweeper/gcp_sweeper_test.go index 10dd4dddf08..cf47586edea 100644 --- a/google/sweeper/gcp_sweeper_test.go +++ b/google/sweeper/gcp_sweeper_test.go @@ -84,6 +84,7 @@ import ( _ "github.com/hashicorp/terraform-provider-google/google/services/logging" _ "github.com/hashicorp/terraform-provider-google/google/services/looker" _ "github.com/hashicorp/terraform-provider-google/google/services/memcache" + _ "github.com/hashicorp/terraform-provider-google/google/services/memorystore" _ "github.com/hashicorp/terraform-provider-google/google/services/migrationcenter" _ "github.com/hashicorp/terraform-provider-google/google/services/mlengine" _ "github.com/hashicorp/terraform-provider-google/google/services/monitoring" diff --git a/google/transport/config.go b/google/transport/config.go index 6738c6191cd..101bf2f34f4 100644 --- a/google/transport/config.go +++ b/google/transport/config.go @@ -266,6 +266,7 @@ type Config struct { LoggingBasePath string LookerBasePath string MemcacheBasePath string + MemorystoreBasePath string MigrationCenterBasePath string MLEngineBasePath string MonitoringBasePath string @@ -408,6 +409,7 @@ const KMSBasePathKey = "KMS" const LoggingBasePathKey = "Logging" const LookerBasePathKey = "Looker" const MemcacheBasePathKey = "Memcache" +const MemorystoreBasePathKey = "Memorystore" const MigrationCenterBasePathKey = "MigrationCenter" const MLEngineBasePathKey = "MLEngine" const MonitoringBasePathKey = "Monitoring" @@ -544,6 +546,7 @@ var DefaultBasePaths = map[string]string{ LoggingBasePathKey: "https://logging.googleapis.com/v2/", LookerBasePathKey: "https://looker.googleapis.com/v1/", MemcacheBasePathKey: "https://memcache.googleapis.com/v1/", + MemorystoreBasePathKey: "https://memorystore.googleapis.com/v1/", MigrationCenterBasePathKey: "https://migrationcenter.googleapis.com/v1/", MLEngineBasePathKey: "https://ml.googleapis.com/v1/", MonitoringBasePathKey: "https://monitoring.googleapis.com/", @@ -1063,6 +1066,11 @@ func SetEndpointDefaults(d *schema.ResourceData) error { "GOOGLE_MEMCACHE_CUSTOM_ENDPOINT", }, DefaultBasePaths[MemcacheBasePathKey])) } + if d.Get("memorystore_custom_endpoint") == "" { + d.Set("memorystore_custom_endpoint", MultiEnvDefault([]string{ + "GOOGLE_MEMORYSTORE_CUSTOM_ENDPOINT", + }, DefaultBasePaths[MemorystoreBasePathKey])) + } if d.Get("migration_center_custom_endpoint") == "" { d.Set("migration_center_custom_endpoint", MultiEnvDefault([]string{ "GOOGLE_MIGRATION_CENTER_CUSTOM_ENDPOINT", @@ -2273,6 +2281,7 @@ func ConfigureBasePaths(c *Config) { c.LoggingBasePath = DefaultBasePaths[LoggingBasePathKey] c.LookerBasePath = DefaultBasePaths[LookerBasePathKey] c.MemcacheBasePath = DefaultBasePaths[MemcacheBasePathKey] + c.MemorystoreBasePath = DefaultBasePaths[MemorystoreBasePathKey] c.MigrationCenterBasePath = DefaultBasePaths[MigrationCenterBasePathKey] c.MLEngineBasePath = DefaultBasePaths[MLEngineBasePathKey] c.MonitoringBasePath = DefaultBasePaths[MonitoringBasePathKey] diff --git a/website/docs/r/memorystore_instance.html.markdown b/website/docs/r/memorystore_instance.html.markdown index 7ab74d74605..fa83a8778b4 100644 --- a/website/docs/r/memorystore_instance.html.markdown +++ b/website/docs/r/memorystore_instance.html.markdown @@ -21,8 +21,6 @@ description: |- A Google Cloud Memorystore instance. -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources.
@@ -35,7 +33,6 @@ See [Provider Versions](https://terraform.io/docs/providers/google/guides/provid ```hcl resource "google_memorystore_instance" "instance-basic" { - provider = google-beta instance_id = "basic-instance" shard_count = 3 desired_psc_auto_connections { @@ -54,7 +51,6 @@ resource "google_memorystore_instance" "instance-basic" { } resource "google_network_connectivity_service_connection_policy" "default" { - provider = google-beta name = "my-policy" location = "us-central1" service_class = "gcp-memorystore" @@ -66,7 +62,6 @@ resource "google_network_connectivity_service_connection_policy" "default" { } resource "google_compute_subnetwork" "producer_subnet" { - provider = google-beta name = "my-subnet" ip_cidr_range = "10.0.0.248/29" region = "us-central1" @@ -74,13 +69,11 @@ resource "google_compute_subnetwork" "producer_subnet" { } resource "google_compute_network" "producer_net" { - provider = google-beta name = "my-network" auto_create_subnetworks = false } data "google_project" "project" { - provider = google-beta } ```
@@ -93,7 +86,6 @@ data "google_project" "project" { ```hcl resource "google_memorystore_instance" "instance-full" { - provider = google-beta instance_id = "full-instance" shard_count = 3 desired_psc_auto_connections { @@ -135,7 +127,6 @@ resource "google_memorystore_instance" "instance-full" { } resource "google_network_connectivity_service_connection_policy" "default" { - provider = google-beta name = "my-policy" location = "us-central1" service_class = "gcp-memorystore" @@ -147,7 +138,6 @@ resource "google_network_connectivity_service_connection_policy" "default" { } resource "google_compute_subnetwork" "producer_subnet" { - provider = google-beta name = "my-subnet" ip_cidr_range = "10.0.0.248/29" region = "us-central1" @@ -155,13 +145,11 @@ resource "google_compute_subnetwork" "producer_subnet" { } resource "google_compute_network" "producer_net" { - provider = google-beta name = "my-network" auto_create_subnetworks = false } data "google_project" "project" { - provider = google-beta } ```
@@ -174,7 +162,6 @@ data "google_project" "project" { ```hcl resource "google_memorystore_instance" "instance-persistence-aof" { - provider = google-beta instance_id = "aof-instance" shard_count = 3 desired_psc_auto_connections { @@ -198,7 +185,6 @@ resource "google_memorystore_instance" "instance-persistence-aof" { } resource "google_network_connectivity_service_connection_policy" "default" { - provider = google-beta name = "my-policy" location = "us-central1" service_class = "gcp-memorystore" @@ -210,7 +196,6 @@ resource "google_network_connectivity_service_connection_policy" "default" { } resource "google_compute_subnetwork" "producer_subnet" { - provider = google-beta name = "my-subnet" ip_cidr_range = "10.0.0.248/29" region = "us-central1" @@ -218,13 +203,11 @@ resource "google_compute_subnetwork" "producer_subnet" { } resource "google_compute_network" "producer_net" { - provider = google-beta name = "my-network" auto_create_subnetworks = false } data "google_project" "project" { - provider = google-beta } ```