Skip to content

Commit 66e01cc

Browse files
mloiseleurdanopia
andauthored
feat(source/ambassador): migrate Host CRD to emissary-ingress v3alpha1 (#6482)
* feat(source/ambassador): migrate Host CRD to emissary-ingress v3alpha1 The datawire/ambassador repository was renamed to emissary-ingress/emissary and the v2 Host CRD is no longer installed by the Emissary-ingress 3.10 quickstart, which broke the ambassador-host source. Switch the dependency to github.com/emissary-ingress/emissary/v3 v3.10.0 (latest go-gettable release; v4 tags keep the /v3 module path and are not consumable as a Go module) and read the getambassador.io/v3alpha1 Host CRD. This drops the unmaintained datawire/ambassador dependency and its transitive tree (~800 lines of go.sum). Add a runnable kind-based tutorial and a test that exercises the v3alpha1 Host in the wire shape the API server serves it (v2 is the storage version, so the served object carries ambassador_id and acmeProvider fields injected by the conversion webhook). Addresses issue 5724. Co-authored-by: Daniel Lamando <40628+danopia@users.noreply.github.com> * docs(ambassador): add inmemory flags so local run shows updates/deletes The inmemory provider keeps no state without a zone, so the tutorial's local run re-emitted the same CREATE every loop and never showed UPDATE or DELETE. Add --inmemory-zone=example.com (persist records) and --interval=10s (shorter reconcile loop) to the go run and Deployment snippets. --------- Co-authored-by: Daniel Lamando <40628+danopia@users.noreply.github.com>
1 parent 5dc3ada commit 66e01cc

5 files changed

Lines changed: 245 additions & 836 deletions

File tree

docs/tutorials/ambassador.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Ambassador / Emissary-ingress Host
2+
3+
This tutorial describes how to configure ExternalDNS to use the `ambassador-host` source.
4+
It reads `Host.getambassador.io` resources
5+
([Emissary-ingress](https://www.getambassador.io/docs/emissary), formerly Ambassador)
6+
and creates DNS records for the hostnames they declare.
7+
8+
ExternalDNS uses the `getambassador.io/v3alpha1` CRD version. This requires
9+
Emissary-ingress 3.x (the `datawire/ambassador` v2 CRDs are no longer installed by the
10+
3.10 quickstart). For older deployments still serving only the v2 CRD, stay on
11+
ExternalDNS v0.21.0 or earlier.
12+
13+
## How it works
14+
15+
For each `Host`, the source looks at the `external-dns.ambassador-service` annotation. The
16+
value points to the Emissary-ingress `LoadBalancer` Service whose address is used as the
17+
record target:
18+
19+
- `name` &ndash; service in the same namespace as the `Host`.
20+
- `namespace/name` &ndash; service in an explicit namespace.
21+
- `name.namespace` &ndash; Ambassador's historical cross-namespace syntax.
22+
23+
The hostname comes from `spec.hostname`. A `Host` without the
24+
`external-dns.ambassador-service` annotation is ignored. The target can be overridden with
25+
the standard `external-dns.kubernetes.io/target` annotation; TTL and provider-specific
26+
annotations are honored as well.
27+
28+
## Run it locally with kind
29+
30+
The steps below run end to end on a local [kind](https://kind.sigs.k8s.io/) cluster using
31+
the `inmemory` provider, so no cloud credentials are needed &ndash; the DNS changes
32+
ExternalDNS would apply are printed to its log.
33+
34+
### 1. Create a cluster
35+
36+
```bash
37+
kind create cluster --name external-dns-ambassador
38+
```
39+
40+
### 2. Install Emissary-ingress 3.10
41+
42+
```bash
43+
kubectl apply -f https://app.getambassador.io/yaml/emissary/3.10.0/emissary-crds.yaml
44+
kubectl wait --timeout=90s --for=condition=available deployment emissary-apiext -n emissary-system
45+
46+
kubectl create namespace emissary
47+
kubectl apply -f https://app.getambassador.io/yaml/emissary/3.10.0/emissary-emissaryns.yaml
48+
kubectl -n emissary rollout status deployment/emissary-ingress
49+
```
50+
51+
### 3. Deploy ExternalDNS
52+
53+
```yaml
54+
apiVersion: v1
55+
kind: ServiceAccount
56+
metadata:
57+
name: external-dns
58+
namespace: default
59+
---
60+
apiVersion: rbac.authorization.k8s.io/v1
61+
kind: ClusterRole
62+
metadata:
63+
name: external-dns
64+
rules:
65+
- apiGroups: [""]
66+
resources: ["services","endpoints","pods"]
67+
verbs: ["get","watch","list"]
68+
- apiGroups: ["getambassador.io"]
69+
resources: ["hosts"]
70+
verbs: ["get","watch","list"]
71+
---
72+
apiVersion: rbac.authorization.k8s.io/v1
73+
kind: ClusterRoleBinding
74+
metadata:
75+
name: external-dns-viewer
76+
roleRef:
77+
apiGroup: rbac.authorization.k8s.io
78+
kind: ClusterRole
79+
name: external-dns
80+
subjects:
81+
- kind: ServiceAccount
82+
name: external-dns
83+
namespace: default
84+
---
85+
apiVersion: apps/v1
86+
kind: Deployment
87+
metadata:
88+
name: external-dns
89+
namespace: default
90+
spec:
91+
strategy:
92+
type: Recreate
93+
selector:
94+
matchLabels:
95+
app: external-dns
96+
template:
97+
metadata:
98+
labels:
99+
app: external-dns
100+
spec:
101+
serviceAccountName: external-dns
102+
containers:
103+
- name: external-dns
104+
image: registry.k8s.io/external-dns/external-dns:v0.21.0
105+
args:
106+
- --source=ambassador-host
107+
- --provider=inmemory
108+
- --inmemory-zone=example.com # persist records so updates/deletes are observable
109+
- --log-level=debug # show the records that would be created
110+
# for a real provider, replace the two lines above, e.g.:
111+
# - --provider=xxx
112+
# - --domain-filter=example.com
113+
# - --registry=txt
114+
# - --txt-owner-id=my-identifier
115+
```
116+
117+
Apply it:
118+
119+
```bash
120+
kubectl apply -f external-dns.yaml
121+
```
122+
123+
Or run it on the host from sources (handy for testing local changes to the source). This
124+
uses your current kubeconfig context (the kind cluster), so no in-cluster RBAC is needed:
125+
126+
```bash
127+
go run main.go \
128+
--source=ambassador-host \
129+
--provider=inmemory \
130+
--inmemory-zone=example.com \
131+
--interval=10s \
132+
--log-level=debug
133+
```
134+
135+
`--inmemory-zone=example.com` gives the `inmemory` provider a zone to store records in.
136+
Without it the provider keeps no state between reconcile loops, so it re-emits the same
137+
`CREATE` every cycle and you never see an `UPDATE` or `DELETE`. `--interval=10s` shortens
138+
the wait between reconcile loops (default is one minute).
139+
140+
### 4. Create a Host
141+
142+
The `inmemory` provider has no cloud LoadBalancer, so set the target explicitly with the
143+
`external-dns.kubernetes.io/target` annotation. The `external-dns.ambassador-service`
144+
annotation is still required for the `Host` to be processed.
145+
146+
```bash
147+
kubectl apply -f - <<EOF
148+
apiVersion: getambassador.io/v3alpha1
149+
kind: Host
150+
metadata:
151+
name: my-host
152+
namespace: default
153+
annotations:
154+
external-dns.ambassador-service: emissary/emissary-ingress
155+
external-dns.kubernetes.io/target: 203.0.113.10
156+
spec:
157+
hostname: my-host.example.com
158+
acmeProvider:
159+
authority: none
160+
EOF
161+
```
162+
163+
### 5. Verify
164+
165+
```bash
166+
kubectl logs -l app=external-dns -f
167+
```
168+
169+
You should see ExternalDNS pick up the `Host` and create an A record for
170+
`my-host.example.com` pointing at `203.0.113.10`:
171+
172+
```text
173+
... level=debug msg="Endpoints generated from Host: default/my-host: [my-host.example.com 0 IN A 203.0.113.10 []]"
174+
... level=info msg="CREATE: my-host.example.com 0 IN A 203.0.113.10 []"
175+
```
176+
177+
With a real provider and a `LoadBalancer` Service, drop the `target` annotation and point
178+
`external-dns.ambassador-service` at the Emissary-ingress Service
179+
(`emissary/emissary-ingress` in the manifests above); ExternalDNS resolves the Service's
180+
external address as the target.
181+
182+
### Cleanup
183+
184+
```bash
185+
kind delete cluster --name external-dns-ambassador
186+
```

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ require (
2424
github.com/cenkalti/backoff/v5 v5.0.3
2525
github.com/civo/civogo v0.7.2
2626
github.com/cloudflare/cloudflare-go/v5 v5.1.0
27-
github.com/datawire/ambassador v1.12.4
2827
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
2928
github.com/dnsimple/dnsimple-go v1.7.0
29+
github.com/emissary-ingress/emissary/v3 v3.10.0
3030
github.com/exoscale/egoscale/v3 v3.1.37
3131
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
3232
github.com/go-gandi/go-gandi v0.7.0
@@ -131,7 +131,6 @@ require (
131131
github.com/google/s2a-go v0.1.9 // indirect
132132
github.com/googleapis/enterprise-certificate-proxy v0.3.16 // indirect
133133
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
134-
github.com/gopherjs/gopherjs v1.17.2 // indirect
135134
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
136135
github.com/hashicorp/errwrap v1.1.0 // indirect
137136
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect

0 commit comments

Comments
 (0)