File size: 8,236 Bytes
16cf46c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!/bin/bash

# LeaseGuard Production Deployment Script
# Implements secure deployment practices with health checks and rollback capabilities

set -euo pipefail

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Configuration
APP_NAME="leaseguard"
DEPLOYMENT_ENV=${1:-production}
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/${APP_NAME}"
LOG_FILE="/var/log/${APP_NAME}/deploy_${TIMESTAMP}.log"

# Logging function
log() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
    exit 1
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

# Pre-deployment checks
pre_deployment_checks() {
    log "Starting pre-deployment security checks..."
    
    # Check if running as root
    if [[ $EUID -eq 0 ]]; then
        error "This script should not be run as root"
    fi
    
    # Check required environment variables
    required_vars=(
        "REDIS_PASSWORD"
        "GEMINI_API_KEY"
        "SUPABASE_URL"
        "SUPABASE_ANON_KEY"
        "SESSION_SECRET"
        "ALLOWED_ORIGINS"
    )
    
    for var in "${required_vars[@]}"; do
        if [[ -z "${!var:-}" ]]; then
            error "Required environment variable $var is not set"
        fi
    done
    
    # Check Docker and Docker Compose
    if ! command -v docker &> /dev/null; then
        error "Docker is not installed"
    fi
    
    if ! command -v docker-compose &> /dev/null; then
        error "Docker Compose is not installed"
    fi
    
    # Check SSL certificates
    if [[ ! -f "./ssl/cert.pem" ]] || [[ ! -f "./ssl/key.pem" ]]; then
        error "SSL certificates not found in ./ssl/ directory"
    fi
    
    # Check disk space
    available_space=$(df / | awk 'NR==2 {print $4}')
    if [[ $available_space -lt 1048576 ]]; then  # Less than 1GB
        warning "Low disk space available: ${available_space}KB"
    fi
    
    success "Pre-deployment checks passed"
}

# Security scan
security_scan() {
    log "Running security scan..."
    
    # Check for known vulnerabilities in dependencies
    if command -v npm &> /dev/null; then
        log "Running npm audit..."
        if npm audit --audit-level=high; then
            success "No high-severity vulnerabilities found"
        else
            warning "High-severity vulnerabilities found - review before deployment"
        fi
    fi
    
    # Check Docker image for vulnerabilities (if trivy is available)
    if command -v trivy &> /dev/null; then
        log "Running Trivy security scan..."
        trivy image --severity HIGH,CRITICAL node:18-alpine || warning "Trivy scan found vulnerabilities"
    fi
    
    # Check for secrets in code
    log "Checking for potential secrets in code..."
    if grep -r "password\|secret\|key\|token" . --exclude-dir=node_modules --exclude-dir=.git | grep -v "process.env" | grep -v "REDIS_PASSWORD\|GEMINI_API_KEY\|SUPABASE_ANON_KEY\|SESSION_SECRET"; then
        warning "Potential secrets found in code - review before deployment"
    fi
}

# Backup current deployment
backup_current() {
    log "Creating backup of current deployment..."
    
    mkdir -p "$BACKUP_DIR"
    
    # Backup Docker volumes
    if docker volume ls | grep -q "${APP_NAME}_redis_data"; then
        docker run --rm -v "${APP_NAME}_redis_data:/data" -v "$BACKUP_DIR:/backup" alpine tar czf "/backup/redis_backup_${TIMESTAMP}.tar.gz" -C /data .
        success "Redis data backed up"
    fi
    
    # Backup environment file
    if [[ -f ".env" ]]; then
        cp .env "$BACKUP_DIR/env_backup_${TIMESTAMP}"
        success "Environment file backed up"
    fi
    
    # Backup Docker Compose file
    cp docker-compose.prod.yml "$BACKUP_DIR/compose_backup_${TIMESTAMP}.yml"
    success "Docker Compose file backed up"
}

