Skip to content

Commit

Permalink
Add dnsrecord integration test (#379)
Browse files Browse the repository at this point in the history
* Add DNSRecord controller integration test

* Ensure the integration test can use a temporary cluster

* Change credentials secret to route53-operator-aws
  • Loading branch information
stoyanr authored Jul 22, 2021
1 parent c4e9d59 commit d0d5fa7
Show file tree
Hide file tree
Showing 6 changed files with 606 additions and 8 deletions.
19 changes: 19 additions & 0 deletions .ci/testruns/default/templates/testrun.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ spec:
secretKeyRef:
name: shoot-operator-aws
key: secretAccessKey
- name: DNS_ACCESS_KEY_ID
type: env
valueFrom:
secretKeyRef:
name: route53-operator-aws
key: accessKeyID
- name: DNS_SECRET_ACCESS_KEY
type: env
valueFrom:
secretKeyRef:
name: route53-operator-aws
key: secretAccessKey

testflow:
- name: infrastructure-test
Expand All @@ -44,3 +56,10 @@ spec:
- name: REGION
type: env
value: eu-west-1
- name: dnsrecord-test
definition:
name: dnsrecord-test
config:
- name: USE_EXISTING_CLUSTER
type: env
value: "true"
18 changes: 18 additions & 0 deletions .test-defs/dnsrecord-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
kind: TestDefinition
metadata:
name: dnsrecord-test
spec:
owner: [email protected]
description: Integration test for dnsrecord creation, update, and deletion
activeDeadlineSeconds: 1800

command: [bash, -c]
args:
- >-
go test -timeout=25m -mod=vendor ./test/integration/dnsrecord
--v -ginkgo.v -ginkgo.progress -ginkgo.noColor
--kubeconfig=$TM_KUBECONFIG_PATH/testmachinery.config
--access-key-id=$DNS_ACCESS_KEY_ID
--secret-access-key=$DNS_SECRET_ACCESS_KEY
image: golang:1.16.3
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ docker-images:
install-requirements:
@go install -mod=vendor $(REPO_ROOT)/vendor/github.com/ahmetb/gen-crd-api-reference-docs
@go install -mod=vendor $(REPO_ROOT)/vendor/github.com/golang/mock/mockgen
@go install -mod=vendor sigs.k8s.io/controller-runtime/tools/setup-envtest
@go install -mod=vendor $(REPO_ROOT)/vendor/github.com/onsi/ginkgo/ginkgo
@$(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/install-requirements.sh

Expand Down Expand Up @@ -128,11 +129,11 @@ format:

.PHONY: test
test:
@SKIP_FETCH_TOOLS=1 $(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/test.sh ./cmd/... ./pkg/...
@$(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/test.sh ./cmd/... ./pkg/...

.PHONY: test-cov
test-cov:
@SKIP_FETCH_TOOLS=1 $(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/test-cover.sh ./cmd/... ./pkg/...
@$(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/test-cover.sh ./cmd/... ./pkg/...

.PHONY: test-clean
test-clean:
Expand Down Expand Up @@ -161,3 +162,11 @@ integration-test-bastion:
--access-key-id='$(shell cat $(ACCESS_KEY_ID_FILE))' \
--secret-access-key='$(shell cat $(SECRET_ACCESS_KEY_FILE))' \
--region=$(REGION)

.PHONY: integration-test-dnsrecord
integration-test-dnsrecord:
@$(REPO_ROOT)/vendor/github.com/gardener/gardener/hack/test-integration.sh -timeout=0 ./test/integration/dnsrecord \
--v -ginkgo.v -ginkgo.progress \
--kubeconfig=${KUBECONFIG} \
--access-key-id='$(shell cat $(ACCESS_KEY_ID_FILE))' \
--secret-access-key='$(shell cat $(SECRET_ACCESS_KEY_FILE))'
49 changes: 43 additions & 6 deletions pkg/aws/client/client_route53.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ package client
import (
"context"
"fmt"
"strconv"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
Expand All @@ -38,6 +40,30 @@ func (c *Client) GetDNSHostedZones(ctx context.Context) (map[string]string, erro
return zones, nil
}

// CreateDNSHostedZone creates the DNS hosted zone with the given name and comment, and returns the ID of the
// newly created zone.
func (c *Client) CreateDNSHostedZone(ctx context.Context, name, comment string) (string, error) {
out, err := c.Route53.CreateHostedZoneWithContext(ctx, &route53.CreateHostedZoneInput{
CallerReference: aws.String(strconv.Itoa(int(time.Now().Unix()))),
Name: aws.String(name),
HostedZoneConfig: &route53.HostedZoneConfig{
Comment: aws.String(comment),
},
})
if err != nil {
return "", err
}
return aws.StringValue(out.HostedZone.Id), nil
}

// DeleteDNSHostedZone deletes the DNS hosted zone with the given ID.
func (c *Client) DeleteDNSHostedZone(ctx context.Context, zoneId string) error {
_, err := c.Route53.DeleteHostedZoneWithContext(ctx, &route53.DeleteHostedZoneInput{
Id: aws.String(zoneId),
})
return ignoreHostedZoneNotFound(err)
}

func normalizeName(name string) string {
if strings.HasPrefix(name, "\\052.") {
name = "*" + name[4:]
Expand Down Expand Up @@ -76,7 +102,7 @@ func (c *Client) DeleteDNSRecordSet(ctx context.Context, zoneId, name, recordTyp
)
if len(values) == 0 && ttl == 0 {
// No values / ttl were specified, so get the resource recordset from the zone
rrs, err = c.getResourceRecordSet(ctx, zoneId, name, recordType)
rrs, err = c.GetDNSRecordSet(ctx, zoneId, name, recordType)
if err != nil {
return err
}
Expand All @@ -90,7 +116,7 @@ func (c *Client) DeleteDNSRecordSet(ctx context.Context, zoneId, name, recordTyp
if isValuesDoNotMatchError(err) && len(values) > 0 && ttl > 0 {
// The actual values / ttl are different from the given values / ttl
// Get the resource recordset from the zone and try again
rrs, err = c.getResourceRecordSet(ctx, zoneId, name, getRecordType(recordType, values[0]))
rrs, err = c.GetDNSRecordSet(ctx, zoneId, name, getRecordType(recordType, values[0]))
if err != nil {
return err
}
Expand All @@ -99,17 +125,18 @@ func (c *Client) DeleteDNSRecordSet(ctx context.Context, zoneId, name, recordTyp
}
_, err = c.Route53.ChangeResourceRecordSetsWithContext(ctx, newChangeResourceRecordSetsInput(zoneId, route53.ChangeActionDelete, rrs))
}
return ignoreNotFoundRoute53(err)
return ignoreResourceRecordSetNotFound(err)
}

func (c *Client) getResourceRecordSet(ctx context.Context, zoneId, name, recordType string) (*route53.ResourceRecordSet, error) {
// GetDNSRecordSet returns the DNS recordset in the DNS hosted zone with the given zone ID, and with the given name and type.
func (c *Client) GetDNSRecordSet(ctx context.Context, zoneId, name, recordType string) (*route53.ResourceRecordSet, error) {
out, err := c.Route53.ListResourceRecordSetsWithContext(ctx, &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String(zoneId),
MaxItems: aws.String("1"),
StartRecordName: aws.String(name),
StartRecordType: aws.String(recordType),
})
if ignoreNotFoundRoute53(err) != nil {
if ignoreResourceRecordSetNotFound(err) != nil {
return nil, err
}
if out == nil || len(out.ResourceRecordSets) == 0 { // no records in zone
Expand Down Expand Up @@ -262,7 +289,7 @@ func encloseInQuotes(s string) string {
return s
}

func ignoreNotFoundRoute53(err error) error {
func ignoreResourceRecordSetNotFound(err error) error {
if err == nil {
return nil
}
Expand All @@ -272,6 +299,16 @@ func ignoreNotFoundRoute53(err error) error {
return err
}

func ignoreHostedZoneNotFound(err error) error {
if err == nil {
return nil
}
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == route53.ErrCodeHostedZoneNotFound {
return nil
}
return err
}

func isValuesDoNotMatchError(err error) bool {
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == route53.ErrCodeInvalidChangeBatch && strings.Contains(aerr.Message(), "the values provided do not match the current values") {
return true
Expand Down
27 changes: 27 additions & 0 deletions test/integration/dnsrecord/dnsrecord_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// 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 dnsrecord_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestDNSRecord(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "DNSRecord Suite")
}
Loading

0 comments on commit d0d5fa7

Please sign in to comment.