Skip to content
cloudemu

Repository Structure

How cloudemu names and lays out code — the three parallel trees, the no-cloud-prefix rule, and snake_case files

cloudemu is an SDK-compat emulator: pointing a real SDK or CLI at it should feel natural. So the code is named and placed by one consistent rule, so every service is named the same way in every layer, every file lands in a predictable place, and a new service or feature has an obvious home. This page is the map — for the why of the three-layer design, see Architecture.

The three layers#

Every service spans up to three layers, in three parallel trees:

services/<capability>/           # portable API + driver interface (cross-cloud abstraction)
providers/<cloud>/<service>/     # in-memory mock for one cloud
server/<cloud>/<service>/        # SDK-compat wire handler for one cloud

<cloud> is one of aws, azure, gcp. Not every service has all three layers — a product-specific mock (e.g. bedrock) may have no cross-cloud services/ abstraction, and some services have no wire handler yet. The naming rules below apply to whichever layers exist.

Naming#

services/<capability> — generic capability name#

The shared abstraction is named for the capability, never a product: storage, compute, networking, loadbalancer, iam, database, relationaldb, dns, monitoring, logging, secrets, messagequeue, eventbus, serverless, cache, containerregistry, notification.

Product-specific services with no cross-cloud abstraction keep their product name (bedrock, vertexai, databricks, bigtable, ecs, sagemaker, …).

providers/<cloud>/<service> and server/<cloud>/<service> — short service name, no cloud prefix#

The mock and wire layers use the service's own short name, without a cloud prefix — the real SDK/CLI name where a service clearly has one (s3, ec2, elbv2), and a short capability name otherwise:

CapabilityAWSAzureGCP
Storages3blobstoragegcs
Computeec2virtualmachinescompute
Networkingvpcvnetvpc
IAMiamiamiam
Load balancerelbv2loadbalancerloadbalancer
Database (NoSQL)dynamodbcosmosdbfirestore
DNSroute53dnsclouddns
Cacheelasticachecachememorystore
Monitoringcloudwatchmonitormonitoring

Two hard rules:

  • No redundant cloud prefix. The parent directory already encodes the cloud, so an aws/azure/gcp prefix on the leaf is noise. It's dropped: awsiamiam, azurecachecache, azurednsdns, azurelbloadbalancer, azuremonitormonitor, azuresqlsql, gcpiamiam, gcplbloadbalancer, gcpvpcvpc.
  • Provider ↔ wire match within a cloud. For a given cloud, providers/<cloud>/X and server/<cloud>/X share the same name X, so the two layers of a service map by name (e.g. providers/azure/blobstorageserver/azure/blobstorage).

Names are not forced to be identical across clouds at the provider/wire layer — s3 / blobstorage / gcs each read naturally for their own cloud. Cross-cloud unification lives in the services/ name only.

Documented exceptions to the provider ↔ wire match:

  • AWS providers/aws/vpcserver/aws/ec2. AWS's own SDK folds VPC operations under EC2, so the wire handler lives in ec2; the mock keeps the clearer vpc name.
  • services/azureai / services/azuresearch keep the azure in their name, while their providers dropped it (providers/azure/ai, providers/azure/search). These are product-specific services with no cross-cloud abstraction, so the product name is kept at the services/ layer.

File naming#

  • snake_case, full words, no abbreviations — in every layer. natgw.gonat_gateway.go, eip.goelastic_ip.go, eni.gonetwork_interface.go, igw.gointernet_gateway.go.
  • A feature uses the same filename across all three layers, so one grep (or one filename) finds the interface, the mock, and the wire handler for a feature. Test files sit next to what they test: <feature>_test.go.

The driver/ subpackage rule#

Every portable-API service in services/ puts its interface in a driver/ subpackage. Documented exceptions — not portable-API services, so correctly no driver/:

  • services/kubernetes — a self-contained data-plane engine with its own HTTP surface.
  • services/resourcediscovery — a cross-service engine that consumes other drivers.
  • services/cost, services/scope — cross-cutting utilities, not a cloud capability.

If you add a genuine portable-API service, it must have driver/.

Where things go#

A new resource on an existing service: add <feature>.go (same filename) to the provider mock, the wire handler, and — if it crosses the portable API — the driver/ interface + services/<capability> wrapper. Add <feature>_test.go beside each.

A new service: create services/<capability>/driver/driver.go + services/<capability>/<capability>.go, then providers/<cloud>/<service>/ and server/<cloud>/<service>/ for each cloud you implement. Wire it into the provider factory (providers/<cloud>/<cloud>.go) and call SetMonitoring() if it emits metrics.

On this page

On this page