Skip to content

Commit 385d188

Browse files
Add ceritificateManagerCertificates field to ComputeTargetHttpsProxy resource (#9144) (#16179)
* Add ceritificateManagerCertificates field and handle the creation process using encoder and decoder * Add custom_expand function that constructs the full URL if the provided input was relative self_link * update the documetation of certificateManagerCertificates field * Update the example * Fix inconsistent spaces in the resource example * Remove unnecessary ruby template in the custom_expand function --------- [upstream:dc950684e5f0ee1389008bce9ff157ff62fe789d] Signed-off-by: Modular Magician <[email protected]>
1 parent 0a8915b commit 385d188

File tree

6 files changed

+296
-3
lines changed

6 files changed

+296
-3
lines changed

.changelog/9144.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
Add new field `certificateManagerCertificates` in the resource `ComputeTargetHttpsProxy`
3+
```

google/services/compute/resource_compute_target_https_proxy.go

+109-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"fmt"
2222
"log"
2323
"reflect"
24+
"regexp"
25+
"strings"
2426
"time"
2527

2628
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
@@ -72,6 +74,18 @@ character, which cannot be a dash.`,
7274
Description: `A reference to the UrlMap resource that defines the mapping from URL
7375
to the BackendService.`,
7476
},
77+
"certificate_manager_certificates": {
78+
Type: schema.TypeList,
79+
Optional: true,
80+
DiffSuppressFunc: tpgresource.CompareResourceNames,
81+
Description: `A list of Certificate Manager certificate URLs that are used to authenticate
82+
connections between users and the load balancer. At least one resource must be specified.
83+
Accepted format is '//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}' or just the self_link projects/{project}/locations/{location}/certificates/{resourceName}`,
84+
Elem: &schema.Schema{
85+
Type: schema.TypeString,
86+
},
87+
ConflictsWith: []string{"ssl_certificates"},
88+
},
7589
"certificate_map": {
7690
Type: schema.TypeString,
7791
Optional: true,
@@ -132,12 +146,13 @@ If left blank, communications are not encrypted.`,
132146
"ssl_certificates": {
133147
Type: schema.TypeList,
134148
Optional: true,
135-
Description: `A list of SslCertificate resource URLs or Certificate Manager certificate URLs that are used to authenticate
149+
Description: `A list of SslCertificate resource URLs that are used to authenticate
136150
connections between users and the load balancer. At least one resource must be specified.`,
137151
Elem: &schema.Schema{
138152
Type: schema.TypeString,
139153
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
140154
},
155+
ConflictsWith: []string{"certificate_manager_certificates"},
141156
},
142157
"ssl_policy": {
143158
Type: schema.TypeString,
@@ -198,6 +213,12 @@ func resourceComputeTargetHttpsProxyCreate(d *schema.ResourceData, meta interfac
198213
} else if v, ok := d.GetOkExists("quic_override"); !tpgresource.IsEmptyValue(reflect.ValueOf(quicOverrideProp)) && (ok || !reflect.DeepEqual(v, quicOverrideProp)) {
199214
obj["quicOverride"] = quicOverrideProp
200215
}
216+
certificateManagerCertificatesProp, err := expandComputeTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
217+
if err != nil {
218+
return err
219+
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(certificateManagerCertificatesProp)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
220+
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
221+
}
201222
sslCertificatesProp, err := expandComputeTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
202223
if err != nil {
203224
return err
@@ -241,6 +262,11 @@ func resourceComputeTargetHttpsProxyCreate(d *schema.ResourceData, meta interfac
241262
obj["serverTlsPolicy"] = serverTlsPolicyProp
242263
}
243264

265+
obj, err = resourceComputeTargetHttpsProxyEncoder(d, meta, obj)
266+
if err != nil {
267+
return err
268+
}
269+
244270
url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/global/targetHttpsProxies")
245271
if err != nil {
246272
return err
@@ -331,6 +357,18 @@ func resourceComputeTargetHttpsProxyRead(d *schema.ResourceData, meta interface{
331357
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeTargetHttpsProxy %q", d.Id()))
332358
}
333359

360+
res, err = resourceComputeTargetHttpsProxyDecoder(d, meta, res)
361+
if err != nil {
362+
return err
363+
}
364+
365+
if res == nil {
366+
// Decoding the object has resulted in it being gone. It may be marked deleted
367+
log.Printf("[DEBUG] Removing ComputeTargetHttpsProxy because it no longer exists.")
368+
d.SetId("")
369+
return nil
370+
}
371+
334372
if err := d.Set("project", project); err != nil {
335373
return fmt.Errorf("Error reading TargetHttpsProxy: %s", err)
336374
}
@@ -350,6 +388,9 @@ func resourceComputeTargetHttpsProxyRead(d *schema.ResourceData, meta interface{
350388
if err := d.Set("quic_override", flattenComputeTargetHttpsProxyQuicOverride(res["quicOverride"], d, config)); err != nil {
351389
return fmt.Errorf("Error reading TargetHttpsProxy: %s", err)
352390
}
391+
if err := d.Set("certificate_manager_certificates", flattenComputeTargetHttpsProxyCertificateManagerCertificates(res["certificateManagerCertificates"], d, config)); err != nil {
392+
return fmt.Errorf("Error reading TargetHttpsProxy: %s", err)
393+
}
353394
if err := d.Set("ssl_certificates", flattenComputeTargetHttpsProxySslCertificates(res["sslCertificates"], d, config)); err != nil {
354395
return fmt.Errorf("Error reading TargetHttpsProxy: %s", err)
355396
}
@@ -437,9 +478,15 @@ func resourceComputeTargetHttpsProxyUpdate(d *schema.ResourceData, meta interfac
437478
return err
438479
}
439480
}
440-
if d.HasChange("ssl_certificates") {
481+
if d.HasChange("certificate_manager_certificates") || d.HasChange("ssl_certificates") {
441482
obj := make(map[string]interface{})
442483

484+
certificateManagerCertificatesProp, err := expandComputeTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
485+
if err != nil {
486+
return err
487+
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
488+
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
489+
}
443490
sslCertificatesProp, err := expandComputeTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
444491
if err != nil {
445492
return err
@@ -721,6 +768,10 @@ func flattenComputeTargetHttpsProxyQuicOverride(v interface{}, d *schema.Resourc
721768
return v
722769
}
723770

771+
func flattenComputeTargetHttpsProxyCertificateManagerCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
772+
return v
773+
}
774+
724775
func flattenComputeTargetHttpsProxySslCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
725776
if v == nil {
726777
return v
@@ -786,6 +837,31 @@ func expandComputeTargetHttpsProxyQuicOverride(v interface{}, d tpgresource.Terr
786837
return v, nil
787838
}
788839

840+
func expandComputeTargetHttpsProxyCertificateManagerCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
841+
if v == nil {
842+
return nil, nil
843+
}
844+
l := v.([]interface{})
845+
req := make([]interface{}, 0, len(l))
846+
for _, raw := range l {
847+
if raw == nil {
848+
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: nil")
849+
}
850+
if strings.HasPrefix(raw.(string), "//") || strings.HasPrefix(raw.(string), "https://") {
851+
// Any full URL will be passed to the API request (regardless of the resource type). This is to allow self_links of CertificateManagerCeritificate resources.
852+
// If the full URL is an invalid reference, that should be handled by the API.
853+
req = append(req, raw.(string))
854+
} else if reg, _ := regexp.Compile("projects/(.*)/locations/(.*)/certificates/(.*)"); reg.MatchString(raw.(string)) {
855+
// 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.
856+
self_link := "https://certificatemanager.googleapis.com/v1/" + raw.(string)
857+
req = append(req, self_link)
858+
} else {
859+
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: %v is an invalid format for a certificateManagerCertificate resource", raw.(string))
860+
}
861+
}
862+
return req, nil
863+
}
864+
789865
func expandComputeTargetHttpsProxySslCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
790866
l := v.([]interface{})
791867
req := make([]interface{}, 0, len(l))
@@ -833,3 +909,34 @@ func expandComputeTargetHttpsProxyHttpKeepAliveTimeoutSec(v interface{}, d tpgre
833909
func expandComputeTargetHttpsProxyServerTlsPolicy(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
834910
return v, nil
835911
}
912+
913+
func resourceComputeTargetHttpsProxyEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
914+
915+
if _, ok := obj["certificateManagerCertificates"]; ok {
916+
// The field certificateManagerCertificates should not be included in the API request, and it should be renamed to `sslCertificates`
917+
// The API does not allow using both certificate manager certificates and sslCertificates. If that changes
918+
// in the future, the encoder logic should change accordingly because this will mean that both fields are no longer mutual exclusive.
919+
log.Printf("[DEBUG] converting the field CertificateManagerCertificates to sslCertificates before sending the request")
920+
obj["sslCertificates"] = obj["certificateManagerCertificates"]
921+
delete(obj, "certificateManagerCertificates")
922+
}
923+
return obj, nil
924+
}
925+
926+
func resourceComputeTargetHttpsProxyDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
927+
// Since both sslCertificates and certificateManagerCertificates maps to the same API field (sslCertificates), we need to check the types
928+
// of certificates that exist in the array and decide whether to change the field to certificateManagerCertificate or not.
929+
// The decoder logic depends on the fact that the API does not allow mixed type of certificates and it returns
930+
// certificate manager certificates in the format of //certificatemanager.googleapis.com/projects/*/locations/*/certificates/*
931+
if sslCertificates, ok := res["sslCertificates"].([]interface{}); ok && len(sslCertificates) > 0 {
932+
regPat, _ := regexp.Compile("//certificatemanager.googleapis.com/projects/(.*)/locations/(.*)/certificates/(.*)")
933+
934+
if regPat.MatchString(sslCertificates[0].(string)) {
935+
// It is enough to check only the type of one of the provided certificates beacuse all the certificates should be the same type.
936+
log.Printf("[DEBUG] The field sslCertificates contains certificateManagerCertificates, the field name will be converted to certificateManagerCertificates")
937+
res["certificateManagerCertificates"] = res["sslCertificates"]
938+
delete(res, "sslCertificates")
939+
}
940+
}
941+
return res, nil
942+
}

google/services/compute/resource_compute_target_https_proxy_generated_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,81 @@ resource "google_compute_http_health_check" "default" {
190190
`, context)
191191
}
192192

193+
func TestAccComputeTargetHttpsProxy_targetHttpsProxyCertificateManagerCertificateExample(t *testing.T) {
194+
t.Parallel()
195+
196+
context := map[string]interface{}{
197+
"random_suffix": acctest.RandString(t, 10),
198+
}
199+
200+
acctest.VcrTest(t, resource.TestCase{
201+
PreCheck: func() { acctest.AccTestPreCheck(t) },
202+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
203+
CheckDestroy: testAccCheckComputeTargetHttpsProxyDestroyProducer(t),
204+
Steps: []resource.TestStep{
205+
{
206+
Config: testAccComputeTargetHttpsProxy_targetHttpsProxyCertificateManagerCertificateExample(context),
207+
},
208+
{
209+
ResourceName: "google_compute_target_https_proxy.default",
210+
ImportState: true,
211+
ImportStateVerify: true,
212+
ImportStateVerifyIgnore: []string{"ssl_policy", "url_map", "server_tls_policy"},
213+
},
214+
},
215+
})
216+
}
217+
218+
func testAccComputeTargetHttpsProxy_targetHttpsProxyCertificateManagerCertificateExample(context map[string]interface{}) string {
219+
return acctest.Nprintf(`
220+
221+
resource "google_compute_target_https_proxy" "default" {
222+
name = "tf-test-target-http-proxy%{random_suffix}"
223+
url_map = google_compute_url_map.default.id
224+
certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable
225+
}
226+
227+
resource "google_certificate_manager_certificate" "default" {
228+
name = "tf-test-my-certificate%{random_suffix}"
229+
scope = "ALL_REGIONS"
230+
self_managed {
231+
pem_certificate = file("test-fixtures/cert.pem")
232+
pem_private_key = file("test-fixtures/private-key.pem")
233+
}
234+
}
235+
236+
resource "google_compute_url_map" "default" {
237+
name = "tf-test-url-map%{random_suffix}"
238+
description = "a description"
239+
240+
default_service = google_compute_backend_service.default.id
241+
242+
host_rule {
243+
hosts = ["mysite.com"]
244+
path_matcher = "allpaths"
245+
}
246+
247+
path_matcher {
248+
name = "allpaths"
249+
default_service = google_compute_backend_service.default.id
250+
251+
path_rule {
252+
paths = ["/*"]
253+
service = google_compute_backend_service.default.id
254+
}
255+
}
256+
}
257+
258+
resource "google_compute_backend_service" "default" {
259+
name = "tf-test-backend-service%{random_suffix}"
260+
port_name = "http"
261+
protocol = "HTTP"
262+
timeout_sec = 10
263+
load_balancing_scheme = "INTERNAL_MANAGED"
264+
}
265+
`, context)
266+
}
267+
193268
func testAccCheckComputeTargetHttpsProxyDestroyProducer(t *testing.T) func(s *terraform.State) error {
194269
return func(s *terraform.State) error {
195270
for name, rs := range s.RootModule().Resources {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDDzCCAfegAwIBAgIUDOiCLH9QNMMYnjPZVf4VwO9blsEwDQYJKoZIhvcNAQEL
3+
BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjIwODI0MDg0MDUxWhgPMzAy
4+
MTEyMjUwODQwNTFaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG
5+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvOT925GG4lKV9HvAHsbecMhGPAqjhVRC26iZ
6+
UJC8oSWOu95lWJSX5ZhbiF6Nz192wDGV/VAh3Lxj8RYtcn75eDxQKTcKouDld+To
7+
CGIStPFWbR6rbysLuZqFVEXVOTvp2QIegInfrvnGC4j7Qpic7zrFB9HzJx+0HpeE
8+
yO4gkdzJfEK/gMmolUgJrKX59o+0+Rj+Jq3EtcQxL1fVBVJSx0NvpoR1eYpnHMr/
9+
rJKZkUUZ2xE86hrtpiP6OEYQTi00rmf4GnZF5QfGGD0xuoQXtR7Tu+XhKibXIhxc
10+
D4RzPLX1QS040PXvmMPLDb4YlUQ6V3Rs42JDvkkDwIMXZvn8awIDAQABo1MwUTAd
11+
BgNVHQ4EFgQURuo1CCZZAUv7xi02f2nC5tRbf18wHwYDVR0jBBgwFoAURuo1CCZZ
12+
AUv7xi02f2nC5tRbf18wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
13+
AQEAqx3tDxurnYr9EUPhF5/LlDPYM+VI7EgrKdRnuIqUlZI0tm3vOGME0te6dBTC
14+
YLNaHLW3m/4Tm4M2eg0Kpz6CxJfn3109G31dCi0xwzSDHf5TPUWvqIVhq5WRgMIf
15+
n8KYBlQSmqdJBRztUIQH/UPFnSbxymlS4s5qwDgTH5ag9EEBcnWsQ2LZjKi0eqve
16+
MaqAvvB+j8RGZzYY4re94bSJI42zIZ6nMWPtXwRuDc30xl/u+E0jWIgWbPwSd6Km
17+
3wnJnGiU2ezPGq3zEU+Rc39VVIFKQpciNeYuF3neHPJvYOf58qW2Z8s0VH0MR1x3
18+
3DoO/e30FIr9j+PRD+s5BPKF2A==
19+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC85P3bkYbiUpX0
3+
e8Aext5wyEY8CqOFVELbqJlQkLyhJY673mVYlJflmFuIXo3PX3bAMZX9UCHcvGPx
4+
Fi1yfvl4PFApNwqi4OV35OgIYhK08VZtHqtvKwu5moVURdU5O+nZAh6Aid+u+cYL
5+
iPtCmJzvOsUH0fMnH7Qel4TI7iCR3Ml8Qr+AyaiVSAmspfn2j7T5GP4mrcS1xDEv
6+
V9UFUlLHQ2+mhHV5imccyv+skpmRRRnbETzqGu2mI/o4RhBOLTSuZ/gadkXlB8YY
7+
PTG6hBe1HtO75eEqJtciHFwPhHM8tfVBLTjQ9e+Yw8sNvhiVRDpXdGzjYkO+SQPA
8+
gxdm+fxrAgMBAAECggEAV4/A24TQpV4KFBw/WSTvnRFBeXinB1mhamhztWR6hCrA
9+
SPcVPKQY632eRI8sJmpGxl3V/Ogl4khT/cA9jfstEl7G++v/WrRsupCaPLSVnlnX
10+
KdsTNgOauk1WK9P5PMA4rPcuA4Cl91riQpubeWn8KWsxRWg90i+Ak8PB8lBsOaB1
11+
QzjigWlrRWSpodaw0MBIMZFDL2BYK8HEr+wyATYIyGvDQc9zCnMQIQIZyEPYepLO
12+
04Dw17YcjgnoJ5gLAFiTvDrCpTMewud1RQzvW5TAvG2piw34sf3QMGPM7aXNrfuZ
13+
4ZPC/MwVQgq9Nc+jeDsjApQmJKJ+3a8OdIPU89ArTQKBgQDCpHHQe1RzpHmIx47/
14+
9N5r+NPBhh8flDYmvgi6zPeBfrAaLWhidS8c7Voa6HwvMxbhryDEvc0YqI3vllfy
15+
xnRF+DfSryozW0gjrkXDGoOzqOJ3EuQwLSJnyX6La2lmufqsRFazwYJ5sxcjoGHK
16+
/sbwZkIUj1ejuH44ve+ZJQFfpwKBgQD4cLJrJhqImUDhHZRx9jBvxyeHy/RjmHK6
17+
70xQVDi9ZqeExHwtoSbolhXKLB1RtBnw+t5Csy7IDNBDsbUg9fXU8KyCTIdmsyws
18+
bDb5hdKsUF76rkKzlpttiXMRVWGS3CMKWahBpnL3lFB3tdtmskemkBTXVn4VgKAH
19+
xk9XnZ11nQKBgDbQSJ0FnkrSzscOK984/ko50Kh3NNyXyIgwjBTPFASLwNweXX8c
20+
sR/cV7usLQy9vnvf7cJ6EQAYt5/5Httnt+bceBwE6EV+N1qVAWBoXx6BOQV/dHN8
21+
wmun+tMYdJ5RUZ6hwCjvHedX3/RQfjnEdhHNOl6/31Zj5mfkVU0zdqeRAoGAcvIh
22+
erXMfPr7K6y16+xOCMmKHqhc0F/OZXMmSdxNzEPcqe8GzU3MZLxcJIg4oH7FqdtI
23+
Tm/86w4Spd9owHFMZlNcXYTu+LNZcsw2u0gRayxcZXuO3OyHySxZEuIAHSTBCZ7l
24+
3EoY0zfJ6zk249MEl6n+GouoFmbGpBI6z3zbR3kCgYEAlCNZVH4uJrP5beTOZTTR
25+
VJRk7BXvEC6HsM140YtIN7NHy2GtzrgmmY/ZAFB/hX8Ft4ex2MxbIp3hvxroTqGn
26+
bfu7uv97NoPQqbjtc3Mz8h2IaXTVDUnWYY5gDu6rM2w+Z75/sWIGiTWrsdYX4ohb
27+
ujngzJ7Ew7GgKSboj6mtlVM=
28+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)