Skip to content

Commit

Permalink
Add annotation support to DNSEndpoint custom-resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dadeos-Menlo committed Nov 26, 2024
1 parent c2041db commit b21b0ff
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 41 deletions.
2 changes: 1 addition & 1 deletion docs/annotations/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The following table documents which sources support which annotations:
| Connector | | | | | | |
| Contour | Yes | Yes[^1] | | Yes | Yes | Yes |
| CloudFoundry | | | | | | |
| CRD | | | | | | |
| CRD | | Yes | | Yes | Yes | Yes |
| F5 | | | | Yes | Yes | |
| Gateway | Yes | Yes[^1] | | Yes[^4] | Yes | Yes |
| Gloo | | | | Yes | Yes[^5] | Yes[^5] |
Expand Down
21 changes: 7 additions & 14 deletions docs/contributing/crd-source/dnsendpoint-aws-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@ apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplednsrecord
spec:
endpoints:
- dnsName: subdomain.foo.bar.com
providerSpecific:
- name: "aws-failover"
value: "PRIMARY"
- name: "aws-health-check-id"
value: "asdf1234-as12-as12-as12-asdf12345678"
- name: "aws-evaluate-target-health"
value: "true"
recordType: CNAME
setIdentifier: some-unique-id
targets:
- other-subdomain.foo.bar.com
annotations:
external-dns.alpha.kubernetes.io/hostname: "subdomain.foo.bar.com"
external-dns.alpha.kubernetes.io/target: "other-subdomain.foo.bar.com"
external-dns.alpha.kubernetes.io/set-identifier: "some-unique-id"
external-dns.alpha.kubernetes.io/aws-failover: "PRIMARY"
external-dns.alpha.kubernetes.io/aws-health-check-id: "asdf1234-as12-as12-as12-asdf12345678"
external-dns.alpha.kubernetes.io/aws-evaluate-target-health: "true"
16 changes: 5 additions & 11 deletions docs/contributing/crd-source/dnsendpoint-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplednsrecord
spec:
endpoints:
- dnsName: foo.bar.com
recordTTL: 180
recordType: A
targets:
- 192.168.99.216
# Provider specific configurations are set like an annotation would on other sources
providerSpecific:
- name: cloudflare-proxied
value: "true"
annotations:
external-dns.alpha.kubernetes.io/hostname: foo.bar.com
external-dns.alpha.kubernetes.io/target: 192.168.99.216
external-dns.alpha.kubernetes.io/ttl: "180"
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
38 changes: 27 additions & 11 deletions source/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,14 @@ func (cs *crdSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error
}

