diff --git a/.changelog/11504.txt b/.changelog/11504.txt new file mode 100644 index 00000000000..6425ef9211a --- /dev/null +++ b/.changelog/11504.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +compute: added `label_fingerprint` field to `google_compute_global_address` resource (ga) +``` +```release-note:bug +compute: fixed bug where the `labels` field could not be updated on `google_compute_global_address` (ga) +``` \ No newline at end of file diff --git a/google/acctest/resource_test_utils.go b/google/acctest/resource_test_utils.go index 541a942d9a4..a7829591bd0 100644 --- a/google/acctest/resource_test_utils.go +++ b/google/acctest/resource_test_utils.go @@ -3,17 +3,40 @@ package acctest import ( + "context" + "errors" "fmt" + "slices" "testing" "time" + tfjson "github.com/hashicorp/terraform-json" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" ) // General test utils +var _ plancheck.PlanCheck = expectNoDelete{} + +type expectNoDelete struct{} + +func (e expectNoDelete) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) { + var result error + for _, rc := range req.Plan.ResourceChanges { + if slices.Contains(rc.Change.Actions, tfjson.ActionDelete) { + result = errors.Join(result, fmt.Errorf("expected no deletion of resources, but %s has planned deletion", rc.Address)) + } + } + resp.Error = result +} + +func ExpectNoDelete() plancheck.PlanCheck { + return expectNoDelete{} +} + // TestExtractResourceAttr navigates a test's state to find the specified resource (or data source) attribute and makes the value // accessible via the attributeValue string pointer. func TestExtractResourceAttr(resourceName string, attributeName string, attributeValue *string) resource.TestCheckFunc { diff --git a/google/services/compute/resource_compute_global_address.go b/google/services/compute/resource_compute_global_address.go index 492be98b7fa..06b541bcdcb 100644 --- a/google/services/compute/resource_compute_global_address.go +++ b/google/services/compute/resource_compute_global_address.go @@ -155,6 +155,12 @@ when purpose=PRIVATE_SERVICE_CONNECT`, 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}, }, + "label_fingerprint": { + Type: schema.TypeString, + Computed: true, + Description: `The fingerprint used for optimistic locking of this resource. Used +internally during updates.`, + }, "terraform_labels": { Type: schema.TypeMap, Computed: true, @@ -203,6 +209,12 @@ func resourceComputeGlobalAddressCreate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + labelFingerprintProp, err := expandComputeGlobalAddressLabelFingerprint(d.Get("label_fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("label_fingerprint"); !tpgresource.IsEmptyValue(reflect.ValueOf(labelFingerprintProp)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) { + obj["labelFingerprint"] = labelFingerprintProp + } ipVersionProp, err := expandComputeGlobalAddressIpVersion(d.Get("ip_version"), d, config) if err != nil { return err @@ -418,6 +430,9 @@ func resourceComputeGlobalAddressRead(d *schema.ResourceData, meta interface{}) if err := d.Set("labels", flattenComputeGlobalAddressLabels(res["labels"], d, config)); err != nil { return fmt.Errorf("Error reading GlobalAddress: %s", err) } + if err := d.Set("label_fingerprint", flattenComputeGlobalAddressLabelFingerprint(res["labelFingerprint"], d, config)); err != nil { + return fmt.Errorf("Error reading GlobalAddress: %s", err) + } if err := d.Set("ip_version", flattenComputeGlobalAddressIpVersion(res["ipVersion"], d, config)); err != nil { return fmt.Errorf("Error reading GlobalAddress: %s", err) } @@ -463,9 +478,15 @@ func resourceComputeGlobalAddressUpdate(d *schema.ResourceData, meta interface{} d.Partial(true) - if d.HasChange("effective_labels") { + if d.HasChange("label_fingerprint") || d.HasChange("effective_labels") { obj := make(map[string]interface{}) + labelFingerprintProp, err := expandComputeGlobalAddressLabelFingerprint(d.Get("label_fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("label_fingerprint"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) { + obj["labelFingerprint"] = labelFingerprintProp + } labelsProp, err := expandComputeGlobalAddressEffectiveLabels(d.Get("effective_labels"), d, config) if err != nil { return err @@ -621,6 +642,10 @@ func flattenComputeGlobalAddressLabels(v interface{}, d *schema.ResourceData, co return transformed } +func flattenComputeGlobalAddressLabelFingerprint(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenComputeGlobalAddressIpVersion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { return v } @@ -688,6 +713,10 @@ func expandComputeGlobalAddressName(v interface{}, d tpgresource.TerraformResour return v, nil } +func expandComputeGlobalAddressLabelFingerprint(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandComputeGlobalAddressIpVersion(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { return v, nil } diff --git a/google/services/compute/resource_compute_global_address_test.go b/google/services/compute/resource_compute_global_address_test.go index 9af2e5754c2..60b831a8b7e 100644 --- a/google/services/compute/resource_compute_global_address_test.go +++ b/google/services/compute/resource_compute_global_address_test.go @@ -8,8 +8,48 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" ) +func TestAccComputeGlobalAddress_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeGlobalAddressDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeGlobalAddress_update1(context), + }, + { + ResourceName: "google_compute_global_address.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + { + Config: testAccComputeGlobalAddress_update2(context), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_compute_global_address.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + }, + }) +} + func TestAccComputeGlobalAddress_ipv6(t *testing.T) { t.Parallel() @@ -76,3 +116,47 @@ resource "google_compute_global_address" "foobar" { } `, networkName, addressName) } + +func testAccComputeGlobalAddress_update1(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_compute_network" "foobar" { + name = "tf-test-address-%{random_suffix}" +} + +resource "google_compute_global_address" "foobar" { + address = "172.20.181.0" + description = "Description" + name = "tf-test-address-%{random_suffix}" + labels = { + foo = "bar" + } + ip_version = "IPV4" + prefix_length = 24 + address_type = "INTERNAL" + purpose = "VPC_PEERING" + network = google_compute_network.foobar.self_link +} +`, context) +} + +func testAccComputeGlobalAddress_update2(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_compute_network" "foobar" { + name = "tf-test-address-%{random_suffix}" +} + +resource "google_compute_global_address" "foobar" { + address = "172.20.181.0" + description = "Description" + name = "tf-test-address-%{random_suffix}" + labels = { + foo = "baz" + } + ip_version = "IPV4" + prefix_length = 24 + address_type = "INTERNAL" + purpose = "VPC_PEERING" + network = google_compute_network.foobar.self_link +} +`, context) +} diff --git a/website/docs/r/compute_global_address.html.markdown b/website/docs/r/compute_global_address.html.markdown index 35cca779475..d21100e9a3d 100644 --- a/website/docs/r/compute_global_address.html.markdown +++ b/website/docs/r/compute_global_address.html.markdown @@ -150,7 +150,6 @@ In addition to the arguments listed above, the following computed attributes are Creation timestamp in RFC3339 text format. * `label_fingerprint` - - ([Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) The fingerprint used for optimistic locking of this resource. Used internally during updates.