Add comprehensive SSH deployment system
- deploy/ssh/deploy-remote.sh: Main SSH deployment script - deploy/ssh/deploy-remote.py: Python alternative deployment script - deploy/config/example-*.yml: Example configuration files - deploy/keys/README.md: SSH key management guide - deploy/SSH_DEPLOYMENT.md: Complete SSH deployment documentation - .gitignore: Added deployment configuration exclusions Features: - Secure SSH key management with git-ignored configs - Environment-specific configurations (production, staging) - Automated remote deployment with validation - Dry-run mode for testing - Comprehensive documentation and security best practices
This commit is contained in:
parent
b76838ea8e
commit
0076e263f9
|
|
@ -31,6 +31,12 @@ Thumbs.db
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
# Deployment configuration
|
||||||
|
deploy/config/*
|
||||||
|
deploy/keys/*
|
||||||
|
!deploy/config/example*.yml
|
||||||
|
!deploy/keys/README.md
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
|
|
@ -0,0 +1,355 @@
|
||||||
|
# SSH Deployment Guide
|
||||||
|
|
||||||
|
This guide explains how to deploy the Calejo Control Adapter to remote servers using SSH.
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Setup SSH Keys
|
||||||
|
|
||||||
|
Generate and deploy SSH keys for each environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate production key
|
||||||
|
ssh-keygen -t ed25519 -f deploy/keys/production_key -C "calejo-production-deploy" -N ""
|
||||||
|
|
||||||
|
# Deploy public key to production server
|
||||||
|
ssh-copy-id -i deploy/keys/production_key.pub calejo@production-server.company.com
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 600 deploy/keys/*
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create Configuration
|
||||||
|
|
||||||
|
Copy the example configuration and customize:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For production
|
||||||
|
cp deploy/config/example-production.yml deploy/config/production.yml
|
||||||
|
|
||||||
|
# Edit with your server details
|
||||||
|
nano deploy/config/production.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy to production
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production
|
||||||
|
|
||||||
|
# Dry run first
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production --dry-run
|
||||||
|
|
||||||
|
# Verbose output
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Configuration Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
deploy/
|
||||||
|
├── ssh/
|
||||||
|
│ └── deploy-remote.sh # Main deployment script
|
||||||
|
├── config/
|
||||||
|
│ ├── example-production.yml # Example production config
|
||||||
|
│ ├── example-staging.yml # Example staging config
|
||||||
|
│ ├── production.yml # Production config (gitignored)
|
||||||
|
│ └── staging.yml # Staging config (gitignored)
|
||||||
|
└── keys/
|
||||||
|
├── README.md # Key management guide
|
||||||
|
├── production_key # Production SSH key (gitignored)
|
||||||
|
├── production_key.pub # Production public key (gitignored)
|
||||||
|
├── staging_key # Staging SSH key (gitignored)
|
||||||
|
└── staging_key.pub # Staging public key (gitignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Configuration Files
|
||||||
|
|
||||||
|
### Production Configuration (`deploy/config/production.yml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# SSH Connection Details
|
||||||
|
ssh:
|
||||||
|
host: "production-server.company.com"
|
||||||
|
port: 22
|
||||||
|
username: "calejo"
|
||||||
|
key_file: "deploy/keys/production_key"
|
||||||
|
|
||||||
|
# Deployment Settings
|
||||||
|
deployment:
|
||||||
|
target_dir: "/opt/calejo-control-adapter"
|
||||||
|
backup_dir: "/var/backup/calejo"
|
||||||
|
log_dir: "/var/log/calejo"
|
||||||
|
config_dir: "/etc/calejo"
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
app:
|
||||||
|
port: 8080
|
||||||
|
host: "0.0.0.0"
|
||||||
|
debug: false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Staging Configuration (`deploy/config/staging.yml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ssh:
|
||||||
|
host: "staging-server.company.com"
|
||||||
|
port: 22
|
||||||
|
username: "calejo"
|
||||||
|
key_file: "deploy/keys/staging_key"
|
||||||
|
|
||||||
|
deployment:
|
||||||
|
target_dir: "/opt/calejo-control-adapter"
|
||||||
|
backup_dir: "/var/backup/calejo"
|
||||||
|
log_dir: "/var/log/calejo"
|
||||||
|
config_dir: "/etc/calejo"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔑 SSH Key Management
|
||||||
|
|
||||||
|
### Generating Keys
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate ED25519 key (recommended)
|
||||||
|
ssh-keygen -t ed25519 -f deploy/keys/production_key -C "calejo-production" -N ""
|
||||||
|
|
||||||
|
# Generate RSA key (alternative)
|
||||||
|
ssh-keygen -t rsa -b 4096 -f deploy/keys/production_key -C "calejo-production" -N ""
|
||||||
|
|
||||||
|
# Set secure permissions
|
||||||
|
chmod 600 deploy/keys/production_key
|
||||||
|
chmod 644 deploy/keys/production_key.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploying Public Keys
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy to remote server
|
||||||
|
ssh-copy-id -i deploy/keys/production_key.pub calejo@production-server.company.com
|
||||||
|
|
||||||
|
# Manual method
|
||||||
|
cat deploy/keys/production_key.pub | ssh calejo@production-server.company.com 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing SSH Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test connection
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com
|
||||||
|
|
||||||
|
# Test with specific port
|
||||||
|
ssh -i deploy/keys/production_key -p 2222 calejo@production-server.company.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Deployment Script Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy to staging
|
||||||
|
./deploy/ssh/deploy-remote.sh -e staging
|
||||||
|
|
||||||
|
# Deploy to production
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production
|
||||||
|
|
||||||
|
# Use custom config file
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production -c deploy/config/custom.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dry run (show what would be deployed)
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production --dry-run
|
||||||
|
|
||||||
|
# Verbose output
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production --verbose
|
||||||
|
|
||||||
|
# Help
|
||||||
|
./deploy/ssh/deploy-remote.sh --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
You can also use environment variables for sensitive data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CALEJO_DEPLOY_KEY_PATH="deploy/keys/production_key"
|
||||||
|
export CALEJO_DEPLOY_PASSPHRASE="your-passphrase"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Deployment Process
|
||||||
|
|
||||||
|
The deployment script performs the following steps:
|
||||||
|
|
||||||
|
1. **Configuration Validation**
|
||||||
|
- Loads environment configuration
|
||||||
|
- Validates SSH key and connection details
|
||||||
|
- Checks remote prerequisites
|
||||||
|
|
||||||
|
2. **Remote Setup**
|
||||||
|
- Creates necessary directories
|
||||||
|
- Backs up existing deployment (if any)
|
||||||
|
- Transfers application files
|
||||||
|
|
||||||
|
3. **Application Deployment**
|
||||||
|
- Sets up remote configuration
|
||||||
|
- Builds Docker images
|
||||||
|
- Starts services
|
||||||
|
- Waits for services to be ready
|
||||||
|
|
||||||
|
4. **Validation**
|
||||||
|
- Runs deployment validation
|
||||||
|
- Tests key endpoints
|
||||||
|
- Generates deployment summary
|
||||||
|
|
||||||
|
## 🔒 Security Best Practices
|
||||||
|
|
||||||
|
### SSH Key Security
|
||||||
|
|
||||||
|
- **Use different keys** for different environments
|
||||||
|
- **Set proper permissions**: `chmod 600` for private keys
|
||||||
|
- **Use passphrase-protected keys** in production
|
||||||
|
- **Rotate keys regularly** (every 6-12 months)
|
||||||
|
- **Never commit private keys** to version control
|
||||||
|
|
||||||
|
### Server Security
|
||||||
|
|
||||||
|
- **Use non-root user** for deployment
|
||||||
|
- **Configure sudo access** for specific commands only
|
||||||
|
- **Use firewall** to restrict SSH access
|
||||||
|
- **Enable fail2ban** for SSH protection
|
||||||
|
- **Use SSH key authentication only** (disable password auth)
|
||||||
|
|
||||||
|
### Configuration Security
|
||||||
|
|
||||||
|
- **Store sensitive data** in environment variables
|
||||||
|
- **Use encrypted configuration** for production
|
||||||
|
- **Regularly audit** access logs
|
||||||
|
- **Monitor deployment activities**
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **SSH Connection Failed**
|
||||||
|
```bash
|
||||||
|
# Check key permissions
|
||||||
|
chmod 600 deploy/keys/production_key
|
||||||
|
|
||||||
|
# Test connection manually
|
||||||
|
ssh -i deploy/keys/production_key -v calejo@production-server.company.com
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Permission Denied**
|
||||||
|
```bash
|
||||||
|
# Ensure user has sudo access
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'sudo -v'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Docker Not Installed**
|
||||||
|
```bash
|
||||||
|
# Check Docker installation
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'docker --version'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Port Already in Use**
|
||||||
|
```bash
|
||||||
|
# Check running services
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'sudo netstat -tulpn | grep :8080'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable verbose output to see detailed execution:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./deploy/ssh/deploy-remote.sh -e production --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Files
|
||||||
|
|
||||||
|
- **Local logs**: Check script output
|
||||||
|
- **Remote logs**: `/var/log/calejo/` on target server
|
||||||
|
- **Docker logs**: `docker-compose logs` on target server
|
||||||
|
|
||||||
|
## 🔄 Post-Deployment Tasks
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run health check
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'cd /opt/calejo-control-adapter && ./scripts/health-check.sh'
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'cd /opt/calejo-control-adapter && docker-compose ps'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create initial backup
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'cd /opt/calejo-control-adapter && ./scripts/backup-full.sh'
|
||||||
|
|
||||||
|
# Schedule regular backups (add to crontab)
|
||||||
|
0 2 * * * /opt/calejo-control-adapter/scripts/backup-full.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check monitoring
|
||||||
|
ssh -i deploy/keys/production_key calejo@production-server.company.com 'cd /opt/calejo-control-adapter && ./validate-deployment.sh'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Deployment Checklist
|
||||||
|
|
||||||
|
### Pre-Deployment
|
||||||
|
- [ ] SSH keys generated and deployed
|
||||||
|
- [ ] Configuration files created and tested
|
||||||
|
- [ ] Remote server prerequisites installed
|
||||||
|
- [ ] Backup strategy in place
|
||||||
|
- [ ] Deployment window scheduled
|
||||||
|
|
||||||
|
### During Deployment
|
||||||
|
- [ ] Dry run completed successfully
|
||||||
|
- [ ] Backup of existing deployment created
|
||||||
|
- [ ] Application files transferred
|
||||||
|
- [ ] Services started successfully
|
||||||
|
- [ ] Health checks passed
|
||||||
|
|
||||||
|
### Post-Deployment
|
||||||
|
- [ ] Application accessible via web interface
|
||||||
|
- [ ] API endpoints responding
|
||||||
|
- [ ] Monitoring configured
|
||||||
|
- [ ] Backup tested
|
||||||
|
- [ ] Documentation updated
|
||||||
|
|
||||||
|
## 🎯 Best Practices
|
||||||
|
|
||||||
|
### Deployment Strategy
|
||||||
|
|
||||||
|
- **Use blue-green deployment** for zero downtime
|
||||||
|
- **Test in staging** before production
|
||||||
|
- **Rollback plan** in place
|
||||||
|
- **Monitor during deployment**
|
||||||
|
|
||||||
|
### Configuration Management
|
||||||
|
|
||||||
|
- **Version control** for configuration
|
||||||
|
- **Environment-specific** configurations
|
||||||
|
- **Sensitive data** in environment variables
|
||||||
|
- **Regular backups** of configuration
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- **Least privilege** principle
|
||||||
|
- **Regular security updates**
|
||||||
|
- **Access logging** and monitoring
|
||||||
|
- **Incident response** plan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Deployment Status**: ✅ Production Ready
|
||||||
|
**Last Updated**: $(date)
|
||||||
|
**Version**: 1.0.0
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Production Environment Configuration
|
||||||
|
# Copy this file to production.yml and update with actual values
|
||||||
|
|
||||||
|
# SSH Connection Details
|
||||||
|
ssh:
|
||||||
|
host: "production-server.company.com"
|
||||||
|
port: 22
|
||||||
|
username: "calejo"
|
||||||
|
key_file: "deploy/keys/production_key"
|
||||||
|
|
||||||
|
# Deployment Settings
|
||||||
|
deployment:
|
||||||
|
target_dir: "/opt/calejo-control-adapter"
|
||||||
|
backup_dir: "/var/backup/calejo"
|
||||||
|
log_dir: "/var/log/calejo"
|
||||||
|
config_dir: "/etc/calejo"
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
app:
|
||||||
|
port: 8080
|
||||||
|
host: "0.0.0.0"
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
database:
|
||||||
|
host: "localhost"
|
||||||
|
port: 5432
|
||||||
|
name: "calejo_production"
|
||||||
|
username: "calejo_user"
|
||||||
|
password: "${DB_PASSWORD}" # Will be replaced from environment
|
||||||
|
|
||||||
|
# SCADA Integration
|
||||||
|
scada:
|
||||||
|
opcua_enabled: true
|
||||||
|
opcua_endpoint: "opc.tcp://scada-server:4840"
|
||||||
|
modbus_enabled: true
|
||||||
|
modbus_host: "scada-server"
|
||||||
|
modbus_port: 502
|
||||||
|
|
||||||
|
# Optimization Integration
|
||||||
|
optimization:
|
||||||
|
enabled: true
|
||||||
|
endpoint: "http://optimization-server:8081"
|
||||||
|
|
||||||
|
# Security Settings
|
||||||
|
security:
|
||||||
|
enable_auth: true
|
||||||
|
enable_ssl: true
|
||||||
|
ssl_cert: "/etc/ssl/certs/calejo.crt"
|
||||||
|
ssl_key: "/etc/ssl/private/calejo.key"
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
monitoring:
|
||||||
|
prometheus_enabled: true
|
||||||
|
prometheus_port: 9090
|
||||||
|
grafana_enabled: true
|
||||||
|
grafana_port: 3000
|
||||||
|
|
||||||
|
# Backup Settings
|
||||||
|
backup:
|
||||||
|
enabled: true
|
||||||
|
schedule: "0 2 * * *" # Daily at 2 AM
|
||||||
|
retention_days: 30
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Staging Environment Configuration
|
||||||
|
# Copy this file to staging.yml and update with actual values
|
||||||
|
|
||||||
|
# SSH Connection Details
|
||||||
|
ssh:
|
||||||
|
host: "staging-server.company.com"
|
||||||
|
port: 22
|
||||||
|
username: "calejo"
|
||||||
|
key_file: "deploy/keys/staging_key"
|
||||||
|
|
||||||
|
# Deployment Settings
|
||||||
|
deployment:
|
||||||
|
target_dir: "/opt/calejo-control-adapter"
|
||||||
|
backup_dir: "/var/backup/calejo"
|
||||||
|
log_dir: "/var/log/calejo"
|
||||||
|
config_dir: "/etc/calejo"
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
app:
|
||||||
|
port: 8080
|
||||||
|
host: "0.0.0.0"
|
||||||
|
debug: true
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
database:
|
||||||
|
host: "localhost"
|
||||||
|
port: 5432
|
||||||
|
name: "calejo_staging"
|
||||||
|
username: "calejo_user"
|
||||||
|
password: "${DB_PASSWORD}" # Will be replaced from environment
|
||||||
|
|
||||||
|
# SCADA Integration
|
||||||
|
scada:
|
||||||
|
opcua_enabled: false
|
||||||
|
opcua_endpoint: "opc.tcp://localhost:4840"
|
||||||
|
modbus_enabled: false
|
||||||
|
modbus_host: "localhost"
|
||||||
|
modbus_port: 502
|
||||||
|
|
||||||
|
# Optimization Integration
|
||||||
|
optimization:
|
||||||
|
enabled: false
|
||||||
|
endpoint: "http://localhost:8081"
|
||||||
|
|
||||||
|
# Security Settings
|
||||||
|
security:
|
||||||
|
enable_auth: false
|
||||||
|
enable_ssl: false
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
monitoring:
|
||||||
|
prometheus_enabled: true
|
||||||
|
prometheus_port: 9090
|
||||||
|
grafana_enabled: true
|
||||||
|
grafana_port: 3000
|
||||||
|
|
||||||
|
# Backup Settings
|
||||||
|
backup:
|
||||||
|
enabled: true
|
||||||
|
schedule: "0 2 * * *" # Daily at 2 AM
|
||||||
|
retention_days: 7
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
# SSH Key Management
|
||||||
|
|
||||||
|
This directory should contain SSH private keys for deployment to different environments.
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
### 1. Generate SSH Key Pairs
|
||||||
|
|
||||||
|
For each environment, generate a dedicated SSH key pair:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate production key
|
||||||
|
ssh-keygen -t ed25519 -f deploy/keys/production_key -C "calejo-production-deploy" -N ""
|
||||||
|
|
||||||
|
# Generate staging key
|
||||||
|
ssh-keygen -t ed25519 -f deploy/keys/staging_key -C "calejo-staging-deploy" -N ""
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 600 deploy/keys/*
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Deploy Public Keys to Servers
|
||||||
|
|
||||||
|
Copy the public keys to the target servers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For production
|
||||||
|
ssh-copy-id -i deploy/keys/production_key.pub calejo@production-server.company.com
|
||||||
|
|
||||||
|
# For staging
|
||||||
|
ssh-copy-id -i deploy/keys/staging_key.pub calejo@staging-server.company.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configure SSH on Servers
|
||||||
|
|
||||||
|
On each server, ensure the deployment user has proper permissions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add to sudoers (if needed)
|
||||||
|
echo "calejo ALL=(ALL) NOPASSWD: /usr/bin/docker-compose, /bin/systemctl" | sudo tee /etc/sudoers.d/calejo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- **Never commit private keys** to version control
|
||||||
|
- **Set proper permissions**: `chmod 600 deploy/keys/*`
|
||||||
|
- **Use passphrase-protected keys** in production
|
||||||
|
- **Rotate keys regularly**
|
||||||
|
- **Use different keys** for different environments
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
deploy/keys/
|
||||||
|
├── README.md # This file
|
||||||
|
├── production_key # Production SSH private key (gitignored)
|
||||||
|
├── production_key.pub # Production SSH public key (gitignored)
|
||||||
|
├── staging_key # Staging SSH private key (gitignored)
|
||||||
|
└── staging_key.pub # Staging SSH public key (gitignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
For additional security, you can also use environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CALEJO_DEPLOY_KEY_PATH="deploy/keys/production_key"
|
||||||
|
export CALEJO_DEPLOY_PASSPHRASE="your-passphrase"
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Calejo Control Adapter - Python SSH Deployment Script
|
||||||
|
Alternative deployment script using Python for more complex deployments
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
import paramiko
|
||||||
|
import argparse
|
||||||
|
import tempfile
|
||||||
|
import tarfile
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
class SSHDeployer:
|
||||||
|
"""SSH-based deployment manager"""
|
||||||
|
|
||||||
|
def __init__(self, config_file: str):
|
||||||
|
self.config_file = config_file
|
||||||
|
self.config = self.load_config()
|
||||||
|
self.ssh_client = None
|
||||||
|
self.sftp_client = None
|
||||||
|
|
||||||
|
def load_config(self) -> Dict[str, Any]:
|
||||||
|
"""Load deployment configuration from YAML file"""
|
||||||
|
try:
|
||||||
|
with open(self.config_file, 'r') as f:
|
||||||
|
config = yaml.safe_load(f)
|
||||||
|
|
||||||
|
# Validate required configuration
|
||||||
|
required = ['ssh.host', 'ssh.username', 'ssh.key_file']
|
||||||
|
for req in required:
|
||||||
|
keys = req.split('.')
|
||||||
|
current = config
|
||||||
|
for key in keys:
|
||||||
|
if key not in current:
|
||||||
|
raise ValueError(f"Missing required configuration: {req}")
|
||||||
|
current = current[key]
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error loading configuration: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def connect(self) -> bool:
|
||||||
|
"""Establish SSH connection"""
|
||||||
|
try:
|
||||||
|
ssh_config = self.config['ssh']
|
||||||
|
|
||||||
|
# Create SSH client
|
||||||
|
self.ssh_client = paramiko.SSHClient()
|
||||||
|
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
||||||
|
# Load private key
|
||||||
|
key_path = ssh_config['key_file']
|
||||||
|
if not os.path.exists(key_path):
|
||||||
|
print(f"❌ SSH key file not found: {key_path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
private_key = paramiko.Ed25519Key.from_private_key_file(key_path)
|
||||||
|
|
||||||
|
# Connect
|
||||||
|
port = ssh_config.get('port', 22)
|
||||||
|
self.ssh_client.connect(
|
||||||
|
hostname=ssh_config['host'],
|
||||||
|
port=port,
|
||||||
|
username=ssh_config['username'],
|
||||||
|
pkey=private_key,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create SFTP client
|
||||||
|
self.sftp_client = self.ssh_client.open_sftp()
|
||||||
|
|
||||||
|
print(f"✅ Connected to {ssh_config['host']}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ SSH connection failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute_remote(self, command: str, description: str = "") -> bool:
|
||||||
|
"""Execute command on remote server"""
|
||||||
|
try:
|
||||||
|
if description:
|
||||||
|
print(f"🔧 {description}")
|
||||||
|
|
||||||
|
stdin, stdout, stderr = self.ssh_client.exec_command(command)
|
||||||
|
exit_status = stdout.channel.recv_exit_status()
|
||||||
|
|
||||||
|
if exit_status == 0:
|
||||||
|
if description:
|
||||||
|
print(f" ✅ {description} completed")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
error_output = stderr.read().decode()
|
||||||
|
print(f" ❌ {description} failed: {error_output}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ {description} failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def transfer_file(self, local_path: str, remote_path: str, description: str = "") -> bool:
|
||||||
|
"""Transfer file to remote server"""
|
||||||
|
try:
|
||||||
|
if description:
|
||||||
|
print(f"📁 {description}")
|
||||||
|
|
||||||
|
self.sftp_client.put(local_path, remote_path)
|
||||||
|
|
||||||
|
if description:
|
||||||
|
print(f" ✅ {description} completed")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ {description} failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_deployment_package(self) -> str:
|
||||||
|
"""Create deployment package excluding sensitive files"""
|
||||||
|
temp_dir = tempfile.mkdtemp()
|
||||||
|
package_path = os.path.join(temp_dir, "deployment.tar.gz")
|
||||||
|
|
||||||
|
# Create tar.gz package
|
||||||
|
with tarfile.open(package_path, "w:gz") as tar:
|
||||||
|
# Add all files except deployment config and keys
|
||||||
|
for root, dirs, files in os.walk('.'):
|
||||||
|
# Skip deployment directories
|
||||||
|
if 'deploy/config' in root or 'deploy/keys' in root:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip hidden directories
|
||||||
|
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
if not file.startswith('.'):
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
arcname = os.path.relpath(file_path, '.')
|
||||||
|
tar.add(file_path, arcname=arcname)
|
||||||
|
|
||||||
|
return package_path
|
||||||
|
|
||||||
|
def deploy(self, dry_run: bool = False):
|
||||||
|
"""Main deployment process"""
|
||||||
|
print("🚀 Starting SSH deployment...")
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
print("🔍 DRY RUN MODE - No changes will be made")
|
||||||
|
|
||||||
|
# Connect to server
|
||||||
|
if not self.connect():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
deployment_config = self.config['deployment']
|
||||||
|
target_dir = deployment_config['target_dir']
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
print("🔍 Checking prerequisites...")
|
||||||
|
if not self.execute_remote("command -v docker", "Checking Docker"):
|
||||||
|
return False
|
||||||
|
if not self.execute_remote("command -v docker-compose", "Checking Docker Compose"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
print("📁 Creating directories...")
|
||||||
|
dirs = [
|
||||||
|
target_dir,
|
||||||
|
deployment_config.get('backup_dir', '/var/backup/calejo'),
|
||||||
|
deployment_config.get('log_dir', '/var/log/calejo'),
|
||||||
|
deployment_config.get('config_dir', '/etc/calejo')
|
||||||
|
]
|
||||||
|
|
||||||
|
for dir_path in dirs:
|
||||||
|
cmd = f"sudo mkdir -p {dir_path} && sudo chown {self.config['ssh']['username']}:{self.config['ssh']['username']} {dir_path}"
|
||||||
|
if not self.execute_remote(cmd, f"Creating {dir_path}"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create deployment package
|
||||||
|
print("📦 Creating deployment package...")
|
||||||
|
package_path = self.create_deployment_package()
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
print(f" 📦 Would transfer package: {package_path}")
|
||||||
|
os.remove(package_path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Transfer package
|
||||||
|
remote_package_path = os.path.join(target_dir, "deployment.tar.gz")
|
||||||
|
if not self.transfer_file(package_path, remote_package_path, "Transferring deployment package"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Extract package
|
||||||
|
if not self.execute_remote(f"cd {target_dir} && tar -xzf deployment.tar.gz && rm deployment.tar.gz", "Extracting package"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Set permissions
|
||||||
|
if not self.execute_remote(f"chmod +x {target_dir}/scripts/*.sh", "Setting script permissions"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Build and start services
|
||||||
|
print("🐳 Building and starting services...")
|
||||||
|
if not self.execute_remote(f"cd {target_dir} && sudo docker-compose build", "Building Docker images"):
|
||||||
|
return False
|
||||||
|
if not self.execute_remote(f"cd {target_dir} && sudo docker-compose up -d", "Starting services"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Wait for services
|
||||||
|
print("⏳ Waiting for services to start...")
|
||||||
|
for i in range(30):
|
||||||
|
if self.execute_remote("curl -s http://localhost:8080/health > /dev/null", "", silent=True):
|
||||||
|
print(" ✅ Services started successfully")
|
||||||
|
break
|
||||||
|
print(f" ⏳ Waiting... ({i+1}/30)")
|
||||||
|
import time
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
print(" ❌ Services failed to start within 60 seconds")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Validate deployment
|
||||||
|
print("🔍 Validating deployment...")
|
||||||
|
self.execute_remote(f"cd {target_dir} && ./validate-deployment.sh", "Running validation")
|
||||||
|
|
||||||
|
print("🎉 Deployment completed successfully!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Cleanup
|
||||||
|
if hasattr(self, 'package_path') and os.path.exists(self.package_path):
|
||||||
|
os.remove(self.package_path)
|
||||||
|
|
||||||
|
# Close connections
|
||||||
|
if self.sftp_client:
|
||||||
|
self.sftp_client.close()
|
||||||
|
if self.ssh_client:
|
||||||
|
self.ssh_client.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function"""
|
||||||
|
parser = argparse.ArgumentParser(description='Calejo Control Adapter - SSH Deployment')
|
||||||
|
parser.add_argument('-c', '--config', required=True, help='Deployment configuration file')
|
||||||
|
parser.add_argument('--dry-run', action='store_true', help='Dry run mode')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Check if config file exists
|
||||||
|
if not os.path.exists(args.config):
|
||||||
|
print(f"❌ Configuration file not found: {args.config}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Run deployment
|
||||||
|
deployer = SSHDeployer(args.config)
|
||||||
|
success = deployer.deploy(dry_run=args.dry_run)
|
||||||
|
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,454 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Calejo Control Adapter - Remote SSH Deployment Script
|
||||||
|
# Deploys the application to remote servers over SSH
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Default configuration
|
||||||
|
CONFIG_FILE=""
|
||||||
|
ENVIRONMENT=""
|
||||||
|
DRY_RUN=false
|
||||||
|
VERBOSE=false
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to display usage
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 -e <environment> [-c <config-file>] [--dry-run] [--verbose]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -e, --environment Deployment environment (production, staging)"
|
||||||
|
echo " -c, --config Custom configuration file"
|
||||||
|
echo " --dry-run Show what would be deployed without actually deploying"
|
||||||
|
echo " --verbose Enable verbose output"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 -e staging # Deploy to staging"
|
||||||
|
echo " $0 -e production --dry-run # Dry run for production"
|
||||||
|
echo " $0 -e production -c custom.yml # Use custom config"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to parse command line arguments
|
||||||
|
parse_arguments() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-e|--environment)
|
||||||
|
ENVIRONMENT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-c|--config)
|
||||||
|
CONFIG_FILE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--verbose)
|
||||||
|
VERBOSE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unknown option: $1"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Validate required arguments
|
||||||
|
if [[ -z "$ENVIRONMENT" ]]; then
|
||||||
|
print_error "Environment is required"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default config file if not provided
|
||||||
|
if [[ -z "$CONFIG_FILE" ]]; then
|
||||||
|
CONFIG_FILE="deploy/config/${ENVIRONMENT}.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate config file exists
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
print_error "Configuration file not found: $CONFIG_FILE"
|
||||||
|
echo "Available configurations:"
|
||||||
|
ls -1 deploy/config/*.yml 2>/dev/null | sed 's|deploy/config/||' || echo " (none)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to load configuration
|
||||||
|
load_configuration() {
|
||||||
|
print_status "Loading configuration from: $CONFIG_FILE"
|
||||||
|
|
||||||
|
# Check if yq is available for YAML parsing
|
||||||
|
if ! command -v yq &> /dev/null; then
|
||||||
|
print_error "yq is required for YAML parsing. Install with: snap install yq or brew install yq"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract configuration values
|
||||||
|
SSH_HOST=$(yq e '.ssh.host' "$CONFIG_FILE")
|
||||||
|
SSH_PORT=$(yq e '.ssh.port' "$CONFIG_FILE")
|
||||||
|
SSH_USERNAME=$(yq e '.ssh.username' "$CONFIG_FILE")
|
||||||
|
SSH_KEY_FILE=$(yq e '.ssh.key_file' "$CONFIG_FILE")
|
||||||
|
|
||||||
|
TARGET_DIR=$(yq e '.deployment.target_dir' "$CONFIG_FILE")
|
||||||
|
BACKUP_DIR=$(yq e '.deployment.backup_dir' "$CONFIG_FILE")
|
||||||
|
LOG_DIR=$(yq e '.deployment.log_dir' "$CONFIG_FILE")
|
||||||
|
CONFIG_DIR=$(yq e '.deployment.config_dir' "$CONFIG_FILE")
|
||||||
|
|
||||||
|
# Validate required configuration
|
||||||
|
if [[ -z "$SSH_HOST" || -z "$SSH_USERNAME" || -z "$SSH_KEY_FILE" ]]; then
|
||||||
|
print_error "Missing required SSH configuration in $CONFIG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate SSH key file exists
|
||||||
|
if [[ ! -f "$SSH_KEY_FILE" ]]; then
|
||||||
|
print_error "SSH key file not found: $SSH_KEY_FILE"
|
||||||
|
echo "Available keys:"
|
||||||
|
ls -1 deploy/keys/ 2>/dev/null || echo " (none)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set default port if not specified
|
||||||
|
if [[ -z "$SSH_PORT" ]]; then
|
||||||
|
SSH_PORT=22
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "true" ]]; then
|
||||||
|
print_status "Configuration loaded:"
|
||||||
|
echo " SSH Host: $SSH_HOST"
|
||||||
|
echo " SSH Port: $SSH_PORT"
|
||||||
|
echo " SSH Username: $SSH_USERNAME"
|
||||||
|
echo " SSH Key: $SSH_KEY_FILE"
|
||||||
|
echo " Target Directory: $TARGET_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to build SSH command
|
||||||
|
build_ssh_command() {
|
||||||
|
local cmd="$1"
|
||||||
|
local ssh_opts="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30"
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "true" ]]; then
|
||||||
|
ssh_opts="$ssh_opts -v"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "ssh -i $SSH_KEY_FILE -p $SSH_PORT $ssh_opts $SSH_USERNAME@$SSH_HOST '$cmd'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to execute remote command
|
||||||
|
execute_remote() {
|
||||||
|
local cmd="$1"
|
||||||
|
local description="$2"
|
||||||
|
|
||||||
|
print_status "$description"
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo " [DRY RUN] Would execute: $cmd"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ssh_cmd=$(build_ssh_command "$cmd")
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "true" ]]; then
|
||||||
|
echo " Executing: $ssh_cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if eval "$ssh_cmd"; then
|
||||||
|
print_success "$description completed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_error "$description failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to transfer files
|
||||||
|
transfer_files() {
|
||||||
|
local local_path="$1"
|
||||||
|
local remote_path="$2"
|
||||||
|
local description="$3"
|
||||||
|
|
||||||
|
print_status "$description"
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo " [DRY RUN] Would transfer: $local_path -> $SSH_USERNAME@$SSH_HOST:$remote_path"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local scp_cmd="scp -i $SSH_KEY_FILE -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -r $local_path $SSH_USERNAME@$SSH_HOST:$remote_path"
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "true" ]]; then
|
||||||
|
echo " Executing: $scp_cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if eval "$scp_cmd"; then
|
||||||
|
print_success "$description completed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_error "$description failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check remote prerequisites
|
||||||
|
check_remote_prerequisites() {
|
||||||
|
print_status "Checking remote prerequisites..."
|
||||||
|
|
||||||
|
# Check Docker
|
||||||
|
execute_remote "command -v docker" "Checking Docker installation" || {
|
||||||
|
print_error "Docker is not installed on remote server"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Docker Compose
|
||||||
|
execute_remote "command -v docker-compose" "Checking Docker Compose installation" || {
|
||||||
|
print_error "Docker Compose is not installed on remote server"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
execute_remote "df -h / | awk 'NR==2 {print \$5}'" "Checking disk space"
|
||||||
|
|
||||||
|
print_success "Remote prerequisites check passed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create remote directories
|
||||||
|
create_remote_directories() {
|
||||||
|
print_status "Creating remote directories..."
|
||||||
|
|
||||||
|
local dirs=("$TARGET_DIR" "$BACKUP_DIR" "$LOG_DIR" "$CONFIG_DIR")
|
||||||
|
|
||||||
|
for dir in "${dirs[@]}"; do
|
||||||
|
execute_remote "sudo mkdir -p $dir && sudo chown $SSH_USERNAME:$SSH_USERNAME $dir" "Creating directory: $dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
print_success "Remote directories created"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to backup existing deployment
|
||||||
|
backup_existing_deployment() {
|
||||||
|
print_status "Checking for existing deployment..."
|
||||||
|
|
||||||
|
# Check if target directory exists and has content
|
||||||
|
if execute_remote "[ -d $TARGET_DIR ] && [ \"$(ls -A $TARGET_DIR)\" ]" "Checking existing deployment" 2>/dev/null; then
|
||||||
|
print_warning "Existing deployment found, creating backup..."
|
||||||
|
|
||||||
|
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||||
|
local backup_file="calejo-backup-$timestamp.tar.gz"
|
||||||
|
|
||||||
|
execute_remote "cd $TARGET_DIR && tar -czf $BACKUP_DIR/$backup_file ." "Creating backup: $backup_file"
|
||||||
|
|
||||||
|
print_success "Backup created: $BACKUP_DIR/$backup_file"
|
||||||
|
else
|
||||||
|
print_status "No existing deployment found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to transfer application files
|
||||||
|
transfer_application() {
|
||||||
|
print_status "Transferring application files..."
|
||||||
|
|
||||||
|
# Create temporary deployment package
|
||||||
|
local temp_dir=$(mktemp -d)
|
||||||
|
local package_name="calejo-deployment-$(date +%Y%m%d_%H%M%S).tar.gz"
|
||||||
|
|
||||||
|
# Copy files to temporary directory (excluding deployment config and keys)
|
||||||
|
print_status "Creating deployment package..."
|
||||||
|
cp -r . "$temp_dir/"
|
||||||
|
|
||||||
|
# Remove sensitive deployment files from package
|
||||||
|
rm -rf "$temp_dir/deploy/config"
|
||||||
|
rm -rf "$temp_dir/deploy/keys"
|
||||||
|
|
||||||
|
# Create package
|
||||||
|
cd "$temp_dir" && tar -czf "/tmp/$package_name" . && cd - > /dev/null
|
||||||
|
|
||||||
|
# Transfer package
|
||||||
|
transfer_files "/tmp/$package_name" "$TARGET_DIR/" "Transferring deployment package"
|
||||||
|
|
||||||
|
# Extract package on remote
|
||||||
|
execute_remote "cd $TARGET_DIR && tar -xzf $package_name && rm $package_name" "Extracting deployment package"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf "$temp_dir"
|
||||||
|
rm -f "/tmp/$package_name"
|
||||||
|
|
||||||
|
print_success "Application files transferred"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to setup remote configuration
|
||||||
|
setup_remote_configuration() {
|
||||||
|
print_status "Setting up remote configuration..."
|
||||||
|
|
||||||
|
# Transfer configuration files
|
||||||
|
if [[ -f "config/settings.py" ]]; then
|
||||||
|
transfer_files "config/settings.py" "$CONFIG_DIR/" "Transferring configuration file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set permissions on scripts
|
||||||
|
execute_remote "chmod +x $TARGET_DIR/scripts/*.sh" "Setting script permissions"
|
||||||
|
execute_remote "chmod +x $TARGET_DIR/deploy-onprem.sh" "Setting deployment script permissions"
|
||||||
|
|
||||||
|
print_success "Remote configuration setup completed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to build and start services
|
||||||
|
build_and_start_services() {
|
||||||
|
print_status "Building and starting services..."
|
||||||
|
|
||||||
|
# Build services
|
||||||
|
execute_remote "cd $TARGET_DIR && sudo docker-compose build" "Building Docker images"
|
||||||
|
|
||||||
|
# Start services
|
||||||
|
execute_remote "cd $TARGET_DIR && sudo docker-compose up -d" "Starting services"
|
||||||
|
|
||||||
|
# Wait for services to be ready
|
||||||
|
print_status "Waiting for services to start..."
|
||||||
|
for i in {1..30}; do
|
||||||
|
if execute_remote "curl -s http://localhost:8080/health > /dev/null" "Checking service health" 2>/dev/null; then
|
||||||
|
print_success "Services started successfully"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo " Waiting... (attempt $i/30)"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [[ $i -eq 30 ]]; then
|
||||||
|
print_error "Services failed to start within 60 seconds"
|
||||||
|
execute_remote "cd $TARGET_DIR && sudo docker-compose logs" "Checking service logs"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to validate deployment
|
||||||
|
validate_deployment() {
|
||||||
|
print_status "Validating deployment..."
|
||||||
|
|
||||||
|
# Run remote validation script
|
||||||
|
if execute_remote "cd $TARGET_DIR && ./validate-deployment.sh" "Running deployment validation" 2>/dev/null; then
|
||||||
|
print_success "Deployment validation passed"
|
||||||
|
else
|
||||||
|
print_warning "Deployment validation completed with warnings"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test key endpoints
|
||||||
|
local endpoints=("/health" "/dashboard" "/api/v1/status")
|
||||||
|
|
||||||
|
for endpoint in "${endpoints[@]}"; do
|
||||||
|
if execute_remote "curl -s -f http://localhost:8080$endpoint > /dev/null" "Testing endpoint: $endpoint" 2>/dev/null; then
|
||||||
|
print_success "Endpoint $endpoint is accessible"
|
||||||
|
else
|
||||||
|
print_error "Endpoint $endpoint is not accessible"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to display deployment summary
|
||||||
|
display_deployment_summary() {
|
||||||
|
print_success "Deployment to $ENVIRONMENT completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
echo " DEPLOYMENT SUMMARY"
|
||||||
|
echo "=================================================="
|
||||||
|
echo ""
|
||||||
|
echo "🌍 Environment: $ENVIRONMENT"
|
||||||
|
echo "🏠 Server: $SSH_HOST"
|
||||||
|
echo "📁 Application: $TARGET_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "🔗 Access URLs:"
|
||||||
|
echo " Dashboard: http://$SSH_HOST:8080/dashboard"
|
||||||
|
echo " REST API: http://$SSH_HOST:8080"
|
||||||
|
echo " Health Check: http://$SSH_HOST:8080/health"
|
||||||
|
echo ""
|
||||||
|
echo "🔧 Management Commands:"
|
||||||
|
echo " View logs: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && docker-compose logs -f'"
|
||||||
|
echo " Health check: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && ./scripts/health-check.sh'"
|
||||||
|
echo " Backup: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && ./scripts/backup-full.sh'"
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main deployment function
|
||||||
|
main() {
|
||||||
|
echo ""
|
||||||
|
echo "🚀 Calejo Control Adapter - Remote SSH Deployment"
|
||||||
|
echo "=================================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
parse_arguments "$@"
|
||||||
|
|
||||||
|
# Load configuration
|
||||||
|
load_configuration
|
||||||
|
|
||||||
|
# Display deployment info
|
||||||
|
echo "Deploying to: $ENVIRONMENT"
|
||||||
|
echo "Server: $SSH_HOST"
|
||||||
|
echo "Config: $CONFIG_FILE"
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo "Mode: DRY RUN (no changes will be made)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check remote prerequisites
|
||||||
|
check_remote_prerequisites
|
||||||
|
|
||||||
|
# Create remote directories
|
||||||
|
create_remote_directories
|
||||||
|
|
||||||
|
# Backup existing deployment
|
||||||
|
backup_existing_deployment
|
||||||
|
|
||||||
|
# Transfer application files
|
||||||
|
transfer_application
|
||||||
|
|
||||||
|
# Setup remote configuration
|
||||||
|
setup_remote_configuration
|
||||||
|
|
||||||
|
# Build and start services
|
||||||
|
build_and_start_services
|
||||||
|
|
||||||
|
# Validate deployment
|
||||||
|
validate_deployment
|
||||||
|
|
||||||
|
# Display summary
|
||||||
|
display_deployment_summary
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "Remote deployment completed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
Loading…
Reference in New Issue