simulator-aws

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: AGPL-3.0 Imports: 90 Imported by: 0

README

simulator-aws

Local reimplementation of the AWS slice that sockerless touches. Not a mock — workloads execute through real Docker, Amazon Elastic Container Service (ECS) / AWS Lambda tasks run with real exit semantics, Amazon Elastic Container Registry (ECR) stores real image manifests, and the broader CDN / DNS / cert / AWS WAF / AWS Amplify / AWS Identity and Access Management (IAM) surfaces respond on the real wire shapes that the AWS SDK v2 + AWS CLI + Terraform aws provider expect.

Reference adaptor

The simulator exposes one HTTP endpoint (default :4566) that fronts all AWS services. Three external tools exercise that endpoint at AWS-API fidelity:

Adaptor Min version What it proves
AWS SDK for Go v2 (github.com/aws/aws-sdk-go-v2/service/*) v1.30 Wire-level SDK compatibility — request/response shapes, error envelopes, pagination, optimistic concurrency tokens. Covers 30+ services.
aws CLI 2.17+ Endpoint-override fidelity (--endpoint-url). CLI uses the same SDK but exercises a different argument-marshaling path. Some endpoints differ (e.g. Route 53 /rrset/ with trailing slash).
Terraform aws provider v6.50.0 Full plan → apply → destroy round-trip across 60+ resource types (aws_ecs_*, aws_lambda_*, aws_cloudfront_*, aws_route53_*, aws_wafv2_*, aws_amplify_*, aws_acm_*, aws_iam_*, aws_ecr_*, aws_s3_*). Stresses cross-resource references, Lambda invocation through the Runtime API, and stateful drift detection.

Anything any of these three tools does against the real AWS endpoint, it must do against this simulator. Gaps from that contract are real bugs (see BUGS.md).

Validation

Test path What runs Last green
sdk-tests/ — 30 packages (ecs_test.go, ecr_test.go, cloudfront_test.go, route53_test.go, wafv2_test.go, amplify_test.go, acm_test.go, iam_slr_oidc_test.go, …) Real aws-sdk-go-v2 clients against the sim. Per-op assertions on response shape + error codes. 2026-05-15 (PR #159 P159.10)
cli-tests/ — 30 packages (ecs_test.go, iam_slr_oidc_test.go, …) Real aws CLI invoked via os/exec, parses CLI JSON output. 2026-05-15
terraform-tests/TestStackProductionShape Real Terraform aws v6.50.0 against the sim. Provisions CloudFront + ACM + WAFv2 + Route 53 ALIAS + Amplify + IAM SLR/OIDC + ECS + ECR + Cloud Map + Lambda resources together, asserts cross-resource outputs and Lambda Runtime API invocation output, then terraform destroy. 2026-07-29
make simulator-aws/test Leaf-Makefile unit + integration suite per docs/MAKEFILE_STANDARD.md. 2026-05-15

The SDK + Terraform tests are the load-bearing validation. CI runs all four on every PR (.github/workflows/ci.yml).

Wiring the adaptor

# 1. Build + start the sim (default :4566).
cd simulator-aws
go build -tags noui -o simulator-aws .
SIM_LISTEN_ADDR=:4566 ./simulator-aws
# 2. Point any AWS client at it.
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1

aws ecs list-clusters
aws cloudfront list-distributions
aws iam create-service-linked-role --aws-service-name cloudfront.amazonaws.com
Variable Default What it does
SIM_LISTEN_ADDR :4566 Listen address (host:port).
SIM_TLS_CERT, SIM_TLS_KEY unset Enable HTTPS with the given cert/key.
SIM_RUNTIME docker Initializes Docker/Podman for workload execution. Set process only for explicit API-only runs that do not invoke ECS/Lambda workload execution.
SIM_DATA_DIR unset Persistence root: the SQLite control-plane store, plus the default location of every bulk-data root below (<SIM_DATA_DIR>/efs, /ebs, /amplify-cache), so file contents survive restarts alongside the metadata that describes them.
SIM_EBS_DATA_DIR <SIM_DATA_DIR>/ebs, else $TMPDIR/sockerless-sim-ebs Explicit override for the EC2/Firecracker EBS block-image root (volume backing files and snapshots). Not used for ECS managed EBS volumes — those use Docker named volumes (sockerless-ebs-*) so they are topology-independent.
SIM_EFS_DATA_DIR <SIM_DATA_DIR>/efs, else $TMPDIR/sockerless-sim-efs Explicit override for the EFS file-system content root.
AWS_ENDPOINT_URL (client-side) The AWS SDKs and AWS CLI's standard global endpoint setting. It routes every supported service to the simulator.
AWS_ENDPOINT_URL_<SERVICE> (client-side) The AWS SDKs' standard per-service setting (for example AWS_ENDPOINT_URL_SQS). It overrides the global coordinate for that service.
AWS_DEFAULT_REGION us-east-1 The sim accepts any region; some validation (CloudFront → ACM us-east-1 pin) is region-aware.

Docker or Podman is required for ECS and Lambda execution paths. For control-plane or data-plane API checks that do not start workloads, SIM_RUNTIME=process starts the AWS simulator without initializing Docker/Podman. The /health response reports runtime and capabilities.workloadExecution; clients must require that capability before submitting work that needs a running container.

The same endpoint convention applies inside workloads. Pass AWS_ENDPOINT_URL (or AWS_ENDPOINT_URL_<SERVICE>) and ordinary AWS credentials through the real workload configuration surface—Amazon ECS container overrides, AWS CodeBuild environment overrides, or AWS Lambda function environment variables. The simulator does not inject or broker a private endpoint variable. In the Linux real-VPC tier, an explicitly supplied outer-host simulator-listener authority is mapped onto the existing managed task-local route because the isolated namespace intentionally has no route to Docker's host gateway; other host authorities remain unreachable. The official-client suite proves this by having an AWS Step Functions-launched AWS CodeBuild process invoke the vendor AWS CLI against Amazon SQS and by having explicitly deployed AWS Lambda code invoke the bundled AWS SDK against Amazon SQS. It also launches the official HashiCorp Terraform image as an Amazon ECS task from AWS Step Functions; Terraform uses only the standard AWS_ENDPOINT_URL coordinate, applies an Amazon SQS resource to the simulator, and an independent AWS SDK client reads the resulting queue and tags.

AWS Lambda follows the real deployment contract: CreateFunction supplies a ZIP archive or image plus handler, role, and environment configuration. Repository files and database settings are not discovered from the simulator host. An end-to-end SDK test deploys code and environment explicitly, invokes the managed runtime, and observes its authenticated downstream Amazon SQS write.

ECS managed EBS volumes use Docker named volumes (sockerless-ebs-<id>) rather than bind-mounts on the sim process's filesystem. This means the sim can run in a container (with the Docker socket mounted) and task containers will see the correct volume data — no path-sharing between host and sim container is required.

VPC and Subnet creation (CreateVpc, CreateSubnet) always succeeds at the control-plane level (API state is stored). Real Linux network-namespace fabric is set up lazily when a data-plane resource attaches to the VPC/subnet and host networking capabilities (ip, nft, sysctl) are present. Without those capabilities the API calls still succeed and awsvpc tasks fall to the per-VPC Docker-network fabric described below.

ECS task networking

The task definition's networkMode decides the fabric every container in the task lands on, exactly as it does on real Amazon Elastic Container Service (ECS):

networkMode What the task gets How the simulator realizes it
awsvpc Its own elastic network interface in a subnet of the VPC. networkConfiguration is required; RunTask rejects the request without it. On Linux with CAP_NET_ADMIN + nsenter, a pause container holds the task's network namespace and the ENI veth is plumbed into it from the VPC's namespace. Everywhere else, a per-VPC user-defined Docker network (sockerless-sim-vpc-<vpc-id>) whose IPAM subnet is the VPC CIDR, with the container pinned to its ENI address.
bridge (the default when networkMode is unset) An address on the container instance's default Docker bridge. No ENI. networkConfiguration is rejected. The container runtime's default bridge network.
host The container instance's own network stack. No ENI. Docker host network mode.
none No connectivity. No ENI. Docker none network mode.

An awsvpc task is therefore never placed on the default bridge, and only an awsvpc task carries an ElasticNetworkInterface attachment and per-container networkInterfaces.

Guest-kernel requirements for SIM_RUNTIME=docker

Workload execution is the container runtime's job, so the kernel the runtime runs on must be able to program everything that runtime needs. Docker 28 and later adds a raw-table PREROUTING … -j DROP rule (direct access filtering) for every endpoint it creates on a bridge-driver network, so a kernel built without CONFIG_IP_NF_RAW cannot start those containers at all:

failed to create endpoint … on network bridge:
Unable to enable DIRECT ACCESS FILTERING - DROP rule:
(iptables failed: iptables --wait -t raw -A PREROUTING …:
can't initialize iptables table `raw': Table does not exist)

This covers the default bridge network and every user-defined bridge network — Docker programs the rule per endpoint, so the per-VPC networks the awsvpc Docker-network tier uses need the raw table too, and so does the awsvpc namespace tier's pause container (it is created on the default network before being moved into the task's namespace). Only networkMode: host and networkMode: none create no bridge endpoint at all.

The simulator does not work around this — it reports the failure with the missing module named, and the task stops with that reason. Fix it on the host:

  • Run the container runtime on a kernel with the full netfilter set (iptable_raw / CONFIG_IP_NF_RAW, plus nf_tables for the nftables backend). The stock Firecracker CI guest kernel (vmlinux-6.1.128) omits both, so build a container-capable guest kernel before running the simulator inside such a microVM.
  • Or start the Docker daemon with DOCKER_INSECURE_NO_IPTABLES_RAW=1, Docker's own opt-out from the raw rules. That drops the direct-access hardening host-wide; it is the daemon operator's decision, never the simulator's.

For Terraform:

provider "aws" {
  region                      = "us-east-1"
  access_key                  = "test"
  secret_key                  = "test"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    ecs              = "http://localhost:4566"
    ecr              = "http://localhost:4566"
    cloudfront       = "http://localhost:4566"
    acm              = "http://localhost:4566"
    route53          = "http://localhost:4566"
    wafv2            = "http://localhost:4566"
    amplify          = "http://localhost:4566"
    iam              = "http://localhost:4566"
    # …any service you exercise.
  }
}

Services

AWS-JSON 1.1 (POST / + X-Amz-Target)
Service Target Prefix Source file
ECS AmazonEC2ContainerServiceV20141113 ecs.go
ECR AmazonEC2ContainerRegistry_V20150921 ecr.go
CloudWatch Logs Logs_20140328 cloudwatch.go
Cloud Map Route53AutoNaming_v20170314 cloudmap.go
ACM CertificateManager acm.go
WAFv2 AWSWAF_20190729 wafv2.go
KMS TrentService kms.go
Secrets Manager secretsmanager secretsmanager.go
DynamoDB DynamoDB_20120810 dynamodb.go
SSM AmazonSSM ssm.go
AWS Query Protocol (POST / + Action=)
Service Source file
EC2 ec2.go
IAM (roles, policies, instance profiles, service-linked roles, OIDC providers) iam.go + iam_slr_oidc.go
STS sts.go
REST APIs (path routing)
Service Base Path Source file
EFS /2015-02-01/… efs.go
Lambda /2015-03-31/functions/… lambda.go
S3 /{bucket}/{key} s3.go
CloudFront /2020-05-31/… (REST + XML) cloudfront.go + cloudfront_policies.go + cloudfront_functions.go + cloudfront_keys.go
Route 53 /2013-04-01/… (REST + XML) route53.go
Amazon Amplify /apps/… (REST + JSON, versionless); encrypted authenticated Git repository connections, real backend/frontend/test and monorepo builds with declared caches in a managed multi-language image from Amazon ECR Public, a host-addressed hosting data plane ({branch}.{appId}.amplifyapp.com, per-app {hash}.cloudfront.net, verified custom domains), server-side rendering per the deployment-manifest specification, and Route 53-backed domain verification amplify.go + amplify_domains.go + amplify_build.go + amplify_dataplane.go + amplify_compute.go

Amazon RDS DB instances backed by PostgreSQL, MySQL, or MariaDB expose their native database wire protocol at the Endpoint returned by CreateDBInstance. The engine starts on the first data-plane connection, retains its files in an instance-owned volume, and accepts the configured master password or a TLS-protected, 15-minute SigV4 IAM database authentication token. ModifyDBInstance changes IAM authentication and rotates the actual database account while running or across a stopped/start lifecycle without replacing its data volume. Official AWS token generation plus stock PostgreSQL and MySQL drivers exercise schema, insert, query, denial, TLS enforcement, password rotation, and persistence operations against all three real engines.

AWS Step Functions Task states support optimized Amazon ECS RunTask request/response, .sync, and callback-token integrations, plus the optimized AWS CodeBuild build and build-batch operations. Synchronous workflows poll the actual service resources, propagate terminal failures, and stop work they started when the execution is aborted. CodeBuild clones private Git sources with encrypted imported or AWS Secrets Manager credentials and executes the checked-in build specification inside the project's exact configured image; stopping a build or build batch cancels that container.

Amazon Amplify release and retry jobs expose the hosted build ZIP through the BUILD step's artifactsUrl; ListArtifacts and GetArtifactUrl expose only the actual end-to-end test files declared by the build specification. Test artifact bundles and configuration URLs live on that same BUILD step and are deleted with the job. AWS WAF associations update the Amplify app's wafConfiguration, protect the hosting data plane with the WebACL default action and IP-set rules, and feed actual matching requests into GetSampledRequests.

Full per-verb wire shape: see API_SPEC.md.

Sample — end-to-end production-shape stack

The terraform-tests/TestStackProductionShape exercise provisions a CloudFront-fronted application with WAF + ACM + Route 53 + Amplify + IAM SLR in a single terraform apply. Captured 2026-05-15 (sim port :NNNN shown as :46241 here):

# Boot the sim
$ AWS_ENDPOINT_URL=http://127.0.0.1:46241 ./simulator-aws &

# Configure AWS SDK + Terraform
$ export AWS_ENDPOINT_URL=http://127.0.0.1:46241
$ export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1

# Apply the full stack
$ terraform apply -auto-approve
aws_cloudfront_distribution.tf_dist: Creation complete after 0.2s
  [id=E8E549340297351, domain_name=E8E549340297351.cloudfront.net]
aws_wafv2_web_acl_association.tf_assoc: Creation complete after 0.03s
aws_route53_record.tf_alias: Creation complete after 0.1s
aws_acm_certificate.tf_cert: Creation complete after 0.03s
  [arn=arn:aws:acm:us-east-1:000000000000:certificate/...]
aws_iam_service_linked_role.tf_slr_cloudfront: Creation complete after 0.02s
aws_iam_openid_connect_provider.tf_oidc: Creation complete after 0.02s
aws_amplify_app.tf_amplify: Creation complete after 0.01s

# Verify cross-resource references
$ terraform output -json | jq '. | with_entries(.value = .value.value)'
{
  "cloudfront_arn":             "arn:aws:cloudfront::000000000000:distribution/E8E549340297351",
  "cloudfront_domain_name":     "E8E549340297351.cloudfront.net",
  "wafv2_assoc_resource_arn":   "arn:aws:cloudfront::000000000000:distribution/E8E549340297351",
  "route53_alias_target_name":  "E8E549340297351.cloudfront.net",
  "acm_certificate_arn":        "arn:aws:acm:us-east-1:000000000000:certificate/...",
  "iam_slr_arn":                "arn:aws:iam::000000000000:role/aws-service-role/cloudfront.amazonaws.com/AWSServiceRoleForCloudFrontLogger_tftest"
}

The Go test asserts wafv2_assoc_resource_arn == cloudfront_arn, route53_alias_target_name == cloudfront_domain_name, and acm_certificate_arn starts with arn:aws:acm:us-east-1: — the three load-bearing cross-resource invariants of a CloudFront-fronted production stack.

$ terraform destroy -auto-approve
# Destroys all 30+ resources in dependency order.

Building

cd simulator-aws
go build -tags noui -o simulator-aws .

Testing

# SDK tests (AWS SDK v2 against the running sim — sim is built + booted per TestMain)
cd sdk-tests && go test -v ./...

# CLI tests (aws CLI shell-outs)
cd cli-tests && go test -v ./...

# Terraform tests (real terraform apply → assert outputs → destroy)
cd terraform-tests && go test -v ./...

Each test package's TestMain builds the simulator binary, finds a free port, boots the sim, waits for /health, runs the suite, then kills the sim. No external services needed.

Known issues

None open for the services covered here. Selected closed items:

  • BUG-991docker run --rm against backends/docker used to fail with No such container. Fixed by removing the Store-direct shortcut in handleContainerWait.
  • BUG-992docker images used to return empty even when the upstream daemon had images. Fixed by delegating to s.self.ImageList.
  • issue #381 — ECS managed EBS volumes were stored on the sim process's own filesystem and bind-mounted by path, so task containers launched as Docker siblings couldn't see the data. CreateVpc/CreateSubnet also hard-failed without host nftables even when only control-plane API calls were needed. Fixed: ECS EBS volumes now use Docker named volumes; VPC/Subnet store state unconditionally and set up real networking fabric lazily when host caps are present and a data-plane resource attaches.

What's out of scope

  • Edge propagation timing — CloudFront distributions report Status: Deployed immediately; invalidations report Completed immediately. Real CloudFront cycles InProgress → Deployed over 5–15 minutes.
  • DNS resolution — Route 53 stores records but does not serve them via UDP/53. The sim's purpose is API-shape parity, not actual DNS resolution. Use a separate dnsmasq sidecar if you need lookups.
  • WAF traffic inspectionGetSampledRequests returns an empty list. The sim accepts WebACL rule definitions but doesn't actually filter traffic.
  • ACM cert auto-validationRequestCertificate with ValidationMethod=DNS stays PENDING_VALIDATION until you ImportCertificate to flip a cert to ISSUED. Real ACM polls Route 53 for the challenge CNAME.
  • Multi-region routing — sim is single-region (defaults to us-east-1). Cross-region replication / failover is not modelled.
  • Cost / billing surfacescur, pricing, cost-explorer are absent.
  • Real authentication — sigv4 headers are accepted but not cryptographically verified.

See also: API_SPEC.md, docs/POD_MATERIALIZATION.md, specs/CLOUD_RESOURCE_MAPPING.md, backends/ecs/README.md, backends/lambda/README.md.

Documentation

Overview

Command simulator-aws runs the AWS service simulator.

It simulates the subset of AWS APIs used by the Sockerless ECS and Lambda backends: ECS, ECR, CloudWatch Logs, EFS, Cloud Map, Lambda, S3, EC2, IAM, and STS.

Configure with environment variables:

SIM_LISTEN_ADDR  — listen address (default ":4566")
SIM_TLS_CERT     — TLS certificate file (optional)
SIM_TLS_KEY      — TLS key file (optional)
SIM_RUNTIME      — "docker" by default; "process" starts API-only mode for runs that do not execute workloads
SIM_LOG_LEVEL    — log level: trace, debug, info, warn, error (default "info")

SDK configuration:

export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1

Source Files

Directories

Path Synopsis
Package simulator provides a shared framework for building cloud service simulators.
Package simulator provides a shared framework for building cloud service simulators.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL