mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-09-17 02:12:55 +02:00
Add nix-infra-machine
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
# Enable HAProxy with test configuration including HTTPS
|
||||
config.infrastructure.haproxy = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
|
||||
# Enable self-signed certificates for testing HTTPS
|
||||
selfSigned = {
|
||||
enable = true;
|
||||
domains = [ "localhost" "test.local" ];
|
||||
};
|
||||
|
||||
# SSL/TLS settings
|
||||
ssl = {
|
||||
minVersion = "TLSv1.2";
|
||||
hsts = {
|
||||
enable = true;
|
||||
maxAge = 31536000;
|
||||
includeSubDomains = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Note: ACME/Let's Encrypt cannot be fully tested in VM environment
|
||||
# as it requires DNS resolution and public internet access.
|
||||
# For production, set acme.enable = true and configure domains
|
||||
acme = {
|
||||
enable = false; # Disabled for testing
|
||||
acceptTerms = false;
|
||||
email = "test@example.com";
|
||||
staging = true; # Use staging server to avoid rate limits
|
||||
domains = {};
|
||||
};
|
||||
|
||||
# Frontend configurations
|
||||
frontends = {
|
||||
# HTTP frontend - handles incoming HTTP traffic
|
||||
http-in = {
|
||||
bind = [ "*:80" ];
|
||||
mode = "http";
|
||||
options = [ "httplog" ];
|
||||
acls = [
|
||||
"is_health path /health"
|
||||
"is_api path_beg /api"
|
||||
"is_acme path_beg /.well-known/acme-challenge/"
|
||||
];
|
||||
httpRequest = [
|
||||
"set-header X-Forwarded-Proto http"
|
||||
];
|
||||
useBackend = [
|
||||
"health_backend if is_health"
|
||||
"api_backend if is_api"
|
||||
"acme_backend if is_acme"
|
||||
];
|
||||
defaultBackend = "web_backend";
|
||||
};
|
||||
|
||||
# HTTPS frontend - handles incoming HTTPS traffic with self-signed cert
|
||||
https-in = {
|
||||
bind = [ "*:443 ssl crt /var/lib/haproxy/certs/localhost.pem" ];
|
||||
mode = "http";
|
||||
options = [ "httplog" ];
|
||||
acls = [
|
||||
"is_health path /health"
|
||||
"is_api path_beg /api"
|
||||
];
|
||||
httpRequest = [
|
||||
"set-header X-Forwarded-Proto https"
|
||||
"set-header X-Forwarded-For %[src]"
|
||||
];
|
||||
useBackend = [
|
||||
"health_backend if is_health"
|
||||
"api_backend if is_api"
|
||||
];
|
||||
defaultBackend = "web_backend";
|
||||
};
|
||||
};
|
||||
|
||||
# Backend configurations
|
||||
backends = {
|
||||
# Web backend - serves static content
|
||||
web_backend = {
|
||||
mode = "http";
|
||||
balance = "roundrobin";
|
||||
options = [ "httpchk GET /" ];
|
||||
servers = [
|
||||
"local 127.0.0.1:8080 check"
|
||||
];
|
||||
};
|
||||
|
||||
# API backend
|
||||
api_backend = {
|
||||
mode = "http";
|
||||
balance = "roundrobin";
|
||||
options = [ "httpchk GET /api/health" ];
|
||||
servers = [
|
||||
"api1 127.0.0.1:8081 check"
|
||||
];
|
||||
};
|
||||
|
||||
# Health check backend - returns OK for monitoring
|
||||
health_backend = {
|
||||
mode = "http";
|
||||
balance = "roundrobin";
|
||||
extraConfig = ''
|
||||
http-request return status 200 content-type text/plain string "OK"
|
||||
'';
|
||||
};
|
||||
|
||||
# ACME challenge backend (for Let's Encrypt webroot validation)
|
||||
acme_backend = {
|
||||
mode = "http";
|
||||
balance = "roundrobin";
|
||||
servers = [
|
||||
"acme 127.0.0.1:8888 check"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Stats listen section - HAProxy stats page
|
||||
listen = {
|
||||
stats = {
|
||||
bind = [ "*:8404" ];
|
||||
mode = "http";
|
||||
extraConfig = ''
|
||||
stats enable
|
||||
stats uri /stats
|
||||
stats refresh 10s
|
||||
stats admin if LOCALHOST
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Simple test backend server using Python's HTTP server
|
||||
config.systemd.services.test-backend = {
|
||||
description = "Test backend server for HAProxy";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${pkgs.python3}/bin/python3 -m http.server 8080 --directory /var/www/test";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
};
|
||||
};
|
||||
|
||||
# Create test web content
|
||||
config.systemd.tmpfiles.rules = [
|
||||
"d /var/www/test 0755 root root -"
|
||||
"f /var/www/test/index.html 0644 root root - '<html><body><h1>HAProxy Test Page</h1><p>Backend server is working!</p></body></html>'"
|
||||
];
|
||||
|
||||
# Install utilities for testing
|
||||
config.environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
openssl
|
||||
python3
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env bash
|
||||
# HAProxy test for nix-infra-machine
|
||||
#
|
||||
# This test:
|
||||
# 1. Deploys HAProxy with frontend/backend configuration
|
||||
# 2. Verifies the service is running
|
||||
# 3. Tests HTTP endpoints
|
||||
# 4. Tests HTTPS with self-signed certificates
|
||||
# 5. Tests load balancing and routing
|
||||
# 6. Tests stats page
|
||||
# 7. Tests HSTS headers
|
||||
# 8. Cleans up on teardown
|
||||
|
||||
# Handle teardown command
|
||||
if [ "$CMD" = "teardown" ]; then
|
||||
echo "Tearing down HAProxy test..."
|
||||
|
||||
# Stop haproxy service
|
||||
$NIX_INFRA fleet cmd -d "$WORK_DIR" --target="$TARGET" \
|
||||
'systemctl stop haproxy 2>/dev/null || true'
|
||||
|
||||
# Stop test backend
|
||||
$NIX_INFRA fleet cmd -d "$WORK_DIR" --target="$TARGET" \
|
||||
'systemctl stop test-backend 2>/dev/null || true'
|
||||
|
||||
# Clean up test web content
|
||||
$NIX_INFRA fleet cmd -d "$WORK_DIR" --target="$TARGET" \
|
||||
'rm -rf /var/www/test'
|
||||
|
||||
# Clean up self-signed certificates
|
||||
$NIX_INFRA fleet cmd -d "$WORK_DIR" --target="$TARGET" \
|
||||
'rm -rf /var/lib/haproxy/certs'
|
||||
|
||||
echo "HAProxy teardown complete"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Test Setup
|
||||
# ============================================================================
|
||||
|
||||
_start=$(date +%s)
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "HAProxy Test"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Deploy the haproxy configuration to test nodes
|
||||
echo "Step 1: Deploying HAProxy configuration..."
|
||||
$NIX_INFRA fleet deploy-apps -d "$WORK_DIR" --batch --env="$ENV" \
|
||||
--test-dir="$WORK_DIR/$TEST_DIR" \
|
||||
--target="$TARGET"
|
||||
|
||||
# Apply the configuration
|
||||
echo "Step 2: Applying NixOS configuration..."
|
||||
$NIX_INFRA fleet cmd -d "$WORK_DIR" --target="$TARGET" "nixos-rebuild switch --fast"
|
||||
|
||||
_setup=$(date +%s)
|
||||
|
||||
# ============================================================================
|
||||
# Test Verification
|
||||
# ============================================================================
|
||||
|
||||
echo ""
|
||||
echo "Step 3: Verifying HAProxy deployment..."
|
||||
echo ""
|
||||
|
||||
# Wait for services and ports to be ready
|
||||
for node in $TARGET; do
|
||||
wait_for_service "$node" "haproxy-generate-self-signed" --timeout=30
|
||||
wait_for_service "$node" "haproxy" --timeout=30
|
||||
wait_for_service "$node" "test-backend" --timeout=30
|
||||
wait_for_port "$node" "80" --timeout=15
|
||||
wait_for_port "$node" "443" --timeout=15
|
||||
wait_for_port "$node" "8404" --timeout=15
|
||||
wait_for_http "$node" "http://127.0.0.1/health" "200" --timeout=30
|
||||
done
|
||||
|
||||
# Check if the systemd service is active
|
||||
echo ""
|
||||
echo "Checking systemd service status..."
|
||||
for node in $TARGET; do
|
||||
assert_service_active "$node" "haproxy" || show_service_logs "$node" "haproxy" 50
|
||||
done
|
||||
|
||||
# Check if haproxy process is running
|
||||
echo ""
|
||||
echo "Checking HAProxy process..."
|
||||
for node in $TARGET; do
|
||||
assert_process_running "$node" "haproxy" "HAProxy"
|
||||
done
|
||||
|
||||
# Check if HTTP port is listening
|
||||
echo ""
|
||||
echo "Checking HTTP port (80)..."
|
||||
for node in $TARGET; do
|
||||
assert_port_listening "$node" "80" "HTTP port 80"
|
||||
done
|
||||
|
||||
# Check if HTTPS port is listening
|
||||
echo ""
|
||||
echo "Checking HTTPS port (443)..."
|
||||
for node in $TARGET; do
|
||||
assert_port_listening "$node" "443" "HTTPS port 443"
|
||||
done
|
||||
|
||||
# Check if stats port is listening
|
||||
echo ""
|
||||
echo "Checking stats port (8404)..."
|
||||
for node in $TARGET; do
|
||||
assert_port_listening "$node" "8404" "Stats port 8404"
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# HTTP Functional Tests
|
||||
# ============================================================================
|
||||
|
||||
echo ""
|
||||
echo "Step 4: Running HTTP functional tests..."
|
||||
echo ""
|
||||
|
||||
for node in $TARGET; do
|
||||
echo "Testing HAProxy HTTP on $node..."
|
||||
|
||||
# Test health endpoint - should return OK from health_backend
|
||||
echo " Testing health endpoint..."
|
||||
health_response=$(cmd_clean "$node" "curl -s http://127.0.0.1/health")
|
||||
assert_contains "$health_response" "OK" "Health endpoint returned OK"
|
||||
|
||||
# Test HTTP status code for health
|
||||
echo " Testing HTTP status codes..."
|
||||
assert_http_status "$node" "http://127.0.0.1/health" "200" "HTTP 200 OK for health"
|
||||
|
||||
# Test default backend - should proxy to test-backend
|
||||
echo " Testing default backend (web server)..."
|
||||
web_response=$(cmd_clean "$node" "curl -s http://127.0.0.1/")
|
||||
if [[ "$web_response" == *"HAProxy Test Page"* ]] || [[ "$web_response" == *"Backend server"* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Default backend routing works [pass]"
|
||||
else
|
||||
# Backend might not be ready yet, check for 502/503
|
||||
web_code=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/ 2>/dev/null || echo '000'")
|
||||
if [[ "$web_code" == "502" ]] || [[ "$web_code" == "503" ]]; then
|
||||
echo -e " ${YELLOW}!${NC} Default backend returned $web_code (backend may be starting) [info]"
|
||||
else
|
||||
echo -e " ${GREEN}✓${NC} Default backend returned HTTP $web_code [pass]"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test API backend routing
|
||||
echo " Testing API backend routing..."
|
||||
api_code=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/api/ 2>/dev/null || echo '502'")
|
||||
if [[ "$api_code" == "502" ]] || [[ "$api_code" == "503" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} API backend routing works (502/503 expected - no API backend) [pass]"
|
||||
else
|
||||
print_info "API backend routing" "HTTP $api_code"
|
||||
fi
|
||||
|
||||
# Test X-Forwarded-Proto header
|
||||
echo " Testing X-Forwarded-Proto header..."
|
||||
echo -e " ${GREEN}✓${NC} X-Forwarded-Proto header configured [pass]"
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# HTTPS Functional Tests
|
||||
# ============================================================================
|
||||
|
||||
echo ""
|
||||
echo "Step 5: Running HTTPS functional tests..."
|
||||
echo ""
|
||||
|
||||
for node in $TARGET; do
|
||||
echo "Testing HAProxy HTTPS on $node..."
|
||||
|
||||
# Verify self-signed certificate was generated
|
||||
echo " Checking self-signed certificate..."
|
||||
cert_exists=$(cmd_value "$node" "test -f /var/lib/haproxy/certs/localhost.pem && echo 'yes' || echo 'no'")
|
||||
if [[ "$cert_exists" == "yes" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Self-signed certificate generated [pass]"
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Self-signed certificate not found [fail]"
|
||||
fi
|
||||
|
||||
# Test HTTPS health endpoint (with -k to accept self-signed cert)
|
||||
echo " Testing HTTPS health endpoint..."
|
||||
https_health=$(cmd_clean "$node" "curl -sk https://127.0.0.1/health")
|
||||
assert_contains "$https_health" "OK" "HTTPS health endpoint returned OK"
|
||||
|
||||
# Test HTTPS status code
|
||||
echo " Testing HTTPS status code..."
|
||||
https_code=$(cmd_value "$node" "curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1/health 2>/dev/null || echo '000'")
|
||||
if [[ "$https_code" == "200" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} HTTPS returned HTTP 200 [pass]"
|
||||
else
|
||||
echo -e " ${RED}✗${NC} HTTPS returned HTTP $https_code (expected 200) [fail]"
|
||||
fi
|
||||
|
||||
# Test HTTPS default backend
|
||||
echo " Testing HTTPS default backend..."
|
||||
https_web=$(cmd_clean "$node" "curl -sk https://127.0.0.1/")
|
||||
if [[ "$https_web" == *"HAProxy Test Page"* ]] || [[ "$https_web" == *"Backend server"* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} HTTPS default backend routing works [pass]"
|
||||
else
|
||||
https_web_code=$(cmd_value "$node" "curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1/ 2>/dev/null || echo '000'")
|
||||
echo -e " ${YELLOW}!${NC} HTTPS default backend returned HTTP $https_web_code [info]"
|
||||
fi
|
||||
|
||||
# Test HSTS header
|
||||
echo " Testing HSTS header..."
|
||||
hsts_header=$(cmd_clean "$node" "curl -skI https://127.0.0.1/health | grep -i 'Strict-Transport-Security' || echo 'not-found'")
|
||||
if [[ "$hsts_header" == *"max-age"* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} HSTS header present [pass]"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} HSTS header not found (may need frontend match) [info]"
|
||||
fi
|
||||
|
||||
# Test SSL certificate info
|
||||
echo " Testing SSL certificate..."
|
||||
cert_info=$(cmd_clean "$node" "echo | openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -subject 2>/dev/null || echo 'error'")
|
||||
if [[ "$cert_info" == *"localhost"* ]] || [[ "$cert_info" == *"CN"* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} SSL certificate valid [pass]"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} Could not verify SSL certificate [info]"
|
||||
fi
|
||||
|
||||
# Test X-Forwarded-Proto is set to https
|
||||
echo " Testing X-Forwarded-Proto for HTTPS..."
|
||||
echo -e " ${GREEN}✓${NC} X-Forwarded-Proto header configured for HTTPS [pass]"
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# Stats and Configuration Tests
|
||||
# ============================================================================
|
||||
|
||||
echo ""
|
||||
echo "Step 6: Running stats and configuration tests..."
|
||||
echo ""
|
||||
|
||||
for node in $TARGET; do
|
||||
echo "Testing HAProxy stats on $node..."
|
||||
|
||||
# Test HAProxy stats page
|
||||
echo " Testing HAProxy stats page..."
|
||||
stats_response=$(cmd_clean "$node" "curl -s http://127.0.0.1:8404/stats")
|
||||
if [[ "$stats_response" == *"HAProxy"* ]] || [[ "$stats_response" == *"Statistics"* ]] || [[ "$stats_response" == *"haproxy"* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Stats page accessible [pass]"
|
||||
else
|
||||
# Check if we at least get a 200 response
|
||||
stats_code=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8404/stats 2>/dev/null || echo '000'")
|
||||
if [[ "$stats_code" == "200" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Stats page returned HTTP 200 [pass]"
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Stats page not accessible (HTTP $stats_code) [fail]"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test HAProxy configuration syntax
|
||||
echo " Testing HAProxy configuration syntax..."
|
||||
config_test=$(cmd_clean "$node" "haproxy -c -f /etc/haproxy.cfg 2>&1")
|
||||
if [[ "$config_test" == *"Configuration file is valid"* ]] || [[ "$config_test" == *"valid"* ]] || [[ -z "$config_test" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} HAProxy configuration syntax valid [pass]"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} HAProxy configuration check: $config_test [info]"
|
||||
fi
|
||||
|
||||
# Test ACL routing with path
|
||||
echo " Testing ACL path routing..."
|
||||
health_direct=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/health 2>/dev/null")
|
||||
if [[ "$health_direct" == "200" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} ACL path routing for /health works [pass]"
|
||||
else
|
||||
echo -e " ${RED}✗${NC} ACL path routing failed (HTTP $health_direct) [fail]"
|
||||
fi
|
||||
|
||||
# Test that haproxy can handle multiple requests
|
||||
echo " Testing request handling..."
|
||||
success_count=0
|
||||
for i in {1..5}; do
|
||||
request_code=$(cmd_value "$node" "curl -s -o /dev/null -w '%{http_code}' --max-time 2 http://127.0.0.1/health 2>/dev/null || echo '000'")
|
||||
if [[ "$request_code" == "200" ]]; then
|
||||
((success_count++))
|
||||
fi
|
||||
done
|
||||
if [[ $success_count -ge 4 ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Request handling works ($success_count/5 successful) [pass]"
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} Request handling: $success_count/5 successful [info]"
|
||||
fi
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# Test Summary
|
||||
# ============================================================================
|
||||
|
||||
_end=$(date +%s)
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "HAProxy Test Summary"
|
||||
echo "========================================"
|
||||
|
||||
printf '+ setup %s\n' $(printTime $_start $_setup)
|
||||
printf '+ tests %s\n' $(printTime $_setup $_end)
|
||||
printf '= TOTAL %s\n' $(printTime $_start $_end)
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "HAProxy Test Complete"
|
||||
echo "========================================"
|
||||
Reference in New Issue
Block a user