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

resource/cloudflare_ruleset: fix ruleset category overrides in DDoS L7 phase by allowing the provider to omit the enabled flag, as required by Cloudflare's API #1492

Closed
wants to merge 2 commits into from

Conversation

Quentin-M
Copy link

@Quentin-M Quentin-M commented Mar 4, 2022

In the DDoS L7 phase, category overrides do not support the enabled flag which must therefore be omitted. This is pretty closely related to #1405.

Terraform example

resource "cloudflare_ruleset" "example" {
  zone_id     = cloudflare_zone.example.id
  name        = "zone_managed_ddos"
  description = "Zone-level Cloudflare DDoS Managed Rulesets"
  kind        = "zone"
  phase       = "ddos_l7"

  // DDoS L7 ruleset
  rules {
    action      = "execute"
    description = "Override DDoS L7 Ruleset"
    expression  = "true"
    enabled     = true

    action_parameters {
      id = "4d21379b4f9f4bb088e0729962c8b3cf"

      overrides {
        // Enable all unusual request rules
        categories {
          category = "unusual-requests"
          action   = "challenge"
        }

API Request (sample) & error before this PR

                        "categories": [
                          {
                            "action": "challenge",
                            "category": "unusual-requests",
                            "enabled": false
                          },
                          {
                            "action": "challenge",
                            "category": "botnets",
                            "enabled": false
                          }
                        ],

Leading to the following 400 Bad Request error returned by the Cloudflare API (doubled, because two categories):

    DDoS L7 overrides cannot use the enabled field, DDoS L7 overrides cannot use the enabled field
    DDoS L7 overrides cannot use the enabled field, DDoS L7 overrides cannot use the enabled field

API Request (sample) after this PR

     "categories": [
      {
       "category": "unusual-requests",
       "action": "challenge"
      },
      {
       "category": "botnets",
       "action": "challenge"
      }
     ],

Notes

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2022

Oops! It looks like no changelog entry is attached to this PR. Please include a release note as described in https://github.com/cloudflare/terraform-provider-cloudflare/blob/master/docs/changelog-process.md.

Example:

```release-note:TYPE
Release note
```

If you do not require a release note to be included, please add the workflow/skip-changelog-entry label.

…7 phase by allowing the provider to omit the `enabled` flag, as required by Cloudflare's API

In the DDoS L7 phase, category overrides do not support the `enabled` flag which must therefore be omitted.
```
    DDoS L7 overrides cannot use the enabled field, DDoS L7 overrides cannot use the enabled field
```

Requires the following cloudflare-go schema: cloudflare/cloudflare-go#824
…CloudflareRulesetStateUpgradeV0ToV1

Introduced in #1477, resourceCloudflareRulesetStateUpgradeV0ToV1 might panic because no checks are performed.
I do not possess the required knowledge to perfectly understand the ins/outs of this migration, therefore simply adding a broad recovery clause in the event the migration is not possible (e.g as below where a nil object is given).

```
2022-03-04T15:16:52.440+0800 [DEBUG] plugin.terraform-provider-cloudflare_99.0.0: panic: interface conversion: interface {} is nil, not []map[string]interface {}

panic: interface conversion: interface {} is nil, not []map[string]interface {}

goroutine 301 [running]:
github.com/cloudflare/terraform-provider-cloudflare/cloudflare.resourceCloudflareRulesetStateUpgradeV0ToV1({0x1e97838, 0xc0008f8600}, 0xc000349bc0, {0x1dca880, 0xc0004a06c0})
	go/src/github.com/cloudflare/terraform-provider-cloudflare/cloudflare/resource_cloudflare_ruleset_migrate.go:344 +0x2d1
github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.(*GRPCProviderServer).upgradeJSONState(0xc00041c048, {0x1e97838, 0xc0008f8600}, 0x0, 0xc000349bc0, 0xc000495a40)
	go/pkg/mod/github.com/hashicorp/terraform-plugin-sdk/[email protected]/helper/schema/grpc_provider.go:424 +0x1c3
github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.(*GRPCProviderServer).UpgradeResourceState(0xc00041c048, {0x1e97838, 0xc0008f8600}, 0xc0007fa420)
	go/pkg/mod/github.com/hashicorp/terraform-plugin-sdk/[email protected]/helper/schema/grpc_provider.go:300 +0x645
github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server.(*server).UpgradeResourceState(0xc0004366e0, {0x1e97838, 0xc0008f8600}, 0xc000bdc230)
	go/pkg/mod/github.com/hashicorp/[email protected]/tfprotov5/tf5server/server.go:715 +0x532
github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5._Provider_UpgradeResourceState_Handler({0x1d9d380, 0xc0004366e0}, {0x1e978e0, 0xc0004b0480}, 0xc0001b0600, 0x0)
	go/pkg/mod/github.com/hashicorp/[email protected]/tfprotov5/internal/tfplugin5/tfplugin5_grpc.pb.go:313 +0x385
google.golang.org/grpc.(*Server).processUnaryRPC(0xc0004d0000, {0x1ea4828, 0xc000483040}, 0xc000e80240, 0xc0004b1a70, 0x23e38e0, 0x0)
	go/pkg/mod/google.golang.org/[email protected]/server.go:1282 +0x14c9
google.golang.org/grpc.(*Server).handleStream(0xc0004d0000, {0x1ea4828, 0xc000483040}, 0xc000e80240, 0x0)
	go/pkg/mod/google.golang.org/[email protected]/server.go:1616 +0x85e
google.golang.org/grpc.(*Server).serveStreams.func1.2()
	go/pkg/mod/google.golang.org/[email protected]/server.go:921 +0x11d
created by google.golang.org/grpc.(*Server).serveStreams.func1
	go/pkg/mod/google.golang.org/[email protected]/server.go:919 +0x345
```
@Quentin-M
Copy link
Author

Quentin-M commented Mar 4, 2022

Resource creation now works appropriately, however, it appears that "enabled": false still gets written to the Terraform state despite being skipped in buildRulesetRulesFromResource, and never returned by the Cloudflare API - which makes updates break as the boolean ends up getting into the request during updates. I am not 100% sure how it gets written to state at this point.


Launching a debugger against the following state:

                        "categories": [
                          {
                            "action": "challenge",
                            "category": "unusual-requests"
                          },
                          {
                            "action": "block",
                            "category": "botnets"
                          }
                        ],

Results in resourceCloudflareRulesetUpdate getting a d.state.Attributes with rules.0.action_parameters.0.overrides.0.categories.0.enabled -> "false" rather than something representing nil, which subsequently will lead buildRulesetRulesFromResource to append the boolean wrongly into our rules object for submission against UpdateZoneRuleset. Same goes when setting enabled: null within the same, and same behavior can be observed when using d.RawState() regardless of whether the state contains false or null.

Ultimately, I feel like trying to use a tristate TypeBool with Terraform is bound to failure as cty/TF don't appear to be able to write/read the state file while maintaining the tri-state status. This leads to Create working properly but TF writing enabled into the state, and reading enabled: false regardless of whether the key is present in the state or not, and regardless of whether it's present but written as null.. which leads to Update breaking. Given DDoS_L7 is a special case of ruleset, I am not sure we can safely assume we can omit the field in the ruleset API calls if enabled: false... We might need a special case defined (ugh), or another bit..

I won't get too lost into writing some kind of fix implementation given I am not super-duper experienced writing providers, I don't have a solution that doesn't sound gross, there probably is a different issue/approach I'm not seeing).

image

@jacobbednarz
Copy link
Member

FWIW, this isn’t the first time we’ve tangled with tristate booleans…I have a SDK bug open to address exactly this (hashicorp/terraform-plugin-sdk#817)

@Quentin-M
Copy link
Author

Yeah, I've been reading that thread while trying to fix up the provider, looks fundamentally broken. I am not 100% sure how the SDK/cty ends up writing a *Bool{nil} as bool{false} but it did not seem like a great use of time to dive deep into it, I just assumed TypeBool is just a two-state bool and therefore we're losing information on state save/read.. making it basically impossible to determine later on whether we need to include it in our API requests or not, and thus breaking ddos_l7 updates. From my past experience facing Terraform core issues, it is most likely going to be significantly faster and probably much more robust to fix the problem within terraform-provider-cloudflare by patching up a TypeString<->*bool conversion between the state and the cloudflare-go API.

Comment on lines +345 to +351
// Broadly intercept the panic generated from our inability to perform the upgrade.
defer func() {
if err := recover(); err != nil {
log.Printf("[DEBUG] Unable to perform resourceCloudflareRulesetStateUpgradeV0ToV1")
}
}()

Copy link
Member

Choose a reason for hiding this comment

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

please remove this; it doesn't actually help the upgrade process. there was an update to the migrate in another PR.

@jacobbednarz
Copy link
Member

looks like you'll need to update from master to resolve the conflicts. additionally, i recently landed some helpers (cloudflare/cloudflare-go#825) to swap between types easily. you'll probably want this for BoolPtr like we've done in other places recently.

@jacobbednarz jacobbednarz added workflow/pending-contributor-response Indicates an issue or PR requires a response from a contributor. service/rulesets Categorizes issue or PR as related to the Rulesets service. labels Mar 30, 2022
@github-actions
Copy link
Contributor

Marking this pull request as stale due to 14 days of inactivity. This helps our maintainers find and focus on the active pull requests. If this pull request receives no comments in the next 7 days it will automatically be closed. Maintainers can also remove the lifecycle/stale label.
If this pull request was automatically closed and you feel this pull request should be reopened, we encourage creating a new pull request linking back to this one for added context. Thank you!

@github-actions
Copy link
Contributor

This PR was closed because it has been stalled for 7 days with no activity.

@github-actions github-actions bot closed this Apr 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lifecycle/stale service/rulesets Categorizes issue or PR as related to the Rulesets service. workflow/pending-contributor-response Indicates an issue or PR requires a response from a contributor.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants