From 48136b117258f5ea0e5c94666d89d2df9db7e9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 11:24:28 +0200 Subject: [PATCH 1/9] test: add integration tests - they run daily plus on PRs --- .github/workflows/e2e.yml | 47 +++++++++++ Makefile | 52 ++++++++++++ tests/e2e/deploy/external-dns.yaml | 81 +++++++++++++++++++ tests/e2e/kuttl-test.yaml | 5 ++ tests/e2e/scripts/check-dns.sh | 34 ++++++++ .../record-lifecycle/00-assert.yaml | 7 ++ .../record-lifecycle/00-service.yaml | 38 +++++++++ .../record-lifecycle/01-assert.yaml | 7 ++ .../record-lifecycle/01-delete.yaml | 12 +++ 9 files changed, 283 insertions(+) create mode 100644 .github/workflows/e2e.yml create mode 100644 tests/e2e/deploy/external-dns.yaml create mode 100644 tests/e2e/kuttl-test.yaml create mode 100755 tests/e2e/scripts/check-dns.sh create mode 100644 tests/e2e/test-suite/record-lifecycle/00-assert.yaml create mode 100644 tests/e2e/test-suite/record-lifecycle/00-service.yaml create mode 100644 tests/e2e/test-suite/record-lifecycle/01-assert.yaml create mode 100644 tests/e2e/test-suite/record-lifecycle/01-delete.yaml diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..05dbdd6 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,47 @@ +name: E2E Integration Tests + +on: + pull_request: + branches: ["main", "release/**"] + schedule: + - cron: '0 2 * * *' # Runs daily at 2:00 AM UTC + +jobs: + e2e-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' # Automatically picks up Go 1.26.5 from your go.mod + + - name: Install Dependencies (Kind, Kuttl, Dig) + run: | + # Install dig (required for our custom check-dns.sh script) + sudo apt-get update && sudo apt-get install -y dnsutils + + # Install Kind + curl -sSLo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + # Install Kuttl + curl -sSLo kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.15.0/kubectl-kuttl_0.15.0_linux_x86_64 + chmod +x kubectl-kuttl + sudo mv kubectl-kuttl /usr/local/bin/ + + - name: Prepare STACKIT Service Account Key + run: | + # Write the secret from GitHub Actions into a temporary file so the Makefile can mount it + echo '${{ secrets.STACKIT_SERVICE_ACCOUNT_KEY }}' > /tmp/sa.json + + - name: Run E2E Tests + env: + PROJECT_ID: ${{ secrets.STACKIT_PROJECT_ID }} + ZONE_NAME: ${{ secrets.STACKIT_ZONE_NAME }} + AUTH_KEY_PATH: /tmp/sa.json + run: | + make test-e2e-local \ No newline at end of file diff --git a/Makefile b/Makefile index 21b35b8..4ccb3ab 100644 --- a/Makefile +++ b/Makefile @@ -80,3 +80,55 @@ license-check: $(GO_LICENSES) reports ## Check licenses against code. .PHONY: license-report license-report: $(GO_LICENSES) reports ## Create licenses report against code. $(GO_LICENSES) report --include_tests --ignore $(LICENCES_IGNORE_LIST) ./... > ./reports/licenses/licenses-list.csv + +# ============================================================================== +# E2E Local Testing +# ============================================================================== + +E2E_TMP_DIR = tests/e2e-tmp + +.PHONY: build-linux +build-linux: + CGO_ENABLED=0 GOOS=linux GOARCH=$(shell go env GOARCH) go build -ldflags "-s -w" -o ./external-dns-stackit-webhook -v cmd/webhook/main.go + +.PHONY: docker-build-e2e +docker-build-e2e: build-linux + docker build -t stackitcloud/external-dns-stackit-webhook:e2e -f Dockerfile . + rm ./external-dns-stackit-webhook # Clean up the binary after build + +# Run this to test the webhook locally +# make test-e2e-local \ + PROJECT_ID="your-project-id" \ + ZONE_NAME="your.test.zone.cloud" \ + AUTH_KEY_PATH="/absolute/path/to/your/sa.json" +.PHONY: test-e2e-local +test-e2e-local: docker-build-e2e + @if [ -z "$(PROJECT_ID)" ] || [ -z "$(ZONE_NAME)" ] || [ -z "$(AUTH_KEY_PATH)" ]; then \ + echo "Error: Missing PROJECT_ID, ZONE_NAME, or AUTH_KEY_PATH environment variables."; \ + exit 1; \ + fi + @echo "=> Creating Kind cluster..." + kind create cluster --name stackit-e2e || true + @echo "=> Loading image into Kind..." + kind load docker-image stackitcloud/external-dns-stackit-webhook:e2e --name stackit-e2e + @echo "=> Setting up STACKIT credentials..." + kubectl create secret generic external-dns-stackit-webhook \ + --from-file=sa.json=$(AUTH_KEY_PATH) \ + --dry-run=client -o yaml | kubectl apply -f - + @echo "=> Preparing test manifests..." + rm -rf $(E2E_TMP_DIR) + cp -r tests/e2e $(E2E_TMP_DIR) + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + + find $(E2E_TMP_DIR) -type f -name "*.bak" -delete + @echo "=> Deploying ExternalDNS and Webhook..." + kubectl apply -f $(E2E_TMP_DIR)/deploy/external-dns.yaml + kubectl wait --for=condition=available --timeout=60s deployment/external-dns + @echo "=> Running Kuttl Tests..." + cd $(E2E_TMP_DIR) && kubectl kuttl test + @echo "=> Cleaning up templates..." + rm -rf $(E2E_TMP_DIR) + +.PHONY: clean-e2e-local +clean-e2e-local: + kind delete cluster --name stackit-e2e \ No newline at end of file diff --git a/tests/e2e/deploy/external-dns.yaml b/tests/e2e/deploy/external-dns.yaml new file mode 100644 index 0000000..e6a40a3 --- /dev/null +++ b/tests/e2e/deploy/external-dns.yaml @@ -0,0 +1,81 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: external-dns +rules: + - apiGroups: [""] + resources: ["services","endpoints","pods","nodes"] + verbs: ["get","watch","list"] + - apiGroups: ["extensions","networking.k8s.io"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: + - kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + volumes: + - name: stackit-sa-key + secret: + secretName: external-dns-stackit-webhook + items: + - key: sa.json + path: sa.json + containers: + - name: external-dns + image: registry.k8s.io/external-dns/external-dns:v0.14.0 + args: + - --log-level=info + - --interval=10s + - --source=service + - --policy=sync + - --provider=webhook + - --domain-filter=${ZONE_NAME} + - --txt-prefix=txt- + - name: webhook + image: stackitcloud/external-dns-stackit-webhook:e2e + imagePullPolicy: Never # Forces the use of our sideloaded local build + args: + - --project-id=${PROJECT_ID} + - --log-level=debug + ports: + - name: http + containerPort: 8888 + env: + - name: AUTH_KEY_PATH + value: /var/run/secrets/stackit/sa.json + volumeMounts: + - name: stackit-sa-key + mountPath: /var/run/secrets/stackit + readOnly: true \ No newline at end of file diff --git a/tests/e2e/kuttl-test.yaml b/tests/e2e/kuttl-test.yaml new file mode 100644 index 0000000..cc8f777 --- /dev/null +++ b/tests/e2e/kuttl-test.yaml @@ -0,0 +1,5 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +testDirs: + - test-suite +timeout: 60 \ No newline at end of file diff --git a/tests/e2e/scripts/check-dns.sh b/tests/e2e/scripts/check-dns.sh new file mode 100755 index 0000000..da29e41 --- /dev/null +++ b/tests/e2e/scripts/check-dns.sh @@ -0,0 +1,34 @@ +#!/bin/sh +RECORD_TYPE="$1" +RECORD_NAME="$2" +EXPECTED_RESULT="$3" + +# 1. Dynamically fetch one of the authoritative nameservers for your zone +AUTH_NS=$(dig +short NS "${ZONE_NAME}" | head -n 1) + +if [ -z "$AUTH_NS" ]; then + echo "Error: Could not determine authoritative nameserver for ${ZONE_NAME}" + exit 1 +fi + +# 2. Query the authoritative nameserver directly +RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "${RECORD_NAME}") + +# 3. Check for deletion or creation +if [ -z "$EXPECTED_RESULT" ]; then + # Deletion Case + if [ -z "$RESULT" ]; then + echo "DNS $RECORD_TYPE record $RECORD_NAME successfully deleted from $AUTH_NS!" + exit 0 + fi +else + # Creation Case (Using grep to handle quotes around TXT records or trailing dots on CNAMEs) + if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then + echo "DNS $RECORD_TYPE record $RECORD_NAME successfully verified on $AUTH_NS!" + exit 0 + fi +fi + +# 4. If not matched, print wait message, sleep, and exit 1 so Kuttl retries +echo "Waiting for DNS update... (Type: $RECORD_TYPE, Name: $RECORD_NAME, Expected: '$EXPECTED_RESULT', Got: '$RESULT')" +exit 1 \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml new file mode 100644 index 0000000..8f6b2e0 --- /dev/null +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 120 +commands: + - script: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" + - script: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" + - script: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}" \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/00-service.yaml b/tests/e2e/test-suite/record-lifecycle/00-service.yaml new file mode 100644 index 0000000..d38da79 --- /dev/null +++ b/tests/e2e/test-suite/record-lifecycle/00-service.yaml @@ -0,0 +1,38 @@ +# 1. Triggers an A Record (IPv4) +apiVersion: v1 +kind: Service +metadata: + name: test-service-a + annotations: + external-dns.alpha.kubernetes.io/hostname: "e2e-a.${ZONE_NAME}" + external-dns.alpha.kubernetes.io/target: "10.0.0.1" +spec: + type: LoadBalancer + ports: + - port: 80 +--- +# 2. Triggers an AAAA Record (IPv6) +apiVersion: v1 +kind: Service +metadata: + name: test-service-aaaa + annotations: + external-dns.alpha.kubernetes.io/hostname: "e2e-aaaa.${ZONE_NAME}" + external-dns.alpha.kubernetes.io/target: "2001:db8::1" +spec: + type: LoadBalancer + ports: + - port: 80 +--- +# 3. Triggers a CNAME Record (Hostname) +apiVersion: v1 +kind: Service +metadata: + name: test-service-cname + annotations: + external-dns.alpha.kubernetes.io/hostname: "e2e-cname.${ZONE_NAME}" + external-dns.alpha.kubernetes.io/target: "e2e-a.${ZONE_NAME}" +spec: + type: LoadBalancer + ports: + - port: 80 \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml new file mode 100644 index 0000000..e4cd60b --- /dev/null +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 60 +commands: + - script: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" + - script: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" + - script: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-delete.yaml b/tests/e2e/test-suite/record-lifecycle/01-delete.yaml new file mode 100644 index 0000000..5cba986 --- /dev/null +++ b/tests/e2e/test-suite/record-lifecycle/01-delete.yaml @@ -0,0 +1,12 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: + - apiVersion: v1 + kind: Service + name: test-service-a + - apiVersion: v1 + kind: Service + name: test-service-aaaa + - apiVersion: v1 + kind: Service + name: test-service-cname \ No newline at end of file From 7cc6ef59e200a1e48c4a685b02e54270adf8a78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 12:02:06 +0200 Subject: [PATCH 2/9] fix: make script executable --- Makefile | 1 + tests/e2e/test-suite/record-lifecycle/00-assert.yaml | 6 +++--- tests/e2e/test-suite/record-lifecycle/01-assert.yaml | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 4ccb3ab..db4c328 100644 --- a/Makefile +++ b/Makefile @@ -118,6 +118,7 @@ test-e2e-local: docker-build-e2e @echo "=> Preparing test manifests..." rm -rf $(E2E_TMP_DIR) cp -r tests/e2e $(E2E_TMP_DIR) + chmod +x $(E2E_TMP_DIR)/scripts/*.sh find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.bak" -delete diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml index 8f6b2e0..8e4e899 100644 --- a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - script: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" - - script: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" - - script: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}" \ No newline at end of file + - script: sh ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" + - script: sh ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" + - script: sh ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}" \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml index e4cd60b..95b722d 100644 --- a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 60 commands: - - script: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" - - script: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" - - script: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file + - script: sh ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" + - script: sh ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" + - script: sh ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file From dc5cfd9b0de3dce87043993979045f746649430d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 12:34:47 +0200 Subject: [PATCH 3/9] fix: make script executable once more --- tests/e2e/test-suite/record-lifecycle/00-assert.yaml | 6 +++--- tests/e2e/test-suite/record-lifecycle/01-assert.yaml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml index 8e4e899..1dba63d 100644 --- a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - script: sh ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" - - script: sh ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" - - script: sh ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}" \ No newline at end of file + - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" + - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" + - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml index 95b722d..73d0146 100644 --- a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -1,7 +1,7 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 60 +timeout: 120 commands: - - script: sh ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" - - script: sh ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" - - script: sh ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file + - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" + - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" + - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file From 158259ec0e82ecebaf4073ea3cf13de78a934bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 15:14:30 +0200 Subject: [PATCH 4/9] fix: add debugging --- Makefile | 5 +++-- tests/e2e/scripts/check-dns.sh | 27 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index db4c328..1ee59fb 100644 --- a/Makefile +++ b/Makefile @@ -119,8 +119,9 @@ test-e2e-local: docker-build-e2e rm -rf $(E2E_TMP_DIR) cp -r tests/e2e $(E2E_TMP_DIR) chmod +x $(E2E_TMP_DIR)/scripts/*.sh - find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + - find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + + # Updated to include .sh files in the sed replacement! + find $(E2E_TMP_DIR) -type f \( -name "*.yaml" -o -name "*.sh" \) -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + + find $(E2E_TMP_DIR) -type f \( -name "*.yaml" -o -name "*.sh" \) -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.bak" -delete @echo "=> Deploying ExternalDNS and Webhook..." kubectl apply -f $(E2E_TMP_DIR)/deploy/external-dns.yaml diff --git a/tests/e2e/scripts/check-dns.sh b/tests/e2e/scripts/check-dns.sh index da29e41..64a8958 100755 --- a/tests/e2e/scripts/check-dns.sh +++ b/tests/e2e/scripts/check-dns.sh @@ -1,34 +1,45 @@ #!/bin/sh + +# Enable shell debugging: prints every command and variable expansion to stdout +set -x + RECORD_TYPE="$1" RECORD_NAME="$2" EXPECTED_RESULT="$3" -# 1. Dynamically fetch one of the authoritative nameservers for your zone +echo "=== DEBUG: DNS CHECK STARTED ===" +echo "Type: $RECORD_TYPE | Name: $RECORD_NAME | Expected: '$EXPECTED_RESULT' | Zone: '${ZONE_NAME}'" + +# 1. Fetch Auth NS AUTH_NS=$(dig +short NS "${ZONE_NAME}" | head -n 1) +echo "DEBUG: Discovered Auth NS: '$AUTH_NS'" if [ -z "$AUTH_NS" ]; then - echo "Error: Could not determine authoritative nameserver for ${ZONE_NAME}" + echo "ERROR: Could not determine authoritative nameserver for ${ZONE_NAME}" + # Sleep so the log isn't spammed 100 times a second + sleep 5 exit 1 fi -# 2. Query the authoritative nameserver directly +# 2. Query the authoritative nameserver RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "${RECORD_NAME}") +echo "DEBUG: Dig Result: '$RESULT'" # 3. Check for deletion or creation if [ -z "$EXPECTED_RESULT" ]; then # Deletion Case if [ -z "$RESULT" ]; then - echo "DNS $RECORD_TYPE record $RECORD_NAME successfully deleted from $AUTH_NS!" + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" exit 0 fi else - # Creation Case (Using grep to handle quotes around TXT records or trailing dots on CNAMEs) + # Creation Case if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then - echo "DNS $RECORD_TYPE record $RECORD_NAME successfully verified on $AUTH_NS!" + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" exit 0 fi fi -# 4. If not matched, print wait message, sleep, and exit 1 so Kuttl retries -echo "Waiting for DNS update... (Type: $RECORD_TYPE, Name: $RECORD_NAME, Expected: '$EXPECTED_RESULT', Got: '$RESULT')" +echo "FAILED: Condition not met. Retrying in 5s..." +sleep 5 exit 1 \ No newline at end of file From b1fb631a16d288d93312b7f5beed8eac6a36055a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 15:33:58 +0200 Subject: [PATCH 5/9] fix: fix script --- Makefile | 22 ++++++++++++++----- tests/e2e/scripts/check-dns.sh | 11 ++-------- .../record-lifecycle/00-assert.yaml | 6 ++--- .../record-lifecycle/01-assert.yaml | 6 ++--- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 1ee59fb..4ce262e 100644 --- a/Makefile +++ b/Makefile @@ -118,18 +118,28 @@ test-e2e-local: docker-build-e2e @echo "=> Preparing test manifests..." rm -rf $(E2E_TMP_DIR) cp -r tests/e2e $(E2E_TMP_DIR) + + # 1. Strip Windows line-endings (CRLF) that cause 'fork/exec no such file' errors in Linux! + sed -i.bak 's/\r$$//' $(E2E_TMP_DIR)/scripts/*.sh chmod +x $(E2E_TMP_DIR)/scripts/*.sh - # Updated to include .sh files in the sed replacement! - find $(E2E_TMP_DIR) -type f \( -name "*.yaml" -o -name "*.sh" \) -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + - find $(E2E_TMP_DIR) -type f \( -name "*.yaml" -o -name "*.sh" \) -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + + + # 2. Only run replacement on YAML files now + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.bak" -delete + @echo "=> Deploying ExternalDNS and Webhook..." kubectl apply -f $(E2E_TMP_DIR)/deploy/external-dns.yaml kubectl wait --for=condition=available --timeout=60s deployment/external-dns + @echo "=> Running Kuttl Tests..." - cd $(E2E_TMP_DIR) && kubectl kuttl test - @echo "=> Cleaning up templates..." - rm -rf $(E2E_TMP_DIR) + cd $(E2E_TMP_DIR) && \ + kubectl kuttl test; \ + RET=$$?; \ + echo "=> Cleaning up local test environment..."; \ + kind delete cluster --name stackit-e2e; \ + cd .. && rm -rf $(E2E_TMP_DIR); \ + exit $$RET .PHONY: clean-e2e-local clean-e2e-local: diff --git a/tests/e2e/scripts/check-dns.sh b/tests/e2e/scripts/check-dns.sh index 64a8958..b19e973 100755 --- a/tests/e2e/scripts/check-dns.sh +++ b/tests/e2e/scripts/check-dns.sh @@ -1,39 +1,32 @@ #!/bin/sh - -# Enable shell debugging: prints every command and variable expansion to stdout set -x RECORD_TYPE="$1" RECORD_NAME="$2" EXPECTED_RESULT="$3" +ZONE_NAME="$4" # Grab it from the 4th argument! echo "=== DEBUG: DNS CHECK STARTED ===" -echo "Type: $RECORD_TYPE | Name: $RECORD_NAME | Expected: '$EXPECTED_RESULT' | Zone: '${ZONE_NAME}'" +echo "Type: $RECORD_TYPE | Name: $RECORD_NAME | Expected: '$EXPECTED_RESULT' | Zone: '$ZONE_NAME'" -# 1. Fetch Auth NS AUTH_NS=$(dig +short NS "${ZONE_NAME}" | head -n 1) echo "DEBUG: Discovered Auth NS: '$AUTH_NS'" if [ -z "$AUTH_NS" ]; then echo "ERROR: Could not determine authoritative nameserver for ${ZONE_NAME}" - # Sleep so the log isn't spammed 100 times a second sleep 5 exit 1 fi -# 2. Query the authoritative nameserver RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "${RECORD_NAME}") echo "DEBUG: Dig Result: '$RESULT'" -# 3. Check for deletion or creation if [ -z "$EXPECTED_RESULT" ]; then - # Deletion Case if [ -z "$RESULT" ]; then echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" exit 0 fi else - # Creation Case if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" exit 0 diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml index 1dba63d..b57784e 100644 --- a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" - - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" - - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." \ No newline at end of file + - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" "${ZONE_NAME}" + - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" "${ZONE_NAME}" + - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." "${ZONE_NAME}" \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml index 73d0146..a2aca3e 100644 --- a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" - - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" - - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" \ No newline at end of file + - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" "${ZONE_NAME}" + - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" "${ZONE_NAME}" + - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" "${ZONE_NAME}" \ No newline at end of file From 60ed2e8db950af7bce6fdf8edb737f70f04b6c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 16:03:28 +0200 Subject: [PATCH 6/9] fix: embed the scripts. --- .../record-lifecycle/00-assert.yaml | 45 +++++++++++++++++-- .../record-lifecycle/01-assert.yaml | 40 +++++++++++++++-- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml index b57784e..8535a42 100644 --- a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -2,6 +2,45 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "10.0.0.1" "${ZONE_NAME}" - - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" "${ZONE_NAME}" - - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." "${ZONE_NAME}" \ No newline at end of file + - script: | + # Enable verbose debugging + set -x + + echo "=== KUTTL ENVIRONMENT DEBUG ===" + echo "Working Directory: $(pwd)" + echo "Zone: ${ZONE_NAME}" + + check_record() { + RECORD_TYPE=$1 + RECORD_NAME=$2 + EXPECTED_RESULT=$3 + ZONE=$4 + + AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) + if [ -z "$AUTH_NS" ]; then + echo "ERROR: Could not determine authoritative nameserver for $ZONE" + return 1 + fi + + RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") + echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" + + if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" + return 0 + fi + + echo "FAILED: Condition not met for $RECORD_NAME." + return 1 + } + + # Run all checks. If any fail, the chain short-circuits and we exit 1 to trigger a Kuttl retry. + check_record A "e2e-a.${ZONE_NAME}" "10.0.0.1" "${ZONE_NAME}" && \ + check_record AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" "${ZONE_NAME}" && \ + check_record CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." "${ZONE_NAME}" + + if [ $? -ne 0 ]; then + sleep 5 + exit 1 + fi + exit 0 \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml index a2aca3e..d226eb1 100644 --- a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -1,7 +1,39 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 120 +timeout: 60 commands: - - command: ../../scripts/check-dns.sh A "e2e-a.${ZONE_NAME}" "" "${ZONE_NAME}" - - command: ../../scripts/check-dns.sh AAAA "e2e-aaaa.${ZONE_NAME}" "" "${ZONE_NAME}" - - command: ../../scripts/check-dns.sh CNAME "e2e-cname.${ZONE_NAME}" "" "${ZONE_NAME}" \ No newline at end of file + - script: | + set -x + + check_deleted() { + RECORD_TYPE=$1 + RECORD_NAME=$2 + ZONE=$3 + + AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) + if [ -z "$AUTH_NS" ]; then + echo "ERROR: Could not determine authoritative nameserver for $ZONE" + return 1 + fi + + RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") + echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" + + if [ -z "$RESULT" ]; then + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" + return 0 + fi + + echo "FAILED: $RECORD_NAME still exists." + return 1 + } + + check_deleted A "e2e-a.${ZONE_NAME}" "${ZONE_NAME}" && \ + check_deleted AAAA "e2e-aaaa.${ZONE_NAME}" "${ZONE_NAME}" && \ + check_deleted CNAME "e2e-cname.${ZONE_NAME}" "${ZONE_NAME}" + + if [ $? -ne 0 ]; then + sleep 5 + exit 1 + fi + exit 0 \ No newline at end of file From 83d5376b2a4a9613534f492f1eab4507a6b17245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 16:39:14 +0200 Subject: [PATCH 7/9] fix: cleanup --- Makefile | 6 -- tests/e2e/kuttl-test.yaml | 2 +- .../record-lifecycle/00-assert.yaml | 77 +++++++++---------- .../record-lifecycle/01-assert.yaml | 70 ++++++++--------- 4 files changed, 70 insertions(+), 85 deletions(-) diff --git a/Makefile b/Makefile index 4ce262e..ac58d6f 100644 --- a/Makefile +++ b/Makefile @@ -118,12 +118,6 @@ test-e2e-local: docker-build-e2e @echo "=> Preparing test manifests..." rm -rf $(E2E_TMP_DIR) cp -r tests/e2e $(E2E_TMP_DIR) - - # 1. Strip Windows line-endings (CRLF) that cause 'fork/exec no such file' errors in Linux! - sed -i.bak 's/\r$$//' $(E2E_TMP_DIR)/scripts/*.sh - chmod +x $(E2E_TMP_DIR)/scripts/*.sh - - # 2. Only run replacement on YAML files now find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${PROJECT_ID}/$(PROJECT_ID)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.yaml" -exec sed -i.bak "s/\$${ZONE_NAME}/$(ZONE_NAME)/g" {} + find $(E2E_TMP_DIR) -type f -name "*.bak" -delete diff --git a/tests/e2e/kuttl-test.yaml b/tests/e2e/kuttl-test.yaml index cc8f777..eb1cdd2 100644 --- a/tests/e2e/kuttl-test.yaml +++ b/tests/e2e/kuttl-test.yaml @@ -2,4 +2,4 @@ apiVersion: kuttl.dev/v1beta1 kind: TestSuite testDirs: - test-suite -timeout: 60 \ No newline at end of file +timeout: 120 \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml index 8535a42..5cbca51 100644 --- a/tests/e2e/test-suite/record-lifecycle/00-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/00-assert.yaml @@ -2,45 +2,38 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 120 commands: - - script: | - # Enable verbose debugging - set -x - - echo "=== KUTTL ENVIRONMENT DEBUG ===" - echo "Working Directory: $(pwd)" - echo "Zone: ${ZONE_NAME}" - - check_record() { - RECORD_TYPE=$1 - RECORD_NAME=$2 - EXPECTED_RESULT=$3 - ZONE=$4 - - AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) - if [ -z "$AUTH_NS" ]; then - echo "ERROR: Could not determine authoritative nameserver for $ZONE" - return 1 - fi - - RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") - echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" - - if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then - echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" - return 0 - fi - - echo "FAILED: Condition not met for $RECORD_NAME." - return 1 - } - - # Run all checks. If any fail, the chain short-circuits and we exit 1 to trigger a Kuttl retry. - check_record A "e2e-a.${ZONE_NAME}" "10.0.0.1" "${ZONE_NAME}" && \ - check_record AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" "${ZONE_NAME}" && \ - check_record CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." "${ZONE_NAME}" - - if [ $? -ne 0 ]; then - sleep 5 - exit 1 - fi - exit 0 \ No newline at end of file + - script: | + check_record() { + RECORD_TYPE=$1 + RECORD_NAME=$2 + EXPECTED_RESULT=$3 + ZONE=$4 + + AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) + if [ -z "$AUTH_NS" ]; then + echo "ERROR: Could not determine authoritative nameserver for $ZONE" + return 1 + fi + + RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") + echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" + + if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" + return 0 + fi + + echo "FAILED: Condition not met for $RECORD_NAME." + return 1 + } + + # Run all checks. If any fail, the chain short-circuits and we exit 1 to trigger a Kuttl retry. + check_record A "e2e-a.${ZONE_NAME}" "10.0.0.1" "${ZONE_NAME}" && \ + check_record AAAA "e2e-aaaa.${ZONE_NAME}" "2001:db8::1" "${ZONE_NAME}" && \ + check_record CNAME "e2e-cname.${ZONE_NAME}" "e2e-a.${ZONE_NAME}." "${ZONE_NAME}" + + if [ $? -ne 0 ]; then + sleep 5 + exit 1 + fi + exit 0 \ No newline at end of file diff --git a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml index d226eb1..3081bf0 100644 --- a/tests/e2e/test-suite/record-lifecycle/01-assert.yaml +++ b/tests/e2e/test-suite/record-lifecycle/01-assert.yaml @@ -1,39 +1,37 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 60 +timeout: 120 commands: - - script: | - set -x - - check_deleted() { - RECORD_TYPE=$1 - RECORD_NAME=$2 - ZONE=$3 - - AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) - if [ -z "$AUTH_NS" ]; then - echo "ERROR: Could not determine authoritative nameserver for $ZONE" - return 1 - fi - - RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") - echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" - - if [ -z "$RESULT" ]; then - echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" - return 0 - fi - - echo "FAILED: $RECORD_NAME still exists." - return 1 - } - - check_deleted A "e2e-a.${ZONE_NAME}" "${ZONE_NAME}" && \ - check_deleted AAAA "e2e-aaaa.${ZONE_NAME}" "${ZONE_NAME}" && \ - check_deleted CNAME "e2e-cname.${ZONE_NAME}" "${ZONE_NAME}" - - if [ $? -ne 0 ]; then - sleep 5 - exit 1 - fi - exit 0 \ No newline at end of file + - script: | + check_deleted() { + RECORD_TYPE=$1 + RECORD_NAME=$2 + ZONE=$3 + + AUTH_NS=$(dig +short NS "$ZONE" | head -n 1) + if [ -z "$AUTH_NS" ]; then + echo "ERROR: Could not determine authoritative nameserver for $ZONE" + return 1 + fi + + RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "$RECORD_NAME") + echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'" + + if [ -z "$RESULT" ]; then + echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" + return 0 + fi + + echo "FAILED: $RECORD_NAME still exists." + return 1 + } + + check_deleted A "e2e-a.${ZONE_NAME}" "${ZONE_NAME}" && \ + check_deleted AAAA "e2e-aaaa.${ZONE_NAME}" "${ZONE_NAME}" && \ + check_deleted CNAME "e2e-cname.${ZONE_NAME}" "${ZONE_NAME}" + + if [ $? -ne 0 ]; then + sleep 5 + exit 1 + fi + exit 0 \ No newline at end of file From c542676db5abd3b94018e1ac79dd69adc396ef1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Tue, 11 Aug 2026 16:45:25 +0200 Subject: [PATCH 8/9] fix: bump action versions --- .github/workflows/e2e.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 05dbdd6..8c194b8 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: - go-version-file: 'go.mod' # Automatically picks up Go 1.26.5 from your go.mod + go-version-file: 'go.mod' - name: Install Dependencies (Kind, Kuttl, Dig) run: | From 73b0105af1b915b2b44f66cb0c4021c29aace1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20B=C4=9Bh=C3=A1vka?= Date: Wed, 12 Aug 2026 08:54:42 +0200 Subject: [PATCH 9/9] docu: add readme entry, remove unused script, fix cleanup --- .github/workflows/e2e.yml | 2 +- Makefile | 10 +++------ README.md | 13 ++++++++++++ tests/e2e/scripts/check-dns.sh | 38 ---------------------------------- 4 files changed, 17 insertions(+), 46 deletions(-) delete mode 100755 tests/e2e/scripts/check-dns.sh diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 8c194b8..bee0090 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -20,7 +20,7 @@ jobs: - name: Install Dependencies (Kind, Kuttl, Dig) run: | - # Install dig (required for our custom check-dns.sh script) + # Install dig sudo apt-get update && sudo apt-get install -y dnsutils # Install Kind diff --git a/Makefile b/Makefile index ac58d6f..20421a3 100644 --- a/Makefile +++ b/Makefile @@ -97,11 +97,6 @@ docker-build-e2e: build-linux rm ./external-dns-stackit-webhook # Clean up the binary after build # Run this to test the webhook locally -# make test-e2e-local \ - PROJECT_ID="your-project-id" \ - ZONE_NAME="your.test.zone.cloud" \ - AUTH_KEY_PATH="/absolute/path/to/your/sa.json" -.PHONY: test-e2e-local test-e2e-local: docker-build-e2e @if [ -z "$(PROJECT_ID)" ] || [ -z "$(ZONE_NAME)" ] || [ -z "$(AUTH_KEY_PATH)" ]; then \ echo "Error: Missing PROJECT_ID, ZONE_NAME, or AUTH_KEY_PATH environment variables."; \ @@ -132,9 +127,10 @@ test-e2e-local: docker-build-e2e RET=$$?; \ echo "=> Cleaning up local test environment..."; \ kind delete cluster --name stackit-e2e; \ - cd .. && rm -rf $(E2E_TMP_DIR); \ + cd ../.. && rm -rf $(E2E_TMP_DIR); \ exit $$RET .PHONY: clean-e2e-local clean-e2e-local: - kind delete cluster --name stackit-e2e \ No newline at end of file + kind delete cluster --name stackit-e2e + rm -rf $(E2E_TMP_DIR) \ No newline at end of file diff --git a/README.md b/README.md index 2ca0157..9986654 100644 --- a/README.md +++ b/README.md @@ -361,3 +361,16 @@ Test the code: ```bash make test ``` + +### E2E Testing + +End-to-end integration tests are orchestrated using [Kuttl](https://kuttl.dev/) and run against a dynamically generated [Kind](https://kind.sigs.k8s.io/) cluster. The test suite builds the webhook locally, deploys it alongside ExternalDNS, and directly verifies real DNS record propagation (A, AAAA, CNAME) against the STACKIT authoritative nameservers. + +To run the E2E test suite locally, ensure you have Docker and `kind` installed, then execute: + +```bash +make test-e2e-local \ + PROJECT_ID="your-project-id" \ + ZONE_NAME="your.test.zone.cloud" \ + AUTH_KEY_PATH="/absolute/path/to/your/sa.json" +``` diff --git a/tests/e2e/scripts/check-dns.sh b/tests/e2e/scripts/check-dns.sh deleted file mode 100755 index b19e973..0000000 --- a/tests/e2e/scripts/check-dns.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -set -x - -RECORD_TYPE="$1" -RECORD_NAME="$2" -EXPECTED_RESULT="$3" -ZONE_NAME="$4" # Grab it from the 4th argument! - -echo "=== DEBUG: DNS CHECK STARTED ===" -echo "Type: $RECORD_TYPE | Name: $RECORD_NAME | Expected: '$EXPECTED_RESULT' | Zone: '$ZONE_NAME'" - -AUTH_NS=$(dig +short NS "${ZONE_NAME}" | head -n 1) -echo "DEBUG: Discovered Auth NS: '$AUTH_NS'" - -if [ -z "$AUTH_NS" ]; then - echo "ERROR: Could not determine authoritative nameserver for ${ZONE_NAME}" - sleep 5 - exit 1 -fi - -RESULT=$(dig "@$AUTH_NS" -t "$RECORD_TYPE" +short "${RECORD_NAME}") -echo "DEBUG: Dig Result: '$RESULT'" - -if [ -z "$EXPECTED_RESULT" ]; then - if [ -z "$RESULT" ]; then - echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME successfully deleted!" - exit 0 - fi -else - if echo "$RESULT" | grep -q "$EXPECTED_RESULT"; then - echo "SUCCESS: $RECORD_TYPE record $RECORD_NAME verified!" - exit 0 - fi -fi - -echo "FAILED: Condition not met. Retrying in 5s..." -sleep 5 -exit 1 \ No newline at end of file