From 6bdc868bbfd3101fa16c9b21e02a67cfacdcd9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Beamonte?= Date: Fri, 13 Mar 2020 10:56:42 -0400 Subject: [PATCH 1/2] resources: WAF: handles properly when the target has been deleted on Cloudflare's side In the previous situation, when the target had been deleted, the Read accion would fail and return an error. However, this would be expected that when this happen, the object should be considered as not existing anymore and the process should continue. This thus fixes that and makes sure that we avoid erroring out when a WAF Rule, Group or Package has been deleted on Cloudflare's side. --- cloudflare/resource_cloudflare_waf_group.go | 9 +++- cloudflare/resource_cloudflare_waf_package.go | 8 +++- cloudflare/resource_cloudflare_waf_rule.go | 9 +++- cloudflare/utils.go | 46 +++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/cloudflare/resource_cloudflare_waf_group.go b/cloudflare/resource_cloudflare_waf_group.go index 196b1c05ba..46fb997169 100644 --- a/cloudflare/resource_cloudflare_waf_group.go +++ b/cloudflare/resource_cloudflare_waf_group.go @@ -59,7 +59,14 @@ func resourceCloudflareWAFGroupRead(d *schema.ResourceData, meta interface{}) er group, err := client.WAFGroup(zoneID, packageID, groupID) if err != nil { - return (err) + // 1002 is the 'Invalid or missing WAF Package ID' error + // 1003 is the 'Invalid or missing WAF Rule Set ID' error + if cloudflareErrorIsOneOfCodes(err, []int{1002, 1003}) { + d.SetId("") + return nil + } + + return err } // Only need to set mode as that is the only attribute that could have changed diff --git a/cloudflare/resource_cloudflare_waf_package.go b/cloudflare/resource_cloudflare_waf_package.go index f103da251f..666b10a853 100644 --- a/cloudflare/resource_cloudflare_waf_package.go +++ b/cloudflare/resource_cloudflare_waf_package.go @@ -59,7 +59,13 @@ func resourceCloudflareWAFPackageRead(d *schema.ResourceData, meta interface{}) pkg, err := client.WAFPackage(zoneID, packageID) if err != nil { - return (err) + // 1002 is the 'Invalid or missing WAF Package ID' error + if cloudflareErrorIsCode(err, 1002) { + d.SetId("") + return nil + } + + return err } d.Set("sensitivity", pkg.Sensitivity) diff --git a/cloudflare/resource_cloudflare_waf_rule.go b/cloudflare/resource_cloudflare_waf_rule.go index 07f4fcccd2..c064a52b30 100644 --- a/cloudflare/resource_cloudflare_waf_rule.go +++ b/cloudflare/resource_cloudflare_waf_rule.go @@ -59,7 +59,14 @@ func resourceCloudflareWAFRuleRead(d *schema.ResourceData, meta interface{}) err rule, err := client.WAFRule(zoneID, packageID, ruleID) if err != nil { - return (err) + // 1002 is the 'Invalid or missing WAF Package ID' error + // 1004 is the 'Invalid or missing WAF Rule ID' error + if cloudflareErrorIsOneOfCodes(err, []int{1002, 1004}) { + d.SetId("") + return nil + } + + return err } // Only need to set mode as that is the only attribute that could have changed diff --git a/cloudflare/utils.go b/cloudflare/utils.go index 682d405584..9c92343277 100644 --- a/cloudflare/utils.go +++ b/cloudflare/utils.go @@ -2,9 +2,14 @@ package cloudflare import ( "crypto/md5" + "encoding/json" "fmt" + "log" + "regexp" + "strings" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + errors "github.com/pkg/errors" ) func expandInterfaceToStringList(list interface{}) []string { @@ -62,3 +67,44 @@ func contains(slice []string, item string) bool { _, ok := set[item] return ok } + +type CloudflareAPIError struct { + Code int `json:"code"` + Message string `json:"message"` +} + +type CloudflareAPIErrorResponse struct { + Errors []CloudflareAPIError `json:"errors"` +} + +func cloudflareErrorIsCode(err error, code int) bool { + return cloudflareErrorIsOneOfCodes(err, []int{code}) +} + +func cloudflareErrorIsOneOfCodes(err error, codes []int) bool { + errorMsg := errors.Cause(err).Error() + + // We will parse the error message only if it's an error 400, in which + // case we need to verify the kind of error. + r := regexp.MustCompile(`^HTTP status 400: content "(.*)"$`) + submatchs := r.FindStringSubmatch(errorMsg) + if submatchs != nil { + jsonData := strings.Replace(submatchs[1], "\\\"", "\"", -1) + log.Printf("[DEBUG][cloudflareErrorIsCode] error matching status 400, content: %#v", jsonData) + + var cfer CloudflareAPIErrorResponse + unmarshalErr := json.Unmarshal([]byte(jsonData), &cfer) + + // We check that there is only one error and that its code + // matches what we expected + if unmarshalErr == nil && len(cfer.Errors) == 1 { + for _, code := range codes { + if cfer.Errors[0].Code == code { + return true + } + } + } + } + + return false +} From d3bf286f747cce35e0415d80e477142de10cef21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Beamonte?= Date: Mon, 16 Mar 2020 18:07:50 -0400 Subject: [PATCH 2/2] fixup! 6bdc868bbfd3101fa16c9b21e02a67cfacdcd9bd --- cloudflare/resource_cloudflare_waf_group.go | 13 ++++++++++--- cloudflare/resource_cloudflare_waf_package.go | 11 +++++++++-- cloudflare/resource_cloudflare_waf_rule.go | 13 ++++++++++--- cloudflare/utils.go | 4 ---- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/cloudflare/resource_cloudflare_waf_group.go b/cloudflare/resource_cloudflare_waf_group.go index 46fb997169..f384858f62 100644 --- a/cloudflare/resource_cloudflare_waf_group.go +++ b/cloudflare/resource_cloudflare_waf_group.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) +const CLOUDFLARE_INVALID_OR_REMOVED_WAF_RULE_SET_ID_ERROR = 1003 + func resourceCloudflareWAFGroup() *schema.Resource { return &schema.Resource{ Create: resourceCloudflareWAFGroupCreate, @@ -50,6 +52,13 @@ func resourceCloudflareWAFGroup() *schema.Resource { } } +func errorIsWAFGroupNotFound(err error) bool { + return cloudflareErrorIsOneOfCodes(err, []int{ + CLOUDFLARE_INVALID_OR_REMOVED_WAF_PACKAGE_ID_ERROR, + CLOUDFLARE_INVALID_OR_REMOVED_WAF_RULE_SET_ID_ERROR, + }) +} + func resourceCloudflareWAFGroupRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*cloudflare.API) @@ -59,9 +68,7 @@ func resourceCloudflareWAFGroupRead(d *schema.ResourceData, meta interface{}) er group, err := client.WAFGroup(zoneID, packageID, groupID) if err != nil { - // 1002 is the 'Invalid or missing WAF Package ID' error - // 1003 is the 'Invalid or missing WAF Rule Set ID' error - if cloudflareErrorIsOneOfCodes(err, []int{1002, 1003}) { + if errorIsWAFGroupNotFound(err) { d.SetId("") return nil } diff --git a/cloudflare/resource_cloudflare_waf_package.go b/cloudflare/resource_cloudflare_waf_package.go index 666b10a853..455c3b7f4c 100644 --- a/cloudflare/resource_cloudflare_waf_package.go +++ b/cloudflare/resource_cloudflare_waf_package.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) +const CLOUDFLARE_INVALID_OR_REMOVED_WAF_PACKAGE_ID_ERROR = 1002 + func resourceCloudflareWAFPackage() *schema.Resource { return &schema.Resource{ Create: resourceCloudflareWAFPackageCreate, @@ -51,6 +53,12 @@ func resourceCloudflareWAFPackage() *schema.Resource { } } +func errorIsWAFPackageNotFound(err error) bool { + return cloudflareErrorIsOneOfCodes(err, []int{ + CLOUDFLARE_INVALID_OR_REMOVED_WAF_PACKAGE_ID_ERROR, + }) +} + func resourceCloudflareWAFPackageRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*cloudflare.API) @@ -59,8 +67,7 @@ func resourceCloudflareWAFPackageRead(d *schema.ResourceData, meta interface{}) pkg, err := client.WAFPackage(zoneID, packageID) if err != nil { - // 1002 is the 'Invalid or missing WAF Package ID' error - if cloudflareErrorIsCode(err, 1002) { + if errorIsWAFPackageNotFound(err) { d.SetId("") return nil } diff --git a/cloudflare/resource_cloudflare_waf_rule.go b/cloudflare/resource_cloudflare_waf_rule.go index c064a52b30..d3a5f41851 100644 --- a/cloudflare/resource_cloudflare_waf_rule.go +++ b/cloudflare/resource_cloudflare_waf_rule.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) +const CLOUDFLARE_INVALID_OR_REMOVED_WAF_RULE_ID_ERROR = 1004 + func resourceCloudflareWAFRule() *schema.Resource { return &schema.Resource{ Create: resourceCloudflareWAFRuleCreate, @@ -50,6 +52,13 @@ func resourceCloudflareWAFRule() *schema.Resource { } } +func errorIsWAFRuleNotFound(err error) bool { + return cloudflareErrorIsOneOfCodes(err, []int{ + CLOUDFLARE_INVALID_OR_REMOVED_WAF_PACKAGE_ID_ERROR, + CLOUDFLARE_INVALID_OR_REMOVED_WAF_RULE_ID_ERROR, + }) +} + func resourceCloudflareWAFRuleRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*cloudflare.API) @@ -59,9 +68,7 @@ func resourceCloudflareWAFRuleRead(d *schema.ResourceData, meta interface{}) err rule, err := client.WAFRule(zoneID, packageID, ruleID) if err != nil { - // 1002 is the 'Invalid or missing WAF Package ID' error - // 1004 is the 'Invalid or missing WAF Rule ID' error - if cloudflareErrorIsOneOfCodes(err, []int{1002, 1004}) { + if errorIsWAFRuleNotFound(err) { d.SetId("") return nil } diff --git a/cloudflare/utils.go b/cloudflare/utils.go index 9c92343277..344120ab5f 100644 --- a/cloudflare/utils.go +++ b/cloudflare/utils.go @@ -77,10 +77,6 @@ type CloudflareAPIErrorResponse struct { Errors []CloudflareAPIError `json:"errors"` } -func cloudflareErrorIsCode(err error, code int) bool { - return cloudflareErrorIsOneOfCodes(err, []int{code}) -} - func cloudflareErrorIsOneOfCodes(err error, codes []int) bool { errorMsg := errors.Cause(err).Error()