fix(#2294): make EnsureProvider idempotent via delete-and-recreate - #2296
fix(#2294): make EnsureProvider idempotent via delete-and-recreate#2296fullsend-ai-coder[bot] wants to merge 1 commit into
Conversation
EnsureProvider called `openshell provider create` and treated any error as a hard failure. When a provider already existed from a prior run, the AlreadyExists error blocked subsequent runs, requiring manual cleanup between iterations. Now when the create command fails with AlreadyExists, the function deletes the existing provider and recreates it with current credentials. This makes the function truly idempotent (matching the "Ensure" naming convention used by EnsureGateway and the Provider.Provision() interface contract) while also ensuring credentials are never stale across runs. Also extracted a redactSecrets helper to reduce duplication in error formatting paths. Closes #2294
E2E tests did not runE2E tests run automatically for org/repo members and collaborators on pull requests. For other contributors, a maintainer must add the See E2E testing guide for details. |
Site previewPreview: https://af470785-site.fullsend-ai.workers.dev Commit: |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
🤖 Finished Review · ✅ Success · Started 10:43 AM UTC · Completed 10:55 AM UTC |
ReviewFindingsMedium
Low
Info
|
| // If the provider already exists, delete it and recreate with | ||
| // current credentials. This keeps EnsureProvider idempotent and | ||
| // ensures credentials are never stale across runs. | ||
| if strings.Contains(string(out), "AlreadyExists") { |
There was a problem hiding this comment.
[low] edge-case
AlreadyExists detection uses strings.Contains against combined stdout+stderr. If openshell changes its error format, the detection silently breaks and falls through to the generic error path — safe fallback, but loses idempotency.
| @@ -115,16 +115,34 @@ func EnsureProvider(name, providerType string, credentials, config map[string]st | |||
| cmd.Env = append(os.Environ(), extraEnv...) | |||
| out, err := cmd.CombinedOutput() | |||
| if err != nil { | |||
There was a problem hiding this comment.
[low] intent-scope-alignment
Issue #2294 proposed three approaches. Option 2 (delete-and-recreate) also refreshes credentials on every conflict, which goes slightly beyond pure idempotency but is a reasonable design choice.
| @@ -115,16 +115,34 @@ func EnsureProvider(name, providerType string, credentials, config map[string]st | |||
| cmd.Env = append(os.Environ(), extraEnv...) | |||
| out, err := cmd.CombinedOutput() | |||
| if err != nil { | |||
There was a problem hiding this comment.
[low] architectural-coherence
Delete-and-recreate introduces a timing window where provider deletion could affect concurrent sandbox operations if the gateway is shared.
| // ensures credentials are never stale across runs. | ||
| if strings.Contains(string(out), "AlreadyExists") { | ||
| delCmd := exec.Command("openshell", "provider", "delete", name) | ||
| if delOut, delErr := delCmd.CombinedOutput(); delErr != nil { |
There was a problem hiding this comment.
[info] race-condition
Theoretical TOCTOU window between delete and retry create. Non-actionable given single-process usage; same pattern exists in CreateWithRetry.
| @@ -115,16 +115,34 @@ func EnsureProvider(name, providerType string, credentials, config map[string]st | |||
| cmd.Env = append(os.Environ(), extraEnv...) | |||
| out, err := cmd.CombinedOutput() | |||
| if err != nil { | |||
There was a problem hiding this comment.
[info] architectural-trajectory
Delete-and-recreate is a different reconciliation model from WIF provider create-or-update elsewhere. Creates divergent patterns worth noting.
|
Closed in favor of #2323 |
|
🤖 Finished Retro · ✅ Success · Started 11:46 AM UTC · Completed 11:58 AM UTC |
Retro: PR #2296 — Agent chose reactive fix, human found preventive designOutcome: PR #2296 (agent-authored) was closed without merging, superseded by human-authored PR #2323. Timeline:
What worked well:
Filtered proposals:
Proposals filed
|
EnsureProvider called
openshell provider createand treated any error as a hard failure. When a provider already existed from a prior run, the AlreadyExists error blocked subsequent runs, requiring manual cleanup between iterations.Now when the create command fails with AlreadyExists, the function deletes the existing provider and recreates it with current credentials. This makes the function truly idempotent (matching the "Ensure" naming convention used by EnsureGateway and the Provider.Provision() interface contract) while also ensuring credentials are never stale across runs.
Also extracted a redactSecrets helper to reduce duplication in error formatting paths.
Closes #2294
Post-script verification
agent/2294-idempotent-ensure-provider)9ca6edc3770e16b0f37a6e79046cfba7d01bffc9..HEAD)