ECS
Elastic Container Service — clusters, task definitions, (later) real Fargate-style task execution via Docker, services, and rolling deployments.
fakecloud implements Amazon Elastic Container Service (ECS) with full API coverage. 60 operations, shipped across four batches.
Status: all four batches shipped — full API. Covers clusters, task definitions, real Fargate-style task execution, services with rolling deployments, task sets, container instances, capacity providers, attributes, task protection, ECS Exec, and the agent-side Submit* / DiscoverPollEndpoint surface.
Supported today (full API)
- Clusters —
CreateCluster,DescribeClusters,DeleteCluster,ListClusters,UpdateCluster,UpdateClusterSettings,PutClusterCapacityProviders - Task definitions —
RegisterTaskDefinition,DescribeTaskDefinition,DeregisterTaskDefinition,DeleteTaskDefinitions,ListTaskDefinitions,ListTaskDefinitionFamilies - Tasks —
RunTask,StartTask,StopTask,DescribeTasks,ListTaskswith real Fargate-style execution via Docker/Podman - Services —
CreateService,UpdateService,DeleteService,DescribeServices,ListServices,ListServicesByNamespacewith desired-count enforcement and rolling deployments - Service deployments —
StopServiceDeployment,ListServiceDeployments,DescribeServiceDeployments,DescribeServiceRevisions - Task sets —
CreateTaskSet,UpdateTaskSet,DeleteTaskSet,DescribeTaskSets,UpdateServicePrimaryTaskSet(EXTERNAL deployment controller) - Container instances —
RegisterContainerInstance,DeregisterContainerInstance,DescribeContainerInstances,ListContainerInstances,UpdateContainerAgent,UpdateContainerInstancesState - Attributes —
PutAttributes,DeleteAttributes,ListAttributes - Capacity providers —
CreateCapacityProvider,DeleteCapacityProvider,DescribeCapacityProviders,UpdateCapacityProvider - Task protection —
GetTaskProtection,UpdateTaskProtection - ECS Exec —
ExecuteCommandproxies todocker execagainst the task's running container - Agent surface —
SubmitContainerStateChange,SubmitTaskStateChange,SubmitAttachmentStateChanges,DiscoverPollEndpoint - Tagging —
TagResource,UntagResource,ListTagsForResource(clusters and task definitions) - Account settings —
PutAccountSetting,PutAccountSettingDefault,DeleteAccountSetting,ListAccountSettings
Services + rolling deployments (Batch 3)
CreateService spawns tasks to match desiredCount under the service, tagging each with startedBy=ecs-svc/<name> so the tasks reconcile back to the service. UpdateService supports two independent mutations:
- Scale — set a new
desiredCount. The service spawns additional tasks when scaling up and flips excess tasks todesiredStatus=STOPPED(runtime kill on the container) when scaling down. - Rolling deployment — pass a new
taskDefinition. The service marks the previous PRIMARY deployment asACTIVE, creates a newPRIMARYdeployment for the target revision, and drains tasks on the old task definition while new ones come up. Deployment circuit breaker +minimumHealthyPercent/maximumPercentare honoured indeploymentConfiguration.
DeleteService refuses while desiredCount > 0 unless force=true; the forced path scales to 0 and stops every running task under the service before removing it.
Task-definition families track revisions monotonically; DeleteTaskDefinitions requires DeregisterTaskDefinition first (real AWS behaviour), and the result flips status to DELETE_IN_PROGRESS.
Task execution (Batch 2)
RunTask records the task synchronously and kicks off a background docker execution per spawned task:
docker pull <image>(timestamps captured on the task:pullStartedAt/pullStoppedAt)docker run -d <image>(container ID recorded on the task's container)docker wait <id>(blocks on container exit; exit code →containers[].exitCode)docker logs <id>(captured stdout/stderr stored on the task + exposed via the introspection endpoint)docker rm <id>(cleanup)
Environment variables from the task definition are forwarded with localhost / 127.0.0.1 rewritten to host.docker.internal so containers reach fakecloud itself the same way Lambda does.
Without a container runtime (docker/podman missing), RunTask still returns tasks but they immediately transition to STOPPED with stopCode=TaskFailedToStart. This keeps the API surface shape-correct so tests on CI agents without Docker can still drive the control-plane surface.
Protocol
JSON protocol over POST /, with X-Amz-Target: AmazonEC2ContainerServiceV20141113.<Action>. Request + response bodies are JSON; tags use lowercase key / value (matches AWS SDK serialization).
Introspection
Endpoints bypass the public AWS API so tests can assert deterministic state without pagination or role-assumption noise.
| Endpoint | Method | Purpose |
|---|---|---|
/_fakecloud/ecs/clusters | GET | Dump every cluster across all accounts |
/_fakecloud/ecs/tasks | GET | Dump every task; filter with ?cluster= / ?status= |
/_fakecloud/ecs/tasks/{taskId} | GET | Single-task deep detail |
/_fakecloud/ecs/tasks/{taskId}/logs | GET | Captured docker stdout/stderr + exit code |
/_fakecloud/ecs/tasks/{taskId}/force-stop | POST | SIGTERM + SIGKILL the running container |
/_fakecloud/ecs/tasks/{taskId}/mark-failed | POST | Flip to STOPPED without killing the container (inject exit code + reason) |
/_fakecloud/ecs/events | GET | Replay the lifecycle event log |
All endpoints are sorted deterministically (by ARN for clusters/tasks, by timestamp for events) so test assertions don't flake on map iteration order.
Clusters dump
{
"clusters": [
{
"clusterName": "prod",
"clusterArn": "arn:aws:ecs:us-east-1:111122223333:cluster/prod",
"status": "ACTIVE",
"runningTasksCount": 0,
"pendingTasksCount": 0,
"activeServicesCount": 0,
"registeredContainerInstancesCount": 0,
"capacityProviders": ["FARGATE"],
"tags": [{"key": "env", "value": "prod"}],
"createdAt": "2026-04-23T23:00:00+00:00"
}
]
}SDK usage (testing helper)
The fakecloud client SDKs ship typed wrappers for every introspection endpoint. Use them instead of poking /_fakecloud/* paths by hand.
// Go
clusters, _ := fakecloud.New("http://localhost:4566").ECS().GetClusters(ctx)# Python
async with FakeCloud() as fc:
clusters = await fc.ecs.get_clusters()// TypeScript
const fc = new FakeCloud();
const { clusters } = await fc.ecs.getClusters();// Rust
let fc = FakeCloud::new("http://localhost:4566");
let clusters = fc.ecs().get_clusters().await?;Roadmap
- Batch 4 — Container instances, attributes, capacity providers, task protection, ECS Exec (
ExecuteCommandviadocker exec), task sets (EXTERNAL deployment controller), snapshot/restore of in-flight tasks, IAM task-role credential injection viaAWS_CONTAINER_CREDENTIALS_RELATIVE_URI, EventBridgeECS Task State Changeevents, awslogs-driver CloudWatch Logs streaming.