Skip to content

feat(ecr): add ECR Batch 1 — repository CRUD, tags, policies#717

Merged
vieiralucas merged 7 commits intomainfrom
worktree-worktree-ecr-batch1
Apr 23, 2026
Merged

feat(ecr): add ECR Batch 1 — repository CRUD, tags, policies#717
vieiralucas merged 7 commits intomainfrom
worktree-worktree-ecr-batch1

Conversation

@vieiralucas
Copy link
Copy Markdown
Member

@vieiralucas vieiralucas commented Apr 23, 2026

Summary

  • Adds fakecloud-ecr, a new crate implementing the ECR control-plane foundation (11/58 operations).
  • ECR was picked by surveying LocalStack/Moto issue backlogs for the highest-pain AWS mock service: LocalStack gates it behind its Pro tier and has unresolved bugs around it (#7186 EOF on getCredentials, #5598 pushed/missing images, #8128 403 on docker push, #12043 non-us-east-1 timeouts); Moto has a partial control plane but cannot speak the OCI v2 Distribution protocol at all, so docker push / docker pull do not work across either tool.
  • Tiny API surface (~43–58 ops depending on how you count), zero dependency on new subsystems, and it unblocks ECS (the next wave in the roadmap) — highest pain × demand × effort ratio.

Scope of this PR (Batch 1 of 4):

  • CreateRepository, DeleteRepository, DescribeRepositories
  • PutImageTagMutability, PutImageScanningConfiguration
  • SetRepositoryPolicy, GetRepositoryPolicy, DeleteRepositoryPolicy
  • TagResource, UntagResource, ListTagsForResource

Coverage numbers (cargo run -p fakecloud-conformance --release -- run --services ecr):

  • 11/58 ECR operations implemented
  • All 11 pass 100% of their conformance variants (positive + negative: missing required fields, enum bounds, string length, maxResults range)
  • Baseline updated; every other service stays at 100%.

Follow-up batches

  • Batch 2: image + layer ops (PutImage, BatchGetImage, BatchCheckLayerAvailability, InitiateLayerUpload, UploadLayerPart, CompleteLayerUpload, GetDownloadUrlForLayer, etc.) + EventBridge emission on push/delete.
  • Batch 3: OCI v2 Distribution protocol (/v2/... endpoints) + GetAuthorizationToken + Basic-Auth — the differentiator that makes real docker push work.
  • Batch 4: lifecycle, scanning, registry, pull-through cache, signing, replication, CloudFormation + Lambda CodeUri integration, website landing page, marketing post.

Test plan

  • cargo build --workspace
  • cargo fmt --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test -p fakecloud-e2e --test ecr --test ecr_persistence — 7 passing
  • cargo test -p fakecloud-conformance --test ecr — 11 passing
  • cargo run -p fakecloud-conformance --release -- check — no regressions
  • Introspection endpoint /_fakecloud/ecr/repositories returns expected shape

Summary by cubic

Adds ECR Batch 1 with repository CRUD, tagging, and policies via the new fakecloud-ecr service, plus stricter validation, default paging, and proper token errors. This sets up multi-account state and persistence for future image ops and OCI push/pull.

  • New Features

    • Implemented 11 ECR ops: CreateRepository, DeleteRepository, DescribeRepositories, PutImageTagMutability, PutImageScanningConfiguration, SetRepositoryPolicy, GetRepositoryPolicy, DeleteRepositoryPolicy, TagResource, UntagResource, ListTagsForResource.
    • New fakecloud-ecr crate with JSON-1.1 routing (AmazonEC2ContainerRegistry_V20150921), snapshot persistence, and reset hooks; exposed via /_fakecloud/ecr/repositories.
    • Conformance: 11/58 ops implemented; all implemented variants pass. E2E + persistence tests added.
  • Bug Fixes

    • ECR: enforce AWS repo name regex; DescribeRepositories applies maxResults (default 100), supports nextToken, and returns InvalidContinuationTokenException for bad tokens; default registry scanning to BASIC.
    • Reset: reset_service("ecr") now drops non-default accounts for consistency.
    • API Gateway v2 conformance test: include required fields in closure-route requests to satisfy handler validation.
    • CI: free disk space on the e2e partition check and all E2E matrix jobs; forward nextest stderr on errors.

Written for commit fdebea8. Summary will update on new commits.

Batch 1 of 4 in the ECR buildout. LocalStack gates ECR behind its paid
Pro tier and Moto can't speak the OCI v2 Distribution protocol at all,
so real `docker push` / `docker pull` is broken across both tools. This
batch lays the foundation: a new `fakecloud-ecr` crate, the full Smithy
model pinned in `aws-models/ecr.json`, JSON-1.1 dispatch (`X-Amz-Target:
AmazonEC2ContainerRegistry_V20150921.*`), multi-account state, snapshot
persistence, and the 11 repository-level operations.

Operations:
- CreateRepository / DeleteRepository / DescribeRepositories
- PutImageTagMutability / PutImageScanningConfiguration
- SetRepositoryPolicy / GetRepositoryPolicy / DeleteRepositoryPolicy
- TagResource / UntagResource / ListTagsForResource

Coverage:
- 11/58 ECR ops implemented, 11 pass 100% of their conformance variants
  (`cargo run -p fakecloud-conformance --release -- run --services ecr`).
- 6 E2E tests + 1 persistence round-trip test, all green.
- Conformance baseline updated; other services hold at 100%.
- Introspection endpoint `/_fakecloud/ecr/repositories` exposes
  repository metadata without leaking runtime internals.

Image + layer ops land in Batch 2, OCI v2 Distribution + real
`docker push` in Batch 3, lifecycle + scanning + cross-service in
Batch 4.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 26 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-ecr/src/state.rs">

<violation number="1" location="crates/fakecloud-ecr/src/state.rs:153">
P2: `RegistryScanningConfiguration::default()` currently produces an empty `scan_type`, but this state expects the default to be `"BASIC"`.
This can return incorrect registry scanning configuration after account creation/reset.</violation>
</file>

<file name="crates/fakecloud-ecr/src/service.rs">

<violation number="1" location="crates/fakecloud-ecr/src/service.rs:189">
P2: Repository name validation is incomplete and accepts names that violate the documented ECR pattern.</violation>

<violation number="2" location="crates/fakecloud-ecr/src/service.rs:383">
P2: `DescribeRepositories` validates `maxResults` but does not apply it, so responses can exceed the requested limit.</violation>
</file>

<file name="crates/fakecloud-server/src/reset.rs">

<violation number="1" location="crates/fakecloud-server/src/reset.rs:116">
P2: `reset_service("ecr")` keeps non-default accounts instead of fully resetting the multi-account container. Use `MultiAccountState::reset()` to match reset semantics used by other services.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/fakecloud-ecr/src/state.rs
Comment thread crates/fakecloud-ecr/src/service.rs Outdated
Comment thread crates/fakecloud-ecr/src/service.rs Outdated
Comment thread crates/fakecloud-server/src/reset.rs Outdated
`apigwv2_closure_routes_exist` was sending empty / minimal bodies to
several operations whose handlers enforce the Smithy `@required`
contract. The test was only checking that the routes existed, but the
handlers had since gained input validation that rejects those requests
with 400, so the test started failing regardless of route wiring.

Fixes:
- UpdateApiMapping: include ApiId (required).
- CreateVpcLink: include SubnetIds (required).
- CreateRoutingRule / PutRoutingRule: include Actions, Conditions, Priority.
- CreateModel: include Schema (required).
- ExportApi: include outputType query param (required).
- CreatePortal: include Authorization, EndpointConfiguration, PortalContent.
- CreatePortalProduct: use DisplayName (required), not Name.
- PutPortalProductSharingPolicy: include PolicyDocument.
- CreateProductPage: include DisplayContent object.
- CreateProductRestEndpointPage: include RestEndpointIdentifier.
- RegistryScanningConfiguration::default() now returns scanType=BASIC
  to match AWS's default for new registries (was previously an empty
  string, which would corrupt Get/PutRegistryScanningConfiguration
  responses after account creation or reset).
