Skip to content

feat(ec2): implement IPAM internet-registry associations and routing policy registrations - #2511

Merged
vieiralucas merged 4 commits into
mainfrom
feat/ec2-ipam-routing
Sep 13, 2026
Merged

vieiralucas merged 4 commits into
mainfrom
feat/ec2-ipam-routing

Conversation

@vieiralucas

@vieiralucas vieiralucas commented Sep 4, 2026 •

Copy link
Copy Markdown
Member

Implements the 15 EC2 operations that were still unimplemented, taking EC2 to
791 implemented operations with every one passing conformance.

What these are

An IPAM internet-registry association ties an IPAM to one Regional Internet
Registry (ripe, apnic, arin, lacnic). Routing policy registrations —
RPKI route origin authorizations — hang off an association, keyed by CIDR.

Every change to a registration produces a delta. Deltas are the audit trail, so
they outlive the registrations they describe, and GetIpamRoutingPolicyRegistrationDeltas
filters by delta id, time window, and chronological order.
BatchModifyIpamRoutingPolicyRegistrations applies a whole JSON document of
additions and removals as a single delta.

Enabling an association produces the RPKI child request document the registry
needs to finish provisioning.

Derived views read from real state

The read operations report what the registrations actually say rather than
fabricating data:

  • GetIpamRouteOriginAuthorizations emits one authorization per CIDR and ASN pair.
  • GetIpamInternetRegistryAssociationAsns / Cidrs report what those
    registrations cover.
  • GetIpamDiscoveredRoutes reports what the account advertises in the queried
    region. fakecloud runs no BGP collector, so an invented view of the wider
    internet would be fiction.
  • GetIpamRouteProtectionFindings marks a registration carrying an ASN valid
    and one with none unknown, which is how an unsigned announcement looks to RPKI.

Test harness hang

sweep_instance_containers shelled out to the container CLI with no timeout, so
a wedged Docker daemon blocked the whole test run instead of just that sweep.
Locally this turned a 20-second test into a multi-minute hang. It now shares the
bounded wait that probe_cli already had, with a regression test.

Verification

  • Conformance probe: EC2 776 -> 791 implemented operations, 0 failing, 25,716 variants passed
  • cargo test -p fakecloud-conformance --test ec2 ipam_: 64 passed
  • cargo build --workspace and cargo clippy --workspace --all-targets: clean
  • The vendored aws-sdk-ec2 predates this surface, so the new tests drive the
    Query protocol directly

Summary by cubic

Implements 15 previously unimplemented EC2 IPAM operations for internet-registry associations and routing policy registrations, taking EC2 from 786 to 801 implemented operations (7,491 total). Also fixes the new operations' wire shapes and validation, and bounds every container-CLI call so a wedged Docker daemon can no longer hang a test run or block server startup.

IPAM registry operations

  • An association ties an IPAM to one Regional Internet Registry; registrations are RPKI route origin authorizations keyed by CIDR.
  • Every registration change produces a delta, and deltas outlive the registrations they describe; delta queries filter by id, time window, and chronological order.
  • BatchModifyIpamRoutingPolicyRegistrations applies a JSON document of additions and removals as one delta, and enabling an association returns the RPKI child request document the registry needs.
  • Read operations derive from real state rather than fabricated data: discovered routes show the account's advertisements in the queried region, and findings mark registrations with an ASN as valid, those without as unknown.
  • RPKI strength now reports strict/permissive instead of strong/none, and findings name the ROA prefix prefix.
  • Dry runs resolve the association before skipping the mutation, delta time bounds are parsed as instants, and MaxLength is enforced.

Container CLI bounds

  • The bounded wait lives in container_net, shared by the test harness, server reaper, and liveness probe, with a regression test.
  • The reaper runs synchronously before the server serves, so an unbounded call there blocked startup.
  • The wait now drains the child's stdout, so output that overflows the pipe buffer no longer stalls for the full timeout and sweeps nothing.

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

Review in cubic

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

8 issues found and verified against the latest diff

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="website/static/llms-full.txt">

<violation number="1" location="website/static/llms-full.txt:9">
P3: The updated operation count is paired with the old aggregate conformance total, so this page now reports an internally inconsistent coverage claim. Update the aggregate pass/total to the post-change conformance result and keep the other generated documentation in sync.</violation>
</file>

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

