Skip to content

Adding Labels to Interconnect Attachments #9095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/12755.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
compute: adding Labels field to resource_compute_interconnect_attachment
```
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func ResourceComputeInterconnectAttachment() *schema.Resource {
},

CustomizeDiff: customdiff.All(
tpgresource.SetLabelsDiff,
tpgresource.DefaultProviderProject,
),

Expand Down Expand Up @@ -197,6 +198,17 @@ allocated from regional external IP address pool.`,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
},
},
"labels": {
Type: schema.TypeMap,
Optional: true,
Description: `Labels for this resource. These can only be added or modified by the setLabels
method. Each label key/value pair must comply with RFC1035. Label values may be empty.


**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},
},
"mtu": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -278,11 +290,26 @@ router subinterface for this interconnect attachment.`,
Description: `IPv6 address + prefix length to be configured on the customer
router subinterface for this interconnect attachment.`,
},
"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},
},
"google_reference_id": {
Type: schema.TypeString,
Computed: true,
Description: `Google reference ID, to be used when raising support tickets with
Google or otherwise to debug backend connectivity issues.`,
},
"label_fingerprint": {
Type: schema.TypeString,
Computed: true,
Description: `A fingerprint for the labels being applied to this Interconnect, which is essentially a hash
of the labels set used for optimistic locking. The fingerprint is initially generated by
Compute Engine and changes after every request to modify or update labels.
You must always provide an up-to-date fingerprint hash in order to update or change labels,
otherwise the request will fail with error 412 conditionNotMet.`,
},
"pairing_key": {
Type: schema.TypeString,
Expand Down Expand Up @@ -319,6 +346,13 @@ Google and the customer, going to and from this network and region.`,
Computed: true,
Description: `[Output Only] The current state of this attachment's functionality.`,
},
"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},
},
"project": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -432,6 +466,18 @@ func resourceComputeInterconnectAttachmentCreate(d *schema.ResourceData, meta in
} else if v, ok := d.GetOkExists("subnet_length"); !tpgresource.IsEmptyValue(reflect.ValueOf(subnetLengthProp)) && (ok || !reflect.DeepEqual(v, subnetLengthProp)) {
obj["subnetLength"] = subnetLengthProp
}
labelFingerprintProp, err := expandComputeInterconnectAttachmentLabelFingerprint(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
}
labelsProp, err := expandComputeInterconnectAttachmentEffectiveLabels(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
}
regionProp, err := expandComputeInterconnectAttachmentRegion(d.Get("region"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -490,6 +536,66 @@ func resourceComputeInterconnectAttachmentCreate(d *schema.ResourceData, meta in
return fmt.Errorf("Error waiting to create InterconnectAttachment: %s", err)
}

if v, ok := d.GetOkExists("effective_labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
labels := d.Get("labels")
terraformLables := d.Get("terraform_labels")

// Labels cannot be set in a create. We'll have to set them here.
err = resourceComputeInterconnectAttachmentRead(d, meta)
if err != nil {
return err
}

obj := make(map[string]interface{})
// d.Get("effective_labels") will have been overridden by the Read call.
labelsProp, err := expandComputeInterconnectAttachmentEffectiveLabels(v, d, config)
if err != nil {
return err
}
obj["labels"] = labelsProp
labelFingerprintProp := d.Get("label_fingerprint")
obj["labelFingerprint"] = labelFingerprintProp

url, err = tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/interconnectAttachments/{{name}}/setLabels")
if err != nil {
return err
}
res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "POST",
Project: project,
RawURL: url,
UserAgent: userAgent,
Body: obj,
})
if err != nil {
return fmt.Errorf("Error adding labels to ComputeInterconnectAttachment %q: %s", d.Id(), err)
}

err = ComputeOperationWaitTime(
config, res, project, "Updating ComputeInterconnectAttachment Labels", userAgent,
d.Timeout(schema.TimeoutUpdate))

if err != nil {
return err
}

// Set back the labels field, as it is needed to decide the value of "labels" in the state in the read function.
if err := d.Set("labels", labels); err != nil {
return fmt.Errorf("Error setting back labels: %s", err)
}

// Set back the terraform_labels field, as it is needed to decide the value of "terraform_labels" in the state in the read function.
if err := d.Set("terraform_labels", terraformLables); err != nil {
return fmt.Errorf("Error setting back terraform_labels: %s", err)
}

// Set back the effective_labels field, as it is needed to decide the value of "effective_labels" in the state in the read function.
if err := d.Set("effective_labels", v); err != nil {
return fmt.Errorf("Error setting back effective_labels: %s", err)
}
}

