Skip to content

Commit

Permalink
Import external dns plan package and txt registry
Browse files Browse the repository at this point in the history
Move the external-dns plan package into this repo along with the txt
registry.

To ease development we will initially just work off a copy of the
relevant external-dns code in this repo. When we have a solution that
works for us we will look into how we can submit that back to
external-dns.

Code is copied, unmodified where possible, from the v0.14.1 version of
external-dns. https://github.com/kubernetes-sigs/external-dns/tree/v0.14.1
  • Loading branch information
mikenairn committed Mar 28, 2024
1 parent b11e452 commit d7379a9
Show file tree
Hide file tree
Showing 14 changed files with 4,407 additions and 8 deletions.
4 changes: 3 additions & 1 deletion api/v1alpha1/dnsrecord_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
externaldns "sigs.k8s.io/external-dns/endpoint"
externaldnsprovider "sigs.k8s.io/external-dns/provider"
externaldnsregistry "sigs.k8s.io/external-dns/registry"

"github.com/kuadrant/dns-operator/internal/external-dns/registry"
)

// DNSRecordSpec defines the desired state of DNSRecord
Expand Down Expand Up @@ -154,7 +156,7 @@ func (s *DNSRecord) Validate() error {

func (s *DNSRecord) GetRegistry(provider externaldnsprovider.Provider, managedDNSRecordTypes, excludeDNSRecordTypes []string) (externaldnsregistry.Registry, error) {
if s.Spec.OwnerID != nil {
return externaldnsregistry.NewTXTRegistry(provider, txtRegistryPrefix, txtRegistrySuffix, *s.Spec.OwnerID, txtRegistryCacheInterval,
return registry.NewTXTRegistry(provider, txtRegistryPrefix, txtRegistrySuffix, *s.Spec.OwnerID, txtRegistryCacheInterval,
txtRegistryWildcardReplacement, managedDNSRecordTypes, excludeDNSRecordTypes, txtRegistryEncryptEnabled, []byte(txtRegistryEncryptAESKey))
} else {
return externaldnsregistry.NewNoopRegistry(provider)
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ go 1.21
require (
github.com/aws/aws-sdk-go v1.44.311
github.com/go-logr/logr v1.3.0
github.com/google/uuid v1.3.1
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
github.com/prometheus/client_golang v1.17.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
google.golang.org/api v0.134.0
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
Expand Down Expand Up @@ -48,7 +51,6 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20221212185716-aee1124e3a93 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
Expand All @@ -64,11 +66,11 @@ require (
github.com/openshift/api v0.0.0-20230607130528-611114dca681 // indirect
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/projectcontour/contour v1.25.2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
Expand Down Expand Up @@ -104,8 +106,6 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)

replace sigs.k8s.io/external-dns => github.com/kuadrant/external-dns v0.0.0-20240315162317-073094ed9bea

// To Update with changes from v0.14.0_kuadrant run:
// go mod edit --replace sigs.k8s.io/external-dns=github.com/kuadrant/[email protected]_kuadrant
// go mod tidy
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,6 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kuadrant/external-dns v0.0.0-20240315162317-073094ed9bea h1:Ob3/nd2gCnlM1aa6YyKezQlmcUnBKT6zsS4l7FP7j6E=
github.com/kuadrant/external-dns v0.0.0-20240315162317-073094ed9bea/go.mod h1:d4Knr/BFz8U1Lc6yLhCzTRP6nJOz6fqR/MnqqJPcIlU=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
Expand Down Expand Up @@ -1171,6 +1169,8 @@ sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gE
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/controller-tools v0.3.1-0.20200517180335-820a4a27ea84/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
sigs.k8s.io/external-dns v0.14.0 h1:pgY3DdyoBei+ej1nyZUzRt9ECm9RRwb9s6/CPWe51tc=
sigs.k8s.io/external-dns v0.14.0/go.mod h1:d4Knr/BFz8U1Lc6yLhCzTRP6nJOz6fqR/MnqqJPcIlU=
sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ=
sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/dnsrecord_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
externaldnsendpoint "sigs.k8s.io/external-dns/endpoint"
externaldnsplan "sigs.k8s.io/external-dns/plan"
externaldnsprovider "sigs.k8s.io/external-dns/provider"

"github.com/kuadrant/dns-operator/api/v1alpha1"
"github.com/kuadrant/dns-operator/internal/common/conditions"
externaldnsplan "github.com/kuadrant/dns-operator/internal/external-dns/plan"
"github.com/kuadrant/dns-operator/internal/provider"
)

Expand Down
125 changes: 125 additions & 0 deletions internal/external-dns/plan/conflict.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plan

import (
"sort"

log "github.com/sirupsen/logrus"

"sigs.k8s.io/external-dns/endpoint"
)

// ConflictResolver is used to make a decision in case of two or more different kubernetes resources
// are trying to acquire same DNS name
type ConflictResolver interface {
ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint
ResolveUpdate(current *endpoint.Endpoint, candidates []*endpoint.Endpoint) *endpoint.Endpoint
ResolveRecordTypes(key planKey, row *planTableRow) map[string]*domainEndpoints
}

// PerResource allows only one resource to own a given dns name
type PerResource struct{}

// ResolveCreate is invoked when dns name is not owned by any resource
// ResolveCreate takes "minimal" (string comparison of Target) endpoint to acquire the DNS record
func (s PerResource) ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint {
var min *endpoint.Endpoint
for _, ep := range candidates {
if min == nil || s.less(ep, min) {
min = ep
}
}
return min
}

// ResolveUpdate is invoked when dns name is already owned by "current" endpoint
// ResolveUpdate uses "current" record as base and updates it accordingly with new version of same resource
// if it doesn't exist then pick min
func (s PerResource) ResolveUpdate(current *endpoint.Endpoint, candidates []*endpoint.Endpoint) *endpoint.Endpoint {
currentResource := current.Labels[endpoint.ResourceLabelKey] // resource which has already acquired the DNS
// TODO: sort candidates only needed because we can still have two endpoints from same resource here. We sort for consistency
// TODO: remove once single endpoint can have multiple targets
sort.SliceStable(candidates, func(i, j int) bool {
return s.less(candidates[i], candidates[j])
})
for _, ep := range candidates {
if ep.Labels[endpoint.ResourceLabelKey] == currentResource {
return ep
}
}
return s.ResolveCreate(candidates)
}

// ResolveRecordTypes attempts to detect and resolve record type conflicts in desired
// endpoints for a domain. For eample if the there is more than 1 candidate and at lease one
// of them is a CNAME. Per [RFC 1034 3.6.2] domains that contain a CNAME can not contain any
// other record types. The default policy will prefer A and AAAA record types when a conflict is
// detected (consistent with [endpoint.Targets.Less]).
//
// [RFC 1034 3.6.2]: https://datatracker.ietf.org/doc/html/rfc1034#autoid-15
func (s PerResource) ResolveRecordTypes(key planKey, row *planTableRow) map[string]*domainEndpoints {
// no conflicts if only a single desired record type for the domain
if len(row.candidates) <= 1 {
return row.records
}

cname := false
other := false
for _, c := range row.candidates {
if c.RecordType == endpoint.RecordTypeCNAME {
cname = true
} else {
other = true
}

if cname && other {
break
}
}

// conflict was found, remove candiates of non-preferred record types
if cname && other {
log.Infof("Domain %s contains conflicting record type candidates; discarding CNAME record", key.dnsName)
records := map[string]*domainEndpoints{}
for recordType, recs := range row.records {
// policy is to prefer the non-CNAME record types when a conflict is found
if recordType == endpoint.RecordTypeCNAME {
// discard candidates of conflicting records
// keep currect so they can be deleted
records[recordType] = &domainEndpoints{
current: recs.current,
candidates: []*endpoint.Endpoint{},
}
} else {
records[recordType] = recs
}
}

return records
}

// no conflict, return all records types
return row.records
}

// less returns true if endpoint x is less than y
func (s PerResource) less(x, y *endpoint.Endpoint) bool {
return x.Targets.IsLess(y.Targets)
}

// TODO: with cross-resource/cross-cluster setup alternative variations of ConflictResolver can be used
Loading

0 comments on commit d7379a9

Please sign in to comment.