<violation number="1" location="crates/fakecloud-conformance/tests/ec2.rs:13051">
P2: The routing-policy lifecycle creates a registration through a `pending-enable` association. Enable the association before creating the registration, otherwise this test will preserve and hide an invalid lifecycle and fail to catch enforcement of the enable prerequisite.</violation>
</file>

<file name="crates/fakecloud-testkit/src/lib.rs">

<violation number="1" location="crates/fakecloud-testkit/src/lib.rs:528">
P2: If `try_wait` returns an error, `wait_bounded` leaves the CLI child running and callers drop it without cleanup. Kill and reap the child before returning `false` from this branch.</violation>
</file>

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

<violation number="1" location="crates/fakecloud-ec2/src/service/ipam_registry.rs:68">
P2: Associations returned from China, GovCloud, or isolated regions contain the wrong ARN partition. Use `partition_for(&a.region)` when constructing this ARN.

(Based on your team's feedback about regional ARN partitioning.)</violation>

<violation number="2" location="crates/fakecloud-ec2/src/service/ipam_registry.rs:239">
P2: When an enable parameter contains XML metacharacters, the returned `childRequestXml` is not a well-formed document after XML decoding. Escape each attribute value before assembling the child request.</violation>

<violation number="3" location="crates/fakecloud-ec2/src/service/ipam_registry.rs:299">
P2: These handlers ignore the modeled `Filters` parameter, so callers cannot restrict associations or derived route views. Parse and apply the supported filters before paginating the results.</violation>

<violation number="4" location="crates/fakecloud-ec2/src/service/ipam_registry.rs:315">
P2: These listing operations validate `MaxResults` but never apply it or return `nextToken`. Large associations, registrations, deltas, and derived views therefore cannot be paginated and clients receive incorrect pages; apply `paginate` and emit the returned token.</violation>
</file>

<file name="website/content/docs/parity.md">

<violation number="1" location="website/content/docs/parity.md:107">
P3: The EC2 row now advertises 791 operations but still says `Full 776-op control plane`, making the parity matrix internally contradictory. Update the stale description to 791 so readers do not get conflicting coverage information.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

let c = s.ec2_client().await;
let q = Ec2Query::new(&s);

let id = make_ir_association(&c, &q).await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The routing-policy lifecycle creates a registration through a pending-enable association. Enable the association before creating the registration, otherwise this test will preserve and hide an invalid lifecycle and fail to catch enforcement of the enable prerequisite.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-conformance/tests/ec2.rs, line 13051:

<comment>The routing-policy lifecycle creates a registration through a `pending-enable` association. Enable the association before creating the registration, otherwise this test will preserve and hide an invalid lifecycle and fail to catch enforcement of the enable prerequisite.</comment>

<file context>
@@ -12936,3 +12936,488 @@ async fn ec2_detach_image_watermark() {
+    let c = s.ec2_client().await;
+    let q = Ec2Query::new(&s);
+
+    let id = make_ir_association(&c, &q).await;
+    assert!(id.starts_with("ipam-ir-assoc-"), "{id}");
+
</file context>

match child.try_wait() {
Ok(Some(_)) => return true,
Ok(None) => {}
Err(_) => return false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: If try_wait returns an error, wait_bounded leaves the CLI child running and callers drop it without cleanup. Kill and reap the child before returning false from this branch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-testkit/src/lib.rs, line 528:

<comment>If `try_wait` returns an error, `wait_bounded` leaves the CLI child running and callers drop it without cleanup. Kill and reap the child before returning `false` from this branch.</comment>

<file context>
@@ -471,23 +471,68 @@ fn sweep_instance_containers(cli: &str, pid: u32) {
+        match child.try_wait() {
+            Ok(Some(_)) => return true,
+            Ok(None) => {}
+            Err(_) => return false,
+        }
+        if std::time::Instant::now() >= deadline {
</file context>
Suggested change
Err(_) => return false,
Err(_) => {
let _ = child.kill();
let _ = child.wait();
return false;
}

Comment thread crates/fakecloud-testkit/src/lib.rs
s.push_str(&ec2_elem(
"ipamInternetRegistryAssociationArn",
&format!(
"arn:aws:ec2::{owner}:ipam-internet-registry-association/{}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Associations returned from China, GovCloud, or isolated regions contain the wrong ARN partition. Use partition_for(&a.region) when constructing this ARN.

(Based on your team's feedback about regional ARN partitioning.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-ec2/src/service/ipam_registry.rs, line 68:

<comment>Associations returned from China, GovCloud, or isolated regions contain the wrong ARN partition. Use `partition_for(&a.region)` when constructing this ARN.

(Based on your team's feedback about regional ARN partitioning.) </comment>

<file context>
@@ -0,0 +1,786 @@
+    s.push_str(&ec2_elem(
+        "ipamInternetRegistryAssociationArn",
+        &format!(
+            "arn:aws:ec2::{owner}:ipam-internet-registry-association/{}",
+            a.id
+        ),
</file context>

req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
mr(req)?;
let ids = indexed_list(&req.query_params, "IpamInternetRegistryAssociationId");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: These handlers ignore the modeled Filters parameter, so callers cannot restrict associations or derived route views. Parse and apply the supported filters before paginating the results.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-ec2/src/service/ipam_registry.rs, line 299:

<comment>These handlers ignore the modeled `Filters` parameter, so callers cannot restrict associations or derived route views. Parse and apply the supported filters before paginating the results.</comment>

<file context>
@@ -0,0 +1,786 @@
+    req: &AwsRequest,
+) -> Result<AwsResponse, AwsServiceError> {
+    mr(req)?;
+    let ids = indexed_list(&req.query_params, "IpamInternetRegistryAssociationId");
+    let owner = req.account_id.clone();
+    let accounts = svc.state.read();
</file context>

Ok(Ec2Service::respond(
"DescribeIpamInternetRegistryAssociations",
&req.request_id,
&ec2_list("ipamInternetRegistryAssociationSet", &items),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: These listing operations validate MaxResults but never apply it or return nextToken. Large associations, registrations, deltas, and derived views therefore cannot be paginated and clients receive incorrect pages; apply paginate and emit the returned token.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-ec2/src/service/ipam_registry.rs, line 315:

<comment>These listing operations validate `MaxResults` but never apply it or return `nextToken`. Large associations, registrations, deltas, and derived views therefore cannot be paginated and clients receive incorrect pages; apply `paginate` and emit the returned token.</comment>

<file context>
@@ -0,0 +1,786 @@
+    Ok(Ec2Service::respond(
+        "DescribeIpamInternetRegistryAssociations",
+        &req.request_id,
+        &ec2_list("ipamInternetRegistryAssociationSet", &items),
+    ))
+}
</file context>

let a = get_association(state, &id)?;
// The child request is the RPKI provisioning document the registry needs;
// it is what the caller takes to the RIR to finish setup.
a.child_request_xml = Some(format!(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When an enable parameter contains XML metacharacters, the returned childRequestXml is not a well-formed document after XML decoding. Escape each attribute value before assembling the child request.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-ec2/src/service/ipam_registry.rs, line 239:

<comment>When an enable parameter contains XML metacharacters, the returned `childRequestXml` is not a well-formed document after XML decoding. Escape each attribute value before assembling the child request.</comment>

<file context>
@@ -0,0 +1,786 @@
+    let a = get_association(state, &id)?;
+    // The child request is the RPKI provisioning document the registry needs;
+    // it is what the caller takes to the RIR to finish setup.
+    a.child_request_xml = Some(format!(
+        "<publisher_request version=\"{rpki_version}\" \
+         service_uri=\"{service_uri}\" \
</file context>

Comment thread website/static/llms-full.txt Outdated
fakecloud emulates AWS locally for integration testing and development. It is a single Rust binary (~19 MB, ~300ms startup, ~10 MiB idle memory) — no Docker required to run fakecloud itself, no signup. Point any AWS SDK or the AWS CLI at `http://localhost:4566` with dummy credentials.

**Coverage goal:** 100% of AWS services, each at 100% behavioral conformance, with 100% of cross-service integrations. Approach is depth-first — a service lands when it passes the full Smithy-model test variants and the cross-service wire-ups that matter for it, not when the API surface looks filled in. 105 services (7,408 operations) are shipped today, all at true 100% conformance — 248,557/248,557 generated Smithy variants pass on every commit; more land progressively as they hit the bar.
**Coverage goal:** 100% of AWS services, each at 100% behavioral conformance, with 100% of cross-service integrations. Approach is depth-first — a service lands when it passes the full Smithy-model test variants and the cross-service wire-ups that matter for it, not when the API surface looks filled in. 105 services (7,423 operations) are shipped today, all at true 100% conformance — 248,557/248,557 generated Smithy variants pass on every commit; more land progressively as they hit the bar.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The updated operation count is paired with the old aggregate conformance total, so this page now reports an internally inconsistent coverage claim. Update the aggregate pass/total to the post-change conformance result and keep the other generated documentation in sync.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website/static/llms-full.txt, line 9:

<comment>The updated operation count is paired with the old aggregate conformance total, so this page now reports an internally inconsistent coverage claim. Update the aggregate pass/total to the post-change conformance result and keep the other generated documentation in sync.</comment>

<file context>
@@ -6,7 +6,7 @@
 fakecloud emulates AWS locally for integration testing and development. It is a single Rust binary (~19 MB, ~300ms startup, ~10 MiB idle memory) — no Docker required to run fakecloud itself, no signup. Point any AWS SDK or the AWS CLI at `http://localhost:4566` with dummy credentials.
 
-**Coverage goal:** 100% of AWS services, each at 100% behavioral conformance, with 100% of cross-service integrations. Approach is depth-first — a service lands when it passes the full Smithy-model test variants and the cross-service wire-ups that matter for it, not when the API surface looks filled in. 105 services (7,408 operations) are shipped today, all at true 100% conformance — 248,557/248,557 generated Smithy variants pass on every commit; more land progressively as they hit the bar.
+**Coverage goal:** 100% of AWS services, each at 100% behavioral conformance, with 100% of cross-service integrations. Approach is depth-first — a service lands when it passes the full Smithy-model test variants and the cross-service wire-ups that matter for it, not when the API surface looks filled in. 105 services (7,423 operations) are shipped today, all at true 100% conformance — 248,557/248,557 generated Smithy variants pass on every commit; more land progressively as they hit the bar.
 
 Key design principles:
</file context>

Comment thread website/content/supported-services.md Outdated
Comment thread website/content/docs/parity.md Outdated
| [Glue](@/docs/services/glue.md) | 269 | JSON 1.1 | Full | Partial | Full control plane: Data Catalog (databases, tables, partitions with `GetPartitions` `Expression` pruning), jobs, crawlers, classifiers, connections, triggers, workflows, blueprints, dev endpoints, schema registry, interactive sessions, ML transforms, data quality, user-defined functions, usage profiles, column statistics, and tagging. Status transitions are real (crawler `READY`↔`RUNNING`, trigger/workflow/run lifecycles). Job/crawler/Spark *execution* itself is synthesized — fakecloud is not a Spark engine. |
| [Organizations](@/docs/services/organizations.md) | 63 | JSON 1.1 | Full | Full | Full org tree (roots, OUs, accounts), policies with SCP enforcement, handshakes, delegated administrators, service access, tagging, and a resource policy. Billing responsibility transfers ride handshake-backed records. `CreateAccount` transitions `IN_PROGRESS` -> `SUCCEEDED` after a short synthetic delay. |
| [EC2](@/docs/services/ec2.md) | 776 | ec2Query | Full | Partial | Full 776-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |
| [EC2](@/docs/services/ec2.md) | 791 | ec2Query | Full | Partial | Full 776-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The EC2 row now advertises 791 operations but still says Full 776-op control plane, making the parity matrix internally contradictory. Update the stale description to 791 so readers do not get conflicting coverage information.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website/content/docs/parity.md, line 107:

<comment>The EC2 row now advertises 791 operations but still says `Full 776-op control plane`, making the parity matrix internally contradictory. Update the stale description to 791 so readers do not get conflicting coverage information.</comment>

<file context>
@@ -104,7 +104,7 @@ fakecloud implements **105 AWS services** with **7,408 operations**. **248,557/2
 | [Glue](@/docs/services/glue.md) | 269 | JSON 1.1 | Full | Partial | Full control plane: Data Catalog (databases, tables, partitions with `GetPartitions` `Expression` pruning), jobs, crawlers, classifiers, connections, triggers, workflows, blueprints, dev endpoints, schema registry, interactive sessions, ML transforms, data quality, user-defined functions, usage profiles, column statistics, and tagging. Status transitions are real (crawler `READY`↔`RUNNING`, trigger/workflow/run lifecycles). Job/crawler/Spark *execution* itself is synthesized — fakecloud is not a Spark engine. |
 | [Organizations](@/docs/services/organizations.md) | 63 | JSON 1.1 | Full | Full | Full org tree (roots, OUs, accounts), policies with SCP enforcement, handshakes, delegated administrators, service access, tagging, and a resource policy. Billing responsibility transfers ride handshake-backed records. `CreateAccount` transitions `IN_PROGRESS` -> `SUCCEEDED` after a short synthetic delay. |
-| [EC2](@/docs/services/ec2.md) | 776 | ec2Query | Full | Partial | Full 776-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |
+| [EC2](@/docs/services/ec2.md) | 791 | ec2Query | Full | Partial | Full 776-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |
 
 ## Reading the matrix
</file context>
Suggested change
| [EC2](@/docs/services/ec2.md) | 791 | ec2Query | Full | Partial | Full 776-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |
| [EC2](@/docs/services/ec2.md) | 791 | ec2Query | Full | Partial | Full 791-op control plane: VPCs, subnets, security groups, route tables, gateways, ENIs, instances, EBS volumes/snapshots, AMIs (+ watermarks), network ACLs, VPC peering/endpoints, flow logs, launch templates, spot/fleet, capacity/reserved/dedicated hosts, transit gateways (+ multicast/peering/metering/policy-table entries), VPN + Client VPN, IPAM, Verified Access, Network Insights, Outpost/local-gateway/CoIP, and Instance Connect. Instances run as real containers — Docker/Podman by default or native Kubernetes Pods (`FAKECLOUD_EC2_BACKEND=k8s`) — running user-data at boot, with the instance lifecycle mapped to the container lifecycle and `GetConsoleOutput` returning the container log; the control plane degrades to metadata-only when no container runtime is present. A few model ops absent from the vendored SDK are validated via raw ec2Query. |

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/fakecloud-core/src/container_net.rs
@vieiralucas
vieiralucas requested a lite review from Copilot September 13, 2026 12:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…policy registrations

Implements the 15 remaining unimplemented EC2 operations. An association ties an
IPAM to one Regional Internet Registry; routing policy registrations (RPKI route
origin authorizations) hang off it, keyed by CIDR, and every change produces a
delta. The deltas are the audit trail, so they outlive the registrations they
describe, and the delta query supports id, time-window, and chronological-order
filters.

Enabling an association produces the RPKI child request document the registry
needs. The derived views all read from the registrations rather than inventing
data: route origin authorizations are one per CIDR and ASN pair, the ASN and
CIDR views are what those registrations cover, and discovered routes are what
the account advertises in the queried region, since fakecloud runs no BGP
collector. Route protection findings mark a registration carrying an ASN as
valid and one with none as unknown, which is how an unsigned announcement looks
to RPKI.

Also bounds the container-CLI calls in the test harness. sweep_instance_containers
shelled out to docker with no timeout, so a wedged daemon hung the whole test run
rather than the one sweep: locally that turned a 20-second test into a multi-minute
hang. probe_cli's existing bound is now shared with it.

EC2 conformance: 776 -> 791 implemented operations, all passing, 25,716 variants.
reap_stale_containers runs synchronously before the server starts serving, and
its docker ps / rm calls had no deadline. A liveness probe answering does not
promise the next call will, so a daemon that wedges in between blocked startup
outright rather than just the sweep.

The bounded wait now lives in container_net alongside the existing probe, and
both the reaper and the probe go through it.
A route-protection finding's roaSet carries IpamRouteOriginAuthorization,
whose prefix member is `prefix`. The handler emitted `cidr`, which is the
spelling of IpamRouteOriginAuthorizationInfo -- the shape
GetIpamRouteOriginAuthorizations returns. The two were conflated, so an SDK
read every ROA's prefix as absent and discarded the element. The existing
test only asserted that `<roaSet>` appeared at all, so it passed.

rpkiStrength emitted "strong" and "none". IpamRpkiStrength is
`strict | permissive`, so both values deserialized as an unknown variant and
any client matching the enum fell through. A registration naming its origin
ASNs authorizes exactly those, which is the strict posture.

DryRun returned success before resolving anything, so a dry run against an
association that does not exist reported 200 while the same call without
DryRun returned InvalidIpamInternetRegistryAssociationId.NotFound. Each of the
five ops that address an existing association now resolves it first and skips
only the mutation, which is what the rest of EC2 does.

MaxLength was parsed and then unbounded, though the shape carries @range 0..48
and the member documents that it must cover at least the CIDR's own prefix
length. Both are enforced now.

The delta time bounds compared RFC 3339 strings byte-wise. Stored timestamps
carry milliseconds and an SDK omits them when they are zero, so
StartTime=...:00Z dropped every delta recorded in that same second, and a
malformed bound filtered everything out with a 200 rather than erroring. The
bounds are parsed and compared as instants.

EC2 conformance: 791/801 operations, all 18 IPAM registry ops fully passing.
bounded_output piped the child's stdout and then waited for it to exit
without reading. A child whose output outgrows the pipe buffer (64 KiB on
Linux) blocks on write until someone drains it, so the wait could never
finish: the call burned the full 10-second deadline, killed the child and
reported failure. `docker ps -a` across a host with a few hundred containers
is exactly that much output, which turned the startup reaper and the test
harness's container sweep into a silent 10-second stall that swept nothing.

Both copies now drain on a helper thread while the bounded wait runs. The
regression test asks for 200 KB through the bounded call; against the previous
version it fails at the deadline.
@vieiralucas
vieiralucas merged commit b29f867 into main Sep 13, 2026
159 checks passed
@vieiralucas
vieiralucas deleted the feat/ec2-ipam-routing branch September 13, 2026 16:07
Sorttech pushed a commit to Sorttech/fakecloud that referenced this pull request Sep 24, 2026
A review of the 15 operations that shipped in faiscadev#2511 found them accepting
requests they then failed to honour. Each one is fixed here with the test that
proves it.

- The child request document interpolated `ServiceUri`, the handles, the RPKI
  version and the parent BPKI TA straight into XML, so an ordinary URI carrying
  `&` produced a document the RIR cannot parse, and a handle carrying a quote
  closed an attribute early. All five are escaped.
- BatchModifyIpamRoutingPolicyRegistrations read ASNs only as JSON strings, so
  a document writing them as numbers registered a CIDR that authorized nobody
  and reported itself unknown/permissive. Numbers are accepted.
- A batch entry that could not be applied was skipped after the whole document
  had already been recorded as published, so nine of ten entries applying still
  reported success. The document is validated before anything is recorded, and
  a bad entry fails the request.
- Batch entries bypassed the validation the single-op path runs, so a
  `maxLength` of 200 was stored and emitted for a member the model bounds to
  0..48. Both paths share one validation now, and a batch `add` for a CIDR that
  is already registered is an upsert, matching the operation's own docs.
- ModifyIpamRoutingPolicyRegistration rebuilt the record from the request, so
  `MaxLength`, `Description` and `PermitMoreSpecificAnnouncements` vanished
  whenever the request omitted them. Modify is a partial update.
- DryRun returned success before the checks it exists to surface: a dry-run
  create against an existing CIDR or a nonexistent IPAM, and a dry-run modify
  or delete of a CIDR that is not registered, all reported a success the real
  call then refused. Every dry run now reaches the same verdict as the real
  call.
- The create path parked an association in `pending-enable` and documented that
  it cannot publish until enabled, but nothing enforced it, leaving
  EnableIpamInternetRegistryAssociation decorative. Registration writes require
  an enabled association.
- ClientToken was modeled on all five mutating operations and ignored by all of
  them, so an SDK retrying a timed-out create got a conflict instead of a
  replay. Tokens are recorded per action and replay their original result.
- The eight paginated reads validated MaxResults and then returned everything,
  never emitting nextToken, and discarded the modeled Filters. They paginate
  and filter.
- DeleteIpamInternetRegistryAssociation deleted an association out from under
  its registrations; it now refuses with DependencyViolation until they are
  gone.

Also: a conformance ordering assertion compared `Option<usize>`, so a dropped
delta passed vacuously (`None < Some`), and the bounded container-CLI helper
left its reader thread blocked on a pipe a wrapper script's grandchild still
held, leaking a thread per timed-out call on exactly the wedged-daemon path the
bound exists for. The helper now runs the CLI in its own process group and
kills the group, so the read end closes and the reader exits; collection is
bounded by a drain grace. fakecloud-testkit carried a second copy of those
helpers with the same leak and now shares core's, and `detect_bridge_gateway`,
which still called the CLI unbounded, goes through them too.
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