Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f7fd400
Initial plan
Copilot Feb 15, 2026
56c7c46
Add HTTP health checks and certificate validation to E2E tests
Copilot Feb 15, 2026
a028a39
Add ArgoCD CLI authentication test
Copilot Feb 15, 2026
7cda01c
Document E2E test coverage in README
Copilot Feb 15, 2026
bf3339c
Address security concerns in E2E tests
Copilot Feb 15, 2026
948debb
Fix exit status check and improve /etc/hosts validation
Copilot Feb 15, 2026
94747a1
Fix grep patterns and exit status checks in E2E tests
Copilot Feb 15, 2026
175c81d
Add error handling for ArgoCD CLI download and checksum verification
Copilot Feb 15, 2026
0445650
Improve grep patterns and ArgoCD CLI installation robustness
Copilot Feb 15, 2026
1ad6e1e
Add checksum match count validation and improve comments
Copilot Feb 15, 2026
30ddbf8
Refactor E2E tests: extract scripts and install tools in workflow
Copilot Feb 16, 2026
d829bd3
Update README with test prerequisites and script locations
Copilot Feb 16, 2026
22e3516
Add checksum validation in workflow and README instructions
Copilot Feb 16, 2026
57f1d50
Use more restrictive permissions (555) for ArgoCD CLI binary
Copilot Feb 16, 2026
d124578
Fix ArgoCD checksum filename: use cli_checksums.txt instead of argocd…
Copilot Feb 16, 2026
5045c80
Merge branch 'main' of https://github.com/lentzi90/personal-cloud int…
Copilot Feb 28, 2026
d52f8cf
Update E2E tests for Gateway API: replace nginx-ingress with envoy-ga…
Copilot Feb 28, 2026
28c9e0d
Use word boundaries in /etc/hosts grep patterns for precision
Copilot Feb 28, 2026
eb22156
Fix gateway connectivity: use MetalLB-assigned IP instead of 127.0.0.1
Copilot Feb 28, 2026
c376305
Use kubectl port-forward instead of MetalLB IP for gateway access
Copilot Mar 6, 2026
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,33 @@ sudo nmcli c delete peer1
```
## Chainsaw test

The Chainsaw test suite validates the deployment of all applications in a KinD cluster and includes end-to-end tests to verify actual application functionality.

### Running the tests

```bash
sudo kind create cluster --config=kind-config.yaml
sudo kind get kubeconfig > kubeconfig.yaml
export KUBECONFIG=kubeconfig.yaml
export BITWARDEN_ACCESS_TOKEN=...
chainsaw test
```

### Test Coverage

The test suite includes:

1. **Resource Deployment Tests**: Validates that all Kubernetes resources (Deployments, Pods, etc.) are created and healthy
2. **ArgoCD Application Sync Tests**: Ensures all ArgoCD Applications reach "Healthy" and "Synced" status
3. **HTTP Health Checks**: Tests that applications are accessible via HTTP/HTTPS endpoints
- Nginx ingress controller health
- ArgoCD UI accessibility
- Keycloak UI accessibility
- OpenCloud (Nextcloud) UI accessibility
4. **Certificate Validation**: Verifies TLS certificates are properly configured and not expired
- ArgoCD certificate
- Keycloak certificate
- OpenCloud certificate
5. **Authentication Tests**: Validates login and API access
- ArgoCD CLI login with admin credentials
- Application listing via ArgoCD API
275 changes: 275 additions & 0 deletions chainsaw-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,278 @@ spec:
- podLogs:
name: opencloud
namespace: opencloud
# End-to-End Tests: HTTP Health Checks
- try:
- script:
timeout: 2m
content: |
echo "=== Testing HTTP Endpoints ==="

# Function to test HTTP endpoint
test_http_endpoint() {
local name=$1
local url=$2
local expected_status=${3:-200}

echo "Testing $name at $url..."
response=$(curl -k -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 30 "$url" || echo "000")

if [ "$response" = "$expected_status" ]; then
echo "✓ $name is accessible (HTTP $response)"
return 0
else
echo "✗ $name returned HTTP $response (expected $expected_status)"
return 1
fi
}

# Wait for ingress to be ready
echo "Waiting for nginx ingress controller to be ready..."
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120s

# Add hosts entries for local testing (check if not already present with correct IP)
grep -q "^127\.0\.0\.1[[:space:]]\+argocd\.local[[:space:]]*$" /etc/hosts || echo "127.0.0.1 argocd.local" | sudo tee -a /etc/hosts
grep -q "^127\.0\.0\.1[[:space:]]\+keycloak\.local[[:space:]]*$" /etc/hosts || echo "127.0.0.1 keycloak.local" | sudo tee -a /etc/hosts
grep -q "^127\.0\.0\.1[[:space:]]\+opencloud\.local[[:space:]]*$" /etc/hosts || echo "127.0.0.1 opencloud.local" | sudo tee -a /etc/hosts

# Test ingress controller health
echo ""
echo "--- Testing Ingress Controller ---"
# Note: /healthz endpoint returns 404 when accessed without a valid ingress rule
# This is expected behavior - the 404 confirms the ingress controller is running
# and processing requests (just not routing this particular path)
test_http_endpoint "Nginx Ingress Controller" "http://localhost/healthz" 404

# Test ArgoCD
echo ""
echo "--- Testing ArgoCD ---"
# ArgoCD redirects HTTP to HTTPS, so we test HTTPS endpoint
test_http_endpoint "ArgoCD UI" "https://argocd.local/" 200

# Test Keycloak
echo ""
echo "--- Testing Keycloak ---"
# Keycloak may redirect, so we follow redirects
response=$(curl -k -s -o /dev/null -w "%{http_code}" -L --connect-timeout 10 --max-time 30 "https://keycloak.local/" || echo "000")
if [ "$response" = "200" ]; then
echo "✓ Keycloak is accessible (HTTP $response)"
else
echo "✗ Keycloak returned HTTP $response (expected 200)"
exit 1
fi

# Test Opencloud (Nextcloud)
echo ""
echo "--- Testing Opencloud ---"
# Nextcloud typically returns 302 for root path (redirects to login)
response=$(curl -k -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 30 "https://opencloud.local/" || echo "000")
if [ "$response" = "200" ] || [ "$response" = "302" ] || [ "$response" = "303" ]; then
echo "✓ Opencloud is accessible (HTTP $response)"
else
echo "✗ Opencloud returned HTTP $response (expected 200/302/303)"
exit 1
fi

echo ""
echo "=== All HTTP health checks passed ==="
catch:
- describe:
apiVersion: v1
kind: Service
name: ingress-nginx-controller
namespace: ingress-nginx
- podLogs:
namespace: ingress-nginx
selector: app.kubernetes.io/component=controller
# End-to-End Tests: Certificate Validation
- try:
- script:
timeout: 1m
content: |
echo "=== Testing TLS Certificates ==="

# Function to check certificate
check_certificate() {
local name=$1
local host=$2

echo "Checking certificate for $name ($host)..."

# Get certificate info
cert_info=$(echo | openssl s_client -servername "$host" -connect "$host:443" 2>/dev/null | openssl x509 -noout -subject -issuer -dates 2>/dev/null || echo "FAILED")

if [ "$cert_info" = "FAILED" ]; then
echo "✗ Failed to retrieve certificate for $name"
return 1
fi

echo "$cert_info"

# Check if certificate is valid (not expired)
if echo | openssl s_client -servername "$host" -connect "$host:443" 2>/dev/null | openssl x509 -noout -checkend 0 >/dev/null 2>&1; then
echo "✓ Certificate for $name is valid"
return 0
else
echo "✗ Certificate for $name is expired or invalid"
return 1
fi
}

# Test certificates
echo "--- Testing ArgoCD Certificate ---"
check_certificate "ArgoCD" "argocd.local"

echo ""
echo "--- Testing Keycloak Certificate ---"
check_certificate "Keycloak" "keycloak.local"

echo ""
echo "--- Testing Opencloud Certificate ---"
check_certificate "Opencloud" "opencloud.local"

echo ""
echo "=== All certificate checks passed ==="
catch:
- describe:
apiVersion: cert-manager.io/v1
kind: Certificate
namespace: argocd
- describe:
apiVersion: cert-manager.io/v1
kind: Certificate
namespace: keycloak
- describe:
apiVersion: cert-manager.io/v1
kind: Certificate
namespace: opencloud
# End-to-End Tests: ArgoCD CLI Authentication
- try:
- script:
timeout: 2m
content: |
echo "=== Testing ArgoCD CLI Authentication ==="

# Install ArgoCD CLI with checksum verification
echo "Installing ArgoCD CLI..."

# Use jq for reliable JSON parsing
ARGOCD_VERSION=$(curl -s https://api.github.com/repos/argoproj/argo-cd/releases/latest | jq -r .tag_name)

if [ -z "$ARGOCD_VERSION" ] || [ "$ARGOCD_VERSION" = "null" ]; then
echo "✗ Failed to retrieve ArgoCD version from GitHub API"
exit 1
fi

echo "Latest ArgoCD version: ${ARGOCD_VERSION}"

# Download ArgoCD CLI binary
if ! curl -sSL -f -o /tmp/argocd-linux-amd64 "https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-linux-amd64"; then
echo "✗ Failed to download ArgoCD CLI binary"
exit 1
fi

# Download checksums file
if ! curl -sSL -f -o /tmp/argocd-checksums.txt "https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-checksums.txt"; then
echo "✗ Failed to download ArgoCD checksums"
exit 1
fi

# Verify checksum (use subshell to avoid changing working directory)
echo "Verifying checksum..."
checksum_line=$(grep "argocd-linux-amd64$" /tmp/argocd-checksums.txt)

if [ -z "$checksum_line" ]; then
echo "✗ Could not find checksum for argocd-linux-amd64 in checksums file"
exit 1
fi

# Ensure we got exactly one match
match_count=$(echo "$checksum_line" | wc -l)
if [ "$match_count" -ne 1 ]; then
echo "✗ Found $match_count matches for argocd-linux-amd64, expected exactly 1"
exit 1
fi

if (cd /tmp && echo "$checksum_line" | sha256sum -c -); then
echo "✓ Checksum verification passed"
sudo install -m 555 /tmp/argocd-linux-amd64 /usr/local/bin/argocd
argocd version --client
else
echo "✗ Checksum verification failed"
exit 1
fi

# Get admin password from secret and use it securely
echo ""
echo "Retrieving ArgoCD admin password..."
# Store password in a temporary file with restricted permissions
password_file=$(mktemp)
chmod 600 "$password_file"
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d > "$password_file"

if [ ! -s "$password_file" ]; then
echo "✗ Failed to retrieve ArgoCD admin password"
rm -f "$password_file"
exit 1
fi

echo "✓ Retrieved admin password"

# Login to ArgoCD using port-forward (password via stdin)
echo ""
echo "Logging in to ArgoCD via port-forward..."
cat "$password_file" | argocd login --port-forward --port-forward-namespace argocd \
--username=admin --password-stdin --insecure
login_status=$?

# Clean up password file
rm -f "$password_file"

if [ $login_status -eq 0 ]; then
echo "✓ Successfully logged in to ArgoCD"
else
echo "✗ Failed to log in to ArgoCD"
exit 1
fi

# Verify we can list applications
echo ""
echo "Listing ArgoCD applications..."
apps=$(argocd app list --output name 2>&1)
list_status=$?

if [ $list_status -eq 0 ]; then
echo "✓ Successfully retrieved application list"
echo "Applications found:"
echo "$apps"
else
echo "✗ Failed to list applications"
echo "$apps"
exit 1
fi

# Verify specific applications are present
echo ""
echo "Verifying expected applications are deployed..."
for app in argocd cert-manager keycloak opencloud; do
if echo "$apps" | grep -q "$app"; then
echo "✓ Application '$app' is deployed"
else
echo "⚠ Application '$app' not found in list"
fi
done

echo ""
echo "=== ArgoCD CLI authentication test passed ==="
catch:
- describe:
apiVersion: v1
kind: Secret
name: argocd-initial-admin-secret
namespace: argocd
- podLogs:
namespace: argocd
selector: app.kubernetes.io/name=argocd-server
Loading