docs: record EKS access entry owner extraction

This commit is contained in:
joshsiv-mulga
2026-10-08 12:34:24 +10:00
parent 68ee6f620c
commit 2b877b01a0
2 changed files with 9 additions and 4 deletions
+2
View File
@@ -293,6 +293,7 @@ while the branch is in flight; they do not describe the umbrella repository's
| `1cb868e68` | — | — | ECS task definition characterization before extraction (ADR-0003 S1, ADR-0004): stored keys and field names, every reference form and its not-found fault, repeatable deregister, an inactive revision still launching, bare-family launch resolution, a service pinning its resolved revision. Present contract and gaps, including the bare-family `iam:PassRole` re-resolution security gap, in `docs/package-boundary/ecs-task-definition-lifecycle.md`; no conformance claimed. |
| `544b0c6a0` | `spinifex/handlers/ecs` (`TaskDefRecord`, `ContainerDef`, `TaskDefRevKey`, `TaskDefLatestRevKey`, `ParseTaskDefRef`, `TaskDefARN`, `TaskDefRefARN`, revision register/deregister/resolve/list) | `spinifex/domains/ecs/taskdefinition` (`Owner`, `Record`, `Container`, `PortMapping`, `SystemControl`, `ParseRef`, `ARN`, `RefARN`, keys) | ADR-0004 owner extraction (ADR-0003 S1 contract in `docs/package-boundary/ecs-task-definition-lifecycle.md`). Stored keys and JSON field names unchanged; the owner declares its own persisted `PortMapping`/`SystemControl` so it does not import `handlers/ecs/bus`. Launches use the launch-side `taskDefinitionResolver`; gateway authz and all `iam:PassRole` checks unchanged. `docs/service-interfaces.yaml` now lists `spinifex/handlers/ecs` and the new package under `spinifex_daemon`. Proves ownership and dependency direction only; no conformance claimed. |
| `b36d70b22` | — | — | EKS access entry characterization before extraction (ADR-0003 S1, ADR-0004): persisted and nested field names, pre-existing JSON decodes, actions ignore cluster status, associate without an entry is not found, disassociating an unbound policy is a no-op, an empty update keeps username and groups, cluster delete sweeps only that cluster's entries, token review needs no cluster record. Present contract and gaps in `docs/package-boundary/eks-access-entry-lifecycle.md`; no conformance claimed. |
| `68ee6f620` | `spinifex/handlers/eks` (`AccessEntryRecord`, `AssociatedAccessPolicy`, `AccessScope`, `AccessEntriesPrefix`, `AccessEntryKey`, `PrincipalARNHash`, access-entry KV helpers, policy catalogue, `effectiveGroups`, the access mutation logic) | `spinifex/domains/eks/access` (`Owner`, `Record`, `AssociatedPolicy`, `Scope`, `Spec`, `Get`, `List`, `Key`, `Prefix`, `PrincipalARNHash`, `SupportedPolicies`, `EffectiveGroups`) | ADR-0004 owner extraction (ADR-0003 S1 contract in `docs/package-boundary/eks-access-entry-lifecycle.md`). Stored keys and JSON field names unchanged; writes go only through `Owner`, including the creator-admin seed at cluster launch. The handler keeps AWS validation, the cluster existence check, error mapping and projection; gateway authz switches only its hash import. `docs/service-interfaces.yaml` now lists `spinifex/handlers/eks` and the new package under `spinifex_daemon`. Proves ownership and dependency direction only; no conformance claimed. |
The EC2 contract row is intentionally different from the directory moves: it creates
a compatibility boundary. `ec2.cmd.*` retains its deployed one-token NATS
@@ -392,6 +393,7 @@ These residuals were recorded by individual slices and must be resolved once the
- `handlers/rds` → `domains/rds/subnetgroup` (legacy edges): `instanceReferences` binds `Dependants` by scanning instance records, and `tags.go` writes the group record through its generic tag registry. The first clears when the RDS instance gains its own owner and answers `Dependants` from it. The second clears when tagging invokes a subnet-group-owned tag/untag capability rather than writing the record; moving the tagging adapter to `domains/rds/awsapi` alone does not clear it.
- `handlers/ecs` → `domains/ecs/taskdefinition` (legacy edges): `tags.go` reads and writes the revision record through its generic tag registry; this clears when ECS tagging invokes an owner-provided tag mutation capability, and moving `tags.go` to `domains/ecs/awsapi` alone does not clear it. `taskDefinitionResolver` returns the persisted `Record` to task and service launch code; when the task and service owners are extracted, replace the record crossing that boundary with a consumer-owned resolved task-definition (launch-spec) projection. Moving packages has not made the record private.
- `domains/ecs/taskdefinition` (deliberate duplication, not debt): private `getJSON`, `putJSON` and `keysWithPrefix` copy the raw-KV helpers in `handlers/ecs` so the extraction keeps ECS's get/put/list error and recovery semantics. Revisit only if a later, tested state abstraction proves it preserves those semantics; do not migrate ECS to `kvstore` merely to remove them.
- `handlers/eks` → `domains/eks/access` (legacy edges): `ResolveTokenReview` opens the EKS bucket in the gateway process and reads records through the exported `access.Get`; this clears when token review asks the owner across a contract rather than opening the bucket itself. `DeleteClusterPrefix` erases access-entry keys as part of its blanket cluster sweep; this clears when cluster delete invokes an owner-provided removal of the cluster's entries. The owner returns the persisted `Record` to the AWS adapter and token review; replace it with consumer-owned projections when those consumers are extracted.
- `docs/service-interfaces.yaml` (coverage gap): no `ecs.*` subjects are listed and the manifest lint does not cover them, so ECS publisher/subscriber edges play no part in suite selection. Add them only after tracing the actual publishers and subscribers.
- Merge-time follow-up in the umbrella repository: the `Makefile` `invariants-check` target, the `docs-pr.yml` clause cross-check, `.claude/hooks/architecture-traps.sh` and a `check-invariants.sh` comment hard-code `spinifex/spinifex/network/invariants`. They must change with the pointer bump that brings in this branch, or the docs-pr cross-check silently skips. Live specs and proposals citing `spinifex/network/` will report unresolved citations until updated.
@@ -1,7 +1,8 @@
# EKS access entry: lifecycle contract
Present contract under ADR-0003 S1, recorded before the resource moves.
Present contract under ADR-0003 S1, recorded before the resource moved and updated after the owner extraction.
It describes the code as it stands, so it claims no conformance; the gaps below are observed behaviour, not targets.
The extraction establishes resource ownership and dependency direction only; it adds no ADR-0003 evidence for interruption, recovery, fencing or readiness.
Paths are relative to `spinifex/` unless they start with `tests/`.
## Scope
@@ -13,9 +14,10 @@ Create, update, delete and policy association complete synchronously within the
| Item | Present behaviour | Evidence |
|---|---|---|
| Identity | Cluster name plus principal ARN. Key and ARN suffix use `sha256hex(principalARN)` (`handlers/eks/store.go` `PrincipalARNHash`). ARN `arn:aws:eks:{region}:{account}:access-entry/{cluster}/{hash}` (`foundation/aws/arn/eks.go`); the AWS ARN shape differs and is not verified. | `handlers/eks/service_impl_test.go` `TestAccessEntry_CreateDescribeListDelete`, `handlers/eks/store_test.go`, `gateway/eks/authz_test.go` `TestResourceARNsAccessEntryMatchesHandler` |
| Identity | Cluster name plus principal ARN. Key and ARN suffix use `sha256hex(principalARN)` (`domains/eks/access/store.go` `PrincipalARNHash`). ARN `arn:aws:eks:{region}:{account}:access-entry/{cluster}/{hash}` (`foundation/aws/arn/eks.go`); the AWS ARN shape differs and is not verified. | `handlers/eks/service_impl_test.go` `TestAccessEntry_CreateDescribeListDelete`, `domains/eks/access/store_test.go` `TestKeyPaths`, `gateway/eks/authz_test.go` `TestResourceARNsAccessEntryMatchesHandler` |
| Scope | Account and Region, within one cluster: keys live under `clusters/{cluster}/access-entries/` in the `eks-account-{account}` bucket. | `handlers/eks/access_lifecycle_test.go` `TestAccessEntryPersistedLayout_TopLevelAndNestedFieldNames`, `TestAccessEntryRecord_DecodesHandWrittenJSON`, `handlers/eks/create_cluster_test.go` `TestCreateCluster_SeedsCreatorAdminAccessEntry` |
| Durable owner and record | `handlers/eks/access_entry.go` writes `AccessEntryRecord` with nested `AssociatedAccessPolicy` and `AccessScope` (`handlers/eks/types.go`). The cluster launch also writes the creator-admin entry, and `DeleteClusterPrefix` (`handlers/eks/cluster_state.go`) erases entries with the cluster. The gateway process reads records directly through `ResolveTokenReview` (`handlers/eks/token_review.go`). | `handlers/eks/access_lifecycle_test.go` `TestAccessEntryPersistedLayout_TopLevelAndNestedFieldNames`, `TestAccessEntryRecord_DecodesHandWrittenJSON`, `handlers/eks/create_cluster_test.go` `TestCreateCluster_SeedsCreatorAdminAccessEntry` |
| Durable owner and record | `domains/eks/access` (`Owner`) is the only writer of `Record` with nested `AssociatedPolicy` and `Scope`: Create, SeedCreatorAdmin (cluster launch), Update, Associate, Disassociate and Delete. Keys and field names are unchanged by the move. `DeleteClusterPrefix` (`handlers/eks/cluster_state.go`) still erases entries with the cluster, and the gateway process reads records through the exported `access.Get` in `ResolveTokenReview` (`handlers/eks/token_review.go`). | `handlers/eks/access_lifecycle_test.go` `TestAccessEntryPersistedLayout_TopLevelAndNestedFieldNames`, `TestAccessEntryRecord_DecodesHandWrittenJSON`, `domains/eks/access/owner_test.go`, `domains/eks/access/store_test.go`, `handlers/eks/create_cluster_test.go` `TestCreateCluster_SeedsCreatorAdminAccessEntry` |
| AWS adapter | `handlers/eks` keeps request validation (entry type, supported policy, scope), the cluster existence check (`acctKVForCluster`), awserrors mapping and the AWS projection. `gateway/eks/authz.go` uses `access.PrincipalARNHash`. | `handlers/eks/service_impl_test.go` access cases, `gateway/eks/authz_test.go` `TestResourceARNsAccessEntryMatchesHandler` |
| Desired state | Principal, Kubernetes username and groups, type (`STANDARD` only), tags (set at create only) and cluster-scoped access policy associations. | `handlers/eks/service_impl_test.go` `TestAccessEntry_RejectsNodeType`, `TestAccessPolicy_AssociateRejectsUnsupportedPolicyAndScope`, `TestAccessPolicy_AssociateRejectsNamespaceScope`, `handlers/eks/access_lifecycle_test.go` `TestAccessEntry_UpdateWithNoFieldsKeepsUsernameAndGroups` |
| Generation | None. Update, associate and disassociate retry against the KV revision; create and delete do not. | |
| Observed state | Not tracked; the record is the only state. | |
@@ -34,6 +36,7 @@ Create, update, delete and policy association complete synchronously within the
3. Access actions do not check cluster status (see dependencies row), and opening the bucket creates it for an account that has none.
4. Entries can outlive their cluster (interrupted prefix purge) or carry over to a recreated cluster (reclaim and failed launch keep the meta and do not purge entries separately).
5. Token review authenticates against an entry without checking the cluster exists or its status.
6. `ResolveTokenReview` opens the EKS bucket and reads the record in the gateway process, outside the owner.
6. `ResolveTokenReview` opens the EKS bucket and reads the record in the gateway process through the exported `access.Get`, outside the owner.
7. List ignores `maxResults` and `nextToken`; tagging an access-entry ARN returns `NotImplemented`.
8. No ADR-0003 S5 failure or recovery evidence.
9. Handler code receives the persisted `Record` from the owner, so the storage shape still crosses into the AWS adapter and token review. It is a temporary extraction boundary, not the narrow projection ADR-0004 asks for.