Skip to content

fix(project): encode project_ip_access_list ID via EncodeStateID - #122

Closed
nilpntr wants to merge 1 commit into
crossplane-contrib:mainfrom
nilpntr:fix/project-ip-access-list-encode-state-id
Closed

fix(project): encode project_ip_access_list ID via EncodeStateID#122
nilpntr wants to merge 1 commit into
crossplane-contrib:mainfrom
nilpntr:fix/project-ip-access-list-encode-state-id

Conversation

@nilpntr

@nilpntr nilpntr commented Jun 9, 2026

Copy link
Copy Markdown

Description of your changes

mongodbatlas_project_ip_access_list configured its GetIDFn (in config/resources/project.go) to build a plain "<project_id>-<entry>" terraform ID:

return fmt.Sprintf("%s-%s", project, ip), nil

But terraform-provider-mongodbatlas (v2.x, plugin-framework) reads this resource with conversion.DecodeStateID and rejects any ID that doesn't decode to exactly {project_id, entry}:

decodedIDMap := conversion.DecodeStateID(...)
if len(decodedIDMap) != 2 {
    resp.Diagnostics.AddError("error during the reading operation", "the provided resource ID is not correct")
}

So every observe fails:

observe failed: cannot run refresh: refresh failed: error during the reading
operation: the provided resource ID is not correct

And because GetIDFn returns a non-empty ID from parameters, upjet runs the refresh on the first reconcile and never reaches Create — the access-list entry is never added and the MR is stuck Synced=False with an empty crossplane.io/external-name.

This is the same EncodeStateID scheme the repo already handles for other Atlas resources via encodedStateID. This PR:

  • Adds config.projectIPAccessListExternalName(), which builds the ID with the existing encodeAtlasStateID helper (keys entry + project_id). The entry value comes from either cidr_block or ip_address (mutually exclusive), which the static encodedStateID/encodedStateIDMapped helpers can't express — hence a small dedicated config rather than a map entry.
  • Points "mongodbatlas_project_ip_access_list" at it in externalNameConfigs (was config.IdentifierFromProvider).
  • Removes the stale plain-format GetIDFn override in config/resources/project.go so the default external-name config applies (References/LateInitializer kept).
  • Adds unit tests covering both cidr_block and ip_address, the two-key round-trip through DecodeStateID, the error paths, and entry recovery from external-name.

I have:

  • Read and followed Crossplane's contribution process.
  • Run make reviewable test to ensure this PR is ready for review.

Ran go build ./config/... and go test ./config/... locally (all pass). Full make reviewable left to CI.

How has this code been tested

  • Unit tests in config/external_name_test.go (TestProjectIPAccessListGetIDFn, TestProjectIPAccessListGetIDFn_Errors) — assert the encoded ID decodes back to exactly {project_id, entry} for both cidr_block and ip_address.
  • Verified live against a real MongoDB Atlas project with provider built from this branch (bundling terraform-provider-mongodbatlas v2.12.0): the IPAccessList MR reconciles to Synced=True/Ready=True and the entry appears in the project's access list. Before the fix the same MR failed with the provided resource ID is not correct.

mongodbatlas_project_ip_access_list configured GetIDFn to build a plain
"<project_id>-<entry>" terraform ID. terraform-provider-mongodbatlas (v2.x,
plugin-framework) reads this resource with conversion.DecodeStateID and rejects
any ID that doesn't decode to exactly {project_id, entry}, so every observe
failed with:

  observe failed: cannot run refresh: refresh failed: error during the reading
  operation: the provided resource ID is not correct

Because GetIDFn produces a non-empty ID from parameters, upjet runs the refresh
on the first reconcile and never reaches Create — the entry is never added and
the MR is stuck Synced=False, external-name empty.

Move the external-name handling into config.projectIPAccessListExternalName,
which encodes the ID with the existing encodeAtlasStateID helper (matching the
TF provider's conversion.EncodeStateID over the keys "entry" and
"project_id"). The entry value comes from either cidr_block or ip_address
(mutually exclusive), so the static encodedStateID/encodedStateIDMapped helpers
can't be used directly. The stale GetIDFn override in config/resources/project.go
is removed so the default external-name config applies.

Verified live against a real Atlas project: the IPAccessList reconciles to
Synced=True/Ready=True and the entry appears in the project access list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Sam Mobach <sam.mobach@rtl.nl>
@fernandezcuesta

Copy link
Copy Markdown
Collaborator

Hi @nilpntr, first of all thanks for your contribution!
If I'm not mistaken, this issue -among others- should be fixed in #128 which I plan to release soon in v1.2. Let me know if after the new version is cut you still experience any issues with this resource.

Thanks!

@fernandezcuesta

Copy link
Copy Markdown
Collaborator

Closing this one as it was included in #128

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