# Deploy application
deploy_application() {
    log "Deploying LeaseGuard application..."
    
    # Stop existing containers
    log "Stopping existing containers..."
    docker-compose -f docker-compose.prod.yml down --timeout 30 || warning "Failed to stop some containers"
    
    # Pull latest images
    log "Pulling latest images..."
    docker-compose -f docker-compose.prod.yml pull
    
    # Build and start services
    log "Building and starting services..."
    docker-compose -f docker-compose.prod.yml up -d --build
    
    # Wait for services to be healthy
    log "Waiting for services to be healthy..."
    timeout=300  # 5 minutes
    elapsed=0
    
    while [[ $elapsed -lt $timeout ]]; do
        if docker-compose -f docker-compose.prod.yml ps | grep -q "healthy"; then
            success "All services are healthy"
            break
        fi
        
        sleep 10
        elapsed=$((elapsed + 10))
        log "Waiting for healthy services... ($elapsed/$timeout seconds)"
    done
    
    if [[ $elapsed -ge $timeout ]]; then
        error "Services failed to become healthy within timeout"
    fi
}

# Health checks
health_checks() {
    log "Running post-deployment health checks..."
    
    # Check application health
    local max_retries=10
    local retry_count=0
    
    while [[ $retry_count -lt $max_retries ]]; do
        if curl -f -s http://localhost:3000/api/health > /dev/null; then
            success "Application health check passed"
            break
        fi
        
        retry_count=$((retry_count + 1))
        log "Health check attempt $retry_count/$max_retries failed, retrying in 10 seconds..."
        sleep 10
    done
    
    if [[ $retry_count -ge $max_retries ]]; then
        error "Application health check failed after $max_retries attempts"
    fi
    
    # Check Redis connection
    if docker exec leaseguard-redis redis-cli --raw incr ping > /dev/null 2>&1; then
        success "Redis health check passed"
    else
        error "Redis health check failed"
    fi
    
    # Check SSL certificate
    if openssl s_client -connect localhost:443 -servername localhost < /dev/null 2>/dev/null | grep -q "Verify return code: 0"; then
        success "SSL certificate validation passed"
    else
        warning "SSL certificate validation failed"
    fi
    
    # Performance test
    log "Running performance test..."
    response_time=$(curl -w "%{time_total}" -o /dev/null -s http://localhost:3000/api/health)
    if (( $(echo "$response_time < 2.0" | bc -l) )); then
        success "Performance test passed: ${response_time}s response time"
    else
        warning "Performance test warning: ${response_time}s response time"
    fi
}

# Rollback function
rollback() {
    log "Rolling back deployment..."
    
    # Stop current deployment
    docker-compose -f docker-compose.prod.yml down
    
    # Restore from backup
    if [[ -f "$BACKUP_DIR/compose_backup_${TIMESTAMP}.yml" ]]; then
        cp "$BACKUP_DIR/compose_backup_${TIMESTAMP}.yml" docker-compose.prod.yml
        docker-compose -f docker-compose.prod.yml up -d
        success "Rollback completed"
    else
        error "No backup found for rollback"
    fi
}

# Cleanup old backups
cleanup_backups() {
    log "Cleaning up old backups..."
    
    # Keep only last 5 backups
    find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete 2>/dev/null || true
    find "$BACKUP_DIR" -name "env_backup_*" -mtime +7 -delete 2>/dev/null || true
    find "$BACKUP_DIR" -name "compose_backup_*" -mtime +7 -delete 2>/dev/null || true
    
    success "Cleanup completed"
}

# Main deployment function
main() {
    log "Starting LeaseGuard deployment to $DEPLOYMENT_ENV environment"
    
    # Create log directory
    mkdir -p "$(dirname "$LOG_FILE")"
    
    # Set up error handling
    trap 'error "Deployment failed at line $LINENO"' ERR
    trap 'rollback' EXIT
    
    # Run deployment steps
    pre_deployment_checks
    security_scan
    backup_current
    deploy_application
    health_checks
    
    # Remove rollback trap on success
    trap - EXIT
    
    cleanup_backups
    
    success "Deployment completed successfully!"
    log "Deployment log saved to: $LOG_FILE"
}

# Run main function
main "$@"