Skip to content

Commit d3b77e9

Browse files
committed
feat(coredns): add annotations for coredns groups
Signed-off-by: Jan Jansen <jan.jansen@gdata.de>
1 parent 413015e commit d3b77e9

File tree

6 files changed

+147
-2
lines changed

6 files changed

+147
-2
lines changed

docs/tutorials/coredns.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,78 @@ dnstools# dig @10.100.4.143 nginx.example.org +short
260260
10.0.2.15
261261
dnstools#
262262
```
263+
264+
## Specific service annotation options
265+
266+
### Groups
267+
268+
Groups can be used to group set of services together. The main use of this is to limit recursion,
269+
i.e. don't return all records, but only a subset. Let's say we have a configuration like this:
270+
271+
```yaml
272+
apiVersion: v1
273+
kind: Service
274+
metadata:
275+
name: a
276+
annotations:
277+
external-dns.alpha.kubernetes.io/hostname: a.domain.local
278+
external-dns.alpha.kubernetes.io/coredns-group: "g1"
279+
spec:
280+
type: LoadBalancer
281+
...
282+
status:
283+
loadBalancer:
284+
ingress:
285+
- ip: 127.0.0.1
286+
---
287+
apiVersion: v1
288+
kind: Service
289+
metadata:
290+
name: b
291+
annotations:
292+
external-dns.alpha.kubernetes.io/hostname: b.domain.local
293+
external-dns.alpha.kubernetes.io/coredns-group: "g1"
294+
spec:
295+
type: LoadBalancer
296+
...
297+
status:
298+
loadBalancer:
299+
ingress:
300+
- ip: 127.0.0.2
301+
---
302+
apiVersion: v1
303+
kind: Service
304+
metadata:
305+
name: c
306+
annotations:
307+
external-dns.alpha.kubernetes.io/hostname: c.subdom.domain.local
308+
external-dns.alpha.kubernetes.io/coredns-group: "g2"
309+
spec:
310+
type: LoadBalancer
311+
...
312+
status:
313+
loadBalancer:
314+
ingress:
315+
- ip: 127.0.0.3
316+
---
317+
apiVersion: v1
318+
kind: Service
319+
metadata:
320+
name: d
321+
annotations:
322+
external-dns.alpha.kubernetes.io/hostname: d.subdom.domain.local
323+
external-dns.alpha.kubernetes.io/coredns-group: "g2"
324+
spec:
325+
type: LoadBalancer
326+
...
327+
status:
328+
loadBalancer:
329+
ingress:
330+
- ip: 127.0.0.4
331+
332+
```
333+
334+
And we want domain.local to return (127.0.0.1 and 127.0.0.2) and subdom.domain.local to return (127.0.0.3 and 127.0.0.4).
335+
For this the two domains, need to be in different groups. What those groups are does not matter,
336+
as long as a and b belong to the same group which is different from the group c and d belong to.
337+
If a service is found without a group it is always included.

provider/coredns/coredns.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ const (
4141
priority = 10 // default priority when nothing is set
4242
etcdTimeout = 5 * time.Second
4343

44-
randomPrefixLabel = "prefix"
44+
randomPrefixLabel = "prefix"
45+
providerSpecificGroup = "coredns/group"
4546
)
4647

4748
// coreDNSClient is an interface to work with CoreDNS service records in etcd
@@ -260,6 +261,9 @@ func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error
260261
endpoint.TTL(service.TTL),
261262
service.Host,
262263
)
264+
if service.Group != "" {
265+
ep.WithProviderSpecific(providerSpecificGroup, service.Group)
266+
}
263267
log.Debugf("Creating new ep (%s) with new service host (%s)", ep, service.Host)
264268
}
265269
ep.Labels["originalText"] = service.Text
@@ -346,12 +350,17 @@ func (p coreDNSProvider) createServicesForEndpoint(dnsName string, ep *endpoint.
346350
prefix = fmt.Sprintf("%08x", rand.Int31())
347351
log.Infof("Generating new prefix: (%s)", prefix)
348352
}
353+
group := ""
354+
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificGroup); ok {
355+
group = prop
356+
}
349357
service := Service{
350358
Host: target,
351359
Text: ep.Labels["originalText"],
352360
Key: p.etcdKeyFor(prefix + "." + dnsName),
353361
TargetStrip: strings.Count(prefix, ".") + 1,
354362
TTL: uint32(ep.RecordTTL),
363+
Group: group,
355364
}
356365
services = append(services, &service)
357366
ep.Labels[target] = prefix

provider/coredns/coredns_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ func validateServices(services map[string]Service, expectedServices map[string][
460460
}
461461
found := false
462462
for i, expectedServiceEntry := range expectedServiceEntries {
463-
if value.Host == expectedServiceEntry.Host && value.Text == expectedServiceEntry.Text {
463+
if value.Host == expectedServiceEntry.Host && value.Text == expectedServiceEntry.Text && value.Group == expectedServiceEntry.Group {
464464
expectedServiceEntries = append(expectedServiceEntries[:i], expectedServiceEntries[i+1:]...)
465465
found = true
466466
break
@@ -863,3 +863,48 @@ func TestCoreDNSProvider_updateTXTRecords_ClearsExtraText(t *testing.T) {
863863
assert.Equal(t, "txt-value", services[0].Text)
864864
assert.Empty(t, services[1].Text)
865865
}
866+
867+
func TestApplyChangesAWithGroupServiceTranslation(t *testing.T) {
868+
client := fakeETCDClient{
869+
map[string]Service{},
870+
}
871+
coredns := coreDNSProvider{
872+
client: client,
873+
coreDNSPrefix: defaultCoreDNSPrefix,
874+
}
875+
876+
changes1 := &plan.Changes{
877+
Create: []*endpoint.Endpoint{
878+
endpoint.NewEndpoint("domain1.local", endpoint.RecordTypeA, "5.5.5.5").WithProviderSpecific(providerSpecificGroup, "test1"),
879+
endpoint.NewEndpoint("domain2.local", endpoint.RecordTypeA, "5.5.5.6").WithProviderSpecific(providerSpecificGroup, "test1"),
880+
endpoint.NewEndpoint("domain3.local", endpoint.RecordTypeA, "5.5.5.7").WithProviderSpecific(providerSpecificGroup, "test2"),
881+
},
882+
}
883+
coredns.ApplyChanges(context.Background(), changes1)
884+
885+
expectedServices1 := map[string][]*Service{
886+
"/skydns/local/domain1": {{Host: "5.5.5.5", Group: "test1"}},
887+
"/skydns/local/domain2": {{Host: "5.5.5.6", Group: "test1"}},
888+
"/skydns/local/domain3": {{Host: "5.5.5.7", Group: "test2"}},
889+
}
890+
validateServices(client.services, expectedServices1, t, 1)
891+
}
892+
893+
func TestRecordsAWithGroupServiceTranslation(t *testing.T) {
894+
client := fakeETCDClient{
895+
map[string]Service{
896+
"/skydns/local/domain1": {Host: "5.5.5.5", Group: "test1"},
897+
},
898+
}
899+
coredns := coreDNSProvider{
900+
client: client,
901+
coreDNSPrefix: defaultCoreDNSPrefix,
902+
}
903+
endpoints, err := coredns.Records(context.Background())
904+
require.NoError(t, err)
905+
if prop, ok := endpoints[0].GetProviderSpecificProperty(providerSpecificGroup); !ok {
906+
t.Error("go no Group name")
907+
} else if prop != "test1" {
908+
t.Errorf("got unexpected Group name: %s != %s", prop, "test1")
909+
}
910+
}

source/annotations/annotations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
CloudflareRecordCommentKey = AnnotationKeyPrefix + "cloudflare-record-comment"
3030

3131
AWSPrefix = AnnotationKeyPrefix + "aws-"
32+
CoreDNSPrefix = AnnotationKeyPrefix + "coredns-"
3233
SCWPrefix = AnnotationKeyPrefix + "scw-"
3334
WebhookPrefix = AnnotationKeyPrefix + "webhook-"
3435
CloudflarePrefix = AnnotationKeyPrefix + "cloudflare-"

source/annotations/provider_specific.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid
4949
Name: fmt.Sprintf("webhook/%s", attr),
5050
Value: v,
5151
})
52+
} else if attr, ok := strings.CutPrefix(k, CoreDNSPrefix); ok {
53+
providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{
54+
Name: fmt.Sprintf("coredns/%s", attr),
55+
Value: v,
56+
})
5257
} else if strings.HasPrefix(k, CloudflarePrefix) {
5358
if strings.Contains(k, CloudflareCustomHostnameKey) {
5459
providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{

source/annotations/provider_specific_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ func TestProviderSpecificAnnotations(t *testing.T) {
6464
},
6565
setIdentifier: "",
6666
},
67+
{
68+
name: "CoreDNS annotation",
69+
annotations: map[string]string{
70+
"external-dns.alpha.kubernetes.io/coredns-group": "g1",
71+
},
72+
expected: endpoint.ProviderSpecific{
73+
{Name: "coredns/group", Value: "g1"},
74+
},
75+
setIdentifier: "",
76+
},
6777
{
6878
name: "Set identifier annotation",
6979
annotations: map[string]string{

0 commit comments

Comments
 (0)