Skip to content

Commit 00d3656

Browse files
authored
Added tests (#17)
* Added tests * Clean dns records on tear down * Specify domains sufffix that managed by external dns * Remove unnesasery methods * Cleanup tests * Fix defer order * Replace informers with sleep timeout * fix duplication * Pin version
1 parent e64c0ea commit 00d3656

File tree

20 files changed

+1354
-9
lines changed

20 files changed

+1354
-9
lines changed

src/main.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ locals {
1212
values(module.dns_gbl_primary.outputs.zones)[*].zone_id,
1313
flatten([for k, v in module.additional_dns_components : [for i, j in v.outputs.zones : j.zone_id]])
1414
))
15+
zone_names = compact(concat(
16+
values(module.dns_gbl_delegated.outputs.zones)[*].name,
17+
values(module.dns_gbl_primary.outputs.zones)[*].name,
18+
flatten([for k, v in module.additional_dns_components : [for i, j in v.outputs.zones : j.name]])
19+
))
1520
}
1621

1722
data "aws_partition" "current" {
@@ -99,6 +104,7 @@ module "external_dns" {
99104
txtOwnerId = local.txt_owner
100105
txtPrefix = local.txt_prefix
101106
sources = local.sources
107+
domainFilters = local.zone_names
102108
}),
103109
# hardcoded values
104110
file("${path.module}/resources/values.yaml"),

src/provider-helm.tf

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,8 @@ locals {
133133
"--profile", var.kube_exec_auth_aws_profile
134134
] : []
135135

136-
kube_exec_auth_role_arn = coalesce(var.kube_exec_auth_role_arn, module.iam_roles.terraform_role_arn)
137136
exec_role = local.kube_exec_auth_enabled && var.kube_exec_auth_role_arn_enabled ? [
138-
"--role-arn", local.kube_exec_auth_role_arn
137+
"--role-arn", coalesce(var.kube_exec_auth_role_arn, module.iam_roles.terraform_role_arn)
139138
] : []
140139

141140
# Provide dummy configuration for the case where the EKS cluster is not available.

