Skip to content

Commit

Permalink
feat(spanner): support defining autoscaling limit as nodes (#9606) (#…
Browse files Browse the repository at this point in the history
…6748)

[upstream:6afde3f9effd7c1c7298108a856395f220421201]

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Dec 12, 2023
1 parent 3700308 commit e8383d8
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .changelog/9606.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
spanner: added support for defining autoscaling limit using node count in `google_spanner_instance`
```
90 changes: 89 additions & 1 deletion google-beta/services/spanner/resource_spanner_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,44 @@ the instance.`,
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`,
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{},
},
},
},
Expand Down Expand Up @@ -792,6 +814,10 @@ func flattenSpannerInstanceAutoscalingConfigAutoscalingLimits(v interface{}, d *
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{} {
Expand Down Expand Up @@ -828,6 +854,40 @@ func flattenSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(
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
Expand Down Expand Up @@ -975,6 +1035,20 @@ func expandSpannerInstanceAutoscalingConfigAutoscalingLimits(v interface{}, d tp
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
}

Expand All @@ -986,6 +1060,14 @@ func expandSpannerInstanceAutoscalingConfigAutoscalingLimitsMaxProcessingUnits(v
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 {
Expand Down Expand Up @@ -1077,6 +1159,12 @@ func resourceSpannerInstanceUpdateEncoder(d *schema.ResourceData, meta interface
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")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,11 @@ resource "google_spanner_instance" "example" {
display_name = "Test Spanner Instance"
autoscaling_config {
autoscaling_limits {
max_processing_units = 3000
min_processing_units = 2000
// Define the minimum and maximum compute capacity allocated to the instance
// 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.
max_processing_units = 3000 // OR max_nodes = 3
min_processing_units = 2000 // OR min_nodes = 2
}
autoscaling_targets {
high_priority_cpu_utilization_percent = 75
Expand Down
101 changes: 101 additions & 0 deletions google-beta/services/spanner/resource_spanner_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,67 @@ func TestAccSpannerInstance_basicWithAutoscalingUsingProcessingUnitConfigUpdate(
})
}

func TestAccSpannerInstance_basicWithAutoscalingUsingNodeConfig(t *testing.T) {
t.Parallel()

displayName := fmt.Sprintf("spanner-test-%s-dname", acctest.RandString(t, 10))
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckSpannerInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSpannerInstance_basicWithAutoscalerConfigUsingNodesAsConfigs(displayName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("google_spanner_instance.basic", "state"),
),
},
{
ResourceName: "google_spanner_instance.basic",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccSpannerInstance_basicWithAutoscalingUsingNodeConfigUpdate(t *testing.T) {
t.Parallel()

displayName := fmt.Sprintf("spanner-test-%s-dname", acctest.RandString(t, 10))
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckSpannerInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSpannerInstance_basicWithAutoscalerConfigUsingNodesAsConfigsUpdate(displayName, 1, 2, 65, 95),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("google_spanner_instance.basic", "state"),
),
},
{
ResourceName: "google_spanner_instance.basic",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
},
{
Config: testAccSpannerInstance_basicWithAutoscalerConfigUsingNodesAsConfigsUpdate(displayName, 2, 3, 75, 90),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("google_spanner_instance.basic", "state"),
),
},
{
ResourceName: "google_spanner_instance.basic",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
},
},
})
}

func testAccSpannerInstance_basic(name string) string {
return fmt.Sprintf(`
resource "google_spanner_instance" "basic" {
Expand Down Expand Up @@ -306,3 +367,43 @@ resource "google_spanner_instance" "basic" {
}
`, name, name, maxProcessingUnits, minProcessingUnits, cupUtilizationPercent, storageUtilizationPercent)
}

func testAccSpannerInstance_basicWithAutoscalerConfigUsingNodesAsConfigs(name string) string {
return fmt.Sprintf(`
resource "google_spanner_instance" "basic" {
name = "%s"
config = "regional-us-central1"
display_name = "%s"
autoscaling_config {
autoscaling_limits {
max_nodes = 2
min_nodes = 1
}
autoscaling_targets {
high_priority_cpu_utilization_percent = 65
storage_utilization_percent = 95
}
}
}
`, name, name)
}

func testAccSpannerInstance_basicWithAutoscalerConfigUsingNodesAsConfigsUpdate(name string, minNodes, maxNodes, cupUtilizationPercent, storageUtilizationPercent int) string {
return fmt.Sprintf(`
resource "google_spanner_instance" "basic" {
name = "%s"
config = "regional-us-central1"
display_name = "%s"
autoscaling_config {
autoscaling_limits {
max_nodes = %v
min_nodes = %v
}
autoscaling_targets {
high_priority_cpu_utilization_percent = %v
storage_utilization_percent = %v
}
}
}
`, name, name, maxNodes, minNodes, cupUtilizationPercent, storageUtilizationPercent)
}
23 changes: 20 additions & 3 deletions website/docs/r/spanner_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@ resource "google_spanner_instance" "example" {
display_name = "Test Spanner Instance"
autoscaling_config {
autoscaling_limits {
max_processing_units = 3000
min_processing_units = 2000
// Define the minimum and maximum compute capacity allocated to the instance
// 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.
max_processing_units = 3000 // OR max_nodes = 3
min_processing_units = 2000 // OR min_nodes = 2
}
autoscaling_targets {
high_priority_cpu_utilization_percent = 75
Expand Down Expand Up @@ -181,7 +184,11 @@ This must be set to true if you created a backup manually in the console.
* `autoscaling_limits` -
(Optional)
Defines scale in controls to reduce the risk of response latency
and outages due to abrupt scale-in events
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.
Structure is [documented below](#nested_autoscaling_limits).

* `autoscaling_targets` -
Expand All @@ -204,6 +211,16 @@ This must be set to true if you created a backup manually in the console.
If set, this number should be multiples of 1000 and be greater than or equal to
min_processing_units.

* `min_nodes` -
(Optional)
Specifies number of nodes allocated to the instance. If set, this number
should be greater than or equal to 1.

* `max_nodes` -
(Optional)
Specifies maximum number of nodes allocated to the instance. If set, this number
should be greater than or equal to min_nodes.

<a name="nested_autoscaling_targets"></a>The `autoscaling_targets` block supports:

* `high_priority_cpu_utilization_percent` -
Expand Down

0 comments on commit e8383d8

Please sign in to comment.