AWS Cloud Map
Protocol: JSON 1.1
Header: X-Amz-Target: Route53AutoNaming_v20170314.<Action>
Endpoint prefix: servicediscovery
Floci emulates the AWS Cloud Map (service discovery) control plane in-process: namespaces, services, and registered instances are stored as account-aware domain objects, and discovery queries resolve against that in-memory state.
Asynchronous operations (CreateHttpNamespace, CreatePublicDnsNamespace,
CreatePrivateDnsNamespace, DeleteNamespace, RegisterInstance,
DeregisterInstance) apply their effect synchronously and return an operation
id. The operation reaches SUCCESS immediately by default, so a follow-up
GetOperation call returns a completed operation without polling. No Route 53
hosted zones are created: public and private DNS namespaces are assigned a
synthetic HostedZoneId so SDK and CLI clients can exercise the full Cloud Map
control flow locally.
DNS resolution
A DNS namespace is answerable, not just storable. Floci's embedded DNS server
resolves <service>.<namespace> to the AWS_INSTANCE_IPV4 of the instances
registered under that service, one A record each, so a container that uses Floci
as its resolver reaches its peers by name. Instances registered directly through
RegisterInstance and instances an ECS service registers through its
serviceRegistries resolve the same way.
Which instances answer follows Route 53: the healthy ones while any instance is
healthy, and all of them when none is, so a service whose instances have all gone
unhealthy still resolves rather than disappearing from DNS. MULTIVALUE returns up to
eight instances; WEIGHTED returns one randomly selected instance, with equal weights.
Names inside a DNS namespace stay local when no instance has a usable address:
the resolver answers negatively instead of forwarding them to upstream DNS.
An absent service name receives NXDOMAIN; a service with registered instances
and only other record types receives NOERROR with no A answers. DNS service
names are unique without regard to case, as in AWS Cloud Map.
A and AAAA records use AWS_INSTANCE_IPV4 and AWS_INSTANCE_IPV6, respectively,
and carry the TTL of the corresponding DnsConfig.DnsRecords entry. CNAME services
answer with AWS_INSTANCE_CNAME, including when queried for A or AAAA. SRV queries
return priority 1, weight 1, AWS_INSTANCE_PORT, and
<InstanceId>.<service>.<namespace>. The SRV target also answers A and/or AAAA
queries when those address attributes are registered, using the SRV TTL. The SRV
service name itself has no address answer. Floci uses a 60-second fallback for older stored services without a
usable A record TTL.
New CreateService requests reject TTL values outside AWS's 0 to 2147483647
range. Names owned by Floci's embedded DNS server also use 60 seconds.
SRV registrations must produce an encodable DNS target: ASCII labels of at most
63 bytes and a full name of at most 253 characters. Floci returns InvalidInput
before storing an unencodable target, rather than accepting a registration that
cannot resolve. The API's general 64-character instance identifier limit is not
a guarantee that an identifier can be used as a single DNS label; non-SRV
registrations are unaffected. This early validation is an emulator behavior,
not a verified claim about AWS's error timing. Older mixed A/CNAME configurations
still resolve their explicitly configured A records.
UDP replies respect the legacy 512-byte limit or the client's EDNS payload size, capped at 4096 bytes. Oversized answers are truncated at whole-record boundaries and carry the DNS truncation flag. The embedded server remains UDP-only; TCP retry is not implemented.
Two limits are worth knowing. HTTP namespaces do not resolve, matching AWS,
where they are reachable only through DiscoverInstances. The embedded DNS server only runs
when Floci itself runs inside Docker, so name resolution is available to
containers, not to processes on the host.
Supported Operations
| Operation | Notes |
|---|---|
CreateHttpNamespace |
Creates an HTTP namespace; returns an operation id |
CreatePublicDnsNamespace |
Creates a DNS_PUBLIC namespace with a synthetic HostedZoneId |
CreatePrivateDnsNamespace |
Creates a DNS_PRIVATE namespace; requires Vpc, assigns a HostedZoneId |
GetNamespace |
Returns a namespace by Id |
ListNamespaces |
Lists namespaces in the current region |
DeleteNamespace |
Deletes an empty namespace; fails with ResourceInUse if it still has services |
CreateService |
Creates a service (optionally under a namespace), tracks revision and instance count |
GetService |
Returns a service by Id |
ListServices |
Lists services, optionally filtered by NamespaceId |
DeleteService |
Deletes a service with no instances; fails with ResourceInUse otherwise |
RegisterInstance |
Registers an instance under a service; requires InstanceId and Attributes, bumps service revision |
DeregisterInstance |
Removes a registered instance and bumps service revision |
GetInstance |
Returns a registered instance by ServiceId and InstanceId |
ListInstances |
Lists instances registered under a service |
GetInstancesHealthStatus |
Returns health status keyed by instance id, with optional Instances filter |
DiscoverInstances |
Resolves instances by NamespaceName + ServiceName, with HealthStatus and QueryParameters filtering |
DiscoverInstancesRevision |
Returns the current revision for a discovered service |
GetOperation |
Returns an operation by Id |
ListOperations |
Lists operations with optional STATUS, TYPE, NAMESPACE_ID, and SERVICE_ID filters |
TagResource |
Adds tags to a namespace or service ARN |
UntagResource |
Removes tag keys from a namespace or service ARN |
ListTagsForResource |
Lists tags for a namespace or service ARN |
DiscoverInstances supports the HEALTHY, UNHEALTHY, HEALTHY_OR_ELSE_ALL,
and ALL health filters, and matches instances whose attributes contain every
key/value pair supplied in QueryParameters.
CloudFormation
Floci provisions these resource types:
AWS::ServiceDiscovery::HttpNamespaceAWS::ServiceDiscovery::PrivateDnsNamespaceAWS::ServiceDiscovery::PublicDnsNamespaceAWS::ServiceDiscovery::Service
Ref returns the namespace or service id. Fn::GetAtt exposes Id and Arn on all of them,
HostedZoneId on a DNS namespace, and Name on a service.
Description, DnsConfig, HealthCheckConfig, and Tags update in place and keep the id. Only
the TTLs in DnsConfig can change, as on AWS: an update that changes anything else in it, such as
the record types or RoutingPolicy, fails with InvalidInput. A change to a createOnly property
(Name, and Vpc on a private DNS namespace; Name, NamespaceId, Type, or
HealthCheckCustomConfig on a service) replaces the resource: the new one is created and the old
one is deleted once the stack update completes. As on AWS, a replacement that keeps the same name
fails, since the name is still taken.
ServiceAttributes and Properties.DnsProperties.SOA are accepted but ignored.
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_SERVICES_CLOUDMAP_ENABLED |
true |
Enable or disable the service |
FLOCI_SERVICES_CLOUDMAP_OPERATION_COMPLETION_DELAY_SECONDS |
0 |
Delay before an async operation transitions from PENDING to SUCCESS; 0 completes immediately |
Examples
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
aws servicediscovery create-http-namespace --name floci-demo
aws servicediscovery list-namespaces
aws servicediscovery create-service \
--name backend \
--namespace-id ns-xxxxxxxxxxxxxxxxxxxx
aws servicediscovery register-instance \
--service-id srv-xxxxxxxxxxxxxxxxxxxx \
--instance-id i-0123456789 \
--attributes AWS_INSTANCE_IPV4=10.0.0.10
aws servicediscovery discover-instances \
--namespace-name floci-demo \
--service-name backend
import boto3
client = boto3.client(
"servicediscovery",
endpoint_url="http://localhost:4566",
region_name="us-east-1",
)
ns = client.create_http_namespace(Name="floci-demo")
client.get_operation(OperationId=ns["OperationId"]) # Status == "SUCCESS"
namespace_id = client.list_namespaces()["Namespaces"][0]["Id"]
service = client.create_service(Name="backend", NamespaceId=namespace_id)
client.register_instance(
ServiceId=service["Service"]["Id"],
InstanceId="i-0123456789",
Attributes={"AWS_INSTANCE_IPV4": "10.0.0.10"},
)
found = client.discover_instances(NamespaceName="floci-demo", ServiceName="backend")
print(found["Instances"])
Related Docs
Out of Scope
- Route 53 hosted zone / record set creation.
- Route 53 health checks backing
HealthCheckConfig(custom health status is stored, not actively probed). AWS_INIT_HEALTH_STATUSandAWS_EC2_INSTANCE_IDonRegisterInstance. Every instance registersHEALTHY, which is AWS's own initial status whenAWS_INIT_HEALTH_STATUSis absent, so only a caller that explicitly asks forUNHEALTHYsees a difference.- Cross-region namespace and service discovery. A DNS query carries no region, so a name resolves through whichever namespace copy holds instances.