Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions docs/tutorials/pdns.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,102 @@ Once the API shows the record correctly, you can double check your record using:
```bash
$ dig @${PDNS_FQDN} echo.example.com.
```

## Using CRD source to manage DNS records in PowerDNS

[CRD source](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/contributing/crd-source.md) provides a generic mechanism and declarative way to manage DNS records in PowerDNS using external-dns.

```bash
external-dns --source=crd --provider=pdns \
--pdns-server={{ pdns-api-url }} \
--pdns-api-key={{ pdns-api-key }} \
--domain-filter=example.com \
--managed-record-types=A \
--managed-record-types=CNAME \
--managed-record-types=TXT \
--managed-record-types=MX \
--managed-record-types=SRV
```

Not all the record types are enabled by default so we can enable the required record types using `--managed-record-types`.

* Example for record type `A`

```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplearecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 60
recordType: A
targets:
- 10.0.0.1
```

* Example for record type `CNAME`

```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplecnamerecord
spec:
endpoints:
- dnsName: test-a.example.com
recordTTL: 300
recordType: CNAME
targets:
- example.com
```

* Example for record type `TXT`

```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: exampletxtrecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 3600
recordType: TXT
targets:
- '"v=spf1 include:spf.protection.example.com include:example.org -all"'
- '"apple-domain-verification=XXXXXXXXXXXXX"'
```

* Example for record type `MX`

```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplemxrecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 3600
recordType: MX
targets:
- "10 mailhost1.example.com"
```

* Example for record type `SRV`

```yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplesrvrecord
spec:
endpoints:
- dnsName: _service._tls.example.com
recordTTL: 180
recordType: SRV
targets:
- "100 1 443 service.example.com"
```
2 changes: 1 addition & 1 deletion provider/pdns/pdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet
records := []pgo.Record{}
RecordType_ := ep.RecordType
for _, t := range ep.Targets {
if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" {
if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" || ep.RecordType == "MX" || ep.RecordType == "SRV" {
t = provider.EnsureTrailingDot(t)
}
records = append(records, pgo.Record{Content: t})
Expand Down
39 changes: 38 additions & 1 deletion provider/pdns/pdns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ var (
},
}

// RRSet with MX record
RRSetMXRecord = pgo.RrSet{
Name: "example.com.",
Type_: "MX",
Ttl: 300,
Records: []pgo.Record{
{Content: "10 mailhost1.example.com", Disabled: false, SetPtr: false},
{Content: "10 mailhost2.example.com", Disabled: false, SetPtr: false},
},
}

// RRSet with SRV record
RRSetSRVRecord = pgo.RrSet{
Name: "_service._tls.example.com.",
Type_: "SRV",
Ttl: 300,
Records: []pgo.Record{
{Content: "100 1 443 service.example.com", Disabled: false, SetPtr: false},
},
}

endpointsDisabledRecord = []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8"),
}
Expand Down Expand Up @@ -144,6 +165,8 @@ var (
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(300), "'would smell as sweet'"),
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8", "8.8.4.4", "4.4.4.4"),
endpoint.NewEndpointWithTTL("alias.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(300), "example.by.any.other.name.com"),
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeMX, endpoint.TTL(300), "10 mailhost1.example.com", "10 mailhost2.example.com"),
endpoint.NewEndpointWithTTL("_service._tls.example.com", endpoint.RecordTypeSRV, endpoint.TTL(300), "100 1 443 service.example.com"),
}

endpointsMultipleZones = []*endpoint.Endpoint{
Expand Down Expand Up @@ -233,7 +256,7 @@ var (
Type_: "Zone",
Url: "/api/v1/servers/localhost/zones/example.com.",
Kind: "Native",
Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord},
Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord, RRSetMXRecord, RRSetSRVRecord},
}

ZoneEmptyToSimplePatch = pgo.Zone{
Expand Down Expand Up @@ -944,6 +967,20 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSConvertEndpointsToZones() {
}
}

// Check endpoints of type MX and SRV always have their values end with a trailing dot.
zlist, err = p.ConvertEndpointsToZones(endpointsMixedRecords, PdnsReplace)
assert.Nil(suite.T(), err)

for _, z := range zlist {
for _, rs := range z.Rrsets {
if rs.Type_ == "MX" || rs.Type_ == "SRV" {
for _, r := range rs.Records {
assert.Equal(suite.T(), uint8(0x2e), r.Content[len(r.Content)-1])
}
}
}
}

// Check endpoints of type CNAME are converted to ALIAS on the domain apex
zlist, err = p.ConvertEndpointsToZones(endpointsApexRecords, PdnsReplace)
assert.Nil(suite.T(), err)
Expand Down