Skip to content

Commit d8a9eaa

Browse files
feat: Serverless VPC Access Connector update fields without recreating the resource (#12830) (#22572)
[upstream:44cbeb4d78cda82d121bd4e6780850f6e51b7da8] Signed-off-by: Modular Magician <[email protected]>
1 parent 7863beb commit d8a9eaa

File tree

4 files changed

+346
-3
lines changed

4 files changed

+346
-3
lines changed

.changelog/12830.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
vpcaccess: changed fields `min_instances`, `max_instances`, `machine_type` to allow update `google_vpc_access_connector` without without recreation.
3+
```

google/services/vpcaccess/resource_vpc_access_connector.go

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
package vpcaccess
2121

2222
import (
23+
"context"
2324
"fmt"
2425
"log"
2526
"net/http"
2627
"reflect"
28+
"strings"
2729
"time"
2830

2931
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
@@ -34,10 +36,20 @@ import (
3436
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
3537
)
3638

39+
func isInstanceShrinkage(_ context.Context, old, new, _ interface{}) bool {
40+
// max and min instances can only increase in-place,
41+
// so we must create a new resource if it is decreased.
42+
if old == nil || new == nil {
43+
return false
44+
}
45+
return new.(int) < old.(int)
46+
}
47+
3748
func ResourceVPCAccessConnector() *schema.Resource {
3849
return &schema.Resource{
3950
Create: resourceVPCAccessConnectorCreate,
4051
Read: resourceVPCAccessConnectorRead,
52+
Update: resourceVPCAccessConnectorUpdate,
4153
Delete: resourceVPCAccessConnectorDelete,
4254

4355
Importer: &schema.ResourceImporter{
@@ -46,10 +58,13 @@ func ResourceVPCAccessConnector() *schema.Resource {
4658

4759
Timeouts: &schema.ResourceTimeout{
4860
Create: schema.DefaultTimeout(20 * time.Minute),
61+
Update: schema.DefaultTimeout(20 * time.Minute),
4962
Delete: schema.DefaultTimeout(20 * time.Minute),
5063
},
5164

5265
CustomizeDiff: customdiff.All(
66+
customdiff.ForceNewIfChange("min_instances", isInstanceShrinkage),
67+
customdiff.ForceNewIfChange("max_instances", isInstanceShrinkage),
5368
tpgresource.DefaultProviderProject,
5469
),
5570

@@ -70,18 +85,17 @@ func ResourceVPCAccessConnector() *schema.Resource {
7085
"machine_type": {
7186
Type: schema.TypeString,
7287
Optional: true,
73-
ForceNew: true,
7488
Description: `Machine type of VM Instance underlying connector. Default is e2-micro`,
7589
Default: "e2-micro",
7690
},
7791
"max_instances": {
7892
Type: schema.TypeInt,
7993
Computed: true,
8094
Optional: true,
81-
ForceNew: true,
8295
Description: `Maximum value of instances in autoscaling group underlying the connector. Value must be between 3 and 10, inclusive. Must be
8396
higher than the value specified by min_instances.`,
8497
ConflictsWith: []string{"max_throughput"},
98+
RequiredWith: []string{"min_instances"},
8599
},
86100
"max_throughput": {
87101
Type: schema.TypeInt,
@@ -98,10 +112,10 @@ min_throughput. Only one of 'max_throughput' and 'max_instances' can be specifie
98112
Type: schema.TypeInt,
99113
Computed: true,
100114
Optional: true,
101-
ForceNew: true,
102115
Description: `Minimum value of instances in autoscaling group underlying the connector. Value must be between 2 and 9, inclusive. Must be
103116
lower than the value specified by max_instances.`,
104117
ConflictsWith: []string{"min_throughput"},
118+
RequiredWith: []string{"max_instances"},
105119
},
106120
"min_throughput": {
107121
Type: schema.TypeInt,
@@ -427,6 +441,124 @@ func resourceVPCAccessConnectorRead(d *schema.ResourceData, meta interface{}) er
427441
return nil
428442
}
429443

444+
func resourceVPCAccessConnectorUpdate(d *schema.ResourceData, meta interface{}) error {
445+
config := meta.(*transport_tpg.Config)
446+
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
447+
if err != nil {
448+
return err
449+
}
450+
451+
billingProject := ""
452+
453+
project, err := tpgresource.GetProject(d, config)
454+
if err != nil {
455+
return fmt.Errorf("Error fetching project for Connector: %s", err)
456+
}
457+
billingProject = project
458+
459+
obj := make(map[string]interface{})
460+
machineTypeProp, err := expandVPCAccessConnectorMachineType(d.Get("machine_type"), d, config)
461+
if err != nil {
462+
return err
463+
} else if v, ok := d.GetOkExists("machine_type"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, machineTypeProp)) {
464+
obj["machineType"] = machineTypeProp
465+
}
466+
minInstancesProp, err := expandVPCAccessConnectorMinInstances(d.Get("min_instances"), d, config)
467+
if err != nil {
468+
return err
469+
} else if v, ok := d.GetOkExists("min_instances"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, minInstancesProp)) {
470+
obj["minInstances"] = minInstancesProp
471+
}
472+
maxInstancesProp, err := expandVPCAccessConnectorMaxInstances(d.Get("max_instances"), d, config)
473+
if err != nil {
474+
return err
475+
} else if v, ok := d.GetOkExists("max_instances"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, maxInstancesProp)) {
476+
obj["maxInstances"] = maxInstancesProp
477+
}
478+
479+
obj, err = resourceVPCAccessConnectorEncoder(d, meta, obj)
480+
if err != nil {
481+
return err
482+
}
483+
484+
url, err := tpgresource.ReplaceVars(d, config, "{{VPCAccessBasePath}}projects/{{project}}/locations/{{region}}/connectors/{{name}}")
485+
if err != nil {
486+
return err
487+
}
488+
489+
log.Printf("[DEBUG] Updating Connector %q: %#v", d.Id(), obj)
490+
headers := make(http.Header)
491+
updateMask := []string{}
492+
493+
if d.HasChange("machine_type") {
494+
updateMask = append(updateMask, "machineType")
495+
}
496+
497+
if d.HasChange("min_instances") {
498+
updateMask = append(updateMask, "minInstances")
499+
}
500+
501+
if d.HasChange("max_instances") {
502+
updateMask = append(updateMask, "maxInstances")
503+
}
504+
// updateMask is a URL parameter but not present in the schema, so ReplaceVars
505+
// won't set it
506+
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
507+
if err != nil {
508+
return err
509+
}
510+
if d.HasChange("min_instances") && !d.HasChange("max_instances") {
511+
obj["maxInstances"] = d.Get("max_instances").(int)
512+
updateMask = append(updateMask, "maxInstances", "minInstances")
513+
}
514+
515+
if d.HasChange("max_instances") && !d.HasChange("min_instances") {
516+
obj["minInstances"] = d.Get("min_instances").(int)
517+
updateMask = append(updateMask, "maxInstances", "minInstances")
518+
}
519+
520+
// Overwrite the previously set mask.
521+
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
522+
if err != nil {
523+
return err
524+
}
525+
526+
// err == nil indicates that the billing_project value was found
527+
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
528+
billingProject = bp
529+
}
530+
531+
// if updateMask is empty we are not updating anything so skip the post
532+
if len(updateMask) > 0 {
533+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
534+
Config: config,
535+
Method: "PATCH",
536+
Project: billingProject,
537+
RawURL: url,
538+
UserAgent: userAgent,
539+
Body: obj,
540+
Timeout: d.Timeout(schema.TimeoutUpdate),
541+
Headers: headers,
542+
})
543+
544+
if err != nil {
545+
return fmt.Errorf("Error updating Connector %q: %s", d.Id(), err)
546+
} else {
547+
log.Printf("[DEBUG] Finished updating Connector %q: %#v", d.Id(), res)
548+
}
549+
550+
err = VPCAccessOperationWaitTime(
551+
config, res, project, "Updating Connector", userAgent,
552+
d.Timeout(schema.TimeoutUpdate))
553+
554+
if err != nil {
555+
return err
556+
}
557+
}
558+
559+
return resourceVPCAccessConnectorRead(d, meta)
560+
}
561+
430562
func resourceVPCAccessConnectorDelete(d *schema.ResourceData, meta interface{}) error {
431563
config := meta.(*transport_tpg.Config)
432564
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)

0 commit comments

Comments
 (0)