Compare commits

..

No commits in common. "2beb0d1436f3eef0d782f7cfd2150b794fa72052" and "94da8687b1a1bd612d4ef4b47de643ac26f0bf11" have entirely different histories.

25 changed files with 572 additions and 6 deletions

View File

@ -97,8 +97,8 @@ copy_files() {
# Copy scripts
cp scripts/* $DEPLOYMENT_DIR/scripts/
cp deploy/test-deployment.sh $DEPLOYMENT_DIR/
cp tests/test_dashboard_local.py $DEPLOYMENT_DIR/
cp test-deployment.sh $DEPLOYMENT_DIR/
cp test_dashboard_local.py $DEPLOYMENT_DIR/
# Copy monitoring configuration
cp -r monitoring/* $DEPLOYMENT_DIR/monitoring/

40
pytest-mock.ini Normal file
View File

@ -0,0 +1,40 @@
[tool:pytest]
# Configuration for mock service tests
# Test discovery
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# Output formatting
addopts =
-v
--tb=short
--strict-markers
--strict-config
--disable-warnings
# Markers
markers =
mock: Tests that require mock services
scada: Tests for SCADA functionality
optimizer: Tests for optimizer functionality
integration: Integration tests
e2e: End-to-end tests
slow: Slow running tests
# Filter warnings
filterwarnings =
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
# Test timeout (seconds)
timeout = 30
# Coverage configuration (if coverage is installed)
# --cov=src
# --cov-report=term-missing
# --cov-report=html
# JUnit XML output (for CI/CD)
# junit_family = xunit2

526
setup-server-backup.sh Normal file
View File

@ -0,0 +1,526 @@
#!/bin/bash
# Calejo Control Adapter - One-Click Server Setup Script
# Single command to provision server, install dependencies, deploy application, and start dashboard
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
ENVIRONMENT="production"
SERVER_HOST=""
SSH_USERNAME=""
SSH_KEY_FILE=""
AUTO_DETECT=true
VERBOSE=false
DRY_RUN=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 "Calejo Control Adapter - One-Click Server Setup"
echo "=================================================="
echo ""
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -e, --environment Deployment environment (production, staging) [default: production]"
echo " -h, --host Server hostname or IP address"
echo " -u, --user SSH username"
echo " -k, --key SSH private key file"
echo " --no-auto Disable auto-detection (manual configuration)"
echo " --verbose Enable verbose output"
echo " --dry-run Show what would be done without making changes"
echo " --help Show this help message"
echo ""
echo "Examples:"
echo " $0 # Auto-detect and setup local machine"
echo " $0 -h 192.168.1.100 -u ubuntu -k ~/.ssh/id_rsa # Setup remote server"
echo " $0 --dry-run # Show setup steps without executing"
echo ""
}
# Function to read deployment configuration from files
read_deployment_config() {
local config_dir="deploy"
# Read from production.yml if it exists
if [[ -f "$config_dir/config/production.yml" ]]; then
print_status "Reading configuration from $config_dir/config/production.yml"
# Extract values from production.yml
if [[ -z "$SSH_HOST" ]]; then
SSH_HOST=$(grep -E "^\s*host:\s*" "$config_dir/config/production.yml" | head -1 | sed 's/^[[:space:]]*host:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
if [[ -z "$SSH_USERNAME" ]]; then
SSH_USERNAME=$(grep -E "^\s*username:\s*" "$config_dir/config/production.yml" | head -1 | sed 's/^[[:space:]]*username:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
if [[ -z "$SSH_KEY_FILE" ]]; then
SSH_KEY_FILE=$(grep -E "^\s*key_file:\s*" "$config_dir/config/production.yml" | head -1 | sed 's/^[[:space:]]*key_file:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
fi
# Read from staging.yml if it exists and environment is staging
if [[ "$ENVIRONMENT" == "staging" && -f "$config_dir/config/staging.yml" ]]; then
print_status "Reading configuration from $config_dir/config/staging.yml"
if [[ -z "$SSH_HOST" ]]; then
SSH_HOST=$(grep -E "^\s*host:\s*" "$config_dir/config/staging.yml" | head -1 | sed 's/^[[:space:]]*host:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
if [[ -z "$SSH_USERNAME" ]]; then
SSH_USERNAME=$(grep -E "^\s*username:\s*" "$config_dir/config/staging.yml" | head -1 | sed 's/^[[:space:]]*username:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
if [[ -z "$SSH_KEY_FILE" ]]; then
SSH_KEY_FILE=$(grep -E "^\s*key_file:\s*" "$config_dir/config/staging.yml" | head -1 | sed 's/^[[:space:]]*key_file:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//' | tr -d '\r')
fi
fi
# Check for existing remote deployment script configuration
if [[ -f "$config_dir/ssh/deploy-remote.sh" ]]; then
print_status "Found existing remote deployment script: $config_dir/ssh/deploy-remote.sh"
# Extract default values from deploy-remote.sh
if [[ -z "$SSH_HOST" ]]; then
SSH_HOST=$(grep -E "SSH_HOST=" "$config_dir/ssh/deploy-remote.sh" | head -1 | cut -d'=' -f2 | tr -d '\"' | tr -d '\'')
fi
if [[ -z "$SSH_USERNAME" ]]; then
SSH_USERNAME=$(grep -E "SSH_USER=" "$config_dir/ssh/deploy-remote.sh" | head -1 | cut -d'=' -f2 | tr -d '\"' | tr -d '\'')
fi
if [[ -z "$SSH_KEY_FILE" ]]; then
SSH_KEY_FILE=$(grep -E "SSH_KEY=" "$config_dir/ssh/deploy-remote.sh" | head -1 | cut -d'=' -f2 | tr -d '\"' | tr -d '\'')
fi
fi
# Set defaults if still empty
ENVIRONMENT=${ENVIRONMENT:-production}
SSH_HOST=${SSH_HOST:-localhost}
SSH_USERNAME=${SSH_USERNAME:-$USER}
SSH_KEY_FILE=${SSH_KEY_FILE:-~/.ssh/id_rsa}
# Use SSH_HOST as SERVER_HOST if not specified
SERVER_HOST=${SERVER_HOST:-$SSH_HOST}
}
# Function to parse command line arguments
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
-e|--environment)
ENVIRONMENT="$2"
shift 2
;;
-h|--host)
SERVER_HOST="$2"
AUTO_DETECT=false
shift 2
;;
-u|--user)
SSH_USERNAME="$2"
AUTO_DETECT=false
shift 2
;;
-k|--key)
SSH_KEY_FILE="$2"
AUTO_DETECT=false
shift 2
;;
--no-auto)
AUTO_DETECT=false
shift
;;
--verbose)
VERBOSE=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--help)
usage
exit 0
;;
*)
print_error "Unknown option: $1"
usage
exit 1
;;
esac
done
}
# Function to detect if running locally or needs remote setup
detect_deployment_type() {
if [[ -n "$SERVER_HOST" && "$SERVER_HOST" != "localhost" && "$SERVER_HOST" != "127.0.0.1" ]]; then
echo "remote"
else
echo "local"
fi
}
# Function to check local prerequisites
check_local_prerequisites() {
print_status "Checking local prerequisites..."
# Check if script is running with sufficient privileges
if [[ $EUID -eq 0 ]]; then
print_warning "Running as root - this is not recommended for security reasons"
fi
# Check Docker
if ! command -v docker &> /dev/null; then
print_error "Docker is not installed locally"
echo "Please install Docker first: https://docs.docker.com/get-docker/"
exit 1
fi
# Check Docker Compose
if ! command -v docker-compose &> /dev/null; then
print_error "Docker Compose is not installed locally"
echo "Please install Docker Compose first: https://docs.docker.com/compose/install/"
exit 1
fi
print_success "Local prerequisites check passed"
}
# Function to check remote prerequisites via SSH
check_remote_prerequisites() {
print_status "Checking remote server prerequisites..."
local ssh_cmd="ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SERVER_HOST"
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY RUN] Would check remote prerequisites"
return 0
fi
# Check Docker
if ! $ssh_cmd "command -v docker" &> /dev/null; then
print_error "Docker is not installed on remote server"
return 1
fi
# Check Docker Compose
if ! $ssh_cmd "command -v docker-compose" &> /dev/null; then
print_error "Docker Compose is not installed on remote server"
return 1
fi
# Check disk space
local disk_usage=$($ssh_cmd "df / | awk 'NR==2 {print \$5}' | sed 's/%//'")
if [[ $disk_usage -gt 90 ]]; then
print_warning "Low disk space on remote server: ${disk_usage}%"
fi
print_success "Remote prerequisites check passed"
}
# Function to setup local deployment
setup_local_deployment() {
print_status "Setting up local deployment..."
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY RUN] Would setup local deployment"
return 0
fi
# Create necessary directories
mkdir -p ./data/postgres
mkdir -p ./logs
mkdir -p ./certs
# Set permissions
chmod 755 ./data
chmod 755 ./logs
chmod 700 ./certs
# Generate default configuration if not exists
if [[ ! -f ".env" ]]; then
print_status "Creating default configuration..."
cp config/.env.example .env
# Generate secure JWT secret
local jwt_secret=$(openssl rand -hex 32 2>/dev/null || echo "default-secret-change-in-production")
sed -i.bak "s/your-secret-key-change-in-production/$jwt_secret/" .env
rm -f .env.bak
print_success "Default configuration created with secure JWT secret"
fi
# Build and start services
print_status "Building and starting services..."
docker-compose up --build -d
# Wait for services to be ready
wait_for_services "localhost"
print_success "Local deployment completed successfully"
}
# Function to setup remote deployment
setup_remote_deployment() {
print_status "Setting up remote deployment on $SERVER_HOST..."
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY RUN] Would setup remote deployment on $SERVER_HOST"
return 0
fi
# Use existing deployment script
if [[ -f "deploy/ssh/deploy-remote.sh" ]]; then
print_status "Using existing remote deployment script..."
# Create temporary configuration
local temp_config=$(mktemp)
cat > "$temp_config" << EOF
ssh:
host: $SERVER_HOST
port: 22
username: $SSH_USERNAME
key_file: $SSH_KEY_FILE
deployment:
target_dir: /opt/calejo-control-adapter
backup_dir: /var/backup/calejo
log_dir: /var/log/calejo
config_dir: /etc/calejo
EOF
# Run deployment
./deploy/ssh/deploy-remote.sh -e "$ENVIRONMENT" -c "$temp_config"
# Cleanup
rm -f "$temp_config"
else
print_error "Remote deployment script not found"
return 1
fi
print_success "Remote deployment completed successfully"
}
# Function to wait for services to be ready
wait_for_services() {
local host="$1"
local max_attempts=30
local attempt=1
print_status "Waiting for services to start..."
while [[ $attempt -le $max_attempts ]]; do
if curl -s "http://$host:8080/health" > /dev/null 2>&1; then
print_success "Services are ready and responding"
return 0
fi
echo " Waiting... (attempt $attempt/$max_attempts)"
sleep 5
((attempt++))
done
print_error "Services failed to start within expected time"
return 1
}
# Function to generate SSL certificates for production
generate_ssl_certificates() {
if [[ "$ENVIRONMENT" == "production" ]]; then
print_status "Setting up SSL certificates for production..."
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY RUN] Would generate SSL certificates"
return 0
fi
mkdir -p ./certs
# Generate self-signed certificate for development
# In production, you should use Let's Encrypt or proper CA
if openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ./certs/server.key \
-out ./certs/server.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 2>/dev/null; then
print_success "SSL certificates generated"
else
print_warning "SSL certificate generation failed - using development mode"
fi
print_success "SSL certificates configured"
fi
}
# Function to display setup completion message
display_completion_message() {
local deployment_type="$1"
local host="$2"
echo ""
echo "=================================================="
echo " SETUP COMPLETED SUCCESSFULLY!"
echo "=================================================="
echo ""
echo "🎉 Calejo Control Adapter is now running!"
echo ""
echo "🌍 Access URLs:"
echo " Dashboard: http://$host:8080/dashboard"
echo " REST API: http://$host:8080"
echo " Health Check: http://$host:8080/health"
echo ""
echo "🔧 Next Steps:"
echo " 1. Open the dashboard in your browser"
echo " 2. Configure your SCADA systems and hardware"
echo " 3. Set up safety limits and user accounts"
echo " 4. Integrate with your existing infrastructure"
echo ""
echo "📚 Documentation:"
echo " Full documentation: ./docs/"
echo " Quick start: ./docs/INSTALLATION_CONFIGURATION.md"
echo " Dashboard guide: ./docs/OPERATIONS_MAINTENANCE.md"
echo ""
if [[ "$deployment_type" == "local" ]]; then
echo "💡 Local Development Tips:"
echo " - View logs: docker-compose logs -f"
echo " - Stop services: docker-compose down"
echo " - Restart: docker-compose up -d"
else
echo "💡 Remote Server Tips:"
echo " - View logs: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$host 'cd /opt/calejo-control-adapter && docker-compose logs -f'"
echo " - Stop services: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$host 'cd /opt/calejo-control-adapter && docker-compose down'"
echo " - Restart: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$host 'cd /opt/calejo-control-adapter && docker-compose up -d'"
fi
echo ""
echo "=================================================="
echo ""
}
# Function to validate setup
validate_setup() {
local host="$1"
print_status "Validating setup..."
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY RUN] Would validate setup"
return 0
fi
# Test health endpoint
if ! curl -s "http://$host:8080/health" > /dev/null; then
print_error "Health check failed"
return 1
fi
# Test dashboard endpoint
if ! curl -s "http://$host:8080/dashboard" > /dev/null; then
print_error "Dashboard check failed"
return 1
fi
# Test API endpoint
if ! curl -s "http://$host:8080/api/v1/status" > /dev/null; then
print_warning "API status check failed (may require authentication)"
fi
print_success "Setup validation passed"
return 0
}
# Main setup function
main() {
echo ""
echo "🚀 Calejo Control Adapter - One-Click Server Setup"
echo "=================================================="
echo ""
# Parse command line arguments
parse_arguments "$@"
# Read deployment configuration from files
read_deployment_config
# Detect deployment type
local deployment_type=$(detect_deployment_type)
# Display setup information
echo "Setup Configuration:"
echo " Environment: $ENVIRONMENT"
echo " Deployment: $deployment_type"
if [[ "$deployment_type" == "remote" ]]; then
echo " Server: $SERVER_HOST"
echo " User: $SSH_USERNAME"
else
echo " Server: localhost"
fi
if [[ "$DRY_RUN" == "true" ]]; then
echo " Mode: DRY RUN"
fi
echo ""
# Check prerequisites
if [[ "$deployment_type" == "local" ]]; then
check_local_prerequisites
else
if [[ -z "$SERVER_HOST" || -z "$SSH_USERNAME" || -z "$SSH_KEY_FILE" ]]; then
print_error "Remote deployment requires --host, --user, and --key parameters"
usage
exit 1
fi
check_remote_prerequisites
fi
# Generate SSL certificates for production
generate_ssl_certificates
# Perform deployment
if [[ "$deployment_type" == "local" ]]; then
setup_local_deployment
local final_host="localhost"
else
setup_remote_deployment
local final_host="$SERVER_HOST"
fi
# Validate setup
validate_setup "$final_host"
# Display completion message
display_completion_message "$deployment_type" "$final_host"
echo ""
print_success "One-click setup completed!"
echo ""
}
# Run main function
main "$@"

View File

@ -37,14 +37,14 @@ class E2ETestRunner:
try:
# Start mock SCADA server
self.mock_scada_process = subprocess.Popen(
[sys.executable, "tests/mocks/mock-scada-server.py"],
[sys.executable, "mock-scada-server.py"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# Start mock optimization server
self.mock_optimization_process = subprocess.Popen(
[sys.executable, "tests/mocks/mock-optimization-server.py"],
[sys.executable, "mock-optimization-server.py"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
@ -377,7 +377,7 @@ def main():
print("="*60)
# Check if required files exist
required_files = ["tests/mocks/mock-scada-server.py", "tests/mocks/mock-optimization-server.py", "src/main.py"]
required_files = ["mock-scada-server.py", "mock-optimization-server.py", "src/main.py"]
for file in required_files:
if not Path(file).exists():
print(f"❌ Required file not found: {file}")

View File

@ -299,7 +299,7 @@ EOF
echo "📋 Next Steps:"
echo " Review full report: $report_file"
echo " Address any warnings or errors"
echo " Run end-to-end tests: python tests/integration/test-e2e-deployment.py"
echo " Run end-to-end tests: python test-e2e-deployment.py"
echo ""
echo "=================================================="
}