Skip to content
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

resources: WAF: handles properly when the target has been deleted on Cloudflare's side #623

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
16 changes: 15 additions & 1 deletion cloudflare/resource_cloudflare_waf_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

const CLOUDFLARE_INVALID_OR_REMOVED_WAF_RULE_SET_ID_ERROR = 1003
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I put those into their own file or is that fine to have per-file constants here ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to start here and we can always extract them out and DRY them up if needed.


func resourceCloudflareWAFGroup() *schema.Resource {
return &schema.Resource{
Create: resourceCloudflareWAFGroupCreate,
Expand Down Expand Up @@ -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)

Expand All @@ -59,7 +68,12 @@ func resourceCloudflareWAFGroupRead(d *schema.ResourceData, meta interface{}) er

group, err := client.WAFGroup(zoneID, packageID, groupID)
if err != nil {
return (err)
if errorIsWAFGroupNotFound(err) {
d.SetId("")
return nil
}

return err
}

// Only need to set mode as that is the only attribute that could have changed
Expand Down
15 changes: 14 additions & 1 deletion cloudflare/resource_cloudflare_waf_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)

Expand All @@ -59,7 +67,12 @@ func resourceCloudflareWAFPackageRead(d *schema.ResourceData, meta interface{})

pkg, err := client.WAFPackage(zoneID, packageID)
if err != nil {
return (err)
if errorIsWAFPackageNotFound(err) {
d.SetId("")
return nil
}

return err
}

d.Set("sensitivity", pkg.Sensitivity)
Expand Down
16 changes: 15 additions & 1 deletion cloudflare/resource_cloudflare_waf_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)

Expand All @@ -59,7 +68,12 @@ func resourceCloudflareWAFRuleRead(d *schema.ResourceData, meta interface{}) err

rule, err := client.WAFRule(zoneID, packageID, ruleID)
if err != nil {
return (err)
if errorIsWAFRuleNotFound(err) {
d.SetId("")
return nil
}

return err
}

// Only need to set mode as that is the only attribute that could have changed
Expand Down
42 changes: 42 additions & 0 deletions cloudflare/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -62,3 +67,40 @@ 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 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
}