-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: Add provider errors e2e specs
Adds a provider errors set of specs to the e2e test suite. The purpose of this is to test each supported provider returns a reasonable response and updates the record status appropriately in cases where a user can misconfigure a DNSRecord and cause a provider error. These mainly focus on what can be set from the DNSPolicy (geo and weight) and the intention isn't to exhaustively test all error scenarios. Added as an e2e test since this can, and will be, different on every provider. Tests also ensure that the records are not falling into a loop of constantly being updated when a provider error is updating the status.
- Loading branch information
Showing
2 changed files
with
256 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
//go:build e2e | ||
|
||
package e2e | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
"time" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
. "github.com/onsi/gomega/gstruct" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
externaldnsendpoint "sigs.k8s.io/external-dns/endpoint" | ||
|
||
"github.com/kuadrant/dns-operator/api/v1alpha1" | ||
) | ||
|
||
// Test Cases covering known provider errors that can be expected with misconfigured DNSRecord resource. | ||
var _ = Describe("DNSRecord Provider Errors", Labels{"provider_errors"}, func() { | ||
// testID is a randomly generated identifier for the test | ||
// it is used to name resources and/or namespaces so different | ||
// tests can be run in parallel in the same cluster | ||
var testID string | ||
// testDomainName generated domain for this test e.g. t-e2e-12345.e2e.hcpapps.net | ||
var testDomainName string | ||
// testHostname generated hostname for this test e.g. t-gw-mgc-12345.t-e2e-12345.e2e.hcpapps.net | ||
var testHostname string | ||
|
||
var dnsRecord *v1alpha1.DNSRecord | ||
|
||
BeforeEach(func(ctx SpecContext) { | ||
testID = "t-errors-" + GenerateName() | ||
testDomainName = strings.Join([]string{testSuiteID, testZoneDomainName}, ".") | ||
testHostname = strings.Join([]string{testID, testDomainName}, ".") | ||
}) | ||
|
||
AfterEach(func(ctx SpecContext) { | ||
if dnsRecord != nil { | ||
err := k8sClient.Delete(ctx, dnsRecord, | ||
client.PropagationPolicy(metav1.DeletePropagationForeground)) | ||
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) | ||
} | ||
}) | ||
|
||
It("correctly handles invalid geo", func(ctx SpecContext) { | ||
var validGeoCode string | ||
var expectedProviderErr string | ||
if testDNSProvider == "gcp" { | ||
//GCP | ||
expectedProviderErr = "location': 'notageocode', invalid" | ||
validGeoCode = "us-east1" | ||
} else { | ||
//AWS | ||
expectedProviderErr = "Value 'notageocode' with length = '11' is not facet-valid with respect to length '2' for type 'ContinentCode'" | ||
validGeoCode = "US" | ||
} | ||
|
||
invalidEndpoint := &externaldnsendpoint.Endpoint{ | ||
DNSName: testHostname, | ||
Targets: []string{ | ||
"foo.example.com", | ||
}, | ||
RecordType: "CNAME", | ||
RecordTTL: 300, | ||
SetIdentifier: "foo.example.com", | ||
ProviderSpecific: externaldnsendpoint.ProviderSpecific{ | ||
{ | ||
Name: "geo-code", | ||
Value: "notageocode", //invalid in all providers | ||
}, | ||
}, | ||
} | ||
testEndpoints := []*externaldnsendpoint.Endpoint{ | ||
invalidEndpoint, | ||
} | ||
|
||
dnsRecord = testBuildDNSRecord(testID, testNamespace, testManagedZoneName, "test-owner", testHostname) | ||
dnsRecord.Spec.Endpoints = testEndpoints | ||
|
||
By("creating dnsrecord " + dnsRecord.Name + " with invalid geo endpoint") | ||
err := k8sClient.Create(ctx, dnsRecord) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
By("checking " + dnsRecord.Name + " is not ready and has the expected provider error in the status") | ||
Eventually(func(g Gomega, ctx context.Context) { | ||
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), | ||
"Message": And(ContainSubstring("The DNS provider failed to ensure the record"), ContainSubstring(expectedProviderErr)), | ||
})), | ||
) | ||
}, 10*time.Second, time.Second, ctx).Should(Succeed()) | ||
|
||
By("checking dnsrecord " + dnsRecord.Name + " is not being updated repeatedly") | ||
tmpRecord := &v1alpha1.DNSRecord{} | ||
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), tmpRecord)).To(Succeed()) | ||
Consistently(func(g Gomega, ctx context.Context) { | ||
g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)).To(Succeed()) | ||
g.Expect(dnsRecord.ResourceVersion).To(Equal(tmpRecord.ResourceVersion)) | ||
}, 10*time.Second, time.Second, ctx).Should(Succeed()) | ||
|
||
By("updating dnsrecord " + dnsRecord.Name + " with valid geo endpoint") | ||
Eventually(func(g Gomega) { | ||
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
invalidEndpoint.ProviderSpecific = externaldnsendpoint.ProviderSpecific{ | ||
{ | ||
Name: "geo-code", | ||
Value: validGeoCode, //valid for provider under test | ||
}, | ||
} | ||
dnsRecord.Spec.Endpoints = testEndpoints | ||
err = k8sClient.Update(ctx, dnsRecord) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
}, TestTimeoutMedium, time.Second).Should(Succeed()) | ||
|
||
By("checking dnsrecord " + dnsRecord.Name + " no longer has provider error") | ||
Eventually(func(g Gomega, ctx context.Context) { | ||
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)), | ||
// Since this is an e2e test we have no idea how long it might take to become ready, so we can only really | ||
// check that the message is one of the expected ones if it was accepted by the provider | ||
"Message": Or(Equal("Provider ensured the dns record"), Equal("Awaiting validation")), | ||
})), | ||
) | ||
}, TestTimeoutMedium, time.Second, ctx).Should(Succeed()) | ||
|
||
}) | ||
|
||
It("correctly handles invalid weight", func(ctx SpecContext) { | ||
var expectedProviderErr string | ||
if testDNSProvider == "gcp" { | ||
//GCP | ||
expectedProviderErr = "weight': '-1.0' Reason: backendError, Message: Invalid Value" | ||
} else { | ||
//AWS | ||
expectedProviderErr = "weight' failed to satisfy constraint: Member must have value greater than or equal to 0" | ||
} | ||
validWeight := "100" | ||
|
||
invalidEndpoint := &externaldnsendpoint.Endpoint{ | ||
DNSName: testHostname, | ||
Targets: []string{ | ||
"foo.example.com", | ||
}, | ||
RecordType: "CNAME", | ||
RecordTTL: 300, | ||
SetIdentifier: "foo.example.com", | ||
ProviderSpecific: externaldnsendpoint.ProviderSpecific{ | ||
{ | ||
Name: "weight", | ||
Value: "-1", | ||
}, | ||
}, | ||
} | ||
testEndpoints := []*externaldnsendpoint.Endpoint{ | ||
invalidEndpoint, | ||
} | ||
|
||
dnsRecord = testBuildDNSRecord(testID, testNamespace, testManagedZoneName, "test-owner", testHostname) | ||
dnsRecord.Spec.Endpoints = testEndpoints | ||
|
||
By("creating dnsrecord " + dnsRecord.Name + " with invalid weight endpoint") | ||
err := k8sClient.Create(ctx, dnsRecord) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
By("checking " + dnsRecord.Name + " is not ready and has the expected provider error in the status") | ||
Eventually(func(g Gomega, ctx context.Context) { | ||
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), | ||
"Message": And(ContainSubstring("The DNS provider failed to ensure the record"), ContainSubstring(expectedProviderErr)), | ||
})), | ||
) | ||
}, 10*time.Second, time.Second, ctx).Should(Succeed()) | ||
|
||
By("checking dnsrecord " + dnsRecord.Name + " is not being updated repeatedly") | ||
tmpRecord := &v1alpha1.DNSRecord{} | ||
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), tmpRecord)).To(Succeed()) | ||
Consistently(func(g Gomega, ctx context.Context) { | ||
g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)).To(Succeed()) | ||
g.Expect(dnsRecord.ResourceVersion).To(Equal(tmpRecord.ResourceVersion)) | ||
}, 10*time.Second, time.Second, ctx).Should(Succeed()) | ||
|
||
By("updating dnsrecord " + dnsRecord.Name + " with valid weight endpoint") | ||
Eventually(func(g Gomega) { | ||
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
invalidEndpoint.ProviderSpecific = externaldnsendpoint.ProviderSpecific{ | ||
{ | ||
Name: "weight", | ||
Value: validWeight, //valid for provider under test | ||
}, | ||
} | ||
dnsRecord.Spec.Endpoints = testEndpoints | ||
err = k8sClient.Update(ctx, dnsRecord) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
}, TestTimeoutMedium, time.Second).Should(Succeed()) | ||
|
||
By("checking dnsrecord " + dnsRecord.Name + " no longer has provider error") | ||
Eventually(func(g Gomega, ctx context.Context) { | ||
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)), | ||
// Since this is an e2e test we have no idea how long it might take to become ready, so we can only really | ||
// check that the message is one of the expected ones if it was accepted by the provider | ||
"Message": Or(Equal("Provider ensured the dns record"), Equal("Awaiting validation")), | ||
})), | ||
) | ||
}, TestTimeoutMedium, time.Second, ctx).Should(Succeed()) | ||
|
||
}) | ||
|
||
}) | ||
|
||
// testBuildDNSRecord creates a valid dnsrecord with a single valid endpoint | ||
func testBuildDNSRecord(name, ns, mzName, ownerID, rootHost string) *v1alpha1.DNSRecord { | ||
return &v1alpha1.DNSRecord{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: name, | ||
Namespace: ns, | ||
}, | ||
Spec: v1alpha1.DNSRecordSpec{ | ||
OwnerID: ownerID, | ||
RootHost: rootHost, | ||
ManagedZoneRef: &v1alpha1.ManagedZoneReference{ | ||
Name: mzName, | ||
}, | ||
Endpoints: []*externaldnsendpoint.Endpoint{ | ||
{ | ||
DNSName: rootHost, | ||
Targets: []string{ | ||
"127.0.0.1", | ||
}, | ||
RecordType: "A", | ||
RecordTTL: 60, | ||
}, | ||
}, | ||
}, | ||
} | ||
} |