You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
EC2 emulates AWS managed resources — instances an AWS service (e.g. ECS Managed
Instances, EKS Auto Mode) provisions on the account's behalf. A managed instance
carries an Operator block (Managed=true, Principal) and is hidden from
DescribeInstances by default once the account's visibility is set to hidden,
reappearing only when the caller opts in with IncludeManagedResources=true.
Non-managed instances are always returned.
Every VPC is created with a main route table, carrying the local route and an
association with Main: true and no subnet. It cannot be deleted or
disassociated on its own and disappears with the VPC. A subnet with no explicit
association is governed by it.
DescribeRouteTables populates RouteTable.Associations; it is the only way a
caller can discover an association ID in order to disassociate.
Network Interfaces (ENI)
Operation
Signature
DescribeNetworkInterfaces
(ctx, ids) ([]NetworkInterface, error)
DetachNetworkInterface
(ctx, attachmentID, force) error
DeleteNetworkInterface
(ctx, id) error
Managed resources attach interfaces of their own — a NAT gateway holds one for
as long as it lives. An attached interface cannot be deleted, which is how a
caller draining a VPC before deleting it learns the drain is not finished.
Both attributes are pointers: nil leaves that attribute unchanged, matching an
API that accepts one attribute per call. New VPCs default to DNS support on and
DNS hostnames off.
VPC Endpoints
Operation
Signature
CreateVPCEndpoint
(ctx, config) (*VPCEndpoint, error)
DeleteVPCEndpoint
(ctx, id) error
DescribeVPCEndpoints
(ctx, ids) ([]VPCEndpoint, error)
ModifyVPCEndpoint
(ctx, id, config) (*VPCEndpoint, error)
Total: 47 operations
AWS-specific networking (optional capabilities)
AWS models several networking resources that don't map cleanly across clouds.
These are AWS-only optional capability interfaces (discovered by type
assertion, like NetworkInterfaces/VPCAttributes) implemented by
providers/aws/vpc and served by the EC2 handler — no Azure/GCP stubs.
IPAM is fully covered (~69 operations). Cross-account/organization and live-network features (Resource Discovery, discovered accounts/resources/public addresses, BYOASN/BYOIP, policies, org-admin) are modeled against the emulator's own single-account state: discovered resources are derived from the stored VPCs/subnets/EIPs, and organization targets resolve to the configured account.
IPAM metrics (AWS/IPAM CloudWatch namespace)
IPAM publishes derived metrics through the CloudWatch service (ListMetrics / GetMetricStatistics): TotalActiveIpCount; pool PercentAllocated/PercentAssigned/PercentAvailable/Compliant/NoncompliantResourceCidrs; scope Managed/Unmanaged/Overlapping/Compliant/NoncompliantResourceCidrs; public-IP insight counts; and resource utilization VpcIPUsage/SubnetIPUsage. Values are computed live from IPAM + VPC/subnet/EIP state.
These two were always in the driver; they are listed here because the ELBv2
handler now exposes them as ModifyLoadBalancerAttributes and
DescribeLoadBalancerAttributes.
Targets
Operation
Signature
RegisterTargets
(ctx, targetGroupARN, targets) error
DeregisterTargets
(ctx, targetGroupARN, targets) error
DescribeTargetHealth
(ctx, targetGroupARN) ([]TargetHealth, error)
SetTargetHealth
(ctx, targetGroupARN, targetID, state) error
Attributes
Operation
Signature
GetLBAttributes
(ctx, lbARN) (*LBAttributes, error)
PutLBAttributes
(ctx, lbARN, attrs) error
These two were always in the driver; they are listed here because the ELBv2
handler now exposes them as ModifyLoadBalancerAttributes and
DescribeLoadBalancerAttributes.
LBAttributes.Extra carries attributes outside the typed set, keyed by their
provider attribute name (load_balancing.cross_zone.enabled and friends).
Providers model attributes as open key/value pairs and add new ones over time, so
a fixed struct would silently drop whatever it had not been taught.
Total: 21 operations
10. Message Queue
Driver interface:services/messagequeue/driver/driver.goAWS: SQS | Azure: Service Bus | GCP: Pub/Sub
A primary node plus replicas, addressed through one primary endpoint. Callers
build a connection string from it, so the endpoint is always populated — a group
without one is indistinguishable from a broken provision.
A durable, in-VPC Redis/Valkey cluster service. Unlike Cache, MemoryDB is a
control-plane-only surface (no Set/Get data plane), so it has its own driver
rather than reusing services/cache. Served as AWS JSON 1.1 on the
AmazonMemoryDB. target prefix (server/aws/memorydb), so a real
aws-sdk-go-v2/service/memorydb client with a custom endpoint works unchanged.
BatchUpdateCluster applies a service update to each named cluster; a name that
does not exist is returned in unprocessed (with ClusterNotFoundFault) rather
than failing the whole batch — matching AWS's partial-success semantics.
The three optional interfaces are AWS-only concepts, discovered by type
assertion.
Pagination: every Describe* operation honors MaxResults/NextToken.
The server pages the deterministic (sorted) result set and returns an opaque
base64 offset token; a malformed token yields InvalidParameterValueException.
A managed, Cassandra-compatible wide-column service. Control-plane only (CQL
data operations are out of scope), so it has its own driver rather than reusing
the relational/cache drivers. Served as AWS JSON 1.0 on the KeyspacesService.
target prefix (server/aws/keyspaces); a real
aws-sdk-go-v2/service/keyspaces client with a custom endpoint works unchanged.
Because Keyspaces models its members in lowerCamelCase, the server lowercases
response keys so the SDK's case-sensitive deserializer decodes them.
Keyspaces
Operation
Signature
CreateKeyspace
(ctx, CreateKeyspaceConfig) (*Keyspace, error)
GetKeyspace
(ctx, name) (*Keyspace, error)
ListKeyspaces
(ctx) ([]Keyspace, error)
UpdateKeyspace
(ctx, name, addRegions) (*Keyspace, error)
DeleteKeyspace
(ctx, name) error
Single- or multi-region replication (ReplicationSpecification); a keyspace
must be empty to delete.
Tables
Operation
Signature
CreateTable
(ctx, CreateTableConfig) (*Table, error)
GetTable
(ctx, keyspace, table) (*Table, error)
ListTables
(ctx, keyspace) ([]Table, error)
UpdateTable
(ctx, UpdateTableConfig) (*Table, error)
DeleteTable
(ctx, keyspace, table) error
RestoreTable
(ctx, RestoreTableConfig) (*Table, error)
Full SchemaDefinition (partition/clustering keys, static & regular columns),
CapacitySpecification (PAY_PER_REQUEST / PROVISIONED + RCU/WCU), encryption,
point-in-time recovery, TTL, client-side timestamps, CDC, comment, and
multi-region replica specs. RestoreTable is point-in-time recovery into a new
table.
User-Defined Types
Operation
Signature
CreateType
(ctx, keyspace, name, fields) (*UDT, error)
GetType
(ctx, keyspace, name) (*UDT, error)
ListTypes
(ctx, keyspace) ([]UDT, error)
DeleteType
(ctx, keyspace, name) (*UDT, error)
Tags
Operation
Signature
TagResource
(ctx, arn, tags) error
UntagResource
(ctx, arn, keys) error
ListTagsForResource
(ctx, arn) ([]Tag, error)
Auto Scaling (optional capability — AutoScaling)
Operation
Signature
GetTableAutoScalingSettings
(ctx, keyspace, table) (*Table, error)
Target-tracking auto scaling for PROVISIONED tables, discovered by type
assertion; errors for PAY_PER_REQUEST tables (matching AWS).
Pagination:ListKeyspaces/ListTables/ListTypes/ListTagsForResource
honor MaxResults/NextToken (server-side opaque base64 offset token over the
deterministic result set; a malformed token yields ValidationException).
A managed, Cassandra-compatible cluster service under Cosmos DB. Control-plane
only (CQL is out of scope), so it has its own driver. Served as ARM REST/JSON
under Microsoft.DocumentDB/cassandraClusters (server/azure/managedcassandra);
a real armcosmosCassandraClusters/CassandraDataCenters client with a
custom endpoint works unchanged. Mutating ops complete synchronously so the
SDK's LRO pollers terminate on the first response (create/patch return the
resource; delete → 204; deallocate/start → 202 + Azure-AsyncOperation;
invokeCommand → 202 + Location returning the command output).
Datacenters live under a cluster (node count, disk capacity, SKU, availability
zone, delegated subnet, seed nodes). Deleting a cluster cascade-deletes its
datacenters; creating a datacenter validates the parent cluster exists;
deallocate/start propagate to all datacenters.
A wide-column NoSQL database. Control-plane only (the data plane is out of
scope), so it has its own driver. Served as GCP REST/JSON under /v2/...
(server/gcp/bigtable); a real google.golang.org/api/bigtableadmin/v2 client
with a custom endpoint works unchanged. The wire layer uses the SDK's own types
for exact fidelity. Long-running RPCs return a Google Operation{done:true}
carrying the resulting resource, and operations.get returns a done Operation,
so SDK LRO waits complete.
GetOperation (LRO poll) plus per-resource IAM on instances, tables, and
backups: GetIamPolicy, SetIamPolicy, TestIamPermissions.
Modeling: instances own clusters/tables/app-profiles (parent linkage, cascade
delete); backups live under a cluster and restore into a new table; serve-node
counts are bounded; clone-on-read on every path.
Total: 38 operations
11e. Cosmos DB for PostgreSQL (Azure)
Driver interface:services/cosmospostgresql/driver/driver.goAzure: Cosmos DB for PostgreSQL (Citus) — Microsoft.DBforPostgreSQL/serverGroupsv2
Real armcosmosforpostgresql clients configured with a custom endpoint hit the
ARM handler (server/azure/cosmospostgresql) the same way they hit
management.azure.com. Create/update RPCs return the resource inline with a
terminal provisioningState; the cluster start/stop/restart/promote actions
reply 202 + Location and the poller reads a terminal status from the
operationStatuses URL.
Modeling: a cluster owns its firewall rules, roles, configurations, and
private-endpoint connections (parent linkage, cascade delete); nodes are derived
from the cluster shape (one coordinator + N workers); read replicas link back to
a source cluster and detach on promote; clone-on-read on every path.
A single portable interface backs every RDBMS handler. Engine selection (MySQL / PostgreSQL / Aurora / Neptune / DocumentDB / Redshift / Cloud SQL / Azure SQL / AlloyDB / …) is a field on the input config, not a separate driver.
AlloyDB (GCP): a PostgreSQL-compatible managed database served on the alloydb.googleapis.com/v1 REST API (server/gcp/alloydb). It reuses the relational driver — AlloyDB clusters map to Cluster, instances (PRIMARY / READ_POOL / SECONDARY) to Instance, and cluster backups to ClusterSnapshot — plus the Users and Databases capabilities. AlloyDB-specific behavior (instance types, machine vCPU config, cross-region secondary clusters + promote, instance failover/restart, continuous/automated backup config) lives in the optional AlloyDB capability. Because AlloyDB's REST paths (/v1/projects/{p}/locations/{l}/clusters…) are identical to GKE's, the two cannot be multiplexed on one server; the combined GCP server leaves Drivers.AlloyDB nil and callers inject it in place of GKE.
DB subnet groups are an AWS concept — Azure and GCP place managed databases with
vnet integration instead. The SubnetGroups interface is therefore kept out of
RelationalDB and discovered by type assertion; drivers that do not implement it
answer InvalidAction.
Operation
Signature
CreateDBSubnetGroup
(ctx, SubnetGroupConfig) (*SubnetGroup, error)
DescribeDBSubnetGroups
(ctx, names) ([]SubnetGroup, error)
DeleteDBSubnetGroup
(ctx, name) error
VPCID is derived from the member subnets rather than supplied by the caller,
matching the real service. Callers tearing down a VPC list subnet groups and
match on it.
Parameter Groups (optional capability — ParameterGroups)
DB and DB cluster parameter groups. Only user-set parameters are modeled;
the emulator does not fabricate the hundreds of engine defaults real AWS
returns. Real AWS reuses the DBParameterGroup* fault codes for the cluster
variants, so error mapping is shared.
The emulator retains no historical timeline, so PITR clones the source's
current spec; RestoreTime / UseLatestRestorableTime are accepted but not
replayed.
RDS Proxy (optional capability — DBProxies)
A proxy has a single implicit default target group; targets are RDS instances
(RDS_INSTANCE) or clusters (TRACKED_CLUSTER), validated on registration.
Each managed-SQL service also exposes its cloud's own child resources and
actions. Like the RDS capabilities above, these are kept out of the core
RelationalDB interface and discovered by type assertion, so a driver only
answers for the resources its cloud actually has; others return InvalidAction.
The server handlers reach them the way real SDK clients do (ARM sub-resource
routes for Azure, sqladmin sub-collections for Cloud SQL), and the mocks
cascade-delete children when their parent server/instance is deleted.
Cloud SQL also serves the startReplica/stopReplica instance actions (mapped
onto Start/Stop) and the static tiers (/v1/projects/{p}/tiers) and flags
(/v1/flags) reference catalogs. Azure SQL adds the SQL Managed Instance family
(Microsoft.Sql/managedInstances + managed databases) alongside the
single-database logical server. Managed relational servers surface in
cross-service discovery (Azure Resource Graph as microsoft.sql/servers,
microsoft.dbformysql/flexibleservers,
microsoft.dbforpostgresql/flexibleservers; GCP Cloud Asset as
sqladmin.googleapis.com/Instance), are billed per instance-hour via the
relationaldb:* cost catalog, and emit their cloud's monitoring metrics
(including the Microsoft.Sql/servers/elasticpools pool namespace).
Total: 21 core operations + 109 optional across 25 type-asserted capability
interfaces — the 12 RDS-oriented ones (SubnetGroups, ParameterGroups,
OptionGroups, ReadReplicas, AdvancedRestore, DBProxies,
EventSubscriptions, ClusterEndpoints, ClusterFailover, GlobalClusters,
Metadata, Tagging) plus the 13 Azure/GCP managed-SQL ones (Databases,
FirewallRules, Configurations, Failover, VNetRules, ElasticPools,
FailoverGroups, AADAdmins, Users, SslCerts, Clonable,
ReplicaPromotion, ManagedInstances). Each cloud implements the subset that
maps to a real resource and answers InvalidAction otherwise.
18. Kubernetes
Control plane: AWS eks, Azure aks, GCP gke — cluster, node-pool, and addon / Fargate-profile / maintenance-config lifecycle, driven by the real cloud SDKs.
Data plane: shared services/kubernetes/ package — an in-memory Kubernetes API server registered by every cluster across all three providers. Kubeconfigs returned by the control plane point at <base>/k8s/<cluster-uid> so client-go and kubectl operate end-to-end.
Each provider exposes its native control-plane API. The data plane has no portable driver — clients connect via the kubeconfig the control plane hands out, then talk standard Kubernetes REST.
ListClusterAdminCredentials, ListClusterUserCredentials, ListClusterMonitoringUserCredentials — return a kubeconfig pointing at the in-memory data plane (or the *-DATAPLANE-NOT-IMPLEMENTED.cloudemu.local sentinel when no APIServer is wired)
Shared in-memory K8s API server registered by every cluster from any provider. URL: <base>/k8s/<cluster-uid>/.... Served over real TLS: the control plane advertises a shared CA (internal/k8spki) that certifies the serving cert, so client-go and kubectl validate the connection normally — kubeconfigs carry certificate-authority-data, not insecure-skip-tls-verify.
Real kubectl works end-to-end, not just client-go: the server decodes the protobuf request bodies kubectl sends on writes (it accepts protobuf and replies JSON, which kubectl's Accept allows), and serves an OpenAPI v3 discovery document (plus a protobuf v2 for the legacy path) carrying every served GVK so kubectl apply validation passes. Verified against kubectl v1.36 across all three providers: create/apply/scale/set image/patch/rollout/delete, get with short names (pvc, hpa, sts, …), and cascade teardown.
It behaves like a tiny always-converged cluster (minikube-like) rather than a bare object store: a synchronous reconcile engine runs on every write — there are no controller goroutines, so results are immediate and deterministic. Controllers materialize Running Pods, Services get Endpoints, PVCs bind, Jobs complete.
Discovery is derived from the resource registry (registeredResources()), so /api, /apis, and every /apis/<group>/<version> list exactly the resources the server serves — discovery can't promise a kind that 404s.
Core (core/v1): Namespace, ConfigMap, Secret (StringData merged into Data), ServiceAccount (default auto-created per namespace), Pod (driven Running with a synthetic Pod IP — a directly-created Pod with a terminal phase is preserved), Service (ClusterIP from 10.96.0.0/12, immutable on update), Endpoints (get/list/watch only — auto-managed per Service), PersistentVolumeClaim (→ Bound), PersistentVolume (→ Available), Node, Event, ResourceQuota, LimitRange.
Workload controllers (apps/v1): Deployment, ReplicaSet, StatefulSet (stable -0..-N names + one Bound PVC per volumeClaimTemplate), DaemonSet (one Pod per node whose labels satisfy the template nodeSelector — zero Pods when it doesn't match the synthetic node). A Deployment interposes a ReplicaSet per pod-template revision (Deployment→RS→Pod, matching real topology), and a template change creates a new ReplicaSet and deletes the old one outright — an instantaneous swap (no revisionHistoryLimit, no kubectl rollout undo, no surge/unavailable pacing). All materialize Running Pods owned via ownerReferences; deleting a controller cascade-deletes the chain and drains Endpoints. Deployments/StatefulSets expose /scale and /status subresources. CronJob scheduling is driven explicitly via TickCronJobs() (no background timer), which performs real due-evaluation against the cluster clock: it parses the standard 5-field spec.schedule (*, */n, lists, a-b ranges) and materializes a Job only when a scheduled time falls in (status.lastScheduleTime, now] — advancing lastScheduleTime to the fired slot so re-ticking the same instant never double-creates — and honors concurrencyPolicy (Forbid/Replace/Allow) and startingDeadlineSeconds.
Custom resources (CRDs): creating a CustomResourceDefinition dynamically materializes a servable store for every served version — the custom-resource kind is then served by the generic handler (CRUD/list/watch//status) and advertised in discovery immediately; the CRD is marked Established. Deleting the CRD deregisters the kind and cascade-deletes its custom resources — including when the CRD carries a finalizer, in which case teardown runs once the last finalizer drains. Structural schema validation of CRs is a documented simplification (accept-and-store).
Selectors & pagination: label selectors on list; field selectors for metadata.name / metadata.namespace, Pod status.phase / spec.nodeName, and Event fields (involvedObject.name/namespace/kind/uid, reason, type). List responses honor ?limit=&continue= chunked pagination across the registry and typed list paths: the metadata.continue token is key-anchored (it encodes the last object's namespace/name), so an insert or delete before that key cannot skip or duplicate later items under concurrent mutation, and a malformed token returns 410 Gone (reason Expired) per client-go's pager contract. A well-formed token whose key was since deleted resumes gracefully at the next greater key rather than 410-ing on a compacted resourceVersion — strictly more forgiving than upstream.
Patch & server-side apply: JSON-merge-patch, JSONPatch (RFC 6902), and strategic-merge-patch (real strategic merge against the typed struct for core/apps kinds, so kubectl set image merges the container list by name). Server-side apply (application/apply-patch+yaml) tracks per-fieldManager field ownership in metadata.managedFields; an apply that changes a field owned by another manager returns 409 Conflict unless ?force=true (which transfers ownership), and an owner re-applying the same value is a no-op. A re-apply by the same manager that omits a field it previously owned removes that field, unless another manager also owns it. Plain PUT/PATCH updates record an Update-operation managedFields entry for their fieldManager (defaulted from the User-Agent when absent), taking or sharing ownership rather than conflicting (only Apply-vs-Apply is a 409). Ownership is tracked at leaf granularity (map keys / whole arrays) — per-element list merging is not modeled, a documented subset of upstream SSA.
Dry-run: writes with ?dryRun=All (kubectl apply|create|delete --dry-run=server) run validation, defaulting, and quota admission (a create against an at-limit namespace returns the same 403 a real create would), echo the object the server would store, and persist nothing — no resourceVersion bump, reconcile, quota reservation, or watch event.
Finalizers: an object carrying metadata.finalizers goes Terminating on delete (deletionTimestamp stamped, object retained) and is removed only when the last finalizer is cleared via update/patch — on the registry path and typed Namespace/Pod. Finalizers are also honored during cascade: a finalizer-bearing child reached by owner garbage-collection or namespace teardown goes Terminating rather than being reaped, until its finalizers drain. The server-owned deletionTimestamp survives a merge-patch — an RFC-7396 null cannot resurrect a Terminating object.
Pod subresources: pods/{name}/log returns synthetic container output; exec/attach/portforward return a typed 501 (they need a streaming protocol upgrade the emulator doesn't implement); pods/{name}/eviction honors PodDisruptionBudgets.
Metrics & autoscaling: metrics.k8s.io/v1beta1 (kubectl top) serves synthetic Pod/Node metrics from the live pods + synthetic node; a HorizontalPodAutoscaler reconcile drives its target Deployment on a Resource CPU averageUtilization metric — sampling the target Pods' CPU from that metrics source and applying the real HPA ratio desiredReplicas = ceil(currentReplicas × currentUtilization ÷ targetUtilization), clamped into [minReplicas, maxReplicas] — and falls back to a plain min/max clamp when no CPU metric is configured or the target Pods declare no CPU request, reporting currentReplicas/desiredReplicas/currentMetrics on status.
Policy enforcement: object-count ResourceQuota is enforced on create (403 over limit) and on server-side dry-run; status.used is updated on create and recomputed from the live count on delete (it tracks the live object count rather than climbing monotonically); LimitRange applies container defaults and min/max validation on pod create; PodDisruptionBudget gates pods/eviction (429 when eviction would violate the budget); RBAC is queryable via authorization.k8s.io/v1 SubjectAccessReview (evaluated against stored Roles/ClusterRoles + bindings); NetworkPolicy is queryable via an in-process evaluation (no live traffic).
Admission webhooks (opt-in): Mutating/ValidatingWebhookConfiguration objects store and round-trip through kubectl apply. With admission explicitly enabled (APIServer.SetAdmissionEnabled), create/update/patch calls matching webhooks apply mutations and honor denials (4xx); it is off by default so the data plane stays zero-network and deterministic.
Watch resume: a watch with resourceVersion>0 skips the initial snapshot replay and streams only subsequent events; allowWatchBookmarks=true emits a post-sync BOOKMARK carrying the current resourceVersion. A slow watcher that overflows its buffer gets a 410 Gone so client-go relists.
Deterministic time: every data-plane timestamp (creationTimestamp, pod start/conditions, managedFields) is sourced from an injectable clock (APIServer.SetClock); a config.FakeClock makes them fully deterministic for tests.
Watch streaming: each list endpoint accepts ?watch=true and upgrades to a Transfer-Encoding: chunked JSON event stream ({"type":"ADDED|MODIFIED|DELETED","object":{...}}). Initial state replays as ADDED events on subscribe, and the request's labelSelector/fieldSelector filters both the initial snapshot and live events, so client-goInformer / SharedIndexInformer machinery (operator-sdk, Helm, ArgoCD, …) — including selective informers — just works. A fresh cluster bootstraps a synthetic Ready node (cloudemu-node-0), and each selector Service's endpoints are mirrored into a discovery.k8s.ioEndpointSlice so EndpointSlice-mode consumers see the same backends as the Endpoints object.
Cascade: deleting a Namespace or an owning controller publishes DELETED events for every child resource (garbage collection follows ownerReferences) — finalizer-bearing children instead go Terminating (MODIFIED) until drained.
Emulation boundaries (deliberate simplifications, not gaps): there is no real kubelet — Pods are driven Running synthetically and pods/log is synthetic while exec/attach/portforward return a typed 501; no real scheduling beyond the single synthetic node (DaemonSet nodeSelector is honored, but affinity/taints/resource-fit are not); admission webhooks make outbound calls only when explicitly enabled (off by default to stay zero-network); server-side apply tracks ownership at leaf granularity (no per-element list merge); NetworkPolicy and RBAC are queryable (SubjectAccessReview / EvaluateNetworkPolicy) rather than request-time-enforced, since the emulator has no packet path or authenticated identity; CronJob has no wall-clock timer (schedules are evaluated only when TickCronJobs is called) and supports only the standard 5-field cron syntax (nonstandard @-macros, L/W/#/? characters, and seconds/year fields are rejected); rollouts converge instantly (no surge/unavailable pacing, minimal revision history); and OpenAPI is served cluster-independently, so CRD schemas aren't published there (custom resources still work via discovery).
19. Resource Discovery
Engine:services/resourcediscovery/ — a cross-service inventory engine that walks the Compute, Networking, Storage, Database, Serverless, Databricks, Kubernetes, and Relational Database drivers of any provider and returns a normalized Resource view (provider, service, type, ID, ARN/URN, region, tags, created-at). Auto-wired by every provider factory and exposed as Provider.ResourceDiscovery.
SDK-compat handlers: AWS Resource Explorer Two + Resource Groups Tagging API, Azure Resource Graph, and GCP Cloud Asset Inventory. All three sit on top of the same engine, so a tag written through any one path is visible through the others.
Kubernetes clusters (EKS/GKE/AKS) and their node groups (nodegroups / node pools / agent pools) are surfaced via a KubernetesClusters discovery adapter each provider wires in over its cluster mock.
Relational databases follow the same pattern via a RelationalDatabases adapter: AWS RDS/Aurora instances, clusters, and snapshots surface through Resource Explorer 2 (filter service:rds) via the rdsDiscovery adapter. GCP Cloud SQL and Azure SQL discovery are not yet wired.
Engine (services/resourcediscovery/)
Operation
Signature
New
(provider, accountID, region string, drivers *Drivers) *Engine
POST /providers/Microsoft.ResourceGraph/resources?api-version=2022-10-01 — KQL-shaped query over the unified inventory; supports subscriptions[] scoping and $top/$skipToken pagination
Cost-discovery field projection. Each row projects the sku (name/tier/capacity)
and properties a real discoverer prices on, per Azure type:
Azure type
Projected fields
microsoft.compute/virtualmachines
properties.priority (Spot), properties.licenseType, properties.storageProfile.osDisk.osType, sku.name, zones
Fields are seeded through the portable driver configs (VolumeConfig.IOPS/Throughput/Tier,
InstanceConfig.OSType/Priority/LicenseType/Zones, ManagedInstanceConfig.StorageAccountType,
ElasticIPConfig.SKU/AllocationMethod, the AKS Tier/ScaleSetPriority inputs, the
Databases capability on Azure SQL, the VMSS ScaleSets + serverfarms AppServicePlans
discovery capabilities, and the optional BucketAttributes / TableAttributes capabilities
that enrich storage accounts and Cosmos DB) so a value set at create time round-trips over
the real armresourcegraph SDK. Storage/Cosmos/serverfarms follow the established discovery
patterns — optional type-asserted capabilities (like networking's NetworkInterfaces) for
per-resource enrichment, and provider-projected discovery adapters (like the relational-DB
and Kubernetes walkers) for the net-new plan/scale-set resources.
Azure-only. The control plane backs the real armdatabricks SDK; the data plane backs the real databricks-sdk-go WorkspaceClient. The SDK-compat-only workspace families (secrets, tokens, git credentials, repos, DBFS, workspace files, SQL warehouses, pipelines, serving endpoints, SCIM identity, Unity Catalog) have no portable Go API — see sdk-server.md.
The rest of the Microsoft.Databricks ARM surface beyond workspaces
(services/databricks/driver/arm_resources.go), reachable over the real
armdatabricks SDK:
Modeled store-and-echo: the ARM resources round-trip faithfully over the SDK
(access connectors and peerings persist and are listed/described; a
system-assigned access-connector identity gets synthesized principal/tenant
IDs; a created peering springs to Connected/Succeeded), but the underlying
Azure networking side effects are not simulated — a private-endpoint
connection stores its approval state without a real private endpoint on the
platform side, private-link resources and outbound-dependency endpoints are a
synthesized (workspace-scoped) catalog rather than a live probe, and a VNet
peering does not actually peer networks. The provider operations list is a
static catalog of the RBAC operations the namespace exposes.
The control plane speaks awsJson1_1 (X-Amz-Target: SageMaker.*); the runtime speaks
restJson1 (POST /endpoints/{name}/invocations). Asynchronous jobs complete synchronously
to a terminal state so Describe/List are deterministic. Auto-metrics → CloudWatch via
SetMonitoring.
SDK-compat HTTP coverage spans every family above, round-tripped against the real
aws-sdk-go-v2/service/sagemaker, sagemakerruntime and sagemakerfeaturestoreruntime
clients. Total: 121 operations.
REST rooted at /v1/projects/{p}/locations/{l}/... with the Model Garden generateContent
surface at /v1/publishers/.... Control-plane mutations return done
google.longrunning.Operations; job-family creates are synchronous (poll the state
field). Auto-metrics → Cloud Monitoring via SetMonitoring.
The full Go API/driver, in-memory provider, and SDK-compat HTTP server (REST round-tripped)
cover every family above — models (+versions/evaluations), endpoints (+predict), datasets,
custom/batch-prediction/hyperparameter-tuning jobs, training & pipeline jobs, tuning jobs,
cached contents, Feature Store (featurestores/entityTypes/features + online read/write),
Feature Registry & online stores, Vector Search (indexes + index endpoints), ML metadata,
tensorboards, schedules, notebook runtimes, and generateContent/countTokens. A portable
Layer-1 wrapper (vertexai/vertexai.go), chaos injection (chaos.WrapVertexAI), and cost
rates integrate Vertex with the cross-cutting layers like every other service.
Total: 128 operations (Go API/driver).
Azure — Azure AI
Driver interface:services/ai/driver/ — spans both ARM providers plus the data planes.
Azure: Azure AI Foundry / AI Studio / Azure OpenAI (Microsoft.CognitiveServices) and
Azure Machine Learning (Microsoft.MachineLearningServices).
ARM control-plane PUT returns the resource inline with a terminal provisioningState so the
SDK LRO poller terminates on the first response. The data plane is host/path-routed
(*.openai.azure.com/openai/..., *.inference.ml.azure.com/score). Auto-metrics push to
Azure Monitor via SetMonitoring.
Family
Resources / Operations
AI Services accounts
accounts CRUD, list by RG/sub, listKeys, regenerateKey, listModels, listSkus, listUsages
Model deployments
accounts/deployments CRUD + list (gpt-4o, embeddings, …)
AI Foundry projects
accounts/projects CRUD + list
Responsible AI
accounts/raiPolicies CRUD + list
Commitment plans
accounts/commitmentPlans CRUD + list
Private endpoints
accounts/privateEndpointConnections CRUD + list
Azure OpenAI inference
chat/completions, completions, embeddings
Agents / Assistants
assistants, threads, messages, runs (CRUD/list)
AML workspaces
workspaces (Default/Hub/Project/FeatureStore) CRUD, list by RG/sub
Full Go API/driver, in-memory provider, SDK-compat ARM + data-plane HTTP server, a portable
Layer-1 wrapper (ai/ai.go), chaos injection (chaos.WrapAzureAI), and cost rates
integrate Azure AI with the cross-cutting layers like every other service.
Total: 92 operations (Go API/driver) — 31 CognitiveServices + 46 MachineLearningServices
15 data plane — all exposed over the SDK-compat HTTP server.
23. AI Search
Driver interface:services/search/driver/ — Microsoft.Search/searchServices (ARM control
plane) plus the {service}.search.windows.net data plane.
Azure: Azure AI Search (the RAG / retrieval backbone). AWS / GCP:not applicable.
ARM PUT returns the resource inline with a terminal provisioningState; the data plane is
host/path-routed (service name from the {service}.search.windows.net subdomain). Auto-metrics
push to Azure Monitor via SetMonitoring.
Family
Resources / Operations
Services (control)
searchServices CRUD, list by RG/sub, update; listAdminKeys, regenerateAdminKey, listQueryKeys, createQueryKey, deleteQueryKey
index (upload/merge/mergeOrUpload/delete), search (+count), suggest, autocomplete, count, get-by-key
Indexers
create-or-update, get, list, delete, run, reset, status
Data sources
create-or-update, get, list, delete
Skillsets
create-or-update, get, list, delete
Synonym maps
create-or-update, get, list, delete
Aliases
create-or-update, get, list, delete
Service statistics
counts + storage usage
Full Go API/driver, in-memory provider, SDK-compat ARM + data-plane HTTP server, a portable
Layer-1 wrapper (search/search.go), chaos injection (chaos.WrapAzureSearch), and
cost rates integrate Azure AI Search with the cross-cutting layers like every other service.
Total: 53 operations (Go API/driver) — 19 control plane + 34 data plane.
AWS-only. Real aws-sdk-go-v2/service/ecs clients work against the SDK-compat
server (awsserver.Drivers{ECS: cloud.ECS}).
Scheduling & placement. Container instances carry CPU/memory capacity;
RunTask/services with launch type EC2 are first-fit placed onto an
instance with sufficient remaining capacity (reserving it, releasing on stop) —
no capacity leaves the task in failures[] (AGENT/RESOURCE:*) or, for a
service, PENDING. FARGATE requires networkConfiguration.awsvpcConfiguration
an awsvpc task-def with cpu+memory, and synthesizes an ENI attachment +
platformVersion (no capacity pool). launchType is validated against the
task-def's requiresCompatibilities.
Services converge synchronously: CreateService actually launches
desiredCount tasks (linked via the service:<name> group), records a PRIMARY
deployment (rolloutState COMPLETED/IN_PROGRESS) and an event; UpdateService
reconciles tasks and promotes a new deployment (superseded deployments drain and
are dropped, so the list does not grow); DAEMON runs one task per container
instance (and rejects a caller-supplied desiredCount). Batch Describe* and
RunTask return partial success (failures[]) rather than erroring; typed
exceptions (ClusterNotFoundException, ServiceNotFoundException,
ClusterContains*Exception, InvalidParameterException, ClientException) match
the SDK.
Accepted but not simulated (stored and round-tripped so SDK calls succeed, but
with no behavioral effect): capacityProviderStrategy (placement still falls
through to EC2/Fargate by launch type — no FARGATE_SPOT/ASG providers),
loadBalancers / serviceRegistries (no target-group registration, health
checks, or Service Connect), and the deployment circuit-breaker / rollback.
Fargate task-level cpu/memoryis validated against the supported
configuration table.
Composes with EC2 (#300).RegisterContainerInstance provisions a backing
managed EC2 instance (Operator.Managed=true, principal ecs.amazonaws.com,
aws:ec2:managed-launch tag), so an ECS container instance is discoverable as a
real EC2 instance subject to managed-resource visibility.
RegisterTaskDefinition (auto-incrementing revision; the full container/task runtime surface — portMappings, environment, secrets, healthCheck, logConfiguration, mountPoints, ulimits, resourceRequirements, volumes, ephemeralStorage, runtimePlatform, proxyConfiguration, … — is accepted and round-tripped on the task definition, not reflected onto launched containers, which carry only name/image/status), ListTaskDefinitions, DescribeTaskDefinition, DeregisterTaskDefinition, ListTaskDefinitionFamilies
AWS-only. Real aws-sdk-go-v2/service/route53resolver clients work against the
SDK-compat server (awsserver.Drivers{Route53Resolver: cloud.Route53Resolver}).
Full parity: all 72 SDK operations, no stubs. Each resource group is stored
in an in-memory memstore.Store guarded by a single mutex; reads are
copy-on-write clones. Every group is covered by a real-SDK round-trip test.
Per-VPC configs (Resolver autodefined-reverse, DNSSEC validation, firewall
fail-open) are lazily materialized on first Get with their AWS defaults
(reverse ENABLED, DNSSEC DISABLED, fail-open DISABLED) and only appear in the
corresponding List once touched. Firewall rules are identified within a group by
(FirewallDomainListId, Qtype); deleting a rule group cascades to its rules.
Accepted but not simulated (stored/echoed so SDK calls succeed, no behavioral
effect): endpoint/rule/config status stays terminal (no async CREATING→OPERATIONAL
transitions); ImportFirewallDomains records the request without fetching the S3
file; ListFirewallRuleTypes returns an empty descriptor list; resource-share
policies are stored verbatim without RAM enforcement.
AWS-only. Real aws-sdk-go-v2/service/vpclattice clients work against the
SDK-compat server (awsserver.Drivers{VPCLattice: cloud.VPCLattice}). Full
parity: all 73 SDK operations, no stubs.
Unlike the AWS JSON 1.1 services, VPC Lattice uses REST-JSON: the operation
is selected by HTTP method + URL path (e.g. POST /services, GET /services/{id}/listeners/{id}, PATCH /servicenetworks/{id}) rather than an
X-Amz-Target header. The handler gates on path root + method + identifier
shape, so a path-style S3 object op on a bucket named like a Lattice root
(e.g. GET /services/mykey) falls through to the S3 catch-all — only a
Lattice-shaped id (a known prefix or a vpc-lattice ARN) is claimed. The single
unavoidable residual is a bare GET /<root> (list) vs. an S3 list-bucket on an
identically-named bucket. Identifiers accept either a bare ID or a full ARN. Union-typed fields
(a listener's defaultAction, a rule's match/action, a target group's
config, a resource configuration's resourceConfigurationDefinition) are
stored as raw JSON and echoed back verbatim. Create-time tags are persisted;
deletes block on live service-network associations and cascade contained
children (service→listeners→rules); association counts are recomputed on read
and skip targets that no longer exist.
Accepted but not simulated (stored/echoed so SDK calls succeed, no behavioral
effect): resources are created directly in a terminal ACTIVE/PENDING status
(no async state machine); VPC-endpoint and resource-endpoint associations are a
managed surface returned as empty lists; Forward/health-check targeting is
stored but not used to route real traffic.
Total: 73 operations.
Provider-specific resources
Resources below are served for one provider only, because the concept exists in
one cloud and has no counterpart to abstract. They are reached through the same
endpoints as everything else; the difference is that no portable driver
interface covers them.
GCP — networking
Resource
Operations
Cloud Routers
insert · get · list · patch · delete
Addresses (global and regional)
insert · get · list · delete
Service Networking connections
list · create · patch · delete
Cloud NAT is configured by patching a router, and private services access
reserves a global address and opens a connection. A caller building a private
network uses all three, and releases them when the network goes away.
Addresses are keyed by the scope they were reserved in, so a global address and
a regional one sharing a name stay distinct.
Azure — Resource Manager
Resource
Operations
Resource groups
create · get · list · delete
Subscriptions
list
Every Azure resource lives in a resource group, so one is created before
anything else and deleted last. A group is usable as soon as it exists;
deleting one that is already gone succeeds, since that is the caller's desired
end state and a teardown retry must not fail on its second pass.
The subscriptions list is empty. This emulator has no tenant model, so it
cannot say which subscriptions a credential reaches, and inventing some would
fabricate an authorization boundary that does not exist here.
Behavior of published AWS resources
Two families exist in every real AWS account without anyone creating them, so
callers reference them directly. Both are materialized on first reference,
matched against the sets AWS actually publishes — an unrecognized name is
rejected, because accepting anything would let a typo through here and fail
only in production.
Family
Recognized
IAM managed policies (arn:aws:iam::aws:policy/…)
A catalog of real policy names, pathed ones included
SSM parameters (/aws/service/…/ami-id)
The published image trees; the id is derived from the parameter name, so it is stable per parameter and distinct across distros
Parameter Store — Run Command (optional capability)
Discovered by type assertion on the parameter-store driver, like the subnet and
replication group capabilities.
Targets are validated: sending to an instance that does not exist is
InvalidInstanceId, which is the most common Run Command failure during
bring-up.
Nothing executes. An emulated instance has no guest operating system, so
invocations report success with empty output. This exercises a caller's
send-and-poll orchestration — that it waits for a terminal status and reads the
response code — but not the script. A caller whose bootstrap script is wrong
still sees success.
Machine Learning — Azure AI (CognitiveServices + MachineLearningServices + data plane)
92
Machine Learning — GCP Vertex AI (Go API/driver)
128
AI Search — Azure AI Search (control + data plane)
53
Container Orchestration — AWS ECS
37
DNS Resolver — AWS Route 53 Resolver
72
Application Networking — AWS VPC Lattice
73
Grand Total
1707 (+138 optional)
Optional operations are capabilities a driver may implement but is not required
to; see the sections marked "optional capability". They are counted separately
because a driver without them is still complete.
Provider-specific resources are not counted: no driver interface covers them,
so there are no driver operations to count.