Architecture
Three-layer design inspired by Go CDK
Architecture
cloudemu follows a three-layer architecture inspired by Go CDK:
Portable API → recording, metrics, rate limiting, error injection
Driver Interface → minimal Go interfaces per service
Provider Mocks → in-memory backends (AWS/Azure/GCP) using generic memstoreLayer 1: Provider Mocks
The bottom layer contains the actual in-memory implementations for each cloud provider. Each provider (AWS, Azure, GCP) implements all 16 service interfaces.
// providers/aws/aws.go
type Provider struct {
S3 *s3.Mock
EC2 *ec2.Mock
DynamoDB *dynamodb.Mock
Lambda *lambda.Mock
CloudWatch *cloudwatch.Mock
// ... 16 services total
}All mocks are backed by a generic, thread-safe memstore.Store[V] — a simple in-memory key-value store with Get, Set, Delete, Filter, and more.
Services are wired together at initialization. For example, EC2 is connected to CloudWatch so that launching instances automatically emits CPU, Network, and Disk metrics.
Layer 2: Driver Interfaces
Each service category defines a minimal Go interface that all providers must implement:
// compute/driver/driver.go
type Compute interface {
RunInstances(ctx context.Context, config InstanceConfig, count int) ([]Instance, error)
StopInstances(ctx context.Context, instanceIDs []string) error
TerminateInstances(ctx context.Context, instanceIDs []string) error
DescribeInstances(ctx context.Context, ids []string, filters []DescribeFilter) ([]Instance, error)
// ...
}The driver layer ensures that AWS S3, Azure Blob Storage, and GCP GCS all satisfy the same driver.Bucket interface. Your code can work with any of them interchangeably.
Layer 3: Portable API
The top layer wraps driver implementations with cross-cutting concerns:
bucket := storage.NewBucket(aws.S3,
storage.WithRecorder(rec), // record every API call
storage.WithMetrics(mc), // track call counts and durations
storage.WithErrorInjection(inj), // simulate cloud failures
storage.WithRateLimiter(limiter), // simulate API throttling
storage.WithLatency(5*time.Millisecond),// simulate network delay
)The portable API intercepts every call and applies the configured concerns in order:
- Error injection — check if this call should fail
- Rate limiting — check if rate limit is exceeded
- Latency — sleep for configured duration
- Execute — call the underlying driver
- Metrics — record call count, duration, errors
- Recording — log the call details
Key Design Decisions
Generic memstore
All providers share the same memstore.Store[V] for storage. This ensures consistent thread-safety and behavior across providers.
Monitoring auto-wiring
When a provider is created, services that produce metrics (like EC2) are automatically connected to the monitoring service (like CloudWatch). You can query these metrics the same way you would in production.
State machines
Services with lifecycle states (compute, serverless) use formal state machines that enforce valid transitions. Invalid transitions return FailedPrecondition errors.
Zero dependencies
cloudemu has no external dependencies beyond the Go standard library. The only test dependency is testify.