Skip to content

Commit

Permalink
Merge pull request #103 from Kuadrant/add_inmemory_provider
Browse files Browse the repository at this point in the history
Add inmemory provider
  • Loading branch information
mikenairn authored May 2, 2024
2 parents 5e0ebad + 57d0cab commit e7d258b
Show file tree
Hide file tree
Showing 10 changed files with 1,183 additions and 35 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ test-unit: manifests generate fmt vet ## Run unit tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -tags=unit -coverprofile cover-unit.out

.PHONY: test-integration
test-integration: manifests generate fmt vet envtest ## Run integration tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./internal/controller... -tags=integration -coverprofile cover-integration.out
test-integration: manifests generate fmt vet envtest ginkgo ## Run integration tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -tags=integration ./internal/controller -coverprofile cover-integration.out

.PHONY: test-e2e
test-e2e: ginkgo
Expand Down
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/kuadrant/dns-operator/internal/provider"
_ "github.com/kuadrant/dns-operator/internal/provider/aws"
_ "github.com/kuadrant/dns-operator/internal/provider/google"
_ "github.com/kuadrant/dns-operator/internal/provider/inmemory"
//+kubebuilder:scaffold:imports
)

Expand Down
150 changes: 141 additions & 9 deletions internal/controller/dnsrecord_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ var _ = Describe("DNSRecordReconciler", func() {

managedZone = testBuildManagedZone("mz-example-com", testNamespace, "example.com")
Expect(k8sClient.Create(ctx, managedZone)).To(Succeed())
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(managedZone.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(v1alpha1.ConditionTypeReady)),
"Status": Equal(metav1.ConditionTrue),
"ObservedGeneration": Equal(managedZone.Generation),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())

dnsRecord = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -135,8 +146,7 @@ var _ = Describe("DNSRecordReconciler", func() {
})

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

Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed())
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)
g.Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -180,33 +190,123 @@ var _ = Describe("DNSRecordReconciler", func() {
})

It("should increase write counter if fail to publish record or record is overridden", func() {
dnsRecord.Spec.Endpoints = getTestNonExistingEndpoints()
Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed())
dnsRecord = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-record-1",
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,
},
},
},
}
dnsRecord2 = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-record-2",
Namespace: testNamespace,
},
Spec: v1alpha1.DNSRecordSpec{
ManagedZoneRef: &v1alpha1.ManagedZoneReference{
Name: managedZone.Name,
},
Endpoints: []*externaldnsendpoint.Endpoint{
{
DNSName: "foo.example.com",
Targets: []string{
"127.0.0.2",
},
RecordType: "A",
SetIdentifier: "",
RecordTTL: 60,
Labels: nil,
ProviderSpecific: nil,
},
},
},
}

// should be requeue record for validation after the write attempt
By("creating dnsrecord " + dnsRecord.Name + " with endpoint dnsName: `foo.example.com` and target: `127.0.0.1`")
Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed())
By("checking dnsrecord " + dnsRecord.Name + " becomes ready")
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(v1alpha1.ConditionTypeReady)),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal("AwaitingValidation"),
"Message": Equal("Awaiting validation"),
"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())

// should be increasing write counter
By("creating dnsrecord " + dnsRecord2.Name + " with endpoint dnsName: `foo.example.com` and target: `127.0.0.2`")
Expect(k8sClient.Create(ctx, dnsRecord2)).To(Succeed())

By("checking dnsrecord " + dnsRecord.Name + " and " + dnsRecord2.Name + " conflict")
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(v1alpha1.ConditionTypeReady)),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal("AwaitingValidation"),
"Message": Equal("Awaiting validation"),
"ObservedGeneration": Equal(dnsRecord.Generation),
})),
)
g.Expect(dnsRecord.Status.WriteCounter).To(BeNumerically(">", int64(0)))

err = k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord2), dnsRecord2)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(dnsRecord2.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(v1alpha1.ConditionTypeReady)),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal("AwaitingValidation"),
"Message": Equal("Awaiting validation"),
"ObservedGeneration": Equal(dnsRecord2.Generation),
})),
)
g.Expect(dnsRecord2.Status.WriteCounter).To(BeNumerically(">", int64(0)))
}, TestTimeoutLong, time.Second).Should(Succeed())
})

