Skip to content

Commit

Permalink
Merge pull request #2177 from troymjones/add_score_per_period
Browse files Browse the repository at this point in the history
Add support for score_per_period and score_response_header_name
  • Loading branch information
jacobbednarz authored Feb 1, 2023
2 parents 5444069 + 4d72d6a commit ffa55f8
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .changelog/2177.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/cloudflare_ruleset: add support for `score_per_period` and `score_response_header_name`
```
2 changes: 2 additions & 0 deletions docs/resources/ruleset.md
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,8 @@ Optional:
- `period` (Number) The period of time to consider (in seconds) when evaluating the request rate.
- `requests_per_period` (Number) The number of requests over the period of time that will trigger the Rate Limiting rule.
- `requests_to_origin` (Boolean) Whether to include requests to origin within the Rate Limiting count.
- `score_per_period` (Number) The maximum aggregate score over the period of time that will trigger Rate Limiting rule.
- `score_response_header_name` (String) Name of HTTP header in the response, set by the origin server, with the score for the current request.

## Import

Expand Down
18 changes: 12 additions & 6 deletions internal/sdkv2provider/resource_cloudflare_ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,12 +621,14 @@ func buildStateFromRulesetRules(rules []cloudflare.RulesetRule) interface{} {
var rateLimit []map[string]interface{}

rateLimit = append(rateLimit, map[string]interface{}{
"characteristics": r.RateLimit.Characteristics,
"period": r.RateLimit.Period,
"requests_per_period": r.RateLimit.RequestsPerPeriod,
"mitigation_timeout": r.RateLimit.MitigationTimeout,
"counting_expression": r.RateLimit.CountingExpression,
"requests_to_origin": r.RateLimit.RequestsToOrigin,
"characteristics": r.RateLimit.Characteristics,
"period": r.RateLimit.Period,
"requests_per_period": r.RateLimit.RequestsPerPeriod,
"score_per_period": r.RateLimit.ScorePerPeriod,
"score_response_header_name": r.RateLimit.ScoreResponseHeaderName,
"mitigation_timeout": r.RateLimit.MitigationTimeout,
"counting_expression": r.RateLimit.CountingExpression,
"requests_to_origin": r.RateLimit.RequestsToOrigin,
})

rule["ratelimit"] = rateLimit
Expand Down Expand Up @@ -1264,6 +1266,10 @@ func buildRule(d *schema.ResourceData, resourceRule map[string]interface{}, rule
rule.RateLimit.Period = pValue.(int)
case "requests_per_period":
rule.RateLimit.RequestsPerPeriod = pValue.(int)
case "score_per_period":
rule.RateLimit.ScorePerPeriod = pValue.(int)
case "score_response_header_name":
rule.RateLimit.ScoreResponseHeaderName = pValue.(string)
case "mitigation_timeout":
rule.RateLimit.MitigationTimeout = pValue.(int)
case "counting_expression":
Expand Down
83 changes: 83 additions & 0 deletions internal/sdkv2provider/resource_cloudflare_ruleset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,53 @@ func TestAccCloudflareRuleset_RateLimit(t *testing.T) {
})
}

func TestAccCloudflareRuleset_RateLimitScorePerPeriod(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the WAF
// service does not yet support the API tokens and it results in
// misleading state error messages.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
t.Setenv("CLOUDFLARE_API_TOKEN", "")
}

t.Parallel()
rnd := generateRandomResourceName()
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
resourceName := "cloudflare_ruleset." + rnd

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareRulesetRateLimitScorePerPeriod(rnd, "example HTTP rate limit by header score", zoneID, zoneName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", "example HTTP rate limit by header score"),
resource.TestCheckResourceAttr(resourceName, "description", rnd+" ruleset description"),
resource.TestCheckResourceAttr(resourceName, "kind", "zone"),
resource.TestCheckResourceAttr(resourceName, "phase", "http_ratelimit"),

resource.TestCheckResourceAttr(resourceName, "rules.#", "1"),
resource.TestCheckResourceAttr(resourceName, "rules.0.action", "block"),
resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.response.#", "1"),
resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.response.0.status_code", "418"),
resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.response.0.content_type", "text/plain"),
resource.TestCheckResourceAttr(resourceName, "rules.0.action_parameters.0.response.0.content", "test content"),
resource.TestCheckResourceAttr(resourceName, "rules.0.expression", "(http.request.uri.path matches \"^/api/\")"),
resource.TestCheckResourceAttr(resourceName, "rules.0.description", "example http rate limit"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.#", "1"),

resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.characteristics.#", "2"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.period", "60"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.score_per_period", "400"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.score_response_header_name", "my-score"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.mitigation_timeout", "60"),
resource.TestCheckResourceAttr(resourceName, "rules.0.ratelimit.0.requests_to_origin", "true"),
),
},
},
})
}

func TestAccCloudflareRuleset_PreserveRuleIDs(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the WAF
// service does not yet support the API tokens and it results in
Expand Down Expand Up @@ -2608,6 +2655,42 @@ func testAccCheckCloudflareRulesetRateLimit(rnd, name, zoneID, zoneName string)
}`, rnd, name, zoneID, zoneName)
}

func testAccCheckCloudflareRulesetRateLimitScorePerPeriod(rnd, name, zoneID, zoneName string) string {
return fmt.Sprintf(`
resource "cloudflare_ruleset" "%[1]s" {
zone_id = "%[3]s"
name = "%[2]s"
description = "%[1]s ruleset description"
kind = "zone"
phase = "http_ratelimit"
rules {
action = "block"
action_parameters {
response {
status_code = 418
content_type = "text/plain"
content = "test content"
}
}
ratelimit {
characteristics = [
"cf.colo.id",
"ip.src"
]
period = 60
score_per_period = 400
score_response_header_name = "my-score"
mitigation_timeout = 60
requests_to_origin = true
}
expression = "(http.request.uri.path matches \"^/api/\")"
description = "example http rate limit"
enabled = true
}
}`, rnd, name, zoneID, zoneName)
}

func testAccCheckCloudflareRulesetTwoCustomRules(rnd, zoneID string) string {
return fmt.Sprintf(`
resource "cloudflare_ruleset" "%[1]s" {
Expand Down
10 changes: 10 additions & 0 deletions internal/sdkv2provider/schema_cloudflare_ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,16 @@ func resourceCloudflareRulesetSchema() map[string]*schema.Schema {
Optional: true,
Description: "The number of requests over the period of time that will trigger the Rate Limiting rule.",
},
"score_per_period": {
Type: schema.TypeInt,
Optional: true,
Description: "The maximum aggregate score over the period of time that will trigger Rate Limiting rule.",
},
"score_response_header_name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of HTTP header in the response, set by the origin server, with the score for the current request.",
},
"mitigation_timeout": {
Type: schema.TypeInt,
Optional: true,
Expand Down

0 comments on commit ffa55f8

Please sign in to comment.