for _, dnsEndpoint := range result.Items {
resource := fmt.Sprintf("crd/%s/%s", dnsEndpoint.Namespace, dnsEndpoint.Name)
providerSpecific, setIdentifier := getProviderSpecificAnnotations(dnsEndpoint.Annotations)
ttl := getTTLFromAnnotations(dnsEndpoint.Annotations, resource)
targets := getTargetsFromTargetAnnotation(dnsEndpoint.Annotations)
for _, host := range getHostnamesFromAnnotations(dnsEndpoint.Annotations) {
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier, resource)...)
}
// Make sure that all endpoints have targets for A or CNAME type
crdEndpoints := []*endpoint.Endpoint{}
for _, ep := range dnsEndpoint.Spec.Endpoints {
if (ep.RecordType == "CNAME" || ep.RecordType == "A" || ep.RecordType == "AAAA") && len(ep.Targets) < 1 {
log.Warnf("Endpoint %s with DNSName %s has an empty list of targets", dnsEndpoint.ObjectMeta.Name, ep.DNSName)
Expand All @@ -207,13 +213,29 @@ func (cs *crdSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error
if ep.Labels == nil {
ep.Labels = endpoint.NewLabels()
}
ep.Labels[endpoint.ResourceLabelKey] = resource
if ttl.IsConfigured() {
ep.RecordTTL = ttl
}
for _, property := range providerSpecific {
present := false
for _, search := range ep.ProviderSpecific {
if property.Name == search.Name {
present = true
break
}
}
if !present {
ep.ProviderSpecific = append(ep.ProviderSpecific, property)
}
}
if setIdentifier != "" {
ep.SetIdentifier = setIdentifier
}

crdEndpoints = append(crdEndpoints, ep)
endpoints = append(endpoints, ep)
}

cs.setResourceLabel(&dnsEndpoint, crdEndpoints)
endpoints = append(endpoints, crdEndpoints...)

if dnsEndpoint.Status.ObservedGeneration == dnsEndpoint.Generation {
continue
}
Expand All @@ -229,12 +251,6 @@ func (cs *crdSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error
return endpoints, nil
}

func (cs *crdSource) setResourceLabel(crd *endpoint.DNSEndpoint, endpoints []*endpoint.Endpoint) {
for _, ep := range endpoints {
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("crd/%s/%s", crd.ObjectMeta.Namespace, crd.ObjectMeta.Name)
}
}

func (cs *crdSource) watch(ctx context.Context, opts *metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return cs.crdClient.Get().
Expand Down
79 changes: 75 additions & 4 deletions source/crd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,16 @@ func fakeRESTClient(endpoints []*endpoint.Endpoint, apiVersion, kind, namespace,
Labels: labels,
Generation: 1,
},
Spec: endpoint.DNSEndpointSpec{
Endpoints: endpoints,
},
Spec: endpoint.DNSEndpointSpec{},
}
for _, ep := range endpoints {
// Exclude internal fields of Endpoints
dnsEndpoint.Spec.Endpoints = append(dnsEndpoint.Spec.Endpoints, &endpoint.Endpoint{
DNSName: ep.DNSName,
RecordType: ep.RecordType,
RecordTTL: ep.RecordTTL,
Targets: ep.Targets,
})
}

codecFactory := serializer.WithoutConversionCodecFactory{
Expand Down Expand Up @@ -177,6 +184,65 @@ func testCRDSourceEndpoints(t *testing.T) {
expectEndpoints: false,
expectError: true,
},
{
title: "endpoint with annotations",
registeredAPIVersion: "test.k8s.io/v1alpha1",
apiVersion: "test.k8s.io/v1alpha1",
registeredKind: "DNSEndpoint",
kind: "DNSEndpoint",
namespace: "namespace",
registeredNamespace: "namespace",
annotations: map[string]string{
hostnameAnnotationKey: "example.com",
targetAnnotationKey: "1.2.3.4",
ttlAnnotationKey: "180",
annotationKeyPrefix + "property": "value",
},
endpoints: []*endpoint.Endpoint{
{
DNSName: "example.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: 180,
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "property",
Value: "value",
},
},
},
},
expectEndpoints: true,
expectError: false,
},
{
title: "endpoint with provider property",
registeredAPIVersion: "test.k8s.io/v1alpha1",
apiVersion: "test.k8s.io/v1alpha1",
registeredKind: "DNSEndpoint",
kind: "DNSEndpoint",
namespace: "namespace",
registeredNamespace: "namespace",
annotations: map[string]string{
annotationKeyPrefix + "property": "value",
},
endpoints: []*endpoint.Endpoint{
{
DNSName: "example.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: 180,
ProviderSpecific: endpoint.ProviderSpecific{
endpoint.ProviderSpecificProperty{
Name: "property",
Value: "value",
},
},
},
},
expectEndpoints: true,
expectError: false,
},
{
title: "endpoints within a specific namespace",
registeredAPIVersion: "test.k8s.io/v1alpha1",
Expand Down Expand Up @@ -472,7 +538,12 @@ func testCRDSourceEndpoints(t *testing.T) {
t.Run(ti.title, func(t *testing.T) {
t.Parallel()

restClient := fakeRESTClient(ti.endpoints, ti.registeredAPIVersion, ti.registeredKind, ti.registeredNamespace, "test", ti.annotations, ti.labels, t)
var endpoints []*endpoint.Endpoint
if _, ok := ti.annotations[hostnameAnnotationKey]; !ok {
// Only include endpoints in DNSEndpoint.Spec.Endpoints when the DNS specification is not being provided via annotations.
endpoints = ti.endpoints
}
restClient := fakeRESTClient(endpoints, ti.registeredAPIVersion, ti.registeredKind, ti.registeredNamespace, "test", ti.annotations, ti.labels, t)
groupVersion, err := schema.ParseGroupVersion(ti.apiVersion)
require.NoError(t, err)

Expand Down

0 comments on commit b21b0ff

Please sign in to comment.