It("should not allow second record to change the type", 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(v1alpha1.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())

dnsRecord2 = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2.example.com",
Expand Down Expand Up @@ -248,6 +348,22 @@ var _ = Describe("DNSRecordReconciler", func() {
})

It("should not allow owned record to update it", 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(v1alpha1.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())

dnsRecord2 = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2.example.com",
Expand Down Expand Up @@ -278,6 +394,22 @@ var _ = Describe("DNSRecordReconciler", func() {
})

It("should not allow a record to have a target that matches the root host if an endpoint doesn't exist for the target dns name", 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(v1alpha1.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())

dnsRecord2 = &v1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: "bar.example.com",
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

const (
TestTimeoutMedium = time.Second * 10
TestTimeoutMedium = time.Second * 15
TestTimeoutLong = time.Second * 30
TestRetryIntervalMedium = time.Millisecond * 250
RequeueDuration = time.Second * 6
Expand Down
31 changes: 9 additions & 22 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/sirupsen/logrus"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -39,47 +40,28 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
externaldnsendpoint "sigs.k8s.io/external-dns/endpoint"
externaldnsplan "sigs.k8s.io/external-dns/plan"

"github.com/kuadrant/dns-operator/api/v1alpha1"
"github.com/kuadrant/dns-operator/internal/provider"
_ "github.com/kuadrant/dns-operator/internal/provider/aws"
providerFake "github.com/kuadrant/dns-operator/internal/provider/fake"
_ "github.com/kuadrant/dns-operator/internal/provider/google"
"github.com/kuadrant/dns-operator/internal/provider/inmemory"
//+kubebuilder:scaffold:imports
)

// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.

var dnsProvider provider.Provider
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc
var dnsProviderFactory = &providerFake.Factory{
ProviderForFunc: func(ctx context.Context, pa v1alpha1.ProviderAccessor, c provider.Config) (provider.Provider, error) {
return &providerFake.Provider{
RecordsFunc: func(context.Context) ([]*externaldnsendpoint.Endpoint, error) {
return getTestEndpoints(), nil
},
ApplyChangesFunc: func(context.Context, *externaldnsplan.Changes) error {
return nil
},
AdjustEndpointsFunc: func(eps []*externaldnsendpoint.Endpoint) ([]*externaldnsendpoint.Endpoint, error) {
return eps, nil
},
GetDomainFilterFunc: func() externaldnsendpoint.DomainFilter {
return externaldnsendpoint.DomainFilter{}
},
EnsureManagedZoneFunc: func(zone *v1alpha1.ManagedZone) (provider.ManagedZoneOutput, error) {
return provider.ManagedZoneOutput{}, nil
},
DeleteManagedZoneFunc: func(zone *v1alpha1.ManagedZone) error {
return nil
},
}, nil
return dnsProvider, nil
},
}

Expand All @@ -92,6 +74,8 @@ func TestControllers(t *testing.T) {
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))

logrus.SetLevel(logrus.ErrorLevel)

ctx, cancel = context.WithCancel(ctrl.SetupSignalHandler())
By("bootstrapping test environment")
testEnv = &envtest.Environment{
Expand Down Expand Up @@ -135,6 +119,9 @@ var _ = BeforeSuite(func() {
}).SetupWithManager(mgr, RequeueDuration, ValidityDuration)
Expect(err).ToNot(HaveOccurred())

dnsProvider, err = inmemory.NewProviderFromSecret(ctx, &v1.Secret{}, provider.Config{})
Expect(err).ToNot(HaveOccurred())

go func() {
defer GinkgoRecover()
err = mgr.Start(ctx)
Expand Down
2 changes: 1 addition & 1 deletion internal/external-dns/plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func (p *Plan) Calculate() *Plan {
candidate := t.resolver.ResolveUpdate(records.current, records.candidates)
current := records.current.DeepCopy()
owners := []string{}
if endpointOwner, hasOwner := current.Labels[endpoint.OwnerLabelKey]; hasOwner {
if endpointOwner, hasOwner := current.Labels[endpoint.OwnerLabelKey]; hasOwner && endpointOwner != "" {
if p.OwnerID == "" {
// Only allow owned records to be updated by other owned records
errs = append(errs, fmt.Errorf("%w, cannot update endpoint '%s' with no owner when existing endpoint is already owned", ErrOwnerConflict, candidate.DNSName))
Expand Down
Loading

0 comments on commit e7d258b

Please sign in to comment.