Skip to content

Commit 606c6c1

Browse files
Jonas WeberJonas Weber
authored andcommitted
Fix ownership tracking with noop registry
1 parent 209cb89 commit 606c6c1

File tree

3 files changed

+106
-61
lines changed

3 files changed

+106
-61
lines changed

cmd/webhook/main.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
package main
22

33
import (
4+
"external-dns-openstack-webhook/internal/designate/provider"
5+
"external-dns-openstack-webhook/internal/metrics"
6+
"flag"
47
"net"
58
"net/http"
69

710
log "github.com/sirupsen/logrus"
811

9-
"external-dns-openstack-webhook/internal/designate/provider"
10-
"external-dns-openstack-webhook/internal/metrics"
11-
1212
"github.com/prometheus/client_golang/prometheus/promhttp"
1313
"sigs.k8s.io/external-dns/endpoint"
1414
"sigs.k8s.io/external-dns/provider/webhook/api"
1515
)
1616

17-
const (
18-
webhookServerAddr = "127.0.0.1:8888"
19-
statusServerAddr = "0.0.0.0:8080"
20-
)
21-
2217
func main() {
18+
var webhookServerAddr string
19+
var statusServerAddr string
20+
var ownerID string
21+
var dryRun bool
22+
flag.StringVar(&webhookServerAddr, "webhook-address", "127.0.0.1:8888", "Address for webhook to listen on")
23+
flag.StringVar(&statusServerAddr, "status-address", "0.0.0.0:8080", "Address for status server to listen on")
24+
flag.StringVar(&ownerID, "owner-id", "development", "Owner ID to apply instead of TXT registry.")
25+
flag.BoolVar(&dryRun, "dry-run", false, "Do not actually change anything")
26+
flag.Parse()
27+
2328
log.SetLevel(log.DebugLevel)
2429

2530
startedChan := make(chan struct{})
@@ -56,7 +61,7 @@ func main() {
5661
}()
5762

5863
epf := endpoint.NewDomainFilter([]string{})
59-
dp, err := provider.NewDesignateProvider(*epf, false)
64+
dp, err := provider.NewDesignateProvider(*epf, dryRun, ownerID)
6065
if err != nil {
6166
log.Fatalf("NewDesignateProvider: %v", err)
6267
metrics.OpenstackConnectionMetric.Set(0)

internal/designate/provider/provider.go

Lines changed: 91 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package provider
1919

2020
import (
2121
"context"
22+
"errors"
2223
"external-dns-openstack-webhook/internal/designate/client"
2324
"fmt"
2425
"strings"
@@ -52,10 +53,12 @@ type designateProvider struct {
5253
// only consider hosted zones managing domains ending in this suffix
5354
domainFilter endpoint.DomainFilter
5455
dryRun bool
56+
57+
ownerID string
5558
}
5659

5760
// NewDesignateProvider is a factory function for OpenStack designate providers
58-
func NewDesignateProvider(domainFilter endpoint.DomainFilter, dryRun bool) (provider.Provider, error) {
61+
func NewDesignateProvider(domainFilter endpoint.DomainFilter, dryRun bool, ownerID string) (provider.Provider, error) {
5962
client, err := client.NewDesignateClient()
6063
if err != nil {
6164
return nil, err
@@ -64,6 +67,7 @@ func NewDesignateProvider(domainFilter endpoint.DomainFilter, dryRun bool) (prov
6467
client: client,
6568
domainFilter: domainFilter,
6669
dryRun: dryRun,
70+
ownerID: ownerID,
6771
}, nil
6872
}
6973

@@ -156,10 +160,25 @@ func (p designateProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, e
156160
}
157161

158162
ep := endpoint.NewEndpointWithTTL(recordSet.Name, recordSet.Type, endpoint.TTL(recordSet.TTL), recordSet.Records...)
159-
ep.Labels[designateRecordSetID] = recordSet.ID
160-
ep.Labels[designateZoneID] = recordSet.ZoneID
161-
ep.Labels[designateOriginalRecords] = strings.Join(recordSet.Records, "\000")
162-
result = append(result, ep)
163+
164+
parsedLabels, err := endpoint.NewLabelsFromStringPlain(recordSet.Description)
165+
if err != nil && !errors.Is(err, endpoint.ErrInvalidHeritage) {
166+
return fmt.Errorf("unable to parse label: %w", err)
167+
}
168+
for k, v := range parsedLabels {
169+
ep.WithLabel(k, v)
170+
}
171+
172+
ep.
173+
WithLabel(designateRecordSetID, recordSet.ID).
174+
WithLabel(designateZoneID, recordSet.ZoneID).
175+
WithLabel(designateOriginalRecords, strings.Join(recordSet.Records, "\000"))
176+
177+
if ep.IsOwnedBy(p.ownerID) {
178+
result = append(result, ep)
179+
} else {
180+
log.Debugf("SKIP %s/%s due to owner not matching %s", recordSet.Name, recordSet.Type, p.ownerID)
181+
}
163182

164183
return nil
165184
},
@@ -174,7 +193,7 @@ func (p designateProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, e
174193

175194
func (p designateProvider) supportedRecordType(recordType string) bool {
176195
switch recordType {
177-
case endpoint.RecordTypeA, endpoint.RecordTypeTXT, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS, endpoint.RecordTypeMX:
196+
case endpoint.RecordTypeA, endpoint.RecordTypeTXT, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS, endpoint.RecordTypeMX, "CAA":
178197
return true
179198
default:
180199
return false
@@ -189,70 +208,63 @@ type recordSet struct {
189208
recordSetID string
190209
ttl int
191210
names map[string]bool
211+
description string
192212
}
193213

194214
// adds endpoint into recordset aggregation, loading original values from endpoint labels first
195215
func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, oldEndpoints []*endpoint.Endpoint, delete bool) {
196216
key := fmt.Sprintf("%s/%s", ep.DNSName, ep.RecordType)
217+
197218
rs := recordSets[key]
198219
if rs == nil {
220+
recordedLabels := endpoint.NewLabels()
221+
222+
// Filter out our internal labels
223+
for k, v := range ep.Labels {
224+
switch k {
225+
case designateRecordSetID, designateZoneID, designateOriginalRecords:
226+
default:
227+
recordedLabels[k] = v
228+
}
229+
}
199230
rs = &recordSet{
200-
dnsName: canonicalizeDomainName(ep.DNSName),
201-
recordType: ep.RecordType,
202-
names: make(map[string]bool),
231+
dnsName: canonicalizeDomainName(ep.DNSName),
232+
recordType: ep.RecordType,
233+
names: make(map[string]bool),
234+
description: recordedLabels.SerializePlain(false),
203235
}
204236
}
205237

206-
addDesignateIDLabelsFromExistingEndpoints(oldEndpoints, ep)
207-
208-
if rs.zoneID == "" {
209-
rs.zoneID = ep.Labels[designateZoneID]
238+
if zoneID, ok := ep.Labels[designateZoneID]; ok {
239+
rs.zoneID = zoneID
210240
}
211-
if rs.recordSetID == "" {
212-
rs.recordSetID = ep.Labels[designateRecordSetID]
241+
if recordSetID, ok := ep.Labels[designateRecordSetID]; ok {
242+
rs.recordSetID = recordSetID
213243
}
214-
rs.ttl = int(ep.RecordTTL)
215-
for _, rec := range strings.Split(ep.Labels[designateOriginalRecords], "\000") {
216-
if _, ok := rs.names[rec]; !ok && rec != "" {
217-
rs.names[rec] = true
244+
if origRecords, ok := ep.Labels[designateOriginalRecords]; ok {
245+
for _, rec := range strings.Split(origRecords, "\000") {
246+
if _, ok := rs.names[rec]; !ok && rec != "" {
247+
rs.names[rec] = true
248+
}
218249
}
219250
}
251+
rs.ttl = int(ep.RecordTTL)
252+
220253
targets := ep.Targets
221254
if ep.RecordType == endpoint.RecordTypeCNAME || ep.RecordType == endpoint.RecordTypeNS {
222255
targets = canonicalizeDomainNames(targets)
223256
}
257+
224258
if ep.RecordType == endpoint.RecordTypeMX {
225259
targets = canonicalizeDomainNamesForMX(targets)
226260
}
261+
227262
for _, t := range targets {
228263
rs.names[t] = !delete
229264
}
230265
recordSets[key] = rs
231266
}
232267

233-
// addDesignateIDLabelsFromExistingEndpoints adds the labels identified by the constants designateZoneID and designateRecordSetID
234-
// to an Endpoint. Therefore, it searches all given existing endpoints for an endpoint with the same record type and record
235-
// value. If the given Endpoint already has the labels set, they are left untouched. This fixes an issue with the
236-
// TXTRegistry which generates new TXT entries instead of updating the old ones.
237-
func addDesignateIDLabelsFromExistingEndpoints(existingEndpoints []*endpoint.Endpoint, ep *endpoint.Endpoint) {
238-
_, hasZoneIDLabel := ep.Labels[designateZoneID]
239-
_, hasRecordSetIDLabel := ep.Labels[designateRecordSetID]
240-
if hasZoneIDLabel && hasRecordSetIDLabel {
241-
return
242-
}
243-
for _, oep := range existingEndpoints {
244-
if ep.RecordType == oep.RecordType && ep.DNSName == oep.DNSName {
245-
if !hasZoneIDLabel {
246-
ep.Labels[designateZoneID] = oep.Labels[designateZoneID]
247-
}
248-
if !hasRecordSetIDLabel {
249-
ep.Labels[designateRecordSetID] = oep.Labels[designateRecordSetID]
250-
}
251-
return
252-
}
253-
}
254-
}
255-
256268
// ApplyChanges applies a given set of changes in a given zone.
257269
func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
258270
managedZones, err := p.getZones(ctx)
@@ -265,17 +277,25 @@ func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Chang
265277
return fmt.Errorf("failed to fetch active records: %w", err)
266278
}
267279

280+
for _, ep := range changes.Create {
281+
log.Debugf("CREATE %v", ep)
282+
}
283+
268284
recordSets := map[string]*recordSet{}
285+
286+
logPlan(changes)
287+
269288
for _, ep := range changes.Create {
289+
ep.Labels[endpoint.OwnerLabelKey] = p.ownerID
270290
addEndpoint(ep, recordSets, endpoints, false)
271291
}
272-
for _, ep := range changes.UpdateOld {
292+
for _, ep := range endpoint.FilterEndpointsByOwnerID(p.ownerID, changes.UpdateOld) {
273293
addEndpoint(ep, recordSets, endpoints, true)
274294
}
275-
for _, ep := range changes.UpdateNew {
295+
for _, ep := range endpoint.FilterEndpointsByOwnerID(p.ownerID, changes.UpdateNew) {
276296
addEndpoint(ep, recordSets, endpoints, false)
277297
}
278-
for _, ep := range changes.Delete {
298+
for _, ep := range endpoint.FilterEndpointsByOwnerID(p.ownerID, changes.Delete) {
279299
addEndpoint(ep, recordSets, endpoints, true)
280300
}
281301

@@ -287,6 +307,24 @@ func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Chang
287307
return err
288308
}
289309

310+
func logPlan(plan *plan.Changes) {
311+
infos := []struct {
312+
name string
313+
list []*endpoint.Endpoint
314+
}{
315+
{"CREATE", plan.Create},
316+
{"UPDOLD", plan.UpdateOld},
317+
{"UPDNEW", plan.UpdateNew},
318+
{"DELETE", plan.Delete},
319+
}
320+
321+
for _, i := range infos {
322+
for _, ep := range i.list {
323+
log.Debugf("%s %v", i.name, *ep)
324+
}
325+
}
326+
}
327+
290328
// apply recordset changes by inserting/updating/deleting recordsets
291329
func (p designateProvider) upsertRecordSet(ctx context.Context, rs *recordSet, managedZones map[string]string) error {
292330
if rs.zoneID == "" {
@@ -307,10 +345,11 @@ func (p designateProvider) upsertRecordSet(ctx context.Context, rs *recordSet, m
307345
}
308346
if rs.recordSetID == "" {
309347
opts := recordsets.CreateOpts{
310-
Name: rs.dnsName,
311-
Type: rs.recordType,
312-
Records: records,
313-
TTL: rs.ttl,
348+
Name: rs.dnsName,
349+
Type: rs.recordType,
350+
Records: records,
351+
TTL: rs.ttl,
352+
Description: rs.description,
314353
}
315354
log.Infof("Creating records: %s/%s: %s", rs.dnsName, rs.recordType, strings.Join(records, ","))
316355
if p.dryRun {
@@ -326,8 +365,9 @@ func (p designateProvider) upsertRecordSet(ctx context.Context, rs *recordSet, m
326365
return p.client.DeleteRecordSet(ctx, rs.zoneID, rs.recordSetID)
327366
} else {
328367
opts := recordsets.UpdateOpts{
329-
Records: records,
330-
TTL: &rs.ttl,
368+
Records: records,
369+
TTL: &rs.ttl,
370+
Description: &rs.description,
331371
}
332372
log.Infof("Updating records: %s/%s: %s", rs.dnsName, rs.recordType, strings.Join(records, ","))
333373
if p.dryRun {

internal/designate/provider/provider_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ clouds:
206206
os.Setenv("OS_CLOUD", "unittest")
207207
os.Setenv("OS_CACERT", tmpfile.Name())
208208

209-
if _, err := NewDesignateProvider(endpoint.DomainFilter{}, true); err != nil {
209+
if _, err := NewDesignateProvider(endpoint.DomainFilter{}, true, "dev"); err != nil {
210210
t.Fatalf("Failed to initialize Designate provider: %s", err)
211211
}
212212
}

0 commit comments

Comments
 (0)