Skip to content

feat(e2e): heal corrupted leaked_credential_check_rule state; clean up create-only resources - #331

Open
vaishakdinesh wants to merge 1 commit into
mainfrom
fix-leaked-credential-check-state-corruption
Open

vaishakdinesh wants to merge 1 commit into
mainfrom
fix-leaked-credential-check-state-corruption

Conversation

@vaishakdinesh

Copy link
Copy Markdown
Member

Summary

Two related e2e harness robustness fixes, both live-tested end-to-end against v5.19.0 (all 5 steps passed, 0 unmatched drift).

1. Heal corrupted leaked_credential_check_rule state

Root cause (confirmed directly in the v4 provider source, internal/framework/service/leaked_credential_check_rule/resource.go): Read() lists all detection patterns and loops looking for one matching state's id. If none match (e.g. because the real pattern was deleted outside Terraform), it does not error — it silently writes a zero-value result back to state, leaving id/username/password all null. Every subsequent Update() then fails with "required missing detection ID", and the resource can never recover on its own.

This surfaced in the supportability-matrix CI workflow after a manually-deleted duplicate detection pattern left the real, shared v4 state corrupted — every run from that point on failed at the v4-apply step.

The identical anti-pattern exists in content_scanning_expression's Read() too (same "doesn't offer a single get operation" comment, same no-match handling), but that resource has no tf-migrate migrator/testdata, so it's out of scope here.

Fix: healCorruptedLeakedCredentialCheckRuleState runs after v4 init, before v4 plan. It checks this one resource's state for the id == null corruption signature and removes the entry if found, so the next plan/apply cleanly recreates it instead of repeatedly failing. No-op if the resource is healthy or not present in state.

2. Clean up create-only resources (Step 5)

Problem: v5-side Terraform state is never persisted between runs by design (see e2e/SUPPORTABILITY_MATRIX.md §11) — resources adopted via a moved {} block are always safely re-associated with their real ID regardless, but resources with no v4 counterpart at all have no such anchor. Every run creates them fresh, and since nothing records that creation, the next run has no way to know they already exist. Left unchecked, this silently accumulates duplicates until some account-level quota is exhausted (the mechanism behind a prior Access Policy / Gateway Certificate quota incident, documented in the matrix doc).

Fix: after a fully successful run, cleanupCreateOnlyResources parses the already-captured v5 plan text for resources with a pure "will be created" action, excludes anything protected by a moved {} or import {} block, and terraform destroys exactly what's left. Opt out with --keep-created.

Deliberately parses plan text rather than terraform show -json <planfile>: the JSON form always marshals the entire prior state, so a single unrelated resource elsewhere in state with a stale/incompatible schema makes the whole call fail outright — even though the actual -target-scoped plan/apply succeeded cleanly. Text parsing has no such problem.

Confirmed live: destroyed 7 genuinely create-only resources (zone/account-level singletons and conditionally-created zone_setting instances) while correctly leaving leaked_credential_check_rule and all moved/import-protected resources untouched.

Testing

  • go build ./..., go vet ./... — clean
  • go test ./... — all pass, including new unit tests covering the safety-critical pure logic (plan-text parsing, import-block protection, state-corruption detection)
  • Live end-to-end run against v5.19.0: all 5 steps passed, 0 unmatched drift; verified the healed resource's state ID matches the real account exactly post-run

…p create-only resources

Two related e2e harness robustness fixes, both live-tested against v5.19.0
(all 5 steps passed, 0 unmatched drift):

1. healCorruptedLeakedCredentialCheckRuleState: detects and repairs a known
   v4 provider bug where cloudflare_leaked_credential_check_rule's Read()
   silently zeroes id/username/password to null instead of erroring when
   the tracked resource no longer matches reality (e.g. deleted outside
   Terraform), permanently breaking every subsequent Update() with
   "required missing detection ID". Runs after v4 init, before v4 plan;
   no-op if the resource is healthy or not in state. The identical bug
   exists in content_scanning_expression's Read() too, but that resource
   has no tf-migrate migrator/testdata so it's out of scope here.

2. cleanupCreateOnlyResources (Step 5): after a fully successful run,
   parses the already-captured v5 plan text for resources with a pure
   "will be created" action (no moved {} or import {} block protecting
   them) and destroys exactly those, leaving the real v4 base and every
   adopted v5 resource untouched. Insurance against resources with no v4
   counterpart silently accumulating duplicates across repeated runs,
   since v5-side state is never persisted between invocations. Opt out
   with --keep-created. Deliberately parses plan TEXT rather than
   `terraform show -json <planfile>`: the JSON form always marshals the
   full prior state, so one unrelated resource elsewhere in state with a
   stale schema makes the whole call fail even though the actual -target
   plan succeeded. Confirmed working live: destroyed 7 genuinely
   create-only resources (zone/account singletons and conditional
   zone_setting instances) while correctly leaving leaked_credential_check_rule
   and all moved/import-protected resources untouched.

Both include unit tests for their safety-critical pure logic (text
parsing, import-block protection, state corruption detection).
@vaishakdinesh
vaishakdinesh requested a review from a team as a code owner October 9, 2026 23:57
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.

1 participant