Skip to content

feat(ruleset): add dry-run validation to plan - #7291

Open
jahnavimalhotra1 wants to merge 5 commits into
cloudflare:mainfrom
jahnavimalhotra1:jahnavi/ERE-3680
Open

feat(ruleset): add dry-run validation to plan#7291
jahnavimalhotra1 wants to merge 5 commits into
cloudflare:mainfrom
jahnavimalhotra1:jahnavi/ERE-3680

Conversation

@jahnavimalhotra1

@jahnavimalhotra1 jahnavimalhotra1 commented Aug 4, 2026

Copy link
Copy Markdown
  • I understand that this repository is auto-generated and my pull request may not be merged

Changes being requested

Configuration errors in Cloudflare Rulesets are currently reported during terraform apply, because the provider does not call the API until it writes. This means a plan can look completely healthy and then fail partway through an apply. This change adds dry-run validation for terraform plan, allowing customers to validate their setups beforehand and catch errors early.

We validate the planned configuration against the API during terraform plan by issuing the same request the apply would issue, with ?dry_run=true. The Rulesets API performs full validation and returns the same errors it would for a real write, but does not persist anything.

Acceptance test run results

  • I have added or updated acceptance tests for my changes
  • I have run acceptance tests for my changes and included the results below

Steps to run acceptance tests

export TF_ACC=1
export CLOUDFLARE_API_TOKEN=<token>
export CLOUDFLARE_ACCOUNT_ID=<account id>
export CLOUDFLARE_ZONE_ID=<zone id>
go test ./internal/services/ruleset/ -run '^TestAccCloudflareRuleset_DryRun' -v -timeout 15m

Test output

=== RUN   TestAccCloudflareRuleset_DryRunInvalidOnCreate
--- PASS: TestAccCloudflareRuleset_DryRunInvalidOnCreate (2.67s)
=== RUN   TestAccCloudflareRuleset_DryRunInvalidOnUpdate
--- PASS: TestAccCloudflareRuleset_DryRunInvalidOnUpdate (5.28s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedWhenDependencyIsUnknown
--- PASS: TestAccCloudflareRuleset_DryRunSkippedWhenDependencyIsUnknown (1.23s)
=== RUN   TestAccCloudflareRuleset_DryRunDependencyOnCreateThenInvalidUpdate
--- PASS: TestAccCloudflareRuleset_DryRunDependencyOnCreateThenInvalidUpdate (3.78s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedWhenNothingChanges
--- PASS: TestAccCloudflareRuleset_DryRunSkippedWhenNothingChanges (4.65s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnDelete
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnDelete (5.11s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnReplacement
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnReplacement (5.25s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnReferencedDelete
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnReferencedDelete (7.92s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnReferencedReplacement
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnReferencedReplacement (8.05s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnEntryPointRename
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnEntryPointRename (6.22s)
=== RUN   TestAccCloudflareRuleset_DryRunSkippedOnInvalidEntryPointReplacement
--- PASS: TestAccCloudflareRuleset_DryRunSkippedOnInvalidEntryPointReplacement (6.99s)
=== RUN   TestAccCloudflareRuleset_DryRunInvalidUpdateWithNewDependency
--- PASS: TestAccCloudflareRuleset_DryRunInvalidUpdateWithNewDependency (5.79s)
PASS

Additional context & links

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's add two more cases:

  1. Dependency during creation, and then update with validation error.
  2. No dependency during creation, and then update with dependency and validation error.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's run each test case for an account-level and zone-level ruleset

Comment thread internal/services/ruleset/resource.go Outdated

// planIsKnownForDryRun reports whether enough of the plan is known to build a
// request that the API can validate
func planIsKnownForDryRun(plan *RulesetModel) bool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we want to check if anything in the plan (recursively) is unknown.

Comment thread internal/services/ruleset/ruleset_test.go Outdated

@zakcutner zakcutner Aug 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it might be simpler to test account and zone rulesets in the same test (I believe you can have multiple resources per test). Will also make it easier to ensure that the account and zone tests stay in sync

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You're removing the description in this step, is that intended?

Comment thread internal/services/ruleset/resource.go
Comment thread internal/services/ruleset/resource.go Outdated
}

// Check if enough of the plan is known to build a request for the dry-run
if !req.Config.Raw.IsFullyKnown() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You're checking req.Config here, but you later use plan (derived from req.Plan) instead

Comment thread internal/services/ruleset/resource.go Outdated

var err error
switch {
// The plan is absent only when the resource is being deleted

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it would be nicer to check explicitly that it's a delete event, instead of that the plan is empty. Terraform actually has a lot of different events beyond simple CRUD (e.g., imports), so I think there could be another event (now or in the future) where the plan is also empty.

Comment thread internal/services/ruleset/resource.go Outdated
return
}

// A plan that leaves the computed ID unknown is creating the ruleset

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here, would be nice to check for create events explicitly, rather than inferring from the ID being unknown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants