Skip to content

Commit

Permalink
Merge pull request #56 from mikenairn/48_use_txt_registry
Browse files Browse the repository at this point in the history
registry: Use txt registry for owned records
  • Loading branch information
mikenairn authored Mar 20, 2024
2 parents 8ea4026 + 00b6621 commit 0f0feb3
Show file tree
Hide file tree
Showing 11 changed files with 761 additions and 40 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ local-setup: $(KIND) ## Setup local development kind cluster, dependencies and o
$(KUBECTL) -n ${TEST_NAMESPACE} get managedzones
@echo "local-setup: Complete!!"

.PHONY: local-cleanup
local-cleanup: ## Delete local cluster
$(MAKE) kind-delete-cluster

.PHONY: local-deploy
local-deploy: docker-build kind-load-image ## Deploy the dns operator into local kind cluster from the current code
$(KUBECTL) config use-context kind-$(KIND_CLUSTER_NAME)
Expand Down
20 changes: 19 additions & 1 deletion api/v1alpha1/dnsrecord_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ package v1alpha1
import (
"fmt"
"strings"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
externaldns "sigs.k8s.io/external-dns/endpoint"
externaldnsprovider "sigs.k8s.io/external-dns/provider"
externaldnsregistry "sigs.k8s.io/external-dns/registry"
)

// DNSRecordSpec defines the desired state of DNSRecord
type DNSRecordSpec struct {

// OwnerID is a unique string used to identify all endpoints created by this kuadrant
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="OwnerID is immutable"
// +optional
Expand Down Expand Up @@ -108,6 +110,13 @@ const (
NSRecordType DNSRecordType = "NS"

DefaultGeo string = "default"

txtRegistryPrefix = "kuadrant-"
txtRegistrySuffix = ""
txtRegistryWildcardReplacement = "wildcard"
txtRegistryEncryptEnabled = false
txtRegistryEncryptAESKey = ""
txtRegistryCacheInterval = time.Duration(0)
)

const WildcardPrefix = "*."
Expand Down Expand Up @@ -143,6 +152,15 @@ func (s *DNSRecord) Validate() error {
return nil
}

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,
txtRegistryWildcardReplacement, managedDNSRecordTypes, excludeDNSRecordTypes, txtRegistryEncryptEnabled, []byte(txtRegistryEncryptAESKey))
} else {
return externaldnsregistry.NewNoopRegistry(provider)
}
}

func init() {
SchemeBuilder.Register(&DNSRecord{}, &DNSRecordList{})
}
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,12 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
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

// To Update for local development
// go mod edit --replace sigs.k8s.io/external-dns=/home/mnairn/go/src/github.com/kubernetes-sigs/external-dns
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@ 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 @@ -1169,8 +1171,6 @@ 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
12 changes: 7 additions & 5 deletions internal/controller/dnsrecord_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
externaldnsendpoint "sigs.k8s.io/external-dns/endpoint"
externaldnsplan "sigs.k8s.io/external-dns/plan"
externaldnsprovider "sigs.k8s.io/external-dns/provider"
externaldnsregistry "sigs.k8s.io/external-dns/registry"

"github.com/kuadrant/dns-operator/api/v1alpha1"
"github.com/kuadrant/dns-operator/internal/common/conditions"
Expand Down Expand Up @@ -81,6 +80,9 @@ func (r *DNSRecordReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
logger.Info("Removing Finalizer", "name", DNSRecordFinalizer)
controllerutil.RemoveFinalizer(dnsRecord, DNSRecordFinalizer)
if err = r.Update(ctx, dnsRecord); client.IgnoreNotFound(err) != nil {
if apierrors.IsConflict(err) {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
Expand Down Expand Up @@ -245,7 +247,10 @@ func (r *DNSRecordReconciler) applyChanges(ctx context.Context, dnsRecord *v1alp
return err
}

registry, err := externaldnsregistry.NewNoopRegistry(dnsProvider)
managedDNSRecordTypes := []string{externaldnsendpoint.RecordTypeA, externaldnsendpoint.RecordTypeAAAA, externaldnsendpoint.RecordTypeCNAME}
excludeDNSRecordTypes := []string{}

registry, err := dnsRecord.GetRegistry(dnsProvider, managedDNSRecordTypes, excludeDNSRecordTypes)
if err != nil {
return err
}
Expand All @@ -256,9 +261,6 @@ func (r *DNSRecordReconciler) applyChanges(ctx context.Context, dnsRecord *v1alp
return fmt.Errorf("unknown policy: %s", policyID)
}

managedDNSRecordTypes := []string{externaldnsendpoint.RecordTypeA, externaldnsendpoint.RecordTypeAAAA, externaldnsendpoint.RecordTypeCNAME}
excludeDNSRecordTypes := []string{}

//If we are deleting set the expected endpoints to an empty array
if isDelete {
dnsRecord.Spec.Endpoints = []*externaldnsendpoint.Endpoint{}
Expand Down
148 changes: 148 additions & 0 deletions internal/controller/dnsrecord_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//go:build integration

/*
Copyright 2024.
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 controller

import (
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
externaldnsendpoint "sigs.k8s.io/external-dns/endpoint"

"github.com/kuadrant/dns-operator/api/v1alpha1"
"github.com/kuadrant/dns-operator/internal/common/conditions"
)

var _ = Describe("DNSRecordReconciler", func() {
var dnsRecord *v1alpha1.DNSRecord
var managedZone *v1alpha1.ManagedZone
var testNamespace string

BeforeEach(func() {
CreateNamespace(&testNamespace)

managedZone = testBuildManagedZone("mz-example-com", testNamespace, "example.com")
Expect(k8sClient.Create(ctx, managedZone)).To(Succeed())

dnsRecord = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "foo.example.com",
Namespace: testNamespace,
},
Spec: v1alpha1.DNSRecordSpec{
ManagedZoneRef: &v1alpha1.ManagedZoneReference{
Name: managedZone.Name,
},
Endpoints: []*externaldnsendpoint.Endpoint{
{
DNSName: "foo.example.com",
Targets: []string{
"127.0.0.1",
},
RecordType: "A",
SetIdentifier: "",
RecordTTL: 60,
Labels: nil,
ProviderSpecific: nil,
},
},
},
}
})

AfterEach(func() {
if dnsRecord != nil {
err := k8sClient.Delete(ctx, dnsRecord)
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
}
if managedZone != nil {
err := k8sClient.Delete(ctx, managedZone)
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
}
})

It("should have ready condition with status true", func() {
Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed())
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(dnsRecord.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(conditions.ConditionTypeReady)),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal("ProviderSuccess"),
"Message": Equal("Provider ensured the dns record"),
"ObservedGeneration": Equal(dnsRecord.Generation),
})),
)
g.Expect(dnsRecord.Finalizers).To(ContainElement(DNSRecordFinalizer))
}, TestTimeoutMedium, time.Second).Should(Succeed())
})

It("should not allow ownerID to be updated once set", func() {
Expect(k8sClient.Create(ctx, dnsRecord)).To(BeNil())

Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(dnsRecord.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(conditions.ConditionTypeReady)),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal("ProviderSuccess"),
"Message": Equal("Provider ensured the dns record"),
"ObservedGeneration": Equal(dnsRecord.Generation),
})),
)
g.Expect(dnsRecord.Finalizers).To(ContainElement(DNSRecordFinalizer))
}, TestTimeoutMedium, time.Second).Should(Succeed())

//Allows updating from not owned to owned
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())

dnsRecord.Spec.OwnerID = ptr.To("foobar")
err = k8sClient.Update(ctx, dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
}, TestTimeoutMedium, time.Second).Should(Succeed())

//Does not allow ownerID to change once set
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(dnsRecord.Spec.OwnerID).To(PointTo(Equal("foobar")))

dnsRecord.Spec.OwnerID = ptr.To("foobarbaz")
err = k8sClient.Update(ctx, dnsRecord)
g.Expect(err).To(MatchError(ContainSubstring("OwnerID is immutable")))

err = k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(dnsRecord.Spec.OwnerID).To(PointTo(Equal("foobar")))
}, TestTimeoutMedium, time.Second).Should(Succeed())

})

})
48 changes: 18 additions & 30 deletions internal/controller/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
package controller

import (
"context"
"fmt"
"net/http"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1"
)

const (
Expand All @@ -17,32 +18,19 @@ const (
providerCredential = "secretname"
)

type testHealthServer struct {
Port int
}

func (s *testHealthServer) Start(ctx context.Context) error {
mux := http.NewServeMux()

endpoint := func(expectedCode int) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(expectedCode)
}
}

mux.HandleFunc("/healthy", endpoint(200))
mux.HandleFunc("/unhealthy", endpoint(500))

errCh := make(chan error)

go func() {
errCh <- http.ListenAndServe(fmt.Sprintf(":%d", s.Port), mux)
}()

select {
case <-ctx.Done():
return ctx.Err()
case err := <-errCh:
return err
func testBuildManagedZone(name, ns, domainName string) *kuadrantdnsv1alpha1.ManagedZone {
return &kuadrantdnsv1alpha1.ManagedZone{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: kuadrantdnsv1alpha1.ManagedZoneSpec{
ID: "1234",
DomainName: domainName,
Description: domainName,
SecretRef: kuadrantdnsv1alpha1.ProviderRef{
Name: "secretname",
},
},
}
}
24 changes: 24 additions & 0 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ import (
"context"
"path/filepath"
"testing"
"time"

"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -144,3 +149,22 @@ var _ = AfterSuite(func() {
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

func CreateNamespace(namespace *string) {
var generatedTestNamespace = "test-namespace-" + uuid.New().String()

nsObject := &v1.Namespace{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Namespace"},
ObjectMeta: metav1.ObjectMeta{Name: generatedTestNamespace},
}

err := k8sClient.Create(context.Background(), nsObject)
Expect(err).ToNot(HaveOccurred())

existingNamespace := &v1.Namespace{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: generatedTestNamespace}, existingNamespace)
}, time.Minute, 5*time.Second).ShouldNot(HaveOccurred())

*namespace = existingNamespace.Name
}
Loading

0 comments on commit 0f0feb3

Please sign in to comment.