blob: 54310dcf6a4ec0cddce410876f7a9eecf97d369d [file] [log] [blame]
// 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 spanner
import (
"fmt"
"log"
"net/http"
"reflect"
"regexp"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/verify"
)
func deleteSpannerBackups(d *schema.ResourceData, config *transport_tpg.Config, res map[string]interface{}, userAgent string, billingProject string) error {
var v interface{}
var ok bool
v, ok = res["backups"]
if !ok || v == nil {
return nil
}
// Iterate over the list and delete each backup.
for _, itemRaw := range v.([]interface{}) {
if itemRaw == nil {
continue
}
item := itemRaw.(map[string]interface{})
backupName := item["name"].(string)
log.Printf("[DEBUG] Found backups for resource %q: %#v)", d.Id(), item)
path := "{{SpannerBasePath}}" + backupName
url, err := tpgresource.ReplaceVars(d, config, path)
if err != nil {
return err
}
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "DELETE",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
})
if err != nil {
return err
}
}
return nil
}
func resourceSpannerInstanceVirtualUpdate(d *schema.ResourceData, resourceSchema map[string]*schema.Schema) bool {
// force_destroy is the only virtual field
if d.HasChange("force_destroy") {
for field := range resourceSchema {
if field == "force_destroy" {
continue
}
if d.HasChange(field) {
return false
}
}
return true
}
return false
}
func ResourceSpannerInstance() *schema.Resource {
return &schema.Resource{
Create: resourceSpannerInstanceCreate,
Read: resourceSpannerInstanceRead,
Update: resourceSpannerInstanceUpdate,
Delete: resourceSpannerInstanceDelete,
Importer: &schema.ResourceImporter{
State: resourceSpannerInstanceImport,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(20 * time.Minute),
Update: schema.DefaultTimeout(20 * time.Minute),
Delete: schema.DefaultTimeout(20 * time.Minute),
},
CustomizeDiff: customdiff.All(
tpgresource.SetLabelsDiff,
tpgresource.DefaultProviderProject,
),
Schema: map[string]*schema.Schema{
"config": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
Description: `The name of the instance's configuration (similar but not
quite the same as a region) which defines the geographic placement and
replication of your databases in this instance. It determines where your data
is stored. Values are typically of the form 'regional-europe-west1' , 'us-central' etc.
In order to obtain a valid list please consult the
[Configuration section of the docs](https://cloud.google.com/spanner/docs/instances).`,
},
"display_name": {
Type: schema.TypeString,
Required: true,
Description: `The descriptive name for this instance as it appears in UIs. Must be
unique per project and between 4 and 30 characters in length.`,
},
"name": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidateRegexp(`^[a-z][-a-z0-9]*[a-z0-9]$`),
Description: `A unique identifier for the instance, which cannot be changed after
the instance is created. The name must be between 6 and 30 characters
in length.
If not provided, a random string starting with 'tf-' will be selected.`,
},
"autoscaling_config": {
Type: schema.TypeList,
Optional: true,
Description: `The autoscaling configuration. Autoscaling is enabled if this field is set.
When autoscaling is enabled, num_nodes and processing_units are treated as,
OUTPUT_ONLY fields and reflect the current compute capacity allocated to
the instance.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"autoscaling_limits": {
Type: schema.TypeList,
Optional: true,
Description: `Defines scale in controls to reduce the risk of response latency
and outages due to abrupt scale-in events. Users can define the minimum and
maximum compute capacity allocated to the instance, and the autoscaler will
only scale within that range. Users can either use nodes or processing
units to specify the limits, but should use the same unit to set both the
min_limit and max_limit.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"max_nodes": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies maximum number of nodes allocated to the instance. If set, this number
should be greater than or equal to min_nodes.`,
ExactlyOneOf: []string{},
RequiredWith: []string{},
},
"max_processing_units": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies maximum number of processing units allocated to the instance.
If set, this number should be multiples of 1000 and be greater than or equal to
min_processing_units.`,
ExactlyOneOf: []string{},
},
"min_nodes": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies number of nodes allocated to the instance. If set, this number
should be greater than or equal to 1.`,
ExactlyOneOf: []string{},
RequiredWith: []string{},
},
"min_processing_units": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies minimum number of processing units allocated to the instance.
If set, this number should be multiples of 1000.`,
ExactlyOneOf: []string{},
},
},
},
},
"autoscaling_targets": {
Type: schema.TypeList,
Optional: true,
Description: `Defines scale in controls to reduce the risk of response latency
and outages due to abrupt scale-in events`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"high_priority_cpu_utilization_percent": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies the target high priority cpu utilization percentage that the autoscaler
should be trying to achieve for the instance.
This number is on a scale from 0 (no utilization) to 100 (full utilization)..`,
},
"storage_utilization_percent": {
Type: schema.TypeInt,
Optional: true,
Description: `Specifies the target storage utilization percentage that the autoscaler
should be trying to achieve for the instance.
This number is on a scale from 0 (no utilization) to 100 (full utilization).`,
},
},
},
},
},
},
ExactlyOneOf: []string{"num_nodes", "processing_units", "autoscaling_config"},
},
"labels": {
Type: schema.TypeMap,
Optional: true,
Description: `An object containing a list of "key": value pairs.
Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.
**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},
},
"num_nodes": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
Description: `The number of nodes allocated to this instance. Exactly one of either node_count or processing_units
must be present in terraform.`,
ExactlyOneOf: []string{"num_nodes", "processing_units", "autoscaling_config"},
},
"processing_units": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
Description: `The number of processing units allocated to this instance. Exactly one of processing_units
or node_count must be present in terraform.`,
ExactlyOneOf: []string{"num_nodes", "processing_units", "autoscaling_config"},
},
"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},
},
"state": {
Type: schema.TypeString,
Computed: true,
Description: `Instance status: 'CREATING' or 'READY'.`,
},
"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},
},
"force_destroy": {
Type: schema.TypeBool,
Optional: true,
Description: `When deleting a spanner instance, this boolean option will delete all backups of this instance.
This must be set to true if you created a backup manually in the console.`,
Default: false,
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
UseJSONNumber: true,
}
}
func resourceSpannerInstanceCreate(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{})
nameProp, err := expandSpannerInstanceName(d.Get("name"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
obj["name"] = nameProp
}
configProp, err := expandSpannerInstanceConfig(d.Get("config"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("config"); !tpgresource.IsEmptyValue(reflect.ValueOf(configProp)) && (ok || !reflect.DeepEqual(v, configProp)) {
obj["config"] = configProp
}
displayNameProp, err := expandSpannerInstanceDisplayName(d.Get("display_name"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) {
obj["displayName"] = displayNameProp
}
nodeCountProp, err := expandSpannerInstanceNumNodes(d.Get("num_nodes"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("num_nodes"); !tpgresource.IsEmptyValue(reflect.ValueOf(nodeCountProp)) && (ok || !reflect.DeepEqual(v, nodeCountProp)) {
obj["nodeCount"] = nodeCountProp
}
processingUnitsProp, err := expandSpannerInstanceProcessingUnits(d.Get("processing_units"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("processing_units"); !tpgresource.IsEmptyValue(reflect.ValueOf(processingUnitsProp)) && (ok || !reflect.DeepEqual(v, processingUnitsProp)) {
obj["processingUnits"] = processingUnitsProp
}
autoscalingConfigProp, err := expandSpannerInstanceAutoscalingConfig(d.Get("autoscaling_config"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("autoscaling_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(autoscalingConfigProp)) && (ok || !reflect.DeepEqual(v, autoscalingConfigProp)) {
obj["autoscalingConfig"] = autoscalingConfigProp
}
labelsProp, err := expandSpannerInstanceEffectiveLabels(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 = resourceSpannerInstanceEncoder(d, meta, obj)
if err != nil {
return err
}
url, err := tpgresource.ReplaceVars(d, config, "{{SpannerBasePath}}projects/{{project}}/instances")
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, "{{project}}/{{name}}")
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 = SpannerOperationWaitTimeWithResponse(
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 = resourceSpannerInstanceDecoder(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", flattenSpannerInstanceName(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, "{{project}}/{{name}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)
// This is useful if the resource in question doesn't have a perfectly consistent API
// That is, the Operation for Create might return before the Get operation shows the
// completed state of the resource.
time.Sleep(5 * time.Second)
log.Printf("[DEBUG] Finished creating Instance %q: %#v", d.Id(), res)
return resourceSpannerInstanceRead(d, meta)
}
func resourceSpannerInstanceRead(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, "{{SpannerBasePath}}projects/{{project}}/instances/{{name}}")
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("SpannerInstance %q", d.Id()))
}
res, err = resourceSpannerInstanceDecoder(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 SpannerInstance because it no longer exists.")
d.SetId("")
return nil
}
// Explicitly set virtual fields to default values if unset
if _, ok := d.GetOkExists("force_destroy"); !ok {
if err := d.Set("force_destroy", false); err != nil {
return fmt.Errorf("Error setting force_destroy: %s", err)
}
}
if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("name", flattenSpannerInstanceName(res["name"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("config", flattenSpannerInstanceConfig(res["config"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("display_name", flattenSpannerInstanceDisplayName(res["displayName"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("num_nodes", flattenSpannerInstanceNumNodes(res["nodeCount"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("processing_units", flattenSpannerInstanceProcessingUnits(res["processingUnits"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("labels", flattenSpannerInstanceLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("state", flattenSpannerInstanceState(res["state"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("autoscaling_config", flattenSpannerInstanceAutoscalingConfig(res["autoscalingConfig"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("terraform_labels", flattenSpannerInstanceTerraformLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
if err := d.Set("effective_labels", flattenSpannerInstanceEffectiveLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
return nil
}
func resourceSpannerInstanceUpdate(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{})
displayNameProp, err := expandSpannerInstanceDisplayName(d.Get("display_name"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) {
obj["displayName"] = displayNameProp
}
nodeCountProp, err := expandSpannerInstanceNumNodes(d.Get("num_nodes"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("num_nodes"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nodeCountProp)) {
obj["nodeCount"] = nodeCountProp
}
processingUnitsProp, err := expandSpannerInstanceProcessingUnits(d.Get("processing_units"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("processing_units"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, processingUnitsProp)) {
obj["processingUnits"] = processingUnitsProp
}
autoscalingConfigProp, err := expandSpannerInstanceAutoscalingConfig(d.Get("autoscaling_config"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("autoscaling_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, autoscalingConfigProp)) {
obj["autoscalingConfig"] = autoscalingConfigProp
}
labelsProp, err := expandSpannerInstanceEffectiveLabels(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 = resourceSpannerInstanceUpdateEncoder(d, meta, obj)
if err != nil {
return err
}
url, err := tpgresource.ReplaceVars(d, config, "{{SpannerBasePath}}projects/{{project}}/instances/{{name}}")
if err != nil {
return err
}
log.Printf("[DEBUG] Updating Instance %q: %#v", d.Id(), obj)
headers := make(http.Header)
if resourceSpannerInstanceVirtualUpdate(d, ResourceSpannerInstance().Schema) {
if d.Get("force_destroy") != nil {
if err := d.Set("force_destroy", d.Get("force_destroy")); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
}
return nil
}
// err == nil indicates that the billing_project value was found
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}
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 = SpannerOperationWaitTime(
config, res, project, "Updating Instance", userAgent,
d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}
return resourceSpannerInstanceRead(d, meta)
}
func resourceSpannerInstanceDelete(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, "{{SpannerBasePath}}projects/{{project}}/instances/{{name}}")
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)
if d.Get("force_destroy").(bool) {
backupsUrl, err := tpgresource.ReplaceVars(d, config, "{{SpannerBasePath}}projects/{{project}}/instances/{{name}}/backups")
if err != nil {
return err
}
resp, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: backupsUrl,
UserAgent: userAgent,
})
if err != nil {
// API returns 200 if no backups exist but the instance still exists, hence the error check.
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("SpannerInstance %q", d.Id()))
}
err = deleteSpannerBackups(d, config, resp, billingProject, userAgent)
if err != nil {
return err
}
}
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")
}
log.Printf("[DEBUG] Finished deleting Instance %q: %#v", d.Id(), res)
return nil
}
func resourceSpannerInstanceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*transport_tpg.Config)
if err := tpgresource.ParseImportId([]string{
"^projects/(?P<project>[^/]+)/instances/(?P<name>[^/]+)$",
"^(?P<project>[^/]+)/(?P<name>[^/]+)$",
"^(?P<name>[^/]+)$",
}, d, config); err != nil {
return nil, err
}
// Replace import id for the resource id
id, err := tpgresource.ReplaceVars(d, config, "{{project}}/{{name}}")
if err != nil {
return nil, fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)
// Explicitly set virtual fields to default values on import
if err := d.Set("force_destroy", false); err != nil {
return nil, fmt.Errorf("Error setting force_destroy: %s", err)
}
return []*schema.ResourceData{d}, nil
}
func flattenSpannerInstanceName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
}
return tpgresource.NameFromSelfLinkStateFunc(v)
}
func flattenSpannerInstanceConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
}
return tpgresource.ConvertSelfLinkToV1(v.(string))
}
func flattenSpannerInstanceDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}
func flattenSpannerInstanceNumNodes(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 flattenSpannerInstanceProcessingUnits(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 flattenSpannerInstanceLabels(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 flattenSpannerInstanceState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}
func flattenSpannerInstanceAutoscalingConfig(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["autoscaling_limits"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingLimits(original["autoscalingLimits"], d, config)
transformed["autoscaling_targets"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingTargets(original["autoscalingTargets"], d, config)
return []interface{}{transformed}
}
func flattenSpannerInstanceAutoscalingConfigAutoscalingLimits(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["min_processing_units"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMinProcessingUnits(original["minProcessingUnits"], d, config)
transformed["max_processing_units"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(original["maxProcessingUnits"], d, config)
transformed["min_nodes"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMinNodes(original["minNodes"], d, config)
transformed["max_nodes"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxNodes(original["maxNodes"], d, config)
return []interface{}{transformed}
}
func flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMinProcessingUnits(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 flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(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 flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMinNodes(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 flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxNodes(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 flattenSpannerInstanceAutoscalingConfigAutoscalingTargets(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["high_priority_cpu_utilization_percent"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingTargetsHighPriorityCpuUtilizationPercent(original["highPriorityCpuUtilizationPercent"], d, config)
transformed["storage_utilization_percent"] =
flattenSpannerInstanceAutoscalingConfigAutoscalingTargetsStorageUtilizationPercent(original["storageUtilizationPercent"], d, config)
return []interface{}{transformed}
}
func flattenSpannerInstanceAutoscalingConfigAutoscalingTargetsHighPriorityCpuUtilizationPercent(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 flattenSpannerInstanceAutoscalingConfigAutoscalingTargetsStorageUtilizationPercent(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 flattenSpannerInstanceTerraformLabels(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 flattenSpannerInstanceEffectiveLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}
func expandSpannerInstanceName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
r := regexp.MustCompile("projects/(.+)/instanceConfigs/(.+)")
if r.MatchString(v.(string)) {
return v.(string), nil
}
project, err := tpgresource.GetProject(d, config)
if err != nil {
return nil, err
}
return fmt.Sprintf("projects/%s/instanceConfigs/%s", project, v.(string)), nil
}
func expandSpannerInstanceDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceNumNodes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceProcessingUnits(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfig(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{})
transformedAutoscalingLimits, err := expandSpannerInstanceAutoscalingConfigAutoscalingLimits(original["autoscaling_limits"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedAutoscalingLimits); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["autoscalingLimits"] = transformedAutoscalingLimits
}
transformedAutoscalingTargets, err := expandSpannerInstanceAutoscalingConfigAutoscalingTargets(original["autoscaling_targets"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedAutoscalingTargets); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["autoscalingTargets"] = transformedAutoscalingTargets
}
return transformed, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingLimits(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{})
transformedMinProcessingUnits, err := expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMinProcessingUnits(original["min_processing_units"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedMinProcessingUnits); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["minProcessingUnits"] = transformedMinProcessingUnits
}
transformedMaxProcessingUnits, err := expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(original["max_processing_units"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedMaxProcessingUnits); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["maxProcessingUnits"] = transformedMaxProcessingUnits
}
transformedMinNodes, err := expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMinNodes(original["min_nodes"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedMinNodes); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["minNodes"] = transformedMinNodes
}
transformedMaxNodes, err := expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxNodes(original["max_nodes"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedMaxNodes); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["maxNodes"] = transformedMaxNodes
}
return transformed, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMinProcessingUnits(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMinNodes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxNodes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingTargets(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{})
transformedHighPriorityCpuUtilizationPercent, err := expandSpannerInstanceAutoscalingConfigAutoscalingTargetsHighPriorityCpuUtilizationPercent(original["high_priority_cpu_utilization_percent"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedHighPriorityCpuUtilizationPercent); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["highPriorityCpuUtilizationPercent"] = transformedHighPriorityCpuUtilizationPercent
}
transformedStorageUtilizationPercent, err := expandSpannerInstanceAutoscalingConfigAutoscalingTargetsStorageUtilizationPercent(original["storage_utilization_percent"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedStorageUtilizationPercent); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["storageUtilizationPercent"] = transformedStorageUtilizationPercent
}
return transformed, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingTargetsHighPriorityCpuUtilizationPercent(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceAutoscalingConfigAutoscalingTargetsStorageUtilizationPercent(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
func expandSpannerInstanceEffectiveLabels(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 resourceSpannerInstanceEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
// Temp Logic to accommodate autoscaling_config, processing_units and num_nodes
if obj["processingUnits"] == nil && obj["nodeCount"] == nil && obj["autoscalingConfig"] == nil {
obj["nodeCount"] = 1
}
newObj := make(map[string]interface{})
newObj["instance"] = obj
if obj["name"] == nil {
if err := d.Set("name", id.PrefixedUniqueId("tfgen-spanid-")[:30]); err != nil {
return nil, fmt.Errorf("Error setting name: %s", err)
}
newObj["instanceId"] = d.Get("name").(string)
} else {
newObj["instanceId"] = obj["name"]
}
delete(obj, "name")
return newObj, nil
}
func resourceSpannerInstanceUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
project, err := tpgresource.GetProject(d, meta.(*transport_tpg.Config))
if err != nil {
return nil, err
}
obj["name"] = fmt.Sprintf("projects/%s/instances/%s", project, obj["name"])
newObj := make(map[string]interface{})
newObj["instance"] = obj
updateMask := make([]string, 0)
if d.HasChange("num_nodes") {
updateMask = append(updateMask, "nodeCount")
}
if d.HasChange("display_name") {
updateMask = append(updateMask, "displayName")
}
if d.HasChange("labels") {
updateMask = append(updateMask, "labels")
}
if d.HasChange("processing_units") {
updateMask = append(updateMask, "processingUnits")
}
if d.HasChange("autoscaling_config") {
old, new := d.GetChange("autoscaling_config")
oldSlice := old.([]interface{})
newSlice := new.([]interface{})
if len(oldSlice) == 0 || len(newSlice) == 0 {
updateMask = append(updateMask, "autoscalingConfig")
} else {
if d.HasChange("autoscaling_config.0.autoscaling_limits.0.max_processing_units") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingLimits.maxProcessingUnits")
}
if d.HasChange("autoscaling_config.0.autoscaling_limits.0.min_processing_units") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingLimits.minProcessingUnits")
}
if d.HasChange("autoscaling_config.0.autoscaling_limits.0.max_nodes") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingLimits.maxNodes")
}
if d.HasChange("autoscaling_config.0.autoscaling_limits.0.min_nodes") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingLimits.minNodes")
}
if d.HasChange("autoscaling_config.0.autoscaling_targets.0.high_priority_cpu_utilization_percent") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingTargets.highPriorityCpuUtilizationPercent")
}
if d.HasChange("autoscaling_config.0.autoscaling_targets.0.storage_utilization_percent") {
updateMask = append(updateMask, "autoscalingConfig.autoscalingTargets.storageUtilizationPercent")
}
}
}
newObj["fieldMask"] = strings.Join(updateMask, ",")
return newObj, nil
}
func resourceSpannerInstanceDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
config := meta.(*transport_tpg.Config)
d.SetId(res["name"].(string))
if err := tpgresource.ParseImportId([]string{"projects/(?P<project>[^/]+)/instances/(?P<name>[^/]+)"}, d, config); err != nil {
return nil, err
}
res["project"] = d.Get("project").(string)
res["name"] = d.Get("name").(string)
id, err := tpgresource.ReplaceVars(d, config, "{{project}}/{{name}}")
if err != nil {
return nil, err
}
d.SetId(id)
return res, nil
}