#!/bin/bash # Define icons for status OK_ICON="✓" KO_ICON="✗" # Define log file LOG_FILE="deployment_$(date +%Y%m%d_%H%M%S).log" # Logging function log() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${timestamp} - ${message}" | tee -a "$LOG_FILE" } # Function to print the banner print_banner() { echo " ░▒▓███████▓▒░░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░ ░▒▓████████▓▒░▒▓███████▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ Created by Ilias & Léopold - Group 26 ---------------------------------------------------------------- " | tee -a "$LOG_FILE" } # Function to print check status print_status() { local check_name="$1" local status="$2" printf "Checking %-20s %s\n" "$check_name..." "$status" | tee -a "$LOG_FILE" } # Function to check if Docker is installed check_docker() { if command -v docker &> /dev/null && docker --version &> /dev/null; then return 0 else return 1 fi } # Function to check if Azure CLI is installed check_azure_cli() { if command -v az &> /dev/null && az --version &> /dev/null; then return 0 else return 1 fi } # Function to check if user is logged in to Azure check_azure_login() { if az account show &> /dev/null; then return 0 else return 1 fi } # Function to check if we're in a swarm check_swarm() { log "Checking Docker Swarm status..." # Check if docker is in swarm mode by looking for "Swarm: active" if ! docker info | grep -q "Swarm: active"; then log "ERROR: This node is not part of a Docker Swarm" echo "Please initialize or join a Docker Swarm first using:" echo " docker swarm init # For manager node" echo " docker swarm join # For worker nodes" return 1 fi # Check if we're a manager by looking for "Is Manager: true" if ! docker info | grep -q "Is Manager: true"; then log "ERROR: This node is not a Swarm manager" echo "Please run this script on a Swarm manager node" return 1 fi log "Node is a Swarm manager" return 0 } get_host_ip() { # Try hostname -I first local ip=$(hostname -I | awk '{print $1}') # If that fails, try ip addr if [ -z "$ip" ]; then ip=$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n 1) fi echo "$ip" } # Function to get user inputs for Azure resources get_resource_inputs() { local config_file="azure_config.env" # Check if configuration file exists if [ -f "$config_file" ]; then log "Found existing Azure configuration, loading..." source "$config_file" print_status "Config Load" "$OK_ICON" return 0 fi log "Getting user inputs for Azure resources..." echo -e "\nPlease provide names for your Azure resources:" read -p "Resource Group Name (e.g., myapp-rg): " RESOURCE_GROUP read -p "Storage Account Name (must be globally unique, lowercase, no special chars): " STORAGE_ACCOUNT read -p "Container Name: " CONTAINER_NAME read -p "Location (e.g., westeurope): " LOCATION # Validate inputs if [[ -z "$RESOURCE_GROUP" || -z "$STORAGE_ACCOUNT" || -z "$CONTAINER_NAME" || -z "$LOCATION" ]]; then log "ERROR: All inputs are required" exit 1 fi # Validate storage account name if [[ ! "$STORAGE_ACCOUNT" =~ ^[a-z0-9]{3,24}$ ]]; then log "ERROR: Storage account name must be 3-24 characters long and contain only lowercase letters and numbers" exit 1 fi log "Resource inputs collected successfully" } # Function to create Azure resources create_azure_resources() { local config_file="azure_config.env" log "Starting Azure resource creation..." # Create Resource Group log "Creating Resource Group: $RESOURCE_GROUP" if az group create --name "$RESOURCE_GROUP" --location "$LOCATION" > /dev/null; then log "Resource Group created successfully" print_status "Resource Group" "$OK_ICON" else log "ERROR: Failed to create Resource Group" print_status "Resource Group" "$KO_ICON" return 1 fi # Create Storage Account log "Creating Storage Account: $STORAGE_ACCOUNT" if az storage account create \ --name "$STORAGE_ACCOUNT" \ --resource-group "$RESOURCE_GROUP" \ --location "$LOCATION" \ --sku Standard_RAGRS \ --kind StorageV2 \ --min-tls-version TLS1_2 \ --allow-blob-public-access true > /dev/null; then log "Storage Account created successfully" print_status "Storage Account" "$OK_ICON" else log "ERROR: Failed to create Storage Account" print_status "Storage Account" "$KO_ICON" return 1 fi # Create Container log "Creating Container: $CONTAINER_NAME" if az storage container create \ --name "$CONTAINER_NAME" \ --account-name "$STORAGE_ACCOUNT" \ --auth-mode key \ --public-access blob > /dev/null; then log "Container created successfully" print_status "Container" "$OK_ICON" else log "ERROR: Failed to create Container" print_status "Container" "$KO_ICON" return 1 fi # Get and store connection string log "Retrieving storage account connection string" CONNECTION_STRING=$(az storage account show-connection-string \ --name "$STORAGE_ACCOUNT" \ --resource-group "$RESOURCE_GROUP" \ --query "connectionString" \ --output tsv) # Save configuration to file echo "RESOURCE_GROUP=$RESOURCE_GROUP" > "$config_file" echo "STORAGE_ACCOUNT=$STORAGE_ACCOUNT" >> "$config_file" echo "CONTAINER_NAME=$CONTAINER_NAME" >> "$config_file" echo "LOCATION=$LOCATION" >> "$config_file" echo "CONNECTION_STRING=$CONNECTION_STRING" >> "$config_file" log "Configuration saved to $config_file" log "Azure resources created successfully" } # Function to create overlay network create_overlay_network() { log "Setting up overlay network..." # Check if network already exists if docker network ls | grep -q "scapp-net"; then log "Network scapp-net already exists" print_status "Overlay Network" "$OK_ICON" return 0 fi # Create network if docker network create --driver overlay --attachable scapp-net > /dev/null; then log "Network scapp-net created successfully" print_status "Overlay Network" "$OK_ICON" return 0 else log "ERROR: Failed to create overlay network" print_status "Overlay Network" "$KO_ICON" return 1 fi } # Function to deploy stack deploy_stack() { log "Starting stack deployment..." local stack_file="docker-stack.yml" local modified_stack_file="docker-stack-modified.yml" local stack_name="scapp" local host_ip=$(get_host_ip) # Check if stack already exists if docker stack ls | grep -q "^${stack_name}"; then log "Stack ${stack_name} already exists" echo "Would you like to remove it and redeploy? (y/n)" read -r answer if [[ "$answer" =~ ^[Yy]$ ]]; then log "Removing existing stack..." docker stack rm "$stack_name" while docker stack ls | grep -q "^${stack_name}"; do log "Waiting for stack removal..." sleep 5 done else log "Deployment cancelled by user" return 1 fi fi # Download stack file log "Downloading stack file from Pastebin..." if ! curl -s "http://scapp.tech/stack.yml" -o "$stack_file"; then log "ERROR: Failed to download stack file" print_status "Stack Download" "$KO_ICON" return 1 fi print_status "Stack Download" "$OK_ICON" # Source Azure configuration log "Loading Azure configuration..." if [ -f "azure_config.env" ]; then source azure_config.env else log "ERROR: Azure configuration file not found" print_status "Config Load" "$KO_ICON" return 1 fi print_status "Config Load" "$OK_ICON" # Modify stack file with correct IP addresses log "Updating frontend service URLs with host IP: $host_ip" sed -e "s|http://gateway-service/service/auth|http://$host_ip:3005/service/auth|g" \ -e "s|http://gateway-service/service/items|http://$host_ip:3005/service/items|g" \ -e "s|http://gateway-service|http://$host_ip:3005|g" \ "$stack_file" > "$modified_stack_file" # Deploy the stack using modified file log "Deploying stack..." if AZURE_STORAGE_CONNECTION_STRING="$CONNECTION_STRING" \ AZURE_STORAGE_CONTAINER_NAME="$CONTAINER_NAME" \ docker stack deploy -c "$modified_stack_file" "$stack_name"; then log "Stack deployed successfully" print_status "Stack Deploy" "$OK_ICON" # Print stack information echo -e "\nStack deployed! You can check the status with:" echo " docker stack services $stack_name" echo " docker service logs " echo -e "\nServices will be available at:" echo " Frontend: http://$host_ip:80" echo " Visualizer: http://$host_ip:8081" echo " Gateway: http://$host_ip:3005" echo " Auth Service: http://$host_ip:3005/service/auth" echo " Items Service: http://$host_ip:3005/service/items" else log "ERROR: Failed to deploy stack" print_status "Stack Deploy" "$KO_ICON" return 1 fi # Cleanup modified stack file rm -f "$modified_stack_file" } # Main script main() { log "Starting deployment script" # Print banner print_banner # Check Docker if check_docker; then print_status "Docker" "$OK_ICON" else print_status "Docker" "$KO_ICON" log "Docker is not installed. Installing..." # Execute the commands curl -fsSL https://get.docker.com | sh sudo usermod -aG docker ubuntu newgrp docker log "Docker installation completed" fi # Check if we're in a swarm and we're a manager if ! check_swarm; then exit 1 fi # Check Azure CLI and login if check_azure_cli; then print_status "Azure CLI" "$OK_ICON" if check_azure_login; then print_status "Azure Login" "$OK_ICON" # Get resource inputs and create resources get_resource_inputs create_azure_resources # Create overlay network if ! create_overlay_network; then exit 1 fi # Deploy stack if ! deploy_stack; then exit 1 fi else print_status "Azure Login" "$KO_ICON" log "Not logged in to Azure. Please run 'az login' and try again" exit 1 fi else print_status "Azure CLI" "$KO_ICON" log "Azure CLI not installed. Please install it and try again" exit 1 fi } # Run the main function main