mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-20 19:25:52 +00:00
docs: record EKS access entry lifecycle contract
This commit is contained in:
@@ -292,6 +292,7 @@ while the branch is in flight; they do not describe the umbrella repository's
|
||||
| `17dc29251` | `spinifex/handlers/rds` (`DBSubnetGroupRecord`, `DBSubnetGroupSubnet`, `DBSubnetGroupKey`, `DBSubnetGroupsPrefix`, `getDBSubnetGroup`, `resolveGroupSubnets` and the create/modify/delete bodies) | `spinifex/domains/rds/subnetgroup` (`Owner`, `Record`, `Subnet`, `Key`, `Prefix`) | First ADR-0004 resource owner. The handler keeps AWS validation, projection and paging; delete reaches instances through the owner-declared `Dependants`, and instance placement reaches the group through the instance-declared `subnetGroupPlacement`. Stored shape unchanged; `docs/service-interfaces.yaml` lists the package with the RDS daemon. No conformance claimed; gaps in the lifecycle contract. |
|
||||
| `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. |
|
||||
|
||||
The EC2 contract row is intentionally different from the directory moves: it creates
|
||||
a compatibility boundary. `ec2.cmd.*` retains its deployed one-token NATS
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# EKS access entry: lifecycle contract
|
||||
|
||||
Present contract under ADR-0003 S1, recorded before the resource moves.
|
||||
It describes the code as it stands, so it claims no conformance; the gaps below are observed behaviour, not targets.
|
||||
Paths are relative to `spinifex/` unless they start with `tests/`.
|
||||
|
||||
## Scope
|
||||
|
||||
An access entry has no realization of its own.
|
||||
Create, update, delete and policy association complete synchronously within the request as writes to the account's EKS bucket; the token webhook reads the record when it authenticates a `get-token` bearer.
|
||||
|
||||
## Contract
|
||||
|
||||
| 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` |
|
||||
| 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` |
|
||||
| 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. | |
|
||||
| Readiness | Usable by token review as soon as the write returns. | `handlers/eks/token_review_test.go` `TestResolveTokenReview_AuthenticatesViaVerifyAndKV`, `TestResolveTokenReview_NoAccessEntryDenies`, `handlers/eks/service_impl_test.go` `TestAccessPolicy_DisassociateRemovesProjectedGroup` |
|
||||
| Idempotency | `clientRequestToken` is ignored. Update always advances `modifiedAt`, even when nothing changes. A duplicate create returns `ResourceInUseException`; a repeated delete returns `ResourceNotFoundException`; disassociating an unbound policy succeeds without change. Internal NATS redelivery of create returns `ResourceInUseException` to the redelivered request. | `handlers/eks/service_impl_test.go` `TestAccessEntry_CreateDescribeListDelete`, `TestAccessEntry_DescribeDeleteMissingIsNotFound`, `handlers/eks/access_lifecycle_test.go` `TestAccessPolicy_DisassociateUnassociatedLeavesRecordUnchanged`, `TestAccessPolicy_AssociateWithNoEntryIsNotFound` |
|
||||
| Dependencies | Every action requires only that the cluster's meta record exists (`acctKVForCluster`); cluster status is not read, so actions succeed on a `CREATING`, `FAILED` or `DELETING` cluster. Token review does not check the cluster at all. The principal is not checked against IAM. | `handlers/eks/service_impl_test.go` `TestAccessEntry_UnknownClusterIsNotFound`, `handlers/eks/access_lifecycle_test.go` `TestAccessEntryActionsIgnoreClusterLifecycleStatus`, `TestResolveTokenReview_AuthenticatesWithoutClusterMeta` |
|
||||
| Authorization | Gateway authz derives the access-entry ARN with `PrincipalARNHash` (`gateway/eks/authz.go`). No `iam:PassRole`. The STS step of token review is the existing `eks.VerifyToken` NATS contract served by awsgw. | `gateway/eks/authz_test.go` `TestResourceARNsAccessEntryMatchesHandler` |
|
||||
| Deletion ordering | Delete is get-then-delete, not conditional. Cluster delete erases entries in the prefix purge only after all infrastructure teardown succeeds; on failure they survive while the cluster stays `DELETING`. | `handlers/eks/access_lifecycle_test.go` `TestDeleteClusterPrefix_SweepsAccessEntriesScopedToCluster` |
|
||||
| Restart and interruption | The prefix purge deletes keys in listing order; an interruption after the meta key is gone leaves entries that a new cluster of the same name inherits. Reclaiming a `FAILED` cluster and a failed launch purge with the meta kept, so the recreated cluster inherits the failed attempt's entries. | none |
|
||||
| End-to-end | `tests/e2e/eks/eks_test.go` `AccessEntry` and `GetToken`; `tests/integration/cross_tenant_test.go`. No Terraform apply/destroy evidence for `aws_eks_access_entry` or `aws_eks_access_policy_association`. | |
|
||||
|
||||
## Gaps
|
||||
|
||||
1. Create is get-then-put with no exclusive create, so concurrent creates for one principal both succeed and the later write wins.
|
||||
2. The creator-admin seed at cluster launch is an unconditional put after the cluster meta is final, so it replaces an entry for the same principal created while the cluster was `CREATING`, including its policy associations.
|
||||
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.
|
||||
7. List ignores `maxResults` and `nextToken`; tagging an access-entry ARN returns `NotImplemented`.
|
||||
8. No ADR-0003 S5 failure or recovery evidence.
|
||||
Reference in New Issue
Block a user