Skip to content

fix: Propagates API errors during delete, read, and wait operations of auto-generated resources - #4676

Merged
AgustinBettati merged 9 commits into
masterfrom
CLOUDP-437881
Aug 26, 2026
Merged

fix: Propagates API errors during delete, read, and wait operations of auto-generated resources#4676
AgustinBettati merged 9 commits into
masterfrom
CLOUDP-437881

Conversation

@AgustinBettati

@AgustinBettati AgustinBettati commented Aug 25, 2026

Copy link
Copy Markdown
Member

Description

Before this fix, auto-generated resources ignored every API error during delete, read, and wait operations and treated the failure as "resource not found". From the user's perspective:

Operation Before the fix After the fix
Delete terraform destroy reports a successful destroy even when the Atlas API rejects the delete (400, 401, 409, 500). The resource is removed from Terraform state but still exists in Atlas, with no signal to the operator. The API error surfaces as a diagnostics error and the resource stays in Terraform state. A 404 is still tolerated as already deleted.
Read An API error during terraform plan or refresh silently removes the resource from state, so the next apply tries to recreate it. Data sources report "Resource not found" instead of the real API error. The API error surfaces and the resource stays in state; only a 404, or a hook signaling not-found, removes it.
Wait (long-running operations) While waiting for a delete to complete, a transient API error is taken as confirmation that the resource is gone, so the destroy finishes early and reports success. During create and update waits, the user sees a confusing "unexpected state 'DELETED'" error instead of the real one. The polling error aborts the wait and the real API error surfaces.

Root cause: callAPI returns a nil body on any failed call, isEmptyJSON(nil) is true, and callDelete, handleReadCore, and refreshFunc consulted notFound before Err, so every failure was classified as not-found.

Changes:

  • notFound now classifies only genuine not-found: an HTTP 404, or a hook signaling it via the new autogen.ErrNotFound sentinel. All other errors propagate to diagnostics.
  • The search deployment quirk of returning an ok status with an empty JSON body for missing resources moves out of the core into a PostReadAPICall hook on searchdeploymentapi, covering both read and delete-wait polling.
  • The service account secret hooks wrap autogen.ErrNotFound when the secret is absent from the list response, so out-of-band deletion still removes the resource from state.
  • New unit tests drive HandleRead, HandleDataSourceRead, HandleDataSourceReadList, callDelete, and refreshFunc through a real config.MongoDBClient against an httptest server across 400/401/404/409/500/204/200 responses.

Link to any related issue(s): CLOUDP-437881

Type of change:

  • Bug fix (non-breaking change which fixes an issue). Please, add the "bug" label to the PR.
  • New feature (non-breaking change which adds functionality). Please, add the "enhancement" label to the PR. A migration guide must be created or updated if the new feature will go in a major version.
  • Breaking change (fix or feature that would cause existing functionality to not work as expected). Please, add the "breaking change" label to the PR. A migration guide must be created or updated.
  • This change requires a documentation update
  • Documentation fix/enhancement

Required Checklist:

  • I have signed the MongoDB CLA
  • I have read the contributing guides
  • I have checked that this change does not generate any credentials and that they are NOT accidentally logged anywhere.
  • I have added tests that prove my fix is effective or that my feature works per HashiCorp requirements
  • I have added any necessary documentation (if appropriate)
  • I have run make fix and verified my code
  • If changes include deprecations or removals I have added appropriate changelog entries.
  • If changes include removal or addition of 3rd party GitHub actions, I updated our internal document. Reach out to the APIx Integration slack channel to get access to the internal document.

Comment on lines -174 to -176
if notFound(callResult.Body, callResult.Resp) {
return nil, callResult.Resp, fmt.Errorf("resource not found")
}

@AgustinBettati AgustinBettati Aug 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The check for processing 404 was not reachable as it was being catched in if statement above (callResult.Err != nil). Only case this if captured was a 200 empty body {} with returned with a resource not found, now it will be hanlded as a regular successful response with no results.

@AgustinBettati

Copy link
Copy Markdown
Member Author

will wait on CI execution before merging

@AgustinBettati
AgustinBettati marked this pull request as ready for review August 26, 2026 13:37
Copilot AI lite review requested due to automatic review settings August 26, 2026 13:37
@AgustinBettati
AgustinBettati requested review from a team as code owners August 26, 2026 13:37
@github-actions

Copy link
Copy Markdown
Contributor

APIx bot: a message has been sent to Docs Slack channel

@augmentcode

