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
Make cluster= optional on the Clappform constructor. When omitted, discover the cluster extension from the DNS CNAME of the location's subdomain, so Clappform(location="noord-brabant", api_key=...) just works.
Explicit cluster= — used as-is, no DNS performed (CI / air-gapped / split-DNS stays deterministic).
Discovery — resolve {location}.clappform.com via socket.gethostbyname_ex (stdlib, follows the CNAME chain) and match the canonical name against ^bigip(?:-(?P<ext>.+))?\.clappform\.com$; the ext group (empty for main) is the cluster extension.
Fail loudly — lookup failure or non-matching canonical name raises ConfigurationError with a "pass cluster= explicitly" hint. Never guess.
Discovery runs once at construction, is cached on the instance, and the discovered value is included in error reports next to cluster/location so a wrong discovery is immediately visible. It only picks endpoints — the location metadata header is still sent per call for tenancy, unchanged.
Acceptance criteria
cluster parameter is str | None = None; explicit value skips DNS entirely
Discovery parses the canonical name per the regex above; empty extension maps to the main cluster hosts
DNS failure / unexpected canonical name raises ConfigurationError naming the location and suggesting explicit cluster=
Result cached per instance; with_location() clones re-discover only if their location differs
Discovered cluster appears in error report context (§5.2)
Unit tests stub the resolver (no live DNS in CI): main cluster, suffixed cluster, NXDOMAIN, non-bigip canonical name (musl-style echo of the input)
Docs: quickstart leads with location-only construction; explicit cluster= documented as the override
Notes / constraints
The CNAME convention ({location}.clappform.com → bigip{-ext}.clappform.com, ext matching service hosts) becomes an API contract — needs a line in the infra runbook; converting an environment to a direct A record breaks discovery for it.
musl-based systems (Alpine containers) may not return canonical names from getaddrinfo; there discovery falls into the step-3 error path rather than misbehaving.
Summary
Make
cluster=optional on theClappformconstructor. When omitted, discover the cluster extension from the DNS CNAME of the location's subdomain, soClappform(location="noord-brabant", api_key=...)just works.Design: §3.1.1 Cluster discovery from DNS
How it works
Every client environment is a CNAME to its cluster's BigIP, and the BigIP name carries the same extension as that cluster's service hosts:
So a plain forward lookup already contains the cluster identifier — no reverse/PTR search, no registry, no extra dependency:
Resolution order:
cluster=— used as-is, no DNS performed (CI / air-gapped / split-DNS stays deterministic).{location}.clappform.comviasocket.gethostbyname_ex(stdlib, follows the CNAME chain) and match the canonical name against^bigip(?:-(?P<ext>.+))?\.clappform\.com$; theextgroup (empty for main) is the cluster extension.ConfigurationErrorwith a "pass cluster= explicitly" hint. Never guess.Discovery runs once at construction, is cached on the instance, and the discovered value is included in error reports next to cluster/location so a wrong discovery is immediately visible. It only picks endpoints — the
locationmetadata header is still sent per call for tenancy, unchanged.Acceptance criteria
clusterparameter isstr | None = None; explicit value skips DNS entirelyConfigurationErrornaming the location and suggesting explicitcluster=with_location()clones re-discover only if their location differscluster=documented as the overrideNotes / constraints
{location}.clappform.com → bigip{-ext}.clappform.com, ext matching service hosts) becomes an API contract — needs a line in the infra runbook; converting an environment to a direct A record breaks discovery for it.getaddrinfo; there discovery falls into the step-3 error path rather than misbehaving.