if err := waitForAttachmentToBeProvisioned(d, config, d.Timeout(schema.TimeoutCreate)); err != nil {
return fmt.Errorf("Error waiting for InterconnectAttachment %q to be provisioned: %q", d.Get("name").(string), err)
}
Expand Down Expand Up @@ -610,6 +716,18 @@ func resourceComputeInterconnectAttachmentRead(d *schema.ResourceData, meta inte
if err := d.Set("customer_router_ipv6_address", flattenComputeInterconnectAttachmentCustomerRouterIpv6Address(res["customerRouterIpv6Address"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
if err := d.Set("labels", flattenComputeInterconnectAttachmentLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
if err := d.Set("label_fingerprint", flattenComputeInterconnectAttachmentLabelFingerprint(res["labelFingerprint"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
if err := d.Set("terraform_labels", flattenComputeInterconnectAttachmentTerraformLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
if err := d.Set("effective_labels", flattenComputeInterconnectAttachmentEffectiveLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
if err := d.Set("region", flattenComputeInterconnectAttachmentRegion(res["region"], d, config)); err != nil {
return fmt.Errorf("Error reading InterconnectAttachment: %s", err)
}
Expand Down Expand Up @@ -710,6 +828,61 @@ func resourceComputeInterconnectAttachmentUpdate(d *schema.ResourceData, meta in
if err != nil {
return err
}
d.Partial(true)

if d.HasChange("label_fingerprint") || d.HasChange("effective_labels") {
obj := make(map[string]interface{})

labelFingerprintProp, err := expandComputeInterconnectAttachmentLabelFingerprint(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 := expandComputeInterconnectAttachmentEffectiveLabels(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
}

url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/interconnectAttachments/{{name}}/setLabels")
if err != nil {
return err
}

headers := make(http.Header)

// 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: "POST",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: obj,
Timeout: d.Timeout(schema.TimeoutUpdate),
Headers: headers,
})
if err != nil {
return fmt.Errorf("Error updating InterconnectAttachment %q: %s", d.Id(), err)
} else {
log.Printf("[DEBUG] Finished updating InterconnectAttachment %q: %#v", d.Id(), res)
}

err = ComputeOperationWaitTime(
config, res, project, "Updating InterconnectAttachment", userAgent,
d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}
}

d.Partial(false)

return resourceComputeInterconnectAttachmentRead(d, meta)
}
Expand Down Expand Up @@ -939,6 +1112,44 @@ func flattenComputeInterconnectAttachmentCustomerRouterIpv6Address(v interface{}
return v
}

func flattenComputeInterconnectAttachmentLabels(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 flattenComputeInterconnectAttachmentLabelFingerprint(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenComputeInterconnectAttachmentTerraformLabels(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 flattenComputeInterconnectAttachmentEffectiveLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenComputeInterconnectAttachmentRegion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
Expand Down Expand Up @@ -1022,6 +1233,21 @@ func expandComputeInterconnectAttachmentSubnetLength(v interface{}, d tpgresourc
return v, nil
}

func expandComputeInterconnectAttachmentLabelFingerprint(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandComputeInterconnectAttachmentEffectiveLabels(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 expandComputeInterconnectAttachmentRegion(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
f, err := tpgresource.ParseGlobalFieldValue("regions", v.(string), "project", d, config, true)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestAccComputeInterconnectAttachment_interconnectAttachmentBasicExample(t *
ResourceName: "google_compute_interconnect_attachment.on_prem",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"candidate_subnets", "region", "router", "subnet_length"},
ImportStateVerifyIgnore: []string{"candidate_subnets", "labels", "region", "router", "subnet_length", "terraform_labels"},
},
},
})
Expand All @@ -63,6 +63,7 @@ resource "google_compute_interconnect_attachment" "on_prem" {
type = "PARTNER"
router = google_compute_router.foobar.id
mtu = 1500
labels = { mykey = "myvalue" }
}

resource "google_compute_router" "foobar" {
Expand Down Expand Up @@ -99,7 +100,7 @@ func TestAccComputeInterconnectAttachment_interconnectAttachmentDedicatedExample
ResourceName: "google_compute_interconnect_attachment.on_prem",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"candidate_subnets", "region", "router", "subnet_length"},
ImportStateVerifyIgnore: []string{"candidate_subnets", "labels", "region", "router", "subnet_length", "terraform_labels"},
},
},
})
Expand Down Expand Up @@ -128,6 +129,7 @@ resource "google_compute_interconnect_attachment" "on_prem" {
vlan_tag8021q = 1000
region = "https://www.googleapis.com/compute/v1/projects/${data.google_project.project.name}/regions/us-east4"
stack_type = "IPV4_ONLY"
labels = { mykey = "myvalue" }
}

resource "google_compute_router" "foobar" {
Expand Down Expand Up @@ -165,7 +167,7 @@ func TestAccComputeInterconnectAttachment_computeInterconnectAttachmentIpsecEncr
ResourceName: "google_compute_interconnect_attachment.ipsec-encrypted-interconnect-attachment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"candidate_subnets", "region", "router", "subnet_length"},
ImportStateVerifyIgnore: []string{"candidate_subnets", "labels", "region", "router", "subnet_length", "terraform_labels"},
},
},
})
Expand Down
23 changes: 23 additions & 0 deletions website/docs/r/compute_interconnect_attachment.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ resource "google_compute_interconnect_attachment" "on_prem" {
type = "PARTNER"
router = google_compute_router.foobar.id
mtu = 1500
labels = { mykey = "myvalue" }
}

resource "google_compute_router" "foobar" {
Expand Down Expand Up @@ -233,6 +234,14 @@ The following arguments are supported:
requesting 29 returns an error. Where both 29 and 30 are allowed, 29 is preferred, because it
gives Google Cloud Support more debugging visibility.

* `labels` -
(Optional)
Labels for this resource. These can only be added or modified by the setLabels
method. Each label key/value pair must comply with RFC1035. Label values may be empty.

**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.

* `region` -
(Optional)
Region where the regional interconnect attachment resides.
Expand Down Expand Up @@ -287,6 +296,20 @@ In addition to the arguments listed above, the following computed attributes are
* `customer_router_ipv6_address` -
IPv6 address + prefix length to be configured on the customer
router subinterface for this interconnect attachment.

* `label_fingerprint` -
A fingerprint for the labels being applied to this Interconnect, which is essentially a hash
of the labels set used for optimistic locking. The fingerprint is initially generated by
Compute Engine and changes after every request to modify or update labels.
You must always provide an up-to-date fingerprint hash in order to update or change labels,
otherwise the request will fail with error 412 conditionNotMet.

* `terraform_labels` -
The combination of labels configured directly on the resource
and default labels configured on the provider.

* `effective_labels` -
All of labels (key/value pairs) present on the resource in GCP, including the labels configured through Terraform, other clients and services.
* `self_link` - The URI of the created resource.


Expand Down
Loading