Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: 'go.mod'

- name: Install Helm
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1

- name: Install Dependencies (Kind, Kuttl, Dig)
run: |
# Install dig for DNS assertions
sudo apt-get update && sudo apt-get install -y dnsutils

# Install the latest Kind (v0.32.0)
curl -sSLo ./kind https://kind.sigs.k8s.io/dl/v0.32.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# Install the latest Kuttl (v0.26.0)
curl -sSLo kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.26.0/kubectl-kuttl_0.26.0_linux_x86_64
chmod +x kubectl-kuttl
sudo mv kubectl-kuttl /usr/local/bin/

- name: Prepare STACKIT Service Account Key
run: |
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
81 changes: 81 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,84 @@ 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 ./stackit-cert-manager-webhook -v cmd/webhook/main.go

.PHONY: docker-build-e2e
docker-build-e2e: build-linux
docker build -t stackitcloud/stackit-cert-manager-webhook:e2e -f Dockerfile .
rm ./stackit-cert-manager-webhook

.PHONY: e2e-check-env
e2e-check-env:
@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

.PHONY: e2e-cluster
e2e-cluster: docker-build-e2e
@echo "=> Creating Kind cluster and loading image..."
kind create cluster --name stackit-e2e || true
kind load docker-image stackitcloud/stackit-cert-manager-webhook:e2e --name stackit-e2e

.PHONY: e2e-cert-manager
e2e-cert-manager:
@echo "=> Installing cert-manager via Helm..."
helm repo add jetstack https://charts.jetstack.io --force-update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true

kubectl wait --for=condition=Available --timeout=300s deployment/cert-manager -n cert-manager
kubectl wait --for=condition=Available --timeout=300s deployment/cert-manager-webhook -n cert-manager

.PHONY: e2e-deploy-webhook
e2e-deploy-webhook:
@echo "=> Deploying stackit-cert-manager-webhook..."
kubectl create secret generic stackit-sa-authentication -n cert-manager \
--from-file=sa.json=$(AUTH_KEY_PATH) \
--dry-run=client -o yaml | kubectl apply -f -

helm upgrade --install stackit-cert-manager-webhook ./deploy/stackit \
--namespace cert-manager \
--set image.repository=stackitcloud/stackit-cert-manager-webhook \
--set image.tag=e2e \
--set image.pullPolicy=Never \
--set stackitSaAuthentication.enabled=true \
--set stackitSaAuthentication.secretName=stackit-sa-authentication

kubectl wait --for=condition=available --timeout=120s deployment/stackit-cert-manager-webhook -n cert-manager

.PHONY: e2e-run-kuttl
e2e-run-kuttl:
@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 "=> Running Kuttl Tests..."
cd $(E2E_TMP_DIR) && kubectl kuttl test

.PHONY: clean-e2e-local
clean-e2e-local:
@echo "=> Cleaning up local test environment..."
kind delete cluster --name stackit-e2e
rm -rf $(E2E_TMP_DIR)

# The main target chains the dependencies together
.PHONY: test-e2e-local
test-e2e-local: e2e-check-env e2e-cluster e2e-cert-manager e2e-deploy-webhook
@$(MAKE) e2e-run-kuttl || ( $(MAKE) clean-e2e-local && exit 1 )
@$(MAKE) clean-e2e-local
81 changes: 81 additions & 0 deletions tests/e2e/deploy/external-dns.yaml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions tests/e2e/kuttl-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestSuite
testDirs:
- test-suite
timeout: 300
9 changes: 9 additions & 0 deletions tests/e2e/test-suite/record-lifecycle/00-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: e2e-cert
namespace: default
status:
conditions:
- type: Ready
status: "True"
32 changes: 32 additions & 0 deletions tests/e2e/test-suite/record-lifecycle/00-issuer-cert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: stackit-e2e-issuer
namespace: default
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: e2e-test@stackit.de
privateKeySecretRef:
name: stackit-e2e-issuer-key
solvers:
- dns01:
webhook:
solverName: stackit
groupName: acme.stackit.de
config:
projectId: ${PROJECT_ID}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: e2e-cert
namespace: default
spec:
secretName: e2e-cert-tls
issuerRef:
name: stackit-e2e-issuer
kind: Issuer
commonName: "e2e-cert.${ZONE_NAME}"
dnsNames:
- "e2e-cert.${ZONE_NAME}"
24 changes: 24 additions & 0 deletions tests/e2e/test-suite/record-lifecycle/01-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
timeout: 120
commands:
- script: |
RECORD_NAME="_acme-challenge.e2e-cert.${ZONE_NAME}"
ZONE="${ZONE_NAME}"

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 TXT +short "$RECORD_NAME")
echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'"

if [ -z "$RESULT" ]; then
echo "SUCCESS: TXT record $RECORD_NAME successfully deleted by the webhook!"
exit 0
fi

echo "FAILED: $RECORD_NAME still exists."
exit 1
7 changes: 7 additions & 0 deletions tests/e2e/test-suite/record-lifecycle/01-delete.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
delete:
- apiVersion: cert-manager.io/v1
kind: Certificate
name: e2e-cert
namespace: default
9 changes: 9 additions & 0 deletions tests/e2e/test-suite/wildcard-certificate/00-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: e2e-wildcard-cert
namespace: default
status:
conditions:
- type: Ready
status: "True"
33 changes: 33 additions & 0 deletions tests/e2e/test-suite/wildcard-certificate/00-issuer-cert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: stackit-e2e-wildcard-issuer
namespace: default
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: e2e-wildcard-test@stackit.de
privateKeySecretRef:
name: stackit-e2e-wildcard-issuer-key
solvers:
- dns01:
webhook:
solverName: stackit
groupName: acme.stackit.de
config:
projectId: ${PROJECT_ID}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: e2e-wildcard-cert
namespace: default
spec:
secretName: e2e-wildcard-cert-tls
issuerRef:
name: stackit-e2e-wildcard-issuer
kind: Issuer
commonName: "*.e2e-wildcard.${ZONE_NAME}"
dnsNames:
- "e2e-wildcard.${ZONE_NAME}"
- "*.e2e-wildcard.${ZONE_NAME}"
26 changes: 26 additions & 0 deletions tests/e2e/test-suite/wildcard-certificate/01-assert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
timeout: 120
commands:
- script: |
# For both e2e-wildcard.YOUR_ZONE and *.e2e-wildcard.YOUR_ZONE,
# the challenge record is placed at the exact same location.
RECORD_NAME="_acme-challenge.e2e-wildcard.${ZONE_NAME}"
ZONE="${ZONE_NAME}"

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 TXT +short "$RECORD_NAME")
echo "DEBUG: Dig Result for $RECORD_NAME: '$RESULT'"

if [ -z "$RESULT" ]; then
echo "SUCCESS: TXT record $RECORD_NAME successfully deleted!"
exit 0
fi

echo "FAILED: $RECORD_NAME still exists."
exit 1
7 changes: 7 additions & 0 deletions tests/e2e/test-suite/wildcard-certificate/01-delete.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
delete:
- apiVersion: cert-manager.io/v1
kind: Certificate
name: e2e-wildcard-cert
namespace: default
Loading