src/remote-state.tf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module "eks" {
22
source = "cloudposse/stack-config/yaml//modules/remote-state"
3-
version = "1.5.0"
3+
version = "1.8.0"
44

55
component = var.eks_component_name
66

@@ -9,7 +9,7 @@ module "eks" {
99

1010
module "dns_gbl_delegated" {
1111
source = "cloudposse/stack-config/yaml//modules/remote-state"
12-
version = "1.5.0"
12+
version = "1.8.0"
1313

1414
component = "dns-delegated"
1515
environment = var.dns_gbl_delegated_environment_name
@@ -23,7 +23,7 @@ module "dns_gbl_delegated" {
2323

2424
module "dns_gbl_primary" {
2525
source = "cloudposse/stack-config/yaml//modules/remote-state"
26-
version = "1.5.0"
26+
version = "1.8.0"
2727

2828
component = "dns-primary"
2929
environment = var.dns_gbl_primary_environment_name
@@ -40,7 +40,7 @@ module "dns_gbl_primary" {
4040
module "additional_dns_components" {
4141
for_each = { for obj in var.dns_components : obj.component => obj }
4242
source = "cloudposse/stack-config/yaml//modules/remote-state"
43-
version = "1.5.0"
43+
version = "1.8.0"
4444

4545
component = each.value.component
4646
environment = coalesce(each.value.environment, "gbl")

test/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
state/
2+
.cache
3+
test/test-suite.json
4+
.atmos
5+
test_suite.yaml

test/component_test.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package test
2+
3+
import (
4+
"context"
5+
"testing"
6+
"fmt"
7+
"strings"
8+
"time"
9+
"github.com/cloudposse/test-helpers/pkg/atmos"
10+
"github.com/cloudposse/test-helpers/pkg/helm"
11+
awsHelper "github.com/cloudposse/test-helpers/pkg/aws"
12+
helper "github.com/cloudposse/test-helpers/pkg/atmos/component-helper"
13+
awsTerratest "github.com/gruntwork-io/terratest/modules/aws"
14+
"github.com/gruntwork-io/terratest/modules/random"
15+
16+
"github.com/stretchr/testify/assert"
17+
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
"k8s.io/apimachinery/pkg/runtime/schema"
20+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21+
22+
"k8s.io/client-go/dynamic"
23+
)
24+
25+
type ComponentSuite struct {
26+
helper.TestSuite
27+
}
28+
29+
func (s *ComponentSuite) TestBasic() {
30+
const component = "eks/external-dns/basic"
31+
const stack = "default-test"
32+
const awsRegion = "us-east-2"
33+
34+
clusterOptions := s.GetAtmosOptions("eks/cluster", stack, nil)
35+
clusrerId := atmos.Output(s.T(), clusterOptions, "eks_cluster_id")
36+
cluster := awsHelper.GetEksCluster(s.T(), context.Background(), awsRegion, clusrerId)
37+
38+
dnsDelegatedOptions := s.GetAtmosOptions("dns-delegated", stack, nil)
39+
delegatedDomainName := atmos.Output(s.T(), dnsDelegatedOptions, "default_domain_name")
40+
defaultDNSZoneId := atmos.Output(s.T(), dnsDelegatedOptions, "default_dns_zone_id")
41+
42+
defer func() {
43+
if err := awsHelper.CleanDNSZoneID(s.T(), context.Background(), defaultDNSZoneId, awsRegion); err != nil {
44+
fmt.Printf("Error cleaning DNS zone %s: %v\n", defaultDNSZoneId, err)
45+
}
46+
}()
47+
48+
randomID := strings.ToLower(random.UniqueId())
49+
50+
namespace := fmt.Sprintf("external-dns-%s", randomID)
51+
dnsEndpointName := fmt.Sprintf("example-dns-record-%s", randomID)
52+
dnsRecordHostName := fmt.Sprintf("%s.%s", randomID, delegatedDomainName)
53+
54+
inputs := map[string]interface{}{
55+
"kubernetes_namespace": namespace,
56+
}
57+
58+
defer s.DestroyAtmosComponent(s.T(), component, stack, &inputs)
59+
options, _ := s.DeployAtmosComponent(s.T(), component, stack, &inputs)
60+
assert.NotNil(s.T(), options)
61+
62+
metadataArray := []helm.Metadata{}
63+
64+
atmos.OutputStruct(s.T(), options, "metadata", &metadataArray)
65+
66+
metadata := metadataArray[0]
67+
68+
assert.Equal(s.T(), metadata.AppVersion, "0.14.0")
69+
assert.Equal(s.T(), metadata.Chart, "external-dns")
70+
assert.NotNil(s.T(), metadata.FirstDeployed)
71+
assert.NotNil(s.T(), metadata.LastDeployed)
72+
assert.Equal(s.T(), metadata.Name, "external-dns")
73+
assert.Equal(s.T(), metadata.Namespace, namespace)
74+
assert.NotNil(s.T(), metadata.Values)
75+
assert.Equal(s.T(), metadata.Version, "6.33.0")
76+
77+
78+
config, err := awsHelper.NewK8SClientConfig(cluster)
79+
assert.NoError(s.T(), err)
80+
assert.NotNil(s.T(), config)
81+
82+
dynamicClient, err := dynamic.NewForConfig(config)
83+
if err != nil {
84+
panic(fmt.Errorf("failed to create dynamic client: %v", err))
85+
}
86+
87+
// Define the GroupVersionResource for the DNSEndpoint CRD
88+
dnsEndpointGVR := schema.GroupVersionResource{
89+
Group: "externaldns.k8s.io",
90+
Version: "v1alpha1",
91+
Resource: "dnsendpoints",
92+
}
93+
94+
// Create the DNSEndpoint object as an unstructured resource
95+
dnsEndpoint := &unstructured.Unstructured{
96+
Object: map[string]interface{}{
97+
"apiVersion": "externaldns.k8s.io/v1alpha1",
98+
"kind": "DNSEndpoint",
99+
"metadata": map[string]interface{}{
100+
"name": dnsEndpointName,
101+
"namespace": namespace,
102+
},
103+
"spec": map[string]interface{}{
104+
"endpoints": []interface{}{
105+
map[string]interface{}{
106+
"dnsName": dnsRecordHostName,
107+
"recordTTL": 300,
108+
"recordType": "A",
109+
"targets": []interface{}{
110+
"127.0.0.1",
111+
},
112+
},
113+
},
114+
},
115+
},
116+
}
117+
118+
// Create the DNSEndpoint resource in the "default" namespace
119+
_, err = dynamicClient.Resource(dnsEndpointGVR).Namespace(namespace).Create(context.Background(), dnsEndpoint, metav1.CreateOptions{})
120+
assert.NoError(s.T(), err)
121+
122+
defer func() {
123+
if err := dynamicClient.Resource(dnsEndpointGVR).Namespace(namespace).Delete(context.Background(), dnsEndpointName, metav1.DeleteOptions{}); err != nil {
124+
fmt.Printf("Error deleting external dns %s: %v\n", dnsEndpointName, err)
125+
}
126+
}()
127+
128+
// Wait for the DNS record to be created
129+
// We can't use informers as DNS CRD status is not updated when the record is created
130+
time.Sleep(2 * time.Minute)
131+
132+
delegatedNSRecord := awsTerratest.GetRoute53Record(s.T(), defaultDNSZoneId, dnsRecordHostName, "A", awsRegion)
133+
assert.Equal(s.T(), fmt.Sprintf("%s.", dnsRecordHostName), *delegatedNSRecord.Name)
134+
135+
s.DriftTest(component, stack, &inputs)
136+
}
137+
138+
func (s *ComponentSuite) TestEnabledFlag() {
139+
const component = "eks/external-dns/disabled"
140+
const stack = "default-test"
141+
s.VerifyEnabledFlag(component, stack, nil)
142+
}
143+
144+
func (s *ComponentSuite) SetupSuite() {
145+
s.TestSuite.InitConfig()
146+
s.TestSuite.Config.ComponentDestDir = "components/terraform/eks/external-dns"
147+
s.TestSuite.SetupSuite()
148+
}
149+
150+
func TestRunSuite(t *testing.T) {
151+
suite := new(ComponentSuite)
152+
suite.AddDependency(t, "vpc", "default-test", nil)
153+
suite.AddDependency(t, "eks/cluster", "default-test", nil)
154+
155+
subdomain := strings.ToLower(random.UniqueId())
156+
inputs := map[string]interface{}{
157+
"zone_config": []map[string]interface{}{
158+
{
159+
"subdomain": subdomain,
160+
"zone_name": "components.cptest.test-automation.app",
161+
},
162+
},
163+
}
164+
suite.AddDependency(t, "dns-delegated", "default-test", &inputs)
165+
166+
randomID := strings.ToLower(random.UniqueId())
167+
domainName := fmt.Sprintf("example-%s.net", randomID)
168+
inputsPrimaryDns := map[string]interface{}{
169+
"domain_names": []string{domainName},
170+
"record_config": []map[string]interface{}{
171+
{
172+
"root_zone": domainName,
173+
"name": "",
174+
"type": "A",
175+
"ttl": 60,
176+
"records": []string{"127.0.0.1"},
177+
},
178+
{
179+
"root_zone": domainName,
180+
"name": "www.",
181+
"type": "CNAME",
182+
"ttl": 60,
183+
"records": []string{domainName},
184+
},
185+
{
186+
"root_zone": domainName,
187+
"name": "123456.",
188+
"type": "CNAME",
189+
"ttl": 120,
190+
"records": []string{domainName},
191+
},
192+
},
193+
}
194+
suite.AddDependency(t, "dns-primary", "default-test", &inputsPrimaryDns)
195+
196+
suite.AddDependency(t, "eks/alb-controller", "default-test", nil)
197+
helper.Run(t, suite)
198+
}

test/fixtures/atmos.yaml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# CLI config is loaded from the following locations (from lowest to highest priority):
2+
# system dir (`/usr/local/etc/atmos` on Linux, `%LOCALAPPDATA%/atmos` on Windows)
3+
# home dir (~/.atmos)
4+
# current directory
5+
# ENV vars
6+
# Command-line arguments
7+
#
8+
# It supports POSIX-style Globs for file names/paths (double-star `**` is supported)
9+
# https://en.wikipedia.org/wiki/Glob_(programming)
10+
11+
# Base path for components, stacks and workflows configurations.
12+
# Can also be set using `ATMOS_BASE_PATH` ENV var, or `--base-path` command-line argument.
13+
# Supports both absolute and relative paths.
14+
# If not provided or is an empty string, `components.terraform.base_path`, `components.helmfile.base_path`, `stacks.base_path` and `workflows.base_path`
15+
# are independent settings (supporting both absolute and relative paths).
16+
# If `base_path` is provided, `components.terraform.base_path`, `components.helmfile.base_path`, `stacks.base_path` and `workflows.base_path`
17+
# are considered paths relative to `base_path`.
18+
base_path: ""
19+
20+
components:
21+
terraform:
22+
# Can also be set using `ATMOS_COMPONENTS_TERRAFORM_BASE_PATH` ENV var, or `--terraform-dir` command-line argument
23+
# Supports both absolute and relative paths
24+
base_path: "components/terraform"
25+
# Can also be set using `ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE` ENV var
26+
apply_auto_approve: true
27+
# Can also be set using `ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT` ENV var, or `--deploy-run-init` command-line argument
28+
deploy_run_init: true
29+
# Can also be set using `ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE` ENV var, or `--init-run-reconfigure` command-line argument
30+
init_run_reconfigure: true
31+
# Can also be set using `ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE` ENV var, or `--auto-generate-backend-file` command-line argument
32+
auto_generate_backend_file: true
33+
34+
stacks:
35+
# Can also be set using `ATMOS_STACKS_BASE_PATH` ENV var, or `--config-dir` and `--stacks-dir` command-line arguments
36+
# Supports both absolute and relative paths
37+
base_path: "stacks"
38+
# Can also be set using `ATMOS_STACKS_INCLUDED_PATHS` ENV var (comma-separated values string)
39+
# Since we are distinguishing stacks based on namespace, and namespace is not part
40+
# of the stack name, we have to set `included_paths` via the ENV var in the Dockerfile
41+
included_paths:
42+
- "orgs/**/*"
43+
44+
# Can also be set using `ATMOS_STACKS_EXCLUDED_PATHS` ENV var (comma-separated values string)
45+
excluded_paths:
46+
- "**/_defaults.yaml"
47+
48+
# Can also be set using `ATMOS_STACKS_NAME_PATTERN` ENV var
49+
name_pattern: "{tenant}-{stage}"
50+
51+
workflows:
52+
# Can also be set using `ATMOS_WORKFLOWS_BASE_PATH` ENV var, or `--workflows-dir` command-line arguments
53+
# Supports both absolute and relative paths
54+
base_path: "stacks/workflows"
55+
56+
# https://github.com/cloudposse/atmos/releases/tag/v1.33.0
57+
logs:
58+
file: "/dev/stdout"
59+
# Supported log levels: Trace, Debug, Info, Warning, Off
60+
level: Info
61+
62+
settings:
63+
# Can also be set using 'ATMOS_SETTINGS_LIST_MERGE_STRATEGY' environment variable, or '--settings-list-merge-strategy' command-line argument
64+
list_merge_strategy: replace
65+
66+
# `Go` templates in Atmos manifests
67+
# https://atmos.tools/core-concepts/stacks/templating
68+
# https://pkg.go.dev/text/template
69+
templates:
70+
settings:
71+
enabled: true
72+
# https://masterminds.github.io/sprig
73+
sprig:
74+
enabled: true
75+
# https://docs.gomplate.ca
76+
gomplate:
77+
enabled: true
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
components:
2+
terraform:
3+
account-map:
4+
metadata:
5+
terraform_workspace: core-gbl-root
6+
vars:
7+
tenant: core
8+
environment: gbl
9+
stage: root
10+
11+
# This remote state is only for Cloud Posse internal use.
12+
# It references the Cloud Posse test organizations actual infrastructure.
13+
# remote_state_backend:
14+
# s3:
15+
# bucket: cptest-core-ue2-root-tfstate-core
16+
# dynamodb_table: cptest-core-ue2-root-tfstate-core-lock
17+
# role_arn: arn:aws:iam::822777368227:role/cptest-core-gbl-root-tfstate-core-ro
18+
# encrypt: true
19+
# key: terraform.tfstate
20+
# acl: bucket-owner-full-control
21+
# region: us-east-2
22+
23+
remote_state_backend_type: static
24+
remote_state_backend:
25+
# This static backend is used for tests that only need to use the account map iam-roles module
26+
# to find the role to assume for Terraform operations. It is configured to use whatever
27+
# the current user's role is, but the environment variable `TEST_ACCOUNT_ID` must be set to
28+
# the account ID of the account that the user is currently assuming a role in.
29+
#
30+
# For some components, this backend is missing important data, and those components
31+
# will need that data added to the backend configuration in order to work properly.
32+
static:
33+
account_info_map: {}
34+
all_accounts: []
35+
aws_partition: aws
36+
full_account_map: {}
37+
iam_role_arn_templates: {}
38+
non_eks_accounts: []
39+
profiles_enabled: false
40+
root_account_aws_name: root
41+
terraform_access_map: {}
42+
terraform_dynamic_role_enabled: false
43+
terraform_role_name_map:
44+
apply: terraform
45+
plan: planner
46+
terraform_roles: {}

0 commit comments

Comments
 (0)