Skip to content

Add ceritificateManagerCertificates field to ComputeRegionTargetHttpsProxy resource #17365

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/10011.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
compute: added `certificate_manager_certificates` field to `google_compute_region_target_https_proxy` resource
```
132 changes: 120 additions & 12 deletions google/services/compute/resource_compute_region_target_https_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"fmt"
"log"
"reflect"
"regexp"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
Expand Down Expand Up @@ -64,24 +66,26 @@ first character must be a lowercase letter, and all following
characters must be a dash, lowercase letter, or digit, except the last
character, which cannot be a dash.`,
},
"ssl_certificates": {
Type: schema.TypeList,
Required: true,
Description: `A list of RegionSslCertificate resources that are used to authenticate
connections between users and the load balancer. Currently, exactly
one SSL certificate must be specified.`,
Elem: &schema.Schema{
Type: schema.TypeString,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
},
},
"url_map": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
Description: `A reference to the RegionUrlMap resource that defines the mapping from URL
to the RegionBackendService.`,
},
"certificate_manager_certificates": {
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: tpgresource.CompareResourceNames,
Description: `URLs to certificate manager certificate resources that are used to authenticate connections between users and the load balancer.
Currently, you may specify up to 15 certificates. Certificate manager certificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.
sslCertificates and certificateManagerCertificates fields can not be defined together.
Accepted format is '//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}' or just the self_link 'projects/{project}/locations/{location}/certificates/{resourceName}'`,
Elem: &schema.Schema{
Type: schema.TypeString,
},
ConflictsWith: []string{"ssl_certificates"},
},
"description": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -97,6 +101,18 @@ to the RegionBackendService.`,
Description: `The Region in which the created target https proxy should reside.
If it is not provided, the provider region is used.`,
},
"ssl_certificates": {
Type: schema.TypeList,
Optional: true,
Description: `URLs to SslCertificate resources that are used to authenticate connections between users and the load balancer.
At least one SSL certificate must be specified. Currently, you may specify up to 15 SSL certificates.
sslCertificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.`,
Elem: &schema.Schema{
Type: schema.TypeString,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
},
ConflictsWith: []string{"certificate_manager_certificates"},
},
"ssl_policy": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -151,6 +167,12 @@ func resourceComputeRegionTargetHttpsProxyCreate(d *schema.ResourceData, meta in
} else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
obj["name"] = nameProp
}
certificateManagerCertificatesProp, err := expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(certificateManagerCertificatesProp)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
}
sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
if err != nil {
return err
Expand All @@ -176,6 +198,11 @@ func resourceComputeRegionTargetHttpsProxyCreate(d *schema.ResourceData, meta in
obj["region"] = regionProp
}

obj, err = resourceComputeRegionTargetHttpsProxyEncoder(d, meta, obj)
if err != nil {
return err
}

url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies")
if err != nil {
return err
Expand Down Expand Up @@ -266,6 +293,18 @@ func resourceComputeRegionTargetHttpsProxyRead(d *schema.ResourceData, meta inte
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeRegionTargetHttpsProxy %q", d.Id()))
}

res, err = resourceComputeRegionTargetHttpsProxyDecoder(d, meta, res)
if err != nil {
return err
}

if res == nil {
// Decoding the object has resulted in it being gone. It may be marked deleted
log.Printf("[DEBUG] Removing ComputeRegionTargetHttpsProxy because it no longer exists.")
d.SetId("")
return nil
}

if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
}
Expand All @@ -282,6 +321,9 @@ func resourceComputeRegionTargetHttpsProxyRead(d *schema.ResourceData, meta inte
if err := d.Set("name", flattenComputeRegionTargetHttpsProxyName(res["name"], d, config)); err != nil {
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
}
if err := d.Set("certificate_manager_certificates", flattenComputeRegionTargetHttpsProxyCertificateManagerCertificates(res["certificateManagerCertificates"], d, config)); err != nil {
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
}
if err := d.Set("ssl_certificates", flattenComputeRegionTargetHttpsProxySslCertificates(res["sslCertificates"], d, config)); err != nil {
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
}
Expand Down Expand Up @@ -318,9 +360,15 @@ func resourceComputeRegionTargetHttpsProxyUpdate(d *schema.ResourceData, meta in

d.Partial(true)

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

certificateManagerCertificatesProp, err := expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
}
sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -511,6 +559,10 @@ func flattenComputeRegionTargetHttpsProxyName(v interface{}, d *schema.ResourceD
return v
}

func flattenComputeRegionTargetHttpsProxyCertificateManagerCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenComputeRegionTargetHttpsProxySslCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
Expand Down Expand Up @@ -547,6 +599,31 @@ func expandComputeRegionTargetHttpsProxyName(v interface{}, d tpgresource.Terraf
return v, nil
}

func expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
if v == nil {
return nil, nil
}
l := v.([]interface{})
req := make([]interface{}, 0, len(l))
for _, raw := range l {
if raw == nil {
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: nil")
}
if strings.HasPrefix(raw.(string), "//") || strings.HasPrefix(raw.(string), "https://") {
// Any full URL will be passed to the API request (regardless of the resource type). This is to allow self_links of CertificateManagerCeritificate resources.
// If the full URL is an invalid reference, that should be handled by the API.
req = append(req, raw.(string))
} else if reg, _ := regexp.Compile("projects/(.*)/locations/(.*)/certificates/(.*)"); reg.MatchString(raw.(string)) {
// If the input is the id pattern of CertificateManagerCertificate resource, a prefix will be added to construct the full URL before constructing the API request.
self_link := "https://certificatemanager.googleapis.com/v1/" + raw.(string)
req = append(req, self_link)
} else {
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: %v is an invalid format for a certificateManagerCertificate resource", raw.(string))
}
}
return req, nil
}

func expandComputeRegionTargetHttpsProxySslCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
l := v.([]interface{})
req := make([]interface{}, 0, len(l))
Expand Down Expand Up @@ -586,3 +663,34 @@ func expandComputeRegionTargetHttpsProxyRegion(v interface{}, d tpgresource.Terr
}
return f.RelativeLink(), nil
}

func resourceComputeRegionTargetHttpsProxyEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {

if _, ok := obj["certificateManagerCertificates"]; ok {
// The field certificateManagerCertificates should not be included in the API request, and it should be renamed to `sslCertificates`
// The API does not allow using both certificate manager certificates and sslCertificates. If that changes
// in the future, the encoder logic should change accordingly because this will mean that both fields are no longer mutual exclusive.
log.Printf("[DEBUG] converting the field CertificateManagerCertificates to sslCertificates before sending the request")
obj["sslCertificates"] = obj["certificateManagerCertificates"]
delete(obj, "certificateManagerCertificates")
}
return obj, nil
}

func resourceComputeRegionTargetHttpsProxyDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
// Since both sslCertificates and certificateManagerCertificates maps to the same API field (sslCertificates), we need to check the types
// of certificates that exist in the array and decide whether to change the field to certificateManagerCertificate or not.
// The decoder logic depends on the fact that the API does not allow mixed type of certificates and it returns
// certificate manager certificates in the format of //certificatemanager.googleapis.com/projects/*/locations/*/certificates/*
if sslCertificates, ok := res["sslCertificates"].([]interface{}); ok && len(sslCertificates) > 0 {
regPat, _ := regexp.Compile("//certificatemanager.googleapis.com/projects/(.*)/locations/(.*)/certificates/(.*)")

if regPat.MatchString(sslCertificates[0].(string)) {
// It is enough to check only the type of one of the provided certificates beacuse all the certificates should be the same type.
log.Printf("[DEBUG] The field sslCertificates contains certificateManagerCertificates, the field name will be converted to certificateManagerCertificates")
res["certificateManagerCertificates"] = res["sslCertificates"]
delete(res, "sslCertificates")
}
}
return res, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,64 @@ resource "google_compute_region_health_check" "default" {
`, context)
}

func TestAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(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: testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(context),
},
{
ResourceName: "google_compute_region_target_https_proxy.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"ssl_policy", "url_map", "region"},
},
},
})
}

func testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_compute_region_target_https_proxy" "default" {
name = "tf-test-target-http-proxy%{random_suffix}"
url_map = google_compute_region_url_map.default.id
certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable
}

resource "google_certificate_manager_certificate" "default" {
name = "tf-test-my-certificate%{random_suffix}"
location = "us-central1"
self_managed {
pem_certificate = file("test-fixtures/cert.pem")
pem_private_key = file("test-fixtures/private-key.pem")
}
}

resource "google_compute_region_url_map" "default" {
name = "tf-test-url-map%{random_suffix}"
default_service = google_compute_region_backend_service.default.id
region = "us-central1"
}

resource "google_compute_region_backend_service" "default" {
name = "tf-test-backend-service%{random_suffix}"
region = "us-central1"
protocol = "HTTPS"
timeout_sec = 30
load_balancing_scheme = "INTERNAL_MANAGED"
}
`, context)
}

func testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
Expand Down
57 changes: 51 additions & 6 deletions website/docs/r/compute_region_target_https_proxy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,44 @@ resource "google_compute_region_health_check" "default" {
}
}
```
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.jpy.wang%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=region_target_https_proxy_certificate_manager_certificate&cloudshell_image=gcr.io%2Fcloudshell-images%2Fcloudshell%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
</a>
</div>
## Example Usage - Region Target Https Proxy Certificate Manager Certificate


```hcl
resource "google_compute_region_target_https_proxy" "default" {
name = "target-http-proxy"
url_map = google_compute_region_url_map.default.id
certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable
}

resource "google_certificate_manager_certificate" "default" {
name = "my-certificate"
location = "us-central1"
self_managed {
pem_certificate = file("test-fixtures/cert.pem")
pem_private_key = file("test-fixtures/private-key.pem")
}
}

resource "google_compute_region_url_map" "default" {
name = "url-map"
default_service = google_compute_region_backend_service.default.id
region = "us-central1"
}

resource "google_compute_region_backend_service" "default" {
name = "backend-service"
region = "us-central1"
protocol = "HTTPS"
timeout_sec = 30
load_balancing_scheme = "INTERNAL_MANAGED"
}
```

## Argument Reference

Expand All @@ -110,12 +148,6 @@ The following arguments are supported:
characters must be a dash, lowercase letter, or digit, except the last
character, which cannot be a dash.

* `ssl_certificates` -
(Required)
A list of RegionSslCertificate resources that are used to authenticate
connections between users and the load balancer. Currently, exactly
one SSL certificate must be specified.

* `url_map` -
(Required)
A reference to the RegionUrlMap resource that defines the mapping from URL
Expand All @@ -129,6 +161,19 @@ The following arguments are supported:
(Optional)
An optional description of this resource.

* `certificate_manager_certificates` -
(Optional)
URLs to certificate manager certificate resources that are used to authenticate connections between users and the load balancer.
Currently, you may specify up to 15 certificates. Certificate manager certificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.
sslCertificates and certificateManagerCertificates fields can not be defined together.
Accepted format is `//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}` or just the self_link `projects/{project}/locations/{location}/certificates/{resourceName}`

* `ssl_certificates` -
(Optional)
URLs to SslCertificate resources that are used to authenticate connections between users and the load balancer.
At least one SSL certificate must be specified. Currently, you may specify up to 15 SSL certificates.
sslCertificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.

* `ssl_policy` -
(Optional)
A reference to the Region SslPolicy resource that will be associated with
Expand Down