From 7699accf54c5fa12a3242d6879f3b96b4451ab43 Mon Sep 17 00:00:00 2001 From: Michael Nairn Date: Fri, 8 Mar 2024 15:53:57 +0000 Subject: [PATCH] e2e: Add e2e test suite Adds a new e2e test suite based on the MGC repo test suite with basic single cluster test for "simple" and "loadbalanced" type records. Adds a new make target `make test-e2e` to execute the test suite using AWS or GCP providers: ``` make test-e2e TEST_DNS_MANAGED_ZONE_NAME=dev-mz-aws TEST_DNS_ZONE_DOMAIN_NAME=mn.hcpapps.net TEST_DNS_NAMESPACE=dnstest TEST_DNS_PROVIDER=aws make test-e2e TEST_DNS_MANAGED_ZONE_NAME=dev-mz-gcp TEST_DNS_ZONE_DOMAIN_NAME=mn.google.hcpapps.net TEST_DNS_NAMESPACE=dnstest TEST_DNS_PROVIDER=gcp ``` --- Makefile | 11 ++ README.md | 15 ++ go.mod | 11 +- go.sum | 17 +- test/e2e/single_cluster_record_test.go | 213 +++++++++++++++++++++++++ test/e2e/suite_test.go | 115 +++++++++++++ 6 files changed, 370 insertions(+), 12 deletions(-) create mode 100644 test/e2e/single_cluster_record_test.go create mode 100644 test/e2e/suite_test.go diff --git a/Makefile b/Makefile index 7feecbc..087fc16 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,10 @@ test-unit: manifests generate fmt vet ## Run unit tests. 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 +.PHONY: test-e2e +test-e2e: ginkgo + $(GINKGO) -tags=e2e -v ./test/e2e + .PHONY: local-setup local-setup: DEPLOY=false local-setup: TEST_NAMESPACE=dnstest @@ -247,6 +251,7 @@ OPENSHIFT_GOIMPORTS ?= $(LOCALBIN)/openshift-goimports KIND = $(LOCALBIN)/kind ACT = $(LOCALBIN)/act YQ = $(LOCALBIN)/yq +GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions KUSTOMIZE_VERSION ?= v5.0.1 @@ -255,6 +260,7 @@ OPENSHIFT_GOIMPORTS_VERSION ?= c70783e636f2213cac683f6865d88c5edace3157 KIND_VERSION = v0.20.0 ACT_VERSION = latest YQ_VERSION := v4.34.2 +GINKGO_VERSION ?= v2.13.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. @@ -313,6 +319,11 @@ yq: $(YQ) ## Download yq locally if necessary. $(YQ): $(LOCALBIN) GOBIN=$(LOCALBIN) go install github.com/mikefarah/yq/v4@$(YQ_VERSION) +.PHONY: ginkgo +ginkgo: $(GINKGO) ## Download ginkgo locally if necessary +$(GINKGO): $(LOCALBIN) + GOBIN=$(LOCALBIN) go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION) + .PHONY: bundle bundle: manifests manifests-gen-base-csv kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. $(OPERATOR_SDK) generate kustomize manifests -q diff --git a/README.md b/README.md index 1e72d68..743d0cc 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,21 @@ kubectl logs -f deployments/dns-operator-controller-manager -n dns-operator-syst ## Development +### E2E Test Suite + +The e2e test suite can be executed against any cluster running the DNS Operator with configuration added for any supported provider. + +``` +make test-e2e TEST_DNS_MANAGED_ZONE_NAME= TEST_DNS_ZONE_DOMAIN_NAME= TEST_DNS_NAMESPACE= TEST_DNS_PROVIDER= +``` + +| Environment Variable | Description | +|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| TEST_DNS_MANAGED_ZONE_NAME | Name of the managed zone relevant for the test domain (TEST_DNS_ZONE_DOMAIN_NAME). If using local-setup Managed zones, one of [dev-mz-aws; dev-mz-gcp] | +| TEST_DNS_ZONE_DOMAIN_NAME | Domain name being used for the test, must match the domain of the managed zone (TEST_DNS_MANAGED_ZONE_NAME) | +| TEST_DNS_NAMESPACE | The namesapce to run the test in, must be the same namesapce as the TEST_DNS_MANAGED_ZONE_NAME | +| TEST_DNS_PROVIDER | DNS Provider currently being tested, one of [aws; gcp] | + ### Modifying the API definitions If you are editing the API definitions, generate the manifests such as CRs or CRDs using: diff --git a/go.mod b/go.mod index 49124e4..292bb87 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.21 require ( github.com/aws/aws-sdk-go v1.44.311 - github.com/go-logr/logr v1.2.4 - github.com/onsi/ginkgo/v2 v2.11.0 - github.com/onsi/gomega v1.27.10 + github.com/go-logr/logr v1.3.0 + 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 google.golang.org/api v0.134.0 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 @@ -69,6 +69,7 @@ require ( 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 @@ -77,7 +78,7 @@ require ( golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index 7dfc0dd..b8b48dc 100644 --- a/go.sum +++ b/go.sum @@ -224,8 +224,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= @@ -377,6 +378,8 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= +github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40= +github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -573,16 +576,16 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= +github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -938,8 +941,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/test/e2e/single_cluster_record_test.go b/test/e2e/single_cluster_record_test.go new file mode 100644 index 0000000..828c468 --- /dev/null +++ b/test/e2e/single_cluster_record_test.go @@ -0,0 +1,213 @@ +//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" + "github.com/kuadrant/dns-operator/internal/common/conditions" +) + +var _ = Describe("Single Cluster Record Test", 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 + var geoCode string + + BeforeEach(func(ctx SpecContext) { + testID = "t-single-" + GenerateName() + testDomainName = strings.Join([]string{testSuiteID, testZoneDomainName}, ".") + testHostname = strings.Join([]string{testID, testDomainName}, ".") + + if testDNSProvider == "gcp" { + geoCode = "us-east1" + } else { + geoCode = "US" + } + }) + + AfterEach(func(ctx SpecContext) { + if dnsRecord != nil { + err := k8sClient.Delete(ctx, dnsRecord, + client.PropagationPolicy(metav1.DeletePropagationForeground)) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) + } + }) + + Context("simple", func() { + It("makes available a hostname that can be resolved", func(ctx SpecContext) { + By("creating a dns record") + testTargetIP := "127.0.0.1" + dnsRecord = &v1alpha1.DNSRecord{ + ObjectMeta: metav1.ObjectMeta{ + Name: testID, + Namespace: testNamespace, + }, + Spec: v1alpha1.DNSRecordSpec{ + ManagedZoneRef: &v1alpha1.ManagedZoneReference{ + Name: testManagedZoneName, + }, + Endpoints: []*externaldnsendpoint.Endpoint{ + { + DNSName: testHostname, + Targets: []string{ + testTargetIP, + }, + RecordType: "A", + RecordTTL: 60, + }, + }, + }, + } + err := k8sClient.Create(ctx, dnsRecord) + Expect(err).ToNot(HaveOccurred()) + + 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(conditions.ConditionTypeReady)), + "Status": Equal(metav1.ConditionTrue), + })), + ) + }, 300*time.Second, 10*time.Second, ctx).Should(Succeed()) + + By("ensuring the authoritative nameserver resolves the hostname") + // speed up things by using the authoritative nameserver + authoritativeResolver := ResolverForDomainName(testZoneDomainName) + Eventually(func(g Gomega, ctx context.Context) { + ips, err := authoritativeResolver.LookupHost(ctx, testHostname) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ips).To(ContainElement(testTargetIP)) + }, 300*time.Second, 10*time.Second, ctx).Should(Succeed()) + }) + }) + + Context("loadbalanced", func() { + It("makes available a hostname that can be resolved", func(ctx SpecContext) { + By("creating a dns record") + testTargetIP := "127.0.0.1" + + klbHostName := "klb." + testHostname + geo1KlbHostName := geoCode + "." + klbHostName + cluster1KlbHostName := "cluster1." + klbHostName + + dnsRecord = &v1alpha1.DNSRecord{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-record", + Namespace: testNamespace, + }, + Spec: v1alpha1.DNSRecordSpec{ + ManagedZoneRef: &v1alpha1.ManagedZoneReference{ + Name: testManagedZoneName, + }, + Endpoints: []*externaldnsendpoint.Endpoint{ + { + DNSName: cluster1KlbHostName, + Targets: []string{ + testTargetIP, + }, + RecordType: "A", + RecordTTL: 60, + }, + { + DNSName: testHostname, + Targets: []string{ + klbHostName, + }, + RecordType: "CNAME", + RecordTTL: 300, + }, + { + DNSName: geo1KlbHostName, + Targets: []string{ + cluster1KlbHostName, + }, + RecordType: "CNAME", + RecordTTL: 60, + SetIdentifier: cluster1KlbHostName, + ProviderSpecific: externaldnsendpoint.ProviderSpecific{ + { + Name: "weight", + Value: "200", + }, + }, + }, + { + DNSName: klbHostName, + Targets: []string{ + geo1KlbHostName, + }, + RecordType: "CNAME", + RecordTTL: 300, + SetIdentifier: geoCode, + ProviderSpecific: externaldnsendpoint.ProviderSpecific{ + { + Name: "geo-code", + Value: geoCode, + }, + }, + }, + { + DNSName: klbHostName, + Targets: []string{ + geo1KlbHostName, + }, + RecordType: "CNAME", + RecordTTL: 300, + SetIdentifier: "default", + ProviderSpecific: externaldnsendpoint.ProviderSpecific{ + { + Name: "geo-code", + Value: "*", + }, + }, + }, + }, + }, + } + err := k8sClient.Create(ctx, dnsRecord) + Expect(err).ToNot(HaveOccurred()) + + 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(conditions.ConditionTypeReady)), + "Status": Equal(metav1.ConditionTrue), + })), + ) + }, 300*time.Second, 10*time.Second, ctx).Should(Succeed()) + + By("ensuring the authoritative nameserver resolves the hostname") + // speed up things by using the authoritative nameserver + authoritativeResolver := ResolverForDomainName(testZoneDomainName) + Eventually(func(g Gomega, ctx context.Context) { + ips, err := authoritativeResolver.LookupHost(ctx, testHostname) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ips).To(ContainElement(testTargetIP)) + }, 300*time.Second, 10*time.Second, ctx).Should(Succeed()) + }) + }) + +}) diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go new file mode 100644 index 0000000..02f39cc --- /dev/null +++ b/test/e2e/suite_test.go @@ -0,0 +1,115 @@ +//go:build e2e + +package e2e + +import ( + "context" + "crypto/rand" + "fmt" + "math/big" + "net" + "os" + "slices" + "strings" + "testing" + "time" + + "github.com/goombaio/namegenerator" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kuadrant/dns-operator/api/v1alpha1" +) + +const ( + // configuration environment variables + dnsZoneDomainNameEnvvar = "TEST_DNS_ZONE_DOMAIN_NAME" + dnsManagedZoneName = "TEST_DNS_MANAGED_ZONE_NAME" + dnsNamespace = "TEST_DNS_NAMESPACE" + dnsProvider = "TEST_DNS_PROVIDER" +) + +var ( + k8sClient client.Client + // testSuiteID is a randomly generated identifier for the test suite + testSuiteID string + // testZoneDomainName provided domain name for the testZoneID e.g. e2e.hcpapps.net + testZoneDomainName string + testManagedZoneName string + testNamespace string + testDNSProvider string + supportedProviders = []string{"aws", "gcp"} +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "E2E Tests Suite") +} + +var _ = BeforeSuite(func(ctx SpecContext) { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + err := setConfigFromEnvVars() + Expect(err).NotTo(HaveOccurred()) + + err = v1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + &clientcmd.ConfigOverrides{}, + ).ClientConfig() + Expect(err).NotTo(HaveOccurred()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + testSuiteID = "dns-op-e2e-" + GenerateName() +}) + +func ResolverForDomainName(domainName string) *net.Resolver { + nameservers, err := net.LookupNS(domainName) + Expect(err).ToNot(HaveOccurred()) + GinkgoWriter.Printf("[debug] authoritative nameserver used for DNS record resolution: %s\n", nameservers[0].Host) + + authoritativeResolver := &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{Timeout: 10 * time.Second} + return d.DialContext(ctx, network, strings.Join([]string{nameservers[0].Host, "53"}, ":")) + }, + } + return authoritativeResolver +} + +func setConfigFromEnvVars() error { + // Load test suite configuration from the environment + if testZoneDomainName = os.Getenv(dnsZoneDomainNameEnvvar); testZoneDomainName == "" { + return fmt.Errorf("env variable '%s' must be set", dnsZoneDomainNameEnvvar) + } + if testManagedZoneName = os.Getenv(dnsManagedZoneName); testManagedZoneName == "" { + return fmt.Errorf("env variable '%s' must be set", dnsManagedZoneName) + } + if testNamespace = os.Getenv(dnsNamespace); testNamespace == "" { + return fmt.Errorf("env variable '%s' must be set", dnsNamespace) + } + if testDNSProvider = os.Getenv(dnsProvider); testDNSProvider == "" { + return fmt.Errorf("env variable '%s' must be set", dnsProvider) + } + if !slices.Contains(supportedProviders, testDNSProvider) { + return fmt.Errorf("unsupported provider '%s' must be one of '%s'", testDNSProvider, supportedProviders) + } + return nil +} + +func GenerateName() string { + nBig, _ := rand.Int(rand.Reader, big.NewInt(1000000)) + return namegenerator.NewNameGenerator(nBig.Int64()).Generate() +}