Files
digiserver-v2/verify-deployment.sh

343 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Production Deployment Verification Script
# Run this before and after production deployment
set -e
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ DigiServer v2 Production Deployment Verification ║"
echo "╚════════════════════════════════════════════════════════════════╝"
TIMESTAMP=$(date +%Y-%m-%d\ %H:%M:%S)
cd "$(dirname "$0")"
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Counters
PASSED=0
FAILED=0
WARNINGS=0
# Helper functions
pass() {
echo -e "${GREEN}${NC} $1"
((PASSED++))
}
fail() {
echo -e "${RED}${NC} $1"
((FAILED++))
}
warn() {
echo -e "${YELLOW}${NC} $1"
((WARNINGS++))
}
info() {
echo -e "${BLUE}${NC} $1"
}
section() {
echo -e "\n${BLUE}═══════════════════════════════════════${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}═══════════════════════════════════════${NC}"
}
# ============================================================================
section "1. Git Status"
# ============================================================================
if git rev-parse --git-dir > /dev/null 2>&1; then
pass "Git repository initialized"
BRANCH=$(git rev-parse --abbrev-ref HEAD)
COMMIT=$(git rev-parse --short HEAD)
info "Current branch: $BRANCH, Commit: $COMMIT"
if [ -n "$(git status --porcelain)" ]; then
warn "Uncommitted changes detected"
git status --short
else
pass "All changes committed"
fi
else
fail "Not a git repository"
fi
# ============================================================================
section "2. Environment Configuration"
# ============================================================================
if [ -f .env ]; then
pass ".env file exists"
else
warn ".env file not found (using defaults or docker-compose environment)"
fi
if [ -f .env.example ]; then
pass ".env.example template exists"
else
warn ".env.example template missing"
fi
# ============================================================================
section "3. Docker Configuration"
# ============================================================================
if command -v docker &> /dev/null; then
pass "Docker installed"
DOCKER_VERSION=$(docker --version | cut -d' ' -f3 | tr -d ',')
info "Docker version: $DOCKER_VERSION"
else
fail "Docker not installed"
fi
if command -v docker-compose &> /dev/null; then
pass "Docker Compose installed"
DC_VERSION=$(docker-compose --version | cut -d' ' -f3 | tr -d ',')
info "Docker Compose version: $DC_VERSION"
else
fail "Docker Compose not installed"
fi
if [ -f docker-compose.yml ]; then
pass "docker-compose.yml exists"
# Validate syntax
if docker-compose config > /dev/null 2>&1; then
pass "docker-compose.yml syntax valid"
else
fail "docker-compose.yml syntax error"
fi
else
fail "docker-compose.yml not found"
fi
# ============================================================================
section "4. Dockerfile & Images"
# ============================================================================
if [ -f Dockerfile ]; then
pass "Dockerfile exists"
# Check for security best practices
if grep -q "HEALTHCHECK" Dockerfile; then
pass "Health check configured"
else
warn "No health check in Dockerfile"
fi
if grep -q "USER appuser" Dockerfile || grep -q "USER.*:1000" Dockerfile; then
pass "Non-root user configured"
else
warn "Root user may be used in container"
fi
if grep -q "FROM.*alpine\|FROM.*slim\|FROM.*distroless" Dockerfile; then
pass "Minimal base image used"
else
warn "Large base image detected"
fi
else
fail "Dockerfile not found"
fi
# ============================================================================
section "5. Python Dependencies"
# ============================================================================
if [ -f requirements.txt ]; then
pass "requirements.txt exists"
PACKAGE_COUNT=$(wc -l < requirements.txt)
info "Total packages: $PACKAGE_COUNT"
# Check for critical packages
for pkg in Flask SQLAlchemy gunicorn flask-cors cryptography; do
if grep -q "$pkg" requirements.txt; then
pass "$pkg installed"
else
warn "$pkg not found in requirements.txt"
fi
done
# Check for specific versions
FLASK_VERSION=$(grep "^Flask==" requirements.txt | cut -d'=' -f3)
SQLALCHEMY_VERSION=$(grep "^SQLAlchemy==" requirements.txt | cut -d'=' -f3)
if [ -n "$FLASK_VERSION" ]; then
info "Flask version: $FLASK_VERSION"
fi
if [ -n "$SQLALCHEMY_VERSION" ]; then
info "SQLAlchemy version: $SQLALCHEMY_VERSION"
fi
else
fail "requirements.txt not found"
fi
# ============================================================================
section "6. Database Configuration"
# ============================================================================
if [ -d migrations ]; then
pass "migrations directory exists"
MIGRATION_COUNT=$(find migrations -name "*.py" | wc -l)
info "Migration files: $MIGRATION_COUNT"
if [ "$MIGRATION_COUNT" -gt 0 ]; then
pass "Database migrations configured"
else
warn "No migration files found"
fi
else
warn "migrations directory not found"
fi
# ============================================================================
section "7. SSL/TLS Certificate"
# ============================================================================
if [ -f data/nginx-ssl/cert.pem ]; then
pass "SSL certificate found"
CERT_EXPIRY=$(openssl x509 -enddate -noout -in data/nginx-ssl/cert.pem 2>/dev/null | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$CERT_EXPIRY" +%s 2>/dev/null || echo 0)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
info "Certificate expires: $CERT_EXPIRY"
info "Days remaining: $DAYS_LEFT days"
if [ "$DAYS_LEFT" -lt 0 ]; then
fail "Certificate has expired!"
elif [ "$DAYS_LEFT" -lt 30 ]; then
warn "Certificate expires in less than 30 days"
else
pass "Certificate is valid"
fi
if [ -f data/nginx-ssl/key.pem ]; then
pass "SSL private key found"
else
warn "SSL private key not found"
fi
else
warn "SSL certificate not found (self-signed required)"
fi
# ============================================================================
section "8. Configuration Files"
# ============================================================================
if [ -f app/config.py ]; then
pass "Flask config.py exists"
if grep -q "class ProductionConfig" app/config.py; then
pass "ProductionConfig class defined"
else
warn "ProductionConfig class missing"
fi
if grep -q "SESSION_COOKIE_SECURE" app/config.py; then
pass "SESSION_COOKIE_SECURE configured"
else
warn "SESSION_COOKIE_SECURE not configured"
fi
else
fail "app/config.py not found"
fi
if [ -f nginx.conf ]; then
pass "nginx.conf exists"
if grep -q "ssl_protocols" nginx.conf; then
pass "SSL protocols configured"
else
warn "SSL protocols not configured"
fi
if grep -q "access-control-allow" nginx.conf; then
pass "CORS headers in nginx"
else
info "CORS headers may be handled by Flask only"
fi
else
warn "nginx.conf not found"
fi
# ============================================================================
section "9. Runtime Verification"
# ============================================================================
if docker-compose ps 2>/dev/null | grep -q "Up"; then
pass "Docker containers are running"
# Check if app is healthy
if docker-compose ps 2>/dev/null | grep -q "digiserver-app.*healthy"; then
pass "DigiServer app container is healthy"
else
warn "DigiServer app container health status unknown"
fi
if docker-compose ps 2>/dev/null | grep -q "digiserver-nginx.*healthy"; then
pass "Nginx container is healthy"
else
warn "Nginx container health status unknown"
fi
else
info "Docker containers not running (will start on deployment)"
fi
# ============================================================================
section "10. Security Best Practices"
# ============================================================================
# Check for hardcoded secrets
if grep -r "SECRET_KEY\|PASSWORD\|API_KEY" app/ 2>/dev/null | grep -v "os.getenv\|config.py\|#" | wc -l | grep -q "^0$"; then
pass "No hardcoded secrets found"
else
warn "Possible hardcoded secrets detected (verify they use os.getenv)"
fi
# Check for debug mode
if grep -q "DEBUG.*=.*True" app/config.py 2>/dev/null; then
fail "DEBUG mode is enabled"
else
pass "DEBUG mode is disabled"
fi
# ============================================================================
section "Summary"
# ============================================================================
echo ""
echo -e "Test Results:"
echo -e " ${GREEN}Passed: $PASSED${NC}"
echo -e " ${YELLOW}Warnings: $WARNINGS${NC}"
echo -e " ${RED}Failed: $FAILED${NC}"
TOTAL=$((PASSED + FAILED + WARNINGS))
PERCENTAGE=$((PASSED * 100 / (PASSED + FAILED)))
if [ "$FAILED" -eq 0 ]; then
echo -e "\n${GREEN}✓ Production Deployment Ready!${NC}"
echo "Recommendation: Safe to deploy to production"
exit 0
elif [ "$FAILED" -le 2 ] && [ "$WARNINGS" -gt 0 ]; then
echo -e "\n${YELLOW}⚠ Deployment Possible with Caution${NC}"
echo "Recommendation: Address warnings before deployment"
exit 0
else
echo -e "\n${RED}✗ Deployment Not Recommended${NC}"
echo "Recommendation: Fix critical failures before deployment"
exit 1
fi