augmentcode Bot commented Aug 26, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: This PR corrects error handling for auto-generated resource lifecycle operations.
Changes:

  • Introduces autogen.ErrNotFound as an explicit hook-level missing-resource signal.
  • Limits core not-found classification to HTTP 404 responses or that sentinel error.
  • Propagates non-404 API and transport failures from resource reads and deletes.
  • Ensures wait polling returns API failures rather than treating them as deletion.
  • Preserves the search deployment API's successful-empty-body missing-resource behavior via a read hook.
  • Updates service-account secret list-read hooks to wrap the new sentinel when a secret is absent.
  • Allows successful empty list responses in plural data sources to produce empty results.
  • Exports the empty-JSON helper and updates unmarshalling to use it.
  • Adds focused HTTP-server tests for status handling, waits, reads, list reads, and custom hooks.
  • Updates release notes for the affected generated resources.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed. No suggestions at this time.

Comment augment review to trigger a new review at any time.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes a systemic error-handling bug in the auto-generated (serviceapi) resource framework where API failures during read/delete/wait were incorrectly treated as “resource not found”, causing Terraform state to be removed or waits to succeed early. It introduces a clear not-found classification (HTTP 404 or an explicit sentinel) and adds hooks and unit tests to validate behavior across status codes.

Changes:

  • Tightens “not found” detection to HTTP 404 or autogen.ErrNotFound, and propagates all other API errors for read/delete/wait flows.
  • Adds service-specific hooks for known API quirks (search deployment empty-body “not found”, service account secret missing-from-list) using the new sentinel.
  • Adds unit tests using httptest + real config.MongoDBClient to exercise error propagation and not-found behavior across operations.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/codegen/config.yml Updates wait-state comments around DELETED semantics (not-found handling)
internal/serviceapi/serviceaccountsecret/resource_custom_hooks.go Wraps missing-secret condition with autogen.ErrNotFound
internal/serviceapi/serviceaccountsecret/resource_custom_hooks_internal_test.go Adds tests for service account secret post-read hook behavior
internal/serviceapi/searchdeploymentapi/resource_custom_hooks.go Adds post-read hook mapping empty JSON bodies to autogen.ErrNotFound
internal/serviceapi/searchdeploymentapi/resource_custom_hooks_internal_test.go Adds tests for search deployment empty-body not-found mapping
internal/serviceapi/projectserviceaccountsecret/resource_custom_hooks.go Wraps missing-secret condition with autogen.ErrNotFound
internal/serviceapi/projectserviceaccountsecret/resource_custom_hooks_internal_test.go Adds tests for project service account secret post-read hook behavior
internal/common/autogen/unmarshal.go Uses exported IsEmptyJSON helper
internal/common/autogen/handle_operations.go Refactors not-found logic to use HTTP 404 or ErrNotFound; exports IsEmptyJSON
internal/common/autogen/handle_operations_internal_test.go Adds unit tests covering delete/read/data source read list/wait refresh error propagation
.changelog/4676.txt Adds release notes for affected resources

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tools/codegen/config.yml Outdated
Comment thread internal/common/autogen/handle_operations.go Outdated

@manupedrozo manupedrozo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ty!

Comment on lines +430 to +431
// notFound returns if the API result indicates the resource is not found:
// an HTTP 404 response, or a hook signaling it via ErrNotFound.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Bit confusing :D

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Rephrased comment slightly. The sentinel gives hooks one consistent way to flag "resource not found" regardless of what the API actually returned, instead of each hook fabricating a fake 404 http.Response that misrepresents the real wire response.

@erabil-mdb erabil-mdb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@@ -0,0 +1,312 @@
package autogen

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this a common pattern? Using tests in the same package?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good callout, let me circle back here as this is diverging from common testing pattern in our repo, we have never used *_internal_test.go

@EspenAlbert EspenAlbert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nice. Would be great to have a real acceptance test for this also, but understand it might be tricky to trigger

Resolve handle_operations.go conflict: keep both master's waitRefreshResult
(#4668) and the ErrNotFound-based notFound. Declare IDLE as a pending state
in the refreshFunc test, now required by waitRefreshResult.
@AgustinBettati

Copy link
Copy Markdown
Member Author

Merging so we have changes in upcoming release. Will follow up on unit testing structure that was used + assessing feasibility of capturing fixes in an acceptance test (verified fixes locally to be sure).

@AgustinBettati
AgustinBettati merged commit d694b6f into master Aug 26, 2026
51 of 53 checks passed
@AgustinBettati
AgustinBettati deleted the CLOUDP-437881 branch August 26, 2026 19:17
svc-apix-Bot added a commit that referenced this pull request Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants