Skip to content

fix(resolve): resolve a group_role's group through the id map - #201

Merged
2000game merged 2 commits into
mainfrom
fix/resolve-group-role-via-id-map
Oct 16, 2026
Merged

2000game merged 2 commits into
mainfrom
fix/resolve-group-role-via-id-map

Conversation

@2000game

Copy link
Copy Markdown
Member

Why

ct.groupRole({ group: "<key>", role }) resolves its group through groupIdForRole, which looked only at ct's state and the declared groups. A plain group ref already falls back to the OpenTofu id map (#181), but a group_role did not. So once groups belong to OpenTofu and leave ct's state and config, every grant on them would fail with "no managed group named …", even with the group in .ct/ids.<host>.json.

This blocks the step after tier 1 in the OpenTofu migration: removing the groups from ct while their grants (tier 2) stay in ct.

What

Checks

lint, typecheck, test (1273 passed, one new), build, docs staleness all green locally.

A ct.groupRole names its group by key. groupIdForRole looked only at ct's
state and the declared groups, so once a group leaves both because
OpenTofu owns it, every grant on it failed to resolve, even with the
group in .ct/ids.<host>.json. Plain group refs already consulted the map.

Needed before ct-structure's groups can leave ct's state and config.
…stead of deferring it

A pending group_role is finished at apply from ct's state, which a group
resolved through the id map is not in. A grant naming a role declared in
the same config therefore deferred, let the resource tier write, and then
failed with a misleading 'create failed' message. It now fails at plan
time with the two-step remedy.

Also checks a same-run-declared group before the id map (as resolve()
does), names the id map in the not-found error, and states the limit in
the handbook.
@2000game

Copy link
Copy Markdown
Member Author

Review (/code-review high), each finding checked against the code:

  • Fixed: a tofu-owned group could defer a grant, which then failed after writes. declaresRoleForGroup is lenient without the group's state row, so a mapped group plus a role declared in the same config planned as pending. At apply, resolvePendingGroupRoleDomain looks the group up in state, finds nothing, and throws "the group's create failed" after the resource tier has already written. A group resolved through the id map now never defers. It fails at plan time with the existing two-step remedy (apply the role first). The review's example scenario was off (it named an undeclared role), but the path is real. A regression test in permission-pending-role.test.ts fails before this fix and passes after.
  • Fixed: check order. A same-run-declared group is now checked before the id map, the same order resolve() uses, which drops the double declaredByType check. A precedence test guards it.
  • Fixed: the not-found error never mentioned the id map. It now names the map and gives the ct ids sync hint when a map is loaded, like notFound. Covered by a test.
  • Fixed: docs. The handbook says where the group can come from, and states the plan-time limit for tofu-owned groups. Re-signed.
  • Follow-up, not in this PR: groupMemberField still reads state only. A member-field ref on a tofu-owned group still fails. That belongs to the member-field tier of the migration, so it gets its own change.
  • Not changed: reusing the id-map key helper. entryKey isn't exported, and the plain-ref path builds the key the same way.

No end-to-end run: no host's id map holds groups yet, so the path isn't reachable outside tests today.

@2000game
2000game merged commit 04ca19f into main Oct 16, 2026
3 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.

1 participant