Skip to content

fix: deferred-flush orphan handling + UUID passthrough for type=app trusted_sources#65

Merged
intel352 merged 2 commits intomainfrom
copilot/fix-orphan-handling-in-deferred-update
May 5, 2026
Merged

fix: deferred-flush orphan handling + UUID passthrough for type=app trusted_sources#65
intel352 merged 2 commits intomainfrom
copilot/fix-orphan-handling-in-deferred-update

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 5, 2026

Two follow-up correctness fixes to the DatabaseDriver deferred trusted_sources flush queue introduced in PR #44.

Fix 1 — Orphan deferred entries when resource type leaves the plan

DOProvider.Apply's second-pass flush iterated plan.Actions to find drivers to flush. If a flush failed transiently and the next plan omitted that resource type entirely, the deferred queue entries were permanently stranded.

Fix: iterate p.drivers (the registry) directly — every driver with HasDeferredUpdates() == true is flushed unconditionally, regardless of whether its type appears in the current plan.

// Before — misses drivers whose type is absent from this plan's actions
for _, action := range plan.Actions { ... }

// After — iterates registry; catches orphaned entries from prior failed flushes
for resourceType, d := range p.drivers { ... }

Fix 2 — UUID-shaped type=app values over-deferred

buildCreateFirewallRulesExcludingApps / buildUpdateFirewallRulesExcludingApps excluded all type=app entries unconditionally, deferring even entries whose value is already a resolved UUID (no Apps API lookup needed).

Fix: only exclude type=app entries whose value fails isUUIDLike(). UUID-shaped values pass through to the create/update call immediately; only slug-style names are deferred.

// Before
if ruleType == "" || ruleValue == "" || ruleType == "app" {
    continue
}

// After — UUID app values pass through; only slugs are deferred
if ruleType == "" || ruleValue == "" || (ruleType == "app" && !isUUIDLike(ruleValue)) {
    continue
}

New regression tests cover both paths: orphan flush with type-absent plan, and UUID passthrough on both the create and update deferred paths.

…urces

Fix 1 (provider.go): Change DOProvider.Apply second-pass deferred flush to
iterate p.drivers directly instead of plan.Actions. This ensures orphaned
deferred entries (from a prior transient flush failure) are flushed even
when the resource type no longer appears in the current plan.

Fix 2 (database.go): Modify buildCreateFirewallRulesExcludingApps and
buildUpdateFirewallRulesExcludingApps to pass through UUID-shaped type=app
values directly (not defer them), using the existing isUUIDLike helper.
Only slug-style app names require deferred resolution via Apps API.

Add tests:
- TestDOProvider_Apply_FlushesDeferred_WhenTypeAbsentFromPlan: validates
  orphan handling — flush runs even when resource type absent from plan
- TestDatabaseDriver_Create_AppUUID_PassedThroughNotDeferred: UUID app
  entries pass through to create request; slug entries still deferred
- TestDatabaseDriver_Update_AppUUID_PassedThroughNotDeferred: UUID app
  entries pass through to partial update; slug entries still deferred

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-digitalocean/sessions/a19c1855-ccdd-42c7-8255-80a2d7a79605

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 5, 2026 08:09
Copilot AI changed the title [WIP] Fix orphan handling for missing resource types in deferred updates fix: deferred-flush orphan handling + UUID passthrough for type=app trusted_sources May 5, 2026
Copilot AI requested a review from intel352 May 5, 2026 08:11
@intel352 intel352 marked this pull request as ready for review May 5, 2026 08:12
Copilot AI review requested due to automatic review settings May 5, 2026 08:12
@intel352
Copy link
Copy Markdown
Contributor

intel352 commented May 5, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR delivers two correctness fixes for the DatabaseDriver deferred trusted_sources flush behavior introduced previously: (1) ensure deferred queues can’t become permanently orphaned when a resource type disappears from later plans, and (2) avoid over-deferring type=app trusted sources when the value is already a UUID.

Changes:

  • Flush deferred updates by iterating the provider’s driver registry (p.drivers) rather than plan.Actions, ensuring orphaned deferred entries are still retried.
  • Allow type=app trusted_sources entries with UUID-shaped values to pass through immediately (only slug-style app names are deferred).
  • Add regression tests for orphan flush when the resource type is absent from the plan, and for UUID passthrough on both Create and Update deferred paths.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
internal/provider.go Flush deferred updates by scanning all registered drivers instead of current plan actions.
internal/provider_deferred_test.go Adds a provider-level regression test for flushing orphaned deferred entries when no actions exist for that type.
internal/drivers/database.go Updates partial-rule builders to only exclude non-UUID type=app entries, enabling UUID passthrough.
internal/drivers/database_deferred_test.go Adds regression tests ensuring UUID type=app entries are not deferred on Create/Update partial application.
Comments suppressed due to low confidence (1)

internal/provider.go:299

  • Iterating over p.drivers (a Go map) makes the deferred-flush order and any appended result.Errors ordering non-deterministic across runs. If multiple drivers have deferred updates, this can cause noisy/flaky output comparisons. Consider collecting driver keys, sorting them, then flushing in a stable order.
	for resourceType, d := range p.drivers {
		du, ok := d.(deferredUpdater)
		if !ok || !du.HasDeferredUpdates() {
			continue
		}
		if flushErr := du.FlushDeferredUpdates(ctx); flushErr != nil {
			result.Errors = append(result.Errors, interfaces.ActionError{
				Resource: resourceType,
				Action:   "deferred_update",
				Error:    flushErr.Error(),
			})
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 5, 2026

@copilot Failing PR check https://github.com/GoCodeAlone/workflow-plugin-digitalocean/actions/runs/25365102263/job/74373841833?pr=65

The failure is in the codemod-report workflow, not in the build or tests. The iac-codemod tool itself ran fine (dry-run, 0 errors — the // wfctl:skip-iac-codemod marker on DOProvider.Apply was correctly respected). The 403 "Resource not accessible by integration" error occurs when the workflow tries to post a sticky PR comment via github-script, which is a CI infrastructure permissions issue unrelated to these changes.

@intel352 intel352 merged commit a18d398 into main May 5, 2026
9 of 10 checks passed
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.

DatabaseDriver deferred-update queue: orphan handling when type disappears from plan + UUID-resolvable type=app shortcut

3 participants