From 4ca3bcb0b99b624fea8b23492b3744ada833d52d Mon Sep 17 00:00:00 2001 From: Nicky Semenza Date: Wed, 2 Feb 2022 14:53:09 -0800 Subject: [PATCH 1/4] update validation records/errors for `custom_hostname` and `certificate_pack` * `custom_hostname`: validation tokens are now an array (`validation_records`) instead of a top level, but the only top level record that was previously here was for cname validation, txt/http/email were entirely missing. * `custom_hostname`: also adds missing `validation_errors`, and `certificate_authority` * `certificate_pack`: adds `validation_errors` and `validation_records` with same format as custom hostnames. relies of lib behavior added in https://github.com/cloudflare/cloudflare-go/pull/796 --- .../resource_cloudflare_certificate_pack.go | 25 ++++++++ .../resource_cloudflare_custom_hostname.go | 58 ++++++++++++------ ...esource_cloudflare_custom_hostname_test.go | 5 +- .../schema_cloudflare_certificate_pack.go | 14 +++++ .../schema_cloudflare_custom_hostname.go | 16 +++-- cloudflare/schema_cloudflare_ssl.go | 60 +++++++++++++++++++ 6 files changed, 152 insertions(+), 26 deletions(-) create mode 100644 cloudflare/schema_cloudflare_ssl.go diff --git a/cloudflare/resource_cloudflare_certificate_pack.go b/cloudflare/resource_cloudflare_certificate_pack.go index ef3301836c..acc5c68117 100644 --- a/cloudflare/resource_cloudflare_certificate_pack.go +++ b/cloudflare/resource_cloudflare_certificate_pack.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "reflect" "strings" cloudflare "github.com/cloudflare/cloudflare-go" @@ -78,6 +79,30 @@ func resourceCloudflareCertificatePackRead(d *schema.ResourceData, meta interfac d.Set("type", certificatePack.Type) d.Set("hosts", expandStringListToSet(certificatePack.Hosts)) + if !reflect.ValueOf(certificatePack.ValidationErrors).IsNil() { + errors := []map[string]interface{}{} + for _, e := range certificatePack.ValidationErrors { + errors = append(errors, map[string]interface{}{"message": e.Message}) + } + d.Set("validation_errors", errors) + } + if !reflect.ValueOf(certificatePack.ValidationRecords).IsNil() { + records := []map[string]interface{}{} + for _, e := range certificatePack.ValidationRecords { + records = append(records, + map[string]interface{}{ + "cname_name": e.CnameName, + "cname_target": e.CnameTarget, + "txt_name": e.TxtName, + "txt_value": e.TxtValue, + "http_body": e.HTTPBody, + "http_url": e.HTTPUrl, + "emails": e.Emails, + }) + } + d.Set("validation_records", records) + } + return nil } diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index 21a4187143..769926ece5 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -40,15 +40,14 @@ func resourceCloudflareCustomHostnameRead(d *schema.ResourceData, meta interface var sslConfig []map[string]interface{} if !reflect.ValueOf(customHostname.SSL).IsNil() { - sslConfig = append(sslConfig, map[string]interface{}{ - "type": customHostname.SSL.Type, - "method": customHostname.SSL.Method, - "wildcard": customHostname.SSL.Wildcard, - "status": customHostname.SSL.Status, - "cname_target": customHostname.SSL.CnameTarget, - "cname_name": customHostname.SSL.CnameName, - "custom_certificate": customHostname.SSL.CustomCertificate, - "custom_key": customHostname.SSL.CustomKey, + ssl := map[string]interface{}{ + "type": customHostname.SSL.Type, + "method": customHostname.SSL.Method, + "wildcard": customHostname.SSL.Wildcard, + "status": customHostname.SSL.Status, + "certificate_authority": customHostname.SSL.CertificateAuthority, + "custom_certificate": customHostname.SSL.CustomCertificate, + "custom_key": customHostname.SSL.CustomKey, "settings": []map[string]interface{}{{ "http2": customHostname.SSL.Settings.HTTP2, "tls13": customHostname.SSL.Settings.TLS13, @@ -56,11 +55,35 @@ func resourceCloudflareCustomHostnameRead(d *schema.ResourceData, meta interface "ciphers": customHostname.SSL.Settings.Ciphers, "early_hints": customHostname.SSL.Settings.EarlyHints, }}, - }) + } + if !reflect.ValueOf(customHostname.SSL.ValidationErrors).IsNil() { + errors := []map[string]interface{}{} + for _, e := range customHostname.SSL.ValidationErrors { + errors = append(errors, map[string]interface{}{"message": e.Message}) + } + ssl["validation_errors"] = errors + } + if !reflect.ValueOf(customHostname.SSL.ValidationRecords).IsNil() { + records := []map[string]interface{}{} + for _, e := range customHostname.SSL.ValidationRecords { + records = append(records, + map[string]interface{}{ + "cname_name": e.CnameName, + "cname_target": e.CnameTarget, + "txt_name": e.TxtName, + "txt_value": e.TxtValue, + "http_body": e.HTTPBody, + "http_url": e.HTTPUrl, + "emails": e.Emails, + }) + } + ssl["validation_records"] = records + } + sslConfig = append(sslConfig, ssl) } if err := d.Set("ssl", sslConfig); err != nil { - return fmt.Errorf("failed to see ssl") + return fmt.Errorf("failed to set ssl") } ownershipVerificationCfg := map[string]interface{}{ @@ -153,13 +176,12 @@ func buildCustomHostname(d *schema.ResourceData) cloudflare.CustomHostname { if _, ok := d.GetOk("ssl"); ok { ch.SSL = &cloudflare.CustomHostnameSSL{ - Method: d.Get("ssl.0.method").(string), - Type: d.Get("ssl.0.type").(string), - Wildcard: &[]bool{d.Get("ssl.0.wildcard").(bool)}[0], - CnameTarget: d.Get("ssl.0.cname_target").(string), - CnameName: d.Get("ssl.0.cname_name").(string), - CustomCertificate: d.Get("ssl.0.custom_certificate").(string), - CustomKey: d.Get("ssl.0.custom_key").(string), + Method: d.Get("ssl.0.method").(string), + Type: d.Get("ssl.0.type").(string), + Wildcard: &[]bool{d.Get("ssl.0.wildcard").(bool)}[0], + CustomCertificate: d.Get("ssl.0.custom_certificate").(string), + CustomKey: d.Get("ssl.0.custom_key").(string), + CertificateAuthority: d.Get("ssl.0.certificate_authority").(string), Settings: cloudflare.CustomHostnameSSLSettings{ HTTP2: d.Get("ssl.0.settings.0.http2").(string), TLS13: d.Get("ssl.0.settings.0.tls13").(string), diff --git a/cloudflare/resource_cloudflare_custom_hostname_test.go b/cloudflare/resource_cloudflare_custom_hostname_test.go index e73cd3efdf..0c9f60aa88 100644 --- a/cloudflare/resource_cloudflare_custom_hostname_test.go +++ b/cloudflare/resource_cloudflare_custom_hostname_test.go @@ -197,6 +197,7 @@ func TestAccCloudflareCustomHostname_WithCustomSSLSettings(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.http2", "off"), resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.min_tls_version", "1.2"), resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.certificate_authority", "digicert"), resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.value"), resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.type"), resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.name"), @@ -380,8 +381,8 @@ func TestAccCloudflareCustomHostname_Import(t *testing.T) { ImportStateVerifyIgnore: []string{ "ssl.#", "ssl.0.certificate_authority", - "ssl.0.cname_name", - "ssl.0.cname_target", + "ssl.0.validation_records", + "ssl.0.validation_errors", "ssl.0.custom_certificate", "ssl.0.custom_key", "ssl.0.method", diff --git a/cloudflare/schema_cloudflare_certificate_pack.go b/cloudflare/schema_cloudflare_certificate_pack.go index 9ad4c25791..62a67fac95 100644 --- a/cloudflare/schema_cloudflare_certificate_pack.go +++ b/cloudflare/schema_cloudflare_certificate_pack.go @@ -41,8 +41,22 @@ func resourceCloudflareCertificatePackSchema() map[string]*schema.Schema { "certificate_authority": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"digicert", "lets_encrypt"}, false), + Default: nil, + }, + "validation_records": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: sslValidationRecordsSchema(), + }, + "validation_errors": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: sslValidationErrorsSchema(), }, "cloudflare_branding": { Type: schema.TypeBool, diff --git a/cloudflare/schema_cloudflare_custom_hostname.go b/cloudflare/schema_cloudflare_custom_hostname.go index f9bbdd02a1..2e980b3fa1 100644 --- a/cloudflare/schema_cloudflare_custom_hostname.go +++ b/cloudflare/schema_cloudflare_custom_hostname.go @@ -46,15 +46,19 @@ func resourceCloudflareCustomHostnameSchema() map[string]*schema.Schema { "certificate_authority": { Type: schema.TypeString, Optional: true, + Computed: true, ValidateFunc: validation.StringInSlice([]string{"lets_encrypt", "digicert"}, false), + Default: nil, }, - "cname_target": { - Type: schema.TypeString, - Optional: true, + "validation_records": { + Type: schema.TypeList, + Computed: true, + Elem: sslValidationRecordsSchema(), }, - "cname_name": { - Type: schema.TypeString, - Optional: true, + "validation_errors": { + Type: schema.TypeList, + Computed: true, + Elem: sslValidationErrorsSchema(), }, "wildcard": { Type: schema.TypeBool, diff --git a/cloudflare/schema_cloudflare_ssl.go b/cloudflare/schema_cloudflare_ssl.go new file mode 100644 index 0000000000..e6d402e331 --- /dev/null +++ b/cloudflare/schema_cloudflare_ssl.go @@ -0,0 +1,60 @@ +package cloudflare + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func sslValidationErrorsSchema() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "message": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} +func sslValidationRecordsSchema() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "cname_target": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "cname_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "txt_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "txt_value": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "http_url": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "http_body": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "emails": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} From a94eea4e52aaf8e2cb0cfa754449138769440306 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 11 Feb 2022 14:14:16 +1100 Subject: [PATCH 2/4] add CHANGELOG entry --- .changelog/1424.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changelog/1424.txt diff --git a/.changelog/1424.txt b/.changelog/1424.txt new file mode 100644 index 0000000000..665ef2a581 --- /dev/null +++ b/.changelog/1424.txt @@ -0,0 +1,11 @@ +```release-note:enhancement +resource/custom_hostname: validation tokens are now an array (`validation_records`) instead of a top level, but the only top level record that was previously here was for cname validation, txt/http/email were entirely missing. +``` + +```release-note:enhancement +resource/custom_hostname: also adds missing `validation_errors`, and `certificate_authority` +``` + +```release-note:enhancement +resource/certificate_pack: adds `validation_errors` and `validation_records` with same format as custom hostnames. +``` From 32b57ccf2f820582ea7c16184e79c53958405c0d Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 11 Feb 2022 14:39:10 +1100 Subject: [PATCH 3/4] add better error handling for error types --- .../resource_cloudflare_custom_hostname_fallback_origin.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go b/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go index 5032ade897..f021331039 100644 --- a/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go +++ b/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go @@ -64,10 +64,11 @@ func resourceCloudflareCustomHostnameFallbackOriginCreate(d *schema.ResourceData return resource.Retry(d.Timeout(schema.TimeoutDefault), func() *resource.RetryError { _, err := client.UpdateCustomHostnameFallbackOrigin(context.Background(), zoneID, fallbackOrigin) if err != nil { - if err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { + if errors.As(err, &cloudflare.APIRequestError{}) && err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { return resource.RetryableError(fmt.Errorf("expected custom hostname resource to be ready for modification but is still pending")) + } else { + return resource.NonRetryableError(errors.Wrap(err, "failed to create custom hostname fallback origin")) } - return resource.NonRetryableError(errors.Wrap(err, "failed to create custom hostname fallback origin")) } fallbackHostname, err := client.CustomHostnameFallbackOrigin(context.Background(), zoneID) @@ -104,7 +105,7 @@ func resourceCloudflareCustomHostnameFallbackOriginUpdate(d *schema.ResourceData return resource.Retry(d.Timeout(schema.TimeoutDefault), func() *resource.RetryError { _, err := client.UpdateCustomHostnameFallbackOrigin(context.Background(), zoneID, fallbackOrigin) if err != nil { - if err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { + if errors.As(err, &cloudflare.APIRequestError{}) && err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { return resource.RetryableError(fmt.Errorf("expected custom hostname resource to be ready for modification but is still pending")) } return resource.NonRetryableError(errors.Wrap(err, "failed to update custom hostname fallback origin")) From 17034032690f697643c9bd69069f5d2dbfec63eb Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 11 Feb 2022 15:16:23 +1100 Subject: [PATCH 4/4] clean up error wrapping handling --- ...rce_cloudflare_custom_hostname_fallback_origin.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go b/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go index f021331039..263d257738 100644 --- a/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go +++ b/cloudflare/resource_cloudflare_custom_hostname_fallback_origin.go @@ -31,7 +31,7 @@ func resourceCloudflareCustomHostnameFallbackOriginRead(d *schema.ResourceData, customHostnameFallbackOrigin, err := client.CustomHostnameFallbackOrigin(context.Background(), zoneID) if err != nil { - return errors.Wrap(err, fmt.Sprintf("error reading custom hostname fallback origin %q", zoneID)) + return fmt.Errorf("error reading custom hostname fallback origin %q: %w", zoneID, err) } d.Set("origin", customHostnameFallbackOrigin.Origin) @@ -46,7 +46,7 @@ func resourceCloudflareCustomHostnameFallbackOriginDelete(d *schema.ResourceData err := client.DeleteCustomHostnameFallbackOrigin(context.Background(), zoneID) if err != nil { - return errors.Wrap(err, "failed to delete custom hostname fallback origin") + return fmt.Errorf("failed to delete custom hostname fallback origin: %w", err) } return nil @@ -64,17 +64,18 @@ func resourceCloudflareCustomHostnameFallbackOriginCreate(d *schema.ResourceData return resource.Retry(d.Timeout(schema.TimeoutDefault), func() *resource.RetryError { _, err := client.UpdateCustomHostnameFallbackOrigin(context.Background(), zoneID, fallbackOrigin) if err != nil { + //nolint:errorlint if errors.As(err, &cloudflare.APIRequestError{}) && err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { return resource.RetryableError(fmt.Errorf("expected custom hostname resource to be ready for modification but is still pending")) } else { - return resource.NonRetryableError(errors.Wrap(err, "failed to create custom hostname fallback origin")) + return resource.NonRetryableError(fmt.Errorf("failed to create custom hostname fallback origin: %w", err)) } } fallbackHostname, err := client.CustomHostnameFallbackOrigin(context.Background(), zoneID) if err != nil { - return resource.NonRetryableError(fmt.Errorf("failed to fetch custom hostname: %s", err)) + return resource.NonRetryableError(fmt.Errorf("failed to fetch custom hostname: %w", err)) } // Address an eventual consistency issue where deleting a fallback hostname @@ -105,10 +106,11 @@ func resourceCloudflareCustomHostnameFallbackOriginUpdate(d *schema.ResourceData return resource.Retry(d.Timeout(schema.TimeoutDefault), func() *resource.RetryError { _, err := client.UpdateCustomHostnameFallbackOrigin(context.Background(), zoneID, fallbackOrigin) if err != nil { + //nolint:errorlint if errors.As(err, &cloudflare.APIRequestError{}) && err.(*cloudflare.APIRequestError).InternalErrorCodeIs(1414) { return resource.RetryableError(fmt.Errorf("expected custom hostname resource to be ready for modification but is still pending")) } - return resource.NonRetryableError(errors.Wrap(err, "failed to update custom hostname fallback origin")) + return resource.NonRetryableError(fmt.Errorf("failed to update custom hostname fallback origin: %w", err)) } resourceCloudflareCustomHostnameFallbackOriginRead(d, meta)