- DescribeRepositories now actually applies maxResults and threads a
  nextToken through for pagination instead of validating the value
  and then ignoring it.
- Repository name validation now enforces the full AWS regex
  (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*
  via a byte-level state machine. Rejects consecutive separators,
  trailing/leading separators, empty segments, and non-lowercase
  characters. Unit tests lock the accepted/rejected cases.
- reset_service("ecr") now calls MultiAccountState::reset() like every
  other service, dropping non-default accounts instead of preserving
  them with their state cleared.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-ecr/src/service.rs">

<violation number="1" location="crates/fakecloud-ecr/src/service.rs:392">
P2: `DescribeRepositories` does not apply AWS’s default page size (100) when `maxResults` is omitted.</violation>

<violation number="2" location="crates/fakecloud-ecr/src/service.rs:397">
P2: Invalid `nextToken` values are silently treated as offset `0` instead of returning a parameter error.</violation>
</file>

<file name="crates/fakecloud-conformance/tests/apigatewayv2.rs">

<violation number="1" location="crates/fakecloud-conformance/tests/apigatewayv2.rs:981">
P2: Populate the required `DisplayContent` fields here; an empty object is not a valid `CreateProductPage` request.</violation>

<violation number="2" location="crates/fakecloud-conformance/tests/apigatewayv2.rs:1019">
P2: Fill in the required `RestEndpointIdentifier.IdentifierParts` fields; an empty object is not a valid `CreateProductRestEndpointPage` request.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/fakecloud-ecr/src/service.rs Outdated
Comment thread crates/fakecloud-ecr/src/service.rs Outdated
Comment thread crates/fakecloud-conformance/tests/apigatewayv2.rs Outdated
Comment thread crates/fakecloud-conformance/tests/apigatewayv2.rs Outdated
The e2e-partition-check job was hitting `No space left on device` while
`cargo nextest list` compiled the E2E test suite — the runner disk was
full of cached build artifacts, Android SDKs, .NET runtimes, etc.

Use the same jlumbroso/free-disk-space action the lambda matrix jobs
use to reclaim ~15GB before the nextest compile.
The adding of aws-sdk-ecr to fakecloud-testkit pushed the nextest
compile over the runner's disk budget for matrix jobs like lambda-api
and general-1/2 that previously fit. Run the free-disk-space step on
every E2E matrix shard, not just the lambda-runtimes / container
cliché ones.
- DescribeRepositories now applies AWS's documented default page size
  of 100 when maxResults is absent (was returning all repos).
- Invalid nextToken strings now return
  InvalidContinuationTokenException instead of silently falling back
  to offset 0.
- apigwv2 closure-routes test now sends Smithy-correct DisplayContent
  (Body + Title) and RestEndpointIdentifier.IdentifierParts (Method,
  Path, RestApiId, Stage) per the model's @required traits.
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