From d893d649f1270ca6ac8ba7c84e79174fa615dae0 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Thu, 7 Nov 2024 11:07:34 +0000 Subject: [PATCH] Add performance config field to Filestore instance (#12245) [upstream:8ba278ec6b9c43e8d0a65211cc40d4d02168cb91] Signed-off-by: Modular Magician --- .changelog/12245.txt | 3 + .../filestore/resource_filestore_instance.go | 217 ++++++++++++++++++ .../resource_filestore_instance_test.go | 124 ++++++++++ .../docs/r/filestore_instance.html.markdown | 39 ++++ 4 files changed, 383 insertions(+) create mode 100644 .changelog/12245.txt diff --git a/.changelog/12245.txt b/.changelog/12245.txt new file mode 100644 index 00000000000..850aa4fe40f --- /dev/null +++ b/.changelog/12245.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +filestore: added `performance_config` field to `google_filestore_instance` +``` \ No newline at end of file diff --git a/google/services/filestore/resource_filestore_instance.go b/google/services/filestore/resource_filestore_instance.go index 5abbfb5e1f1..de94f07c0e3 100644 --- a/google/services/filestore/resource_filestore_instance.go +++ b/google/services/filestore/resource_filestore_instance.go @@ -256,6 +256,57 @@ Please refer to the field 'effective_labels' for all of the labels present on th Description: `The name of the location of the instance. This can be a region for ENTERPRISE tier instances.`, ExactlyOneOf: []string{}, }, + "performance_config": { + Type: schema.TypeList, + Optional: true, + Description: `Performance configuration for the instance. If not provided, +the default performance settings will be used.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fixed_iops": { + Type: schema.TypeList, + Optional: true, + Description: `The instance will have a fixed provisioned IOPS value, +which will remain constant regardless of instance +capacity.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_iops": { + Type: schema.TypeInt, + Optional: true, + Description: `The number of IOPS to provision for the instance. +max_iops must be in multiple of 1000.`, + }, + }, + }, + ConflictsWith: []string{}, + }, + "iops_per_tb": { + Type: schema.TypeList, + Optional: true, + Description: `The instance provisioned IOPS will change dynamically +based on the capacity of the instance.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_iops_per_tb": { + Type: schema.TypeInt, + Optional: true, + Description: `The instance max IOPS will be calculated by multiplying +the capacity of the instance (TB) by max_iops_per_tb, +and rounding to the nearest 1000. The instance max IOPS +will be changed dynamically based on the instance +capacity.`, + }, + }, + }, + ConflictsWith: []string{}, + }, + }, + }, + }, "protocol": { Type: schema.TypeString, Optional: true, @@ -367,6 +418,12 @@ func resourceFilestoreInstanceCreate(d *schema.ResourceData, meta interface{}) e } else if v, ok := d.GetOkExists("deletion_protection_reason"); !tpgresource.IsEmptyValue(reflect.ValueOf(deletionProtectionReasonProp)) && (ok || !reflect.DeepEqual(v, deletionProtectionReasonProp)) { obj["deletionProtectionReason"] = deletionProtectionReasonProp } + performanceConfigProp, err := expandFilestoreInstancePerformanceConfig(d.Get("performance_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("performance_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(performanceConfigProp)) && (ok || !reflect.DeepEqual(v, performanceConfigProp)) { + obj["performanceConfig"] = performanceConfigProp + } labelsProp, err := expandFilestoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config) if err != nil { return err @@ -534,6 +591,9 @@ func resourceFilestoreInstanceRead(d *schema.ResourceData, meta interface{}) err if err := d.Set("deletion_protection_reason", flattenFilestoreInstanceDeletionProtectionReason(res["deletionProtectionReason"], d, config)); err != nil { return fmt.Errorf("Error reading Instance: %s", err) } + if err := d.Set("performance_config", flattenFilestoreInstancePerformanceConfig(res["performanceConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } if err := d.Set("terraform_labels", flattenFilestoreInstanceTerraformLabels(res["labels"], d, config)); err != nil { return fmt.Errorf("Error reading Instance: %s", err) } @@ -584,6 +644,12 @@ func resourceFilestoreInstanceUpdate(d *schema.ResourceData, meta interface{}) e } else if v, ok := d.GetOkExists("deletion_protection_reason"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, deletionProtectionReasonProp)) { obj["deletionProtectionReason"] = deletionProtectionReasonProp } + performanceConfigProp, err := expandFilestoreInstancePerformanceConfig(d.Get("performance_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("performance_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, performanceConfigProp)) { + obj["performanceConfig"] = performanceConfigProp + } labelsProp, err := expandFilestoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config) if err != nil { return err @@ -616,6 +682,10 @@ func resourceFilestoreInstanceUpdate(d *schema.ResourceData, meta interface{}) e updateMask = append(updateMask, "deletionProtectionReason") } + if d.HasChange("performance_config") { + updateMask = append(updateMask, "performanceConfig") + } + if d.HasChange("effective_labels") { updateMask = append(updateMask, "labels") } @@ -951,6 +1021,81 @@ func flattenFilestoreInstanceDeletionProtectionReason(v interface{}, d *schema.R return v } +func flattenFilestoreInstancePerformanceConfig(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["iops_per_tb"] = + flattenFilestoreInstancePerformanceConfigIopsPerTb(original["iopsPerTb"], d, config) + transformed["fixed_iops"] = + flattenFilestoreInstancePerformanceConfigFixedIops(original["fixedIops"], d, config) + return []interface{}{transformed} +} +func flattenFilestoreInstancePerformanceConfigIopsPerTb(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["max_iops_per_tb"] = + flattenFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(original["maxIopsPerTb"], d, config) + return []interface{}{transformed} +} +func flattenFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(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 flattenFilestoreInstancePerformanceConfigFixedIops(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["max_iops"] = + flattenFilestoreInstancePerformanceConfigFixedIopsMaxIops(original["maxIops"], d, config) + return []interface{}{transformed} +} +func flattenFilestoreInstancePerformanceConfigFixedIopsMaxIops(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 flattenFilestoreInstanceTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { if v == nil { return v @@ -1189,6 +1334,78 @@ func expandFilestoreInstanceDeletionProtectionReason(v interface{}, d tpgresourc return v, nil } +func expandFilestoreInstancePerformanceConfig(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{}) + + transformedIopsPerTb, err := expandFilestoreInstancePerformanceConfigIopsPerTb(original["iops_per_tb"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIopsPerTb); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["iopsPerTb"] = transformedIopsPerTb + } + + transformedFixedIops, err := expandFilestoreInstancePerformanceConfigFixedIops(original["fixed_iops"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFixedIops); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["fixedIops"] = transformedFixedIops + } + + return transformed, nil +} + +func expandFilestoreInstancePerformanceConfigIopsPerTb(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{}) + + transformedMaxIopsPerTb, err := expandFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(original["max_iops_per_tb"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxIopsPerTb); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["maxIopsPerTb"] = transformedMaxIopsPerTb + } + + return transformed, nil +} + +func expandFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstancePerformanceConfigFixedIops(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{}) + + transformedMaxIops, err := expandFilestoreInstancePerformanceConfigFixedIopsMaxIops(original["max_iops"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxIops); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["maxIops"] = transformedMaxIops + } + + return transformed, nil +} + +func expandFilestoreInstancePerformanceConfigFixedIopsMaxIops(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandFilestoreInstanceEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) { if v == nil { return map[string]string{}, nil diff --git a/google/services/filestore/resource_filestore_instance_test.go b/google/services/filestore/resource_filestore_instance_test.go index 6ece8fa0b3b..87f9196cd08 100644 --- a/google/services/filestore/resource_filestore_instance_test.go +++ b/google/services/filestore/resource_filestore_instance_test.go @@ -287,3 +287,127 @@ resource "google_filestore_instance" "instance" { } `, name, location, tier, deletionProtection, deletionProtectionReason) } + +func TestAccFilestoreInstance_performanceConfig(t *testing.T) { + t.Parallel() + + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + location := "us-central1" + tier := "REGIONAL" + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckFilestoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccFilestoreInstance_fixedIopsPerformanceConfig(name, location, tier), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_filestore_instance.instance", "performance_config.0.fixed_iops.0.max_iops", "17000"), + ), + }, + { + ResourceName: "google_filestore_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zone"}, + }, + { + Config: testAccFilestoreInstance_iopsPerTbPerformanceConfig(name, location, tier), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_filestore_instance.instance", "performance_config.0.iops_per_tb.0.max_iops_per_tb", "17000"), + ), + }, + { + ResourceName: "google_filestore_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zone"}, + }, + { + Config: testAccFilestoreInstance_defaultConfig(name, location, tier), + }, + { + ResourceName: "google_filestore_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zone"}, + }, + }, + }) +} + +func testAccFilestoreInstance_fixedIopsPerformanceConfig(name, location, tier string) string { + return fmt.Sprintf(` +resource "google_filestore_instance" "instance" { + name = "%s" + location = "%s" + tier = "%s" + description = "An instance created during testing." + + file_shares { + capacity_gb = 1024 + name = "share" + } + + networks { + network = "default" + modes = ["MODE_IPV4"] + } + + performance_config { + fixed_iops { + max_iops = 17000 + } + } +} +`, name, location, tier) +} + +func testAccFilestoreInstance_iopsPerTbPerformanceConfig(name, location, tier string) string { + return fmt.Sprintf(` +resource "google_filestore_instance" "instance" { + name = "%s" + zone = "%s" + tier = "%s" + description = "An instance created during testing." + + file_shares { + capacity_gb = 1024 + name = "share" + } + + networks { + network = "default" + modes = ["MODE_IPV4"] + } + + performance_config { + iops_per_tb { + max_iops_per_tb = 17000 + } + } +} +`, name, location, tier) +} + +func testAccFilestoreInstance_defaultConfig(name, location, tier string) string { + return fmt.Sprintf(` +resource "google_filestore_instance" "instance" { + name = "%s" + zone = "%s" + tier = "%s" + description = "An instance created during testing." + + file_shares { + capacity_gb = 1024 + name = "share" + } + + networks { + network = "default" + modes = ["MODE_IPV4"] + } +} +`, name, location, tier) +} diff --git a/website/docs/r/filestore_instance.html.markdown b/website/docs/r/filestore_instance.html.markdown index b8b0e0f94e7..7a79b91950c 100644 --- a/website/docs/r/filestore_instance.html.markdown +++ b/website/docs/r/filestore_instance.html.markdown @@ -304,6 +304,12 @@ The following arguments are supported: (Optional) The reason for enabling deletion protection. +* `performance_config` - + (Optional) + Performance configuration for the instance. If not provided, + the default performance settings will be used. + Structure is [documented below](#nested_performance_config). + * `zone` - (Optional, Deprecated) The name of the Filestore zone of the instance. @@ -318,6 +324,39 @@ The following arguments are supported: If it is not provided, the provider project is used. +The `performance_config` block supports: + +* `iops_per_tb` - + (Optional) + The instance provisioned IOPS will change dynamically + based on the capacity of the instance. + Structure is [documented below](#nested_iops_per_tb). + +* `fixed_iops` - + (Optional) + The instance will have a fixed provisioned IOPS value, + which will remain constant regardless of instance + capacity. + Structure is [documented below](#nested_fixed_iops). + + +The `iops_per_tb` block supports: + +* `max_iops_per_tb` - + (Optional) + The instance max IOPS will be calculated by multiplying + the capacity of the instance (TB) by max_iops_per_tb, + and rounding to the nearest 1000. The instance max IOPS + will be changed dynamically based on the instance + capacity. + +The `fixed_iops` block supports: + +* `max_iops` - + (Optional) + The number of IOPS to provision for the instance. + max_iops must be in multiple of 1000. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: