Internals
Read from the source at commit
65b0dd9ae. Every claim here points at a file and line.
Code map
| Path | Responsibility |
|---|---|
cmd/busyambassador/main.go | BusyBox-style dispatch by os.Args[0] into entrypoint, kubestatus, version |
cmd/entrypoint/ | Go process manager, cluster watcher, and notify logic |
cmd/apiext/ | The emissary-apiext CRD conversion webhook |
pkg/ambex/ | The ADS server that feeds Envoy over xDS |
pkg/snapshot/v1/ | The Snapshot and KubernetesSnapshot types the watcher builds |
pkg/api/getambassador.io/ | CRD Go types for v1, v2, v3alpha1 |
python/ambassador/src/ambassador/ir/ | The intermediate representation and its factories |
python/ambassador/src/ambassador/envoy/v3/ | Envoy v3 config generators |
python/ambassador-diag/src/ambassador_diag/diagd.py | The diagd Flask service and the compile pipeline |
Core data structures
snapshot.SnapshotandKubernetesSnapshot(Go) are the full world state the watcher assembles.KubernetesSnapshotholds Mappings, Hosts, Listeners, Services, Endpoints, Secrets, and Gateway API objects (pkg/snapshot/v1/types.go:23,pkg/snapshot/v1/types.go:57,pkg/snapshot/v1/types.go:84-87).IR(Python) is the intermediate representation. It carries dictionaries such asclusters: Dict[str, IRCluster],groups: Dict[str, IRBaseMappingGroup], andlisteners: Dict[str, IRListener](python/ambassador/src/ambassador/ir/ir.py:90-118).EnvoyConfig(Python) is generated from theIRand split bysplit_config()into bootstrap config, ADS config, and clustermap (python/ambassador-diag/src/ambassador_diag/diagd.py:1628,python/ambassador-diag/src/ambassador_diag/diagd.py:1635).FastpathSnapshot(Go) carries anecp_v3_cache.Snapshotplus*Endpointsfor the path that skips Python (pkg/ambex/fastpath.go:7-11).MappingSpec(Go,v3alpha1) is the user-facing routing resource (pkg/api/getambassador.io/v3alpha1/crd_mapping.go:27).
A path worth tracing
Take a Mapping resource and follow it to a live Envoy route.
- The watcher builds a snapshot.
WatchAllTheThings(cmd/entrypoint/watcher.go:26) watches the cluster and assembles asnapshot.Snapshot(cmd/entrypoint/watcher.go:201). - On
SnapshotReady(cmd/entrypoint/watcher.go:106-118) thenotifyclosure firesnotifyReconfigWebhooks(cmd/entrypoint/watcher.go:62-67). - That POSTs to diagd.
notifyWebhookUrlposts to the URL fromGetEventUrl(cmd/entrypoint/notify.go:42,cmd/entrypoint/env.go:244), which resolves to_internal/v0/watt?url=<snapshot>on port 8004 (cmd/entrypoint/env.go:153). - diagd receives it.
handle_watt_updatereads?url=and enqueues("watt", url)(python/ambassador-diag/src/ambassador_diag/diagd.py:916). - diagd parses it.
load_config_wattfetches the snapshot, saves it to disk, and feeds it into theConfig(python/ambassador-diag/src/ambassador_diag/diagd.py:1539). - diagd compiles it.
_load_irrunsIR.check_deltasto decide complete vs incremental, builds theIR, and callsEnvoyConfig.generatethensplit_config()(python/ambassador-diag/src/ambassador_diag/diagd.py:1585-1635). Mappings becomeIRHTTPMappingobjects viaMappingFactory.load_all(python/ambassador/src/ambassador/ir/irmappingfactory.py:28). - diagd validates it.
validate_envoy_configrunsenvoy --mode validate; an invalid config returns an error and the current config is kept (python/ambassador-diag/src/ambassador_diag/diagd.py:1652). - ambex serves it. The validated config is written to disk, ambex loads it into a go-control-plane
SnapshotCache, and pushes it to Envoy over ADS on127.0.0.1:8003(pkg/ambex/main.go:6-40,cmd/entrypoint/entrypoint.go:164-167).
text
WatchAllTheThings -> SnapshotReady -> notifyReconfigWebhooks
-> POST _internal/v0/watt?url= -> handle_watt_update
-> load_config_watt -> _load_ir (IR -> EnvoyConfig -> split_config)
-> validate_envoy_config (envoy --mode validate)
-> ambex SnapshotCache -> ADS push -> envoyThings that surprised me
- The endpoint fast path skips Python entirely. Pod churn changes EDS endpoints but not routing structure, so the entrypoint passes endpoint-only updates straight to ambex through
fastpathCh chan *ambex.FastpathSnapshot(cmd/entrypoint/entrypoint.go:163-167), and ambex swaps only the EDS data (pkg/ambex/fastpath.go:7). This avoids a reconfigure storm under heavy pod churn. - Emissary validates config with the real Envoy before swapping it. The generated ADS config is run through
envoy --mode validate, and on failure the live config is left untouched (python/ambassador-diag/src/ambassador_diag/diagd.py:1652). A bug in Emissary's own generation cannot take down user traffic. - CRD versions are normalized out of the engine's view. Multiple versions (
v1,v2,v3alpha1) coexist, and the in-clusteremissary-apiextconversion webhook (cmd/apiext/main.go,pkg/apiext) means the Python engine only ever seesv3alpha1, so it never has to reason about version differences.