1006 lines
30 KiB
Bash
1006 lines
30 KiB
Bash
#!/bin/bash
|
|
|
|
# DockMon LXC Container Auto-Creation Script for Proxmox
|
|
# Run this script on your Proxmox host to automatically create and configure DockMon
|
|
# Usage: bash dockmon-lxc.sh
|
|
|
|
set -e
|
|
|
|
# Ensure /usr/sbin is in PATH for Proxmox commands
|
|
export PATH="/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
|
|
|
|
# Color codes for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Default Configuration (modify as needed)
|
|
CONTAINER_ID="" # Leave empty for next available ID
|
|
CONTAINER_NAME="dockmon"
|
|
DEBIAN_VERSION="" # Will be selected by user
|
|
TEMPLATE="" # Will be set based on Debian version
|
|
STORAGE="local-lvm"
|
|
DISK_SIZE="4"
|
|
MEMORY="512"
|
|
SWAP="512"
|
|
CORES="1"
|
|
BRIDGE="vmbr0"
|
|
IP_CONFIG="dhcp" # Set to "dhcp" or specify like "192.168.1.100/24"
|
|
GATEWAY="" # Set if using static IP, e.g., "192.168.1.1"
|
|
DNS="" # Leave empty for host settings or set like "8.8.8.8"
|
|
ROOT_PASSWORD="" # Will be set by user
|
|
SSH_KEY="" # Optional: path to SSH public key file
|
|
START_ON_BOOT="1" # 1 for yes, 0 for no
|
|
PROXMOX_NODE=$(hostname)
|
|
|
|
# Template options
|
|
DEBIAN_12_TEMPLATE="debian-12-standard_12.2-1_amd64.tar.zst"
|
|
DEBIAN_13_TEMPLATE="debian-13-standard_13.0-1_amd64.tar.zst"
|
|
|
|
# GitHub repository
|
|
GITHUB_REPO="https://github.com/darthnorse/dockmon.git"
|
|
|
|
# Function to print colored output
|
|
print_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
print_cyan() {
|
|
echo -e "${CYAN}$1${NC}"
|
|
}
|
|
|
|
# Detect if running in non-interactive mode (curl | bash)
|
|
INTERACTIVE=true
|
|
if [[ ! -t 0 ]]; then
|
|
INTERACTIVE=false
|
|
fi
|
|
|
|
# Helper function for interactive prompts with defaults
|
|
prompt_or_default() {
|
|
local prompt="$1"
|
|
local default="$2"
|
|
local result
|
|
|
|
if [[ "$INTERACTIVE" == "true" ]]; then
|
|
read -p "$prompt" result
|
|
echo "${result:-$default}"
|
|
else
|
|
echo "$default"
|
|
fi
|
|
}
|
|
|
|
# Header
|
|
clear
|
|
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
|
echo -e "${GREEN} DockMon LXC Container Auto-Creation for Proxmox ${NC}"
|
|
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
# Non-interactive mode notification
|
|
if [[ "$INTERACTIVE" == "false" ]]; then
|
|
print_info "Non-interactive mode detected - using default settings"
|
|
print_info "For customization, download and run the script directly"
|
|
echo ""
|
|
fi
|
|
|
|
# Check if running on Proxmox VE (community-scripts approach)
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
print_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_proxmox() {
|
|
if [ "$1" = "--bypass-proxmox-check" ]; then
|
|
print_info "Bypassing Proxmox check for testing"
|
|
return 0
|
|
fi
|
|
|
|
if ! command -v pveversion >/dev/null 2>&1; then
|
|
print_error "No PVE Detected!"
|
|
print_error "This script must be run on a Proxmox VE host"
|
|
exit 1
|
|
fi
|
|
|
|
# Get Proxmox version and validate
|
|
local pve=$(pveversion | grep "pve-manager" | awk '{print substr($2, 1, 3)}')
|
|
if [[ ! "$pve" =~ ^(8\.[0-9]|9\.[0-9])$ ]]; then
|
|
print_error "This version of Proxmox VE is not supported"
|
|
print_info "Requires PVE Version 8.0 or higher"
|
|
print_info "Detected: $(pveversion | grep pve-manager)"
|
|
exit 1
|
|
fi
|
|
|
|
print_info "Detected Proxmox VE: $(pveversion | grep pve-manager | awk '{print $2}')"
|
|
}
|
|
|
|
# Perform checks
|
|
check_root
|
|
check_proxmox "$1"
|
|
|
|
# Function to get next available container ID
|
|
get_next_ctid() {
|
|
local max_id=100
|
|
for ctid in $(pct list 2>/dev/null | tail -n +2 | awk '{print $1}'); do
|
|
if [ "$ctid" -ge "$max_id" ]; then
|
|
max_id=$((ctid + 1))
|
|
fi
|
|
done
|
|
echo $max_id
|
|
}
|
|
|
|
# Function to check if container ID exists
|
|
check_ctid_exists() {
|
|
pct status $1 &>/dev/null
|
|
return $?
|
|
}
|
|
|
|
# Function to validate IP address format
|
|
validate_ip() {
|
|
if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Function to validate IP with CIDR
|
|
validate_ip_cidr() {
|
|
if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Select Debian version
|
|
echo -e "${CYAN}Step 1: Select Debian Version${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo "1) Debian 12 (Bookworm) - Stable, Recommended"
|
|
echo "2) Debian 13 (Trixie) - Testing"
|
|
echo ""
|
|
|
|
# Interactive vs non-interactive selection
|
|
if [[ "$INTERACTIVE" == "true" ]]; then
|
|
# Interactive mode
|
|
while true; do
|
|
read -p "Select Debian version (1 or 2): " debian_choice
|
|
case $debian_choice in
|
|
1)
|
|
DEBIAN_VERSION="12"
|
|
TEMPLATE=$DEBIAN_12_TEMPLATE
|
|
print_success "Selected Debian 12 (Bookworm)"
|
|
break
|
|
;;
|
|
2)
|
|
DEBIAN_VERSION="13"
|
|
TEMPLATE=$DEBIAN_13_TEMPLATE
|
|
print_success "Selected Debian 13 (Trixie)"
|
|
break
|
|
;;
|
|
*)
|
|
print_error "Invalid selection. Please enter 1 or 2"
|
|
;;
|
|
esac
|
|
done
|
|
else
|
|
# Non-interactive mode - use defaults
|
|
DEBIAN_VERSION="12"
|
|
TEMPLATE=$DEBIAN_12_TEMPLATE
|
|
print_success "Using Debian 12 (Bookworm) - default"
|
|
fi
|
|
echo ""
|
|
|
|
# Set root password
|
|
echo -e "${CYAN}Step 2: Set Root Password${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo "Enter the root password for the container"
|
|
echo "(Password will not be visible while typing)"
|
|
echo ""
|
|
|
|
if [[ "$INTERACTIVE" == "true" ]]; then
|
|
while true; do
|
|
read -s -p "Enter root password: " ROOT_PASSWORD
|
|
echo
|
|
read -s -p "Confirm root password: " ROOT_PASSWORD_CONFIRM
|
|
echo
|
|
|
|
if [ "$ROOT_PASSWORD" != "$ROOT_PASSWORD_CONFIRM" ]; then
|
|
print_error "Passwords do not match! Please try again."
|
|
echo ""
|
|
elif [ -z "$ROOT_PASSWORD" ]; then
|
|
print_error "Password cannot be empty! Please try again."
|
|
echo ""
|
|
else
|
|
print_success "Root password set successfully"
|
|
break
|
|
fi
|
|
done
|
|
else
|
|
# Non-interactive mode - generate random password
|
|
ROOT_PASSWORD=$(openssl rand -base64 12 2>/dev/null || echo "dockmon123")
|
|
print_success "Generated random root password: $ROOT_PASSWORD"
|
|
print_warning "Save this password! It will be displayed again at the end."
|
|
fi
|
|
echo ""
|
|
|
|
# Select storage
|
|
echo -e "${CYAN}Step 3: Select Storage${NC}"
|
|
echo "══════════════════════════════════════"
|
|
print_info "Available storage pools:"
|
|
echo ""
|
|
pvesm status | grep -E "^[[:alnum:]]" | awk '{printf " %-20s %s\n", $1, "(" $2 ")"}'
|
|
echo ""
|
|
|
|
while true; do
|
|
read -p "Select storage pool [$STORAGE]: " input
|
|
STORAGE=${input:-$STORAGE}
|
|
|
|
# Verify storage exists
|
|
if pvesm status | grep -q "^$STORAGE "; then
|
|
print_success "Selected storage: $STORAGE"
|
|
break
|
|
else
|
|
print_error "Storage pool '$STORAGE' not found. Please select from the list above."
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Configuration options
|
|
echo -e "${CYAN}Step 4: Container Configuration${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo -e "${BLUE}Default Configuration:${NC}"
|
|
echo " Node: $PROXMOX_NODE"
|
|
echo " Storage: $STORAGE (selected)"
|
|
echo " Disk Size: ${DISK_SIZE}GB"
|
|
echo " Memory: ${MEMORY}MB"
|
|
echo " CPU Cores: $CORES"
|
|
echo " Network Bridge: $BRIDGE"
|
|
echo " IP Configuration: $IP_CONFIG"
|
|
echo " Start on Boot: $([ $START_ON_BOOT -eq 1 ] && echo 'Yes' || echo 'No')"
|
|
echo ""
|
|
|
|
read -p "Do you want to customize these settings? (y/n): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
echo ""
|
|
print_info "Enter custom configuration (press Enter to keep defaults):"
|
|
echo ""
|
|
|
|
# Disk size
|
|
read -p "Disk Size in GB [$DISK_SIZE]: " input
|
|
DISK_SIZE=${input:-$DISK_SIZE}
|
|
|
|
# Memory
|
|
read -p "Memory in MB [$MEMORY]: " input
|
|
MEMORY=${input:-$MEMORY}
|
|
|
|
# CPU cores
|
|
read -p "CPU Cores [$CORES]: " input
|
|
CORES=${input:-$CORES}
|
|
|
|
# Network bridge
|
|
print_cyan "Available bridges:"
|
|
ip link show type bridge | grep -E "^[0-9]+" | awk -F': ' '{print " - " $2}'
|
|
read -p "Network Bridge [$BRIDGE]: " input
|
|
BRIDGE=${input:-$BRIDGE}
|
|
|
|
# IP configuration
|
|
echo ""
|
|
print_cyan "IP Configuration:"
|
|
echo " 1) DHCP (automatic)"
|
|
echo " 2) Static IP"
|
|
read -p "Select IP configuration (1 or 2) [1]: " ip_choice
|
|
|
|
if [ "$ip_choice" == "2" ]; then
|
|
while true; do
|
|
read -p "Enter IP address with CIDR (e.g., 192.168.1.100/24): " IP_CONFIG
|
|
if validate_ip_cidr "$IP_CONFIG"; then
|
|
break
|
|
else
|
|
print_error "Invalid IP format. Please use format: 192.168.1.100/24"
|
|
fi
|
|
done
|
|
|
|
while true; do
|
|
read -p "Enter Gateway IP: " GATEWAY
|
|
if validate_ip "$GATEWAY"; then
|
|
break
|
|
else
|
|
print_error "Invalid gateway IP format"
|
|
fi
|
|
done
|
|
|
|
read -p "DNS Server [8.8.8.8]: " input
|
|
DNS=${input:-"8.8.8.8"}
|
|
else
|
|
IP_CONFIG="dhcp"
|
|
fi
|
|
|
|
# Start on boot
|
|
read -p "Start container on boot? (y/n) [y]: " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
|
START_ON_BOOT="0"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# SSH key option
|
|
read -p "Do you want to add an SSH public key for root access? (y/n): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
read -p "Enter path to SSH public key file: " SSH_KEY
|
|
if [ ! -f "$SSH_KEY" ]; then
|
|
print_warning "SSH key file not found, skipping SSH key configuration"
|
|
SSH_KEY=""
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Get or assign container ID
|
|
echo -e "${CYAN}Step 5: Container ID Assignment${NC}"
|
|
echo "══════════════════════════════════════"
|
|
|
|
# Show existing containers for reference
|
|
print_info "Existing containers:"
|
|
pct list | head -10
|
|
echo ""
|
|
|
|
# Ask user if they want to specify their own ID
|
|
read -p "Do you want to specify a custom container ID? (y/n) [n]: " -n 1 -r
|
|
echo
|
|
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
while true; do
|
|
read -p "Enter desired container ID (e.g., 200): " CUSTOM_ID
|
|
|
|
# Validate input is a number
|
|
if ! [[ "$CUSTOM_ID" =~ ^[0-9]+$ ]]; then
|
|
print_error "Container ID must be a number"
|
|
continue
|
|
fi
|
|
|
|
# Check if ID is in valid range (typically 100-999999)
|
|
if [ "$CUSTOM_ID" -lt 100 ] || [ "$CUSTOM_ID" -gt 999999 ]; then
|
|
print_error "Container ID should be between 100 and 999999"
|
|
continue
|
|
fi
|
|
|
|
# Check if ID already exists
|
|
if check_ctid_exists $CUSTOM_ID; then
|
|
print_error "Container ID $CUSTOM_ID already exists!"
|
|
read -p "Try a different ID? (y/n): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
break
|
|
fi
|
|
else
|
|
CONTAINER_ID=$CUSTOM_ID
|
|
print_success "Will use Container ID: $CONTAINER_ID"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# If no custom ID was set or user cancelled, use next available
|
|
if [ -z "$CONTAINER_ID" ]; then
|
|
CONTAINER_ID=$(get_next_ctid)
|
|
print_info "Using next available Container ID: $CONTAINER_ID"
|
|
fi
|
|
echo ""
|
|
|
|
# Download and prepare template dynamically
|
|
echo -e "${CYAN}Step 6: Template Preparation${NC}"
|
|
echo "══════════════════════════════════════"
|
|
|
|
# Function to get latest Debian template
|
|
get_latest_debian_template() {
|
|
local version=$1
|
|
print_info "Fetching latest Debian $version templates from Proxmox..."
|
|
|
|
# Update template list
|
|
pveam update >/dev/null 2>&1
|
|
|
|
# Find the latest Debian template for the specified version
|
|
local available_template=$(pveam available | grep "debian-$version" | grep "standard" | head -1 | awk '{print $2}')
|
|
|
|
if [ -n "$available_template" ]; then
|
|
echo "$available_template"
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to download template
|
|
download_template() {
|
|
local template_name=$1
|
|
local template_path="/var/lib/vz/template/cache/$template_name"
|
|
|
|
print_info "Checking if template exists locally..."
|
|
|
|
if [ -f "$template_path" ]; then
|
|
print_success "Template already available: $template_name"
|
|
return 0
|
|
fi
|
|
|
|
print_info "Downloading template: $template_name"
|
|
print_info "This may take several minutes depending on your connection..."
|
|
|
|
# Download the template
|
|
if pveam download local "$template_name"; then
|
|
print_success "Template downloaded successfully: $template_name"
|
|
return 0
|
|
else
|
|
print_error "Failed to download template: $template_name"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Get the latest template for selected Debian version
|
|
print_info "Determining latest Debian $DEBIAN_VERSION template..."
|
|
|
|
LATEST_TEMPLATE=$(get_latest_debian_template $DEBIAN_VERSION)
|
|
|
|
if [ -n "$LATEST_TEMPLATE" ]; then
|
|
print_info "Latest available: $LATEST_TEMPLATE"
|
|
TEMPLATE=$LATEST_TEMPLATE
|
|
else
|
|
print_warning "Could not find latest template, using fallback..."
|
|
if [ "$DEBIAN_VERSION" == "12" ]; then
|
|
TEMPLATE=$DEBIAN_12_TEMPLATE
|
|
else
|
|
TEMPLATE=$DEBIAN_13_TEMPLATE
|
|
fi
|
|
|
|
# Check if fallback template exists in available list
|
|
if ! pveam available | grep -q "$TEMPLATE"; then
|
|
print_error "Fallback template $TEMPLATE not available!"
|
|
print_info "Available Debian $DEBIAN_VERSION templates:"
|
|
pveam available | grep "debian-$DEBIAN_VERSION" | awk '{print " - " $2}'
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Download the template
|
|
if ! download_template "$TEMPLATE"; then
|
|
# If download fails, try to find any available Debian template for this version
|
|
print_warning "Primary download failed, searching for alternative templates..."
|
|
|
|
available_templates=$(pveam available | grep "debian-$DEBIAN_VERSION" | awk '{print $2}')
|
|
|
|
if [ -n "$available_templates" ]; then
|
|
print_info "Available Debian $DEBIAN_VERSION templates:"
|
|
echo "$available_templates" | nl -w2 -s'. '
|
|
echo ""
|
|
|
|
while true; do
|
|
read -p "Select template number (or 0 to exit): " template_num
|
|
|
|
if [ "$template_num" == "0" ]; then
|
|
print_error "Template selection cancelled"
|
|
exit 1
|
|
fi
|
|
|
|
selected_template=$(echo "$available_templates" | sed -n "${template_num}p")
|
|
|
|
if [ -n "$selected_template" ]; then
|
|
TEMPLATE=$selected_template
|
|
if download_template "$TEMPLATE"; then
|
|
break
|
|
else
|
|
print_error "Failed to download selected template"
|
|
fi
|
|
else
|
|
print_error "Invalid selection. Please try again."
|
|
fi
|
|
done
|
|
else
|
|
print_error "No Debian $DEBIAN_VERSION templates available!"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
|
|
print_success "Template ready: $TEMPLATE"
|
|
echo ""
|
|
|
|
# Build network configuration
|
|
if [ "$IP_CONFIG" == "dhcp" ]; then
|
|
NET_CONFIG="name=eth0,bridge=$BRIDGE,ip=dhcp"
|
|
else
|
|
NET_CONFIG="name=eth0,bridge=$BRIDGE,ip=$IP_CONFIG"
|
|
if [ -n "$GATEWAY" ]; then
|
|
NET_CONFIG="$NET_CONFIG,gw=$GATEWAY"
|
|
fi
|
|
fi
|
|
|
|
# Create the container
|
|
echo -e "${CYAN}Step 7: Creating LXC Container${NC}"
|
|
echo "══════════════════════════════════════"
|
|
print_info "Creating container with ID $CONTAINER_ID..."
|
|
print_info "Using template: $TEMPLATE"
|
|
|
|
# Create temporary file for password
|
|
PASS_FILE=$(mktemp)
|
|
echo -e "$ROOT_PASSWORD\n$ROOT_PASSWORD" > "$PASS_FILE"
|
|
|
|
# Create container with password from file
|
|
print_info "Executing container creation..."
|
|
pct create $CONTAINER_ID "$TEMPLATE_PATH" \
|
|
--hostname $CONTAINER_NAME \
|
|
--storage $STORAGE \
|
|
--rootfs $STORAGE:$DISK_SIZE \
|
|
--memory $MEMORY \
|
|
--swap $SWAP \
|
|
--cores $CORES \
|
|
--net0 $NET_CONFIG \
|
|
--features nesting=1 \
|
|
--unprivileged 1 \
|
|
--onboot $START_ON_BOOT \
|
|
--password < "$PASS_FILE"
|
|
|
|
# Remove temporary password file
|
|
rm -f "$PASS_FILE"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
print_error "Failed to create container!"
|
|
exit 1
|
|
fi
|
|
|
|
print_success "Container $CONTAINER_ID created successfully!"
|
|
|
|
# Set DNS if specified
|
|
if [ -n "$DNS" ]; then
|
|
pct set $CONTAINER_ID --nameserver "$DNS"
|
|
fi
|
|
|
|
# Add SSH key if provided
|
|
if [ -n "$SSH_KEY" ] && [ -f "$SSH_KEY" ]; then
|
|
print_info "Adding SSH key..."
|
|
pct set $CONTAINER_ID --ssh-public-keys "$SSH_KEY"
|
|
fi
|
|
echo ""
|
|
|
|
# Start the container
|
|
echo -e "${CYAN}Step 8: Starting Container${NC}"
|
|
echo "══════════════════════════════════════"
|
|
print_info "Starting container..."
|
|
pct start $CONTAINER_ID
|
|
|
|
# Wait for container to be ready
|
|
print_info "Waiting for container to be ready..."
|
|
sleep 10
|
|
|
|
# Get container IP
|
|
print_info "Getting container IP address..."
|
|
for i in {1..30}; do
|
|
CONTAINER_IP=$(pct exec $CONTAINER_ID -- ip -4 addr show eth0 2>/dev/null | grep inet | awk '{print $2}' | cut -d/ -f1)
|
|
if [ -n "$CONTAINER_IP" ]; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
if [ -z "$CONTAINER_IP" ]; then
|
|
print_warning "Could not determine container IP address"
|
|
CONTAINER_IP="<container-ip>"
|
|
fi
|
|
echo ""
|
|
|
|
# Install DockMon inside the container
|
|
echo -e "${CYAN}Step 9: Installing DockMon${NC}"
|
|
echo "══════════════════════════════════════"
|
|
print_info "Installing DockMon in the container..."
|
|
|
|
# Create installation script
|
|
cat << 'INSTALL_SCRIPT' > /tmp/install-dockmon.sh
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
# Update system
|
|
echo "Updating system packages..."
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
|
|
|
|
# Install required packages
|
|
echo "Installing nginx, git, Python 3, and required dependencies..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx git curl python3 python3-pip python3-venv supervisor
|
|
|
|
# Clone DockMon repository
|
|
echo "Cloning DockMon repository..."
|
|
cd /opt
|
|
git clone https://github.com/darthnorse/dockmon.git
|
|
|
|
# Set up Python backend
|
|
echo "Setting up DockMon backend..."
|
|
cd /opt/dockmon/backend
|
|
|
|
# Create virtual environment
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
|
|
# Install Python dependencies
|
|
pip install --no-cache-dir -r requirements.txt
|
|
|
|
# Create data directory
|
|
mkdir -p /opt/dockmon/backend/data
|
|
chown -R www-data:www-data /opt/dockmon/backend/data
|
|
|
|
# Copy application to web root
|
|
echo "Setting up DockMon frontend..."
|
|
cp /opt/dockmon/src/index.html /var/www/html/index.html
|
|
cp -r /opt/dockmon/images /var/www/html/
|
|
|
|
# Update frontend to point to backend API (if needed)
|
|
sed -i 's|http://localhost:8080|http://localhost:8080|g' /var/www/html/index.html
|
|
|
|
# Configure nginx to serve on port 8001
|
|
echo "Configuring nginx for port 8001..."
|
|
cat << 'NGINX_CONF' > /etc/nginx/sites-available/dockmon
|
|
server {
|
|
listen 8001 default_server;
|
|
listen [::]:8001 default_server;
|
|
|
|
root /var/www/html;
|
|
index index.html;
|
|
|
|
server_name _;
|
|
|
|
location / {
|
|
try_files \$uri \$uri/ =404;
|
|
}
|
|
|
|
# Optional: Add some basic security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
}
|
|
NGINX_CONF
|
|
|
|
# Enable the new site and disable default
|
|
ln -sf /etc/nginx/sites-available/dockmon /etc/nginx/sites-enabled/
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
|
|
# Configure nginx to start on boot
|
|
systemctl enable nginx
|
|
systemctl restart nginx
|
|
|
|
# Create systemd service for DockMon backend
|
|
cat << 'BACKEND_SERVICE' > /etc/systemd/system/dockmon-backend.service
|
|
[Unit]
|
|
Description=DockMon Backend API
|
|
After=network.target
|
|
Wants=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=www-data
|
|
Group=www-data
|
|
WorkingDirectory=/opt/dockmon/backend
|
|
Environment=PYTHONPATH=/opt/dockmon/backend
|
|
ExecStart=/opt/dockmon/backend/venv/bin/python main.py
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
BACKEND_SERVICE
|
|
|
|
# Create supervisor configuration as backup process manager
|
|
cat << 'SUPERVISOR_CONF' > /etc/supervisor/conf.d/dockmon-backend.conf
|
|
[program:dockmon-backend]
|
|
command=/opt/dockmon/backend/venv/bin/python main.py
|
|
directory=/opt/dockmon/backend
|
|
user=www-data
|
|
autostart=true
|
|
autorestart=true
|
|
stderr_logfile=/var/log/dockmon-backend.err.log
|
|
stdout_logfile=/var/log/dockmon-backend.out.log
|
|
environment=PYTHONPATH="/opt/dockmon/backend"
|
|
SUPERVISOR_CONF
|
|
|
|
# Create a simple frontend systemd service
|
|
cat << 'FRONTEND_SERVICE' > /etc/systemd/system/dockmon-frontend.service
|
|
[Unit]
|
|
Description=DockMon Web Interface
|
|
After=network.target nginx.service dockmon-backend.service
|
|
Requires=nginx.service
|
|
Wants=dockmon-backend.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/bin/true
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
FRONTEND_SERVICE
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable dockmon-backend.service
|
|
systemctl enable dockmon-frontend.service
|
|
systemctl start dockmon-backend.service
|
|
|
|
# Start supervisor as backup
|
|
systemctl enable supervisor
|
|
systemctl start supervisor
|
|
|
|
# Create update script
|
|
echo "Creating update script..."
|
|
cat << 'UPDATE_SCRIPT' > /usr/local/bin/update
|
|
#!/bin/bash
|
|
|
|
# DockMon Update Script
|
|
# Updates both the system and DockMon to latest versions
|
|
|
|
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
|
|
|
|
# Functions for colored output
|
|
print_info() {
|
|
echo -e "\${BLUE}[INFO]\${NC} \$1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "\${GREEN}[SUCCESS]\${NC} \$1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "\${RED}[ERROR]\${NC} \$1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "\${YELLOW}[WARNING]\${NC} \$1"
|
|
}
|
|
|
|
# Header
|
|
echo -e "\${GREEN}════════════════════════════════════════\${NC}"
|
|
echo -e "\${GREEN} DockMon System Update Tool \${NC}"
|
|
echo -e "\${GREEN}════════════════════════════════════════\${NC}"
|
|
echo ""
|
|
|
|
# Check if running as root
|
|
if [ "\$EUID" -ne 0 ]; then
|
|
print_error "This script must be run as root!"
|
|
print_info "Try: sudo update"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 1: Update Debian packages
|
|
echo -e "\${BLUE}Step 1: Updating Debian System\${NC}"
|
|
echo "════════════════════════════════════"
|
|
print_info "Updating package lists..."
|
|
apt-get update
|
|
|
|
print_info "Upgrading installed packages..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
|
|
|
|
print_info "Performing distribution upgrade..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y
|
|
|
|
print_info "Removing unnecessary packages..."
|
|
apt-get autoremove -y
|
|
|
|
print_info "Cleaning package cache..."
|
|
apt-get autoclean
|
|
|
|
print_success "System update completed!"
|
|
echo ""
|
|
|
|
# Step 2: Update DockMon
|
|
echo -e "\${BLUE}Step 2: Updating DockMon\${NC}"
|
|
echo "════════════════════════════════════"
|
|
|
|
# Check if DockMon directory exists
|
|
if [ ! -d "/opt/dockmon" ]; then
|
|
print_error "DockMon directory not found at /opt/dockmon"
|
|
print_info "Attempting to clone repository..."
|
|
cd /opt
|
|
git clone https://github.com/darthnorse/dockmon.git
|
|
if [ \$? -ne 0 ]; then
|
|
print_error "Failed to clone DockMon repository"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Navigate to DockMon directory
|
|
cd /opt/dockmon
|
|
|
|
# Store current version (if exists)
|
|
if [ -f "src/index.html" ]; then
|
|
OLD_VERSION=\$(grep -oP 'DockMon v\K[0-9.]+' src/index.html | head -1 || echo "unknown")
|
|
else
|
|
OLD_VERSION="not installed"
|
|
fi
|
|
|
|
print_info "Current version: \$OLD_VERSION"
|
|
|
|
# Fetch latest changes
|
|
print_info "Fetching latest updates from GitHub..."
|
|
git fetch origin
|
|
|
|
# Check if there are updates
|
|
LOCAL=\$(git rev-parse HEAD)
|
|
REMOTE=\$(git rev-parse origin/main)
|
|
|
|
if [ "\$LOCAL" = "\$REMOTE" ]; then
|
|
print_info "DockMon is already up to date"
|
|
else
|
|
print_info "Updates available, pulling latest version..."
|
|
|
|
# Pull latest changes
|
|
git pull origin main
|
|
|
|
if [ \$? -ne 0 ]; then
|
|
print_warning "Git pull failed, attempting to reset..."
|
|
git reset --hard origin/main
|
|
fi
|
|
|
|
# Get new version
|
|
NEW_VERSION=\$(grep -oP 'DockMon v\K[0-9.]+' src/index.html | head -1 || echo "unknown")
|
|
print_success "Updated DockMon from v\$OLD_VERSION to v\$NEW_VERSION"
|
|
fi
|
|
|
|
# Update the web application
|
|
print_info "Deploying updated application..."
|
|
cp -f /opt/dockmon/src/index.html /var/www/html/index.html
|
|
cp -rf /opt/dockmon/images /var/www/html/
|
|
|
|
if [ \$? -eq 0 ]; then
|
|
print_success "Application deployed successfully!"
|
|
else
|
|
print_error "Failed to deploy application"
|
|
exit 1
|
|
fi
|
|
|
|
# Restart nginx to ensure everything is fresh
|
|
print_info "Restarting web server..."
|
|
systemctl restart nginx
|
|
|
|
if systemctl is-active --quiet nginx; then
|
|
print_success "Web server restarted successfully!"
|
|
else
|
|
print_error "Web server failed to restart"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Step 3: Check for script updates
|
|
echo -e "\${BLUE}Step 3: Checking for Script Updates\${NC}"
|
|
echo "════════════════════════════════════"
|
|
|
|
# Check if this update script itself needs updating
|
|
if [ -f "/opt/dockmon/scripts/update.sh" ]; then
|
|
if ! cmp -s "/opt/dockmon/scripts/update.sh" "/usr/local/bin/update"; then
|
|
print_info "Update script has a newer version, updating..."
|
|
cp -f /opt/dockmon/scripts/update.sh /usr/local/bin/update
|
|
chmod +x /usr/local/bin/update
|
|
print_success "Update script updated! Please run 'update' again if needed."
|
|
else
|
|
print_info "Update script is current"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Summary
|
|
echo -e "\${GREEN}════════════════════════════════════════\${NC}"
|
|
echo -e "\${GREEN} Update Complete! ✅ \${NC}"
|
|
echo -e "\${GREEN}════════════════════════════════════════\${NC}"
|
|
echo ""
|
|
echo -e "\${BLUE}Summary:\${NC}"
|
|
echo "• System packages: Updated"
|
|
echo "• DockMon application: \$([ "\$LOCAL" = "\$REMOTE" ] && echo "Already current" || echo "Updated")"
|
|
echo "• Web server: Running"
|
|
echo ""
|
|
echo -e "\${BLUE}DockMon Access:\${NC}"
|
|
echo "• Web Interface: http://\$(hostname -I | awk '{print \$1}')"
|
|
echo ""
|
|
|
|
# Check if reboot is required
|
|
if [ -f /var/run/reboot-required ]; then
|
|
echo -e "\${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\${NC}"
|
|
echo -e "\${YELLOW}⚠️ REBOOT REQUIRED\${NC}"
|
|
echo -e "\${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\${NC}"
|
|
echo "A system reboot is required to complete updates."
|
|
echo "Please run: reboot"
|
|
echo ""
|
|
fi
|
|
|
|
exit 0
|
|
UPDATE_SCRIPT
|
|
|
|
# Make update script executable and accessible
|
|
chmod +x /usr/local/bin/update
|
|
|
|
# Create shorter alias
|
|
ln -sf /usr/local/bin/update /usr/local/bin/dockmon-update
|
|
|
|
# Also create the update script in the repository for future updates
|
|
mkdir -p /opt/dockmon/scripts
|
|
cp /usr/local/bin/update /opt/dockmon/scripts/update.sh
|
|
chmod +x /opt/dockmon/scripts/update.sh
|
|
|
|
echo "DockMon installation completed!"
|
|
INSTALL_SCRIPT
|
|
|
|
# Copy and execute installation script in container
|
|
pct push $CONTAINER_ID /tmp/install-dockmon.sh /tmp/install-dockmon.sh
|
|
pct exec $CONTAINER_ID -- chmod +x /tmp/install-dockmon.sh
|
|
pct exec $CONTAINER_ID -- /tmp/install-dockmon.sh
|
|
|
|
# Clean up
|
|
rm /tmp/install-dockmon.sh
|
|
|
|
# Final status check
|
|
print_info "Verifying installation..."
|
|
if pct exec $CONTAINER_ID -- systemctl is-active nginx >/dev/null 2>&1; then
|
|
print_success "Nginx is running"
|
|
else
|
|
print_warning "Nginx might not be running properly"
|
|
fi
|
|
echo ""
|
|
|
|
# Summary
|
|
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
|
echo -e "${GREEN} DockMon Installation Complete! 🎉 ${NC}"
|
|
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}Container Details:${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo "Container ID: $CONTAINER_ID"
|
|
echo "Container Name: $CONTAINER_NAME"
|
|
echo "Debian Version: $DEBIAN_VERSION"
|
|
echo "IP Address: $CONTAINER_IP"
|
|
echo "Memory: ${MEMORY}MB"
|
|
echo "Disk Size: ${DISK_SIZE}GB"
|
|
echo "CPU Cores: $CORES"
|
|
echo "Start on Boot: $([ $START_ON_BOOT -eq 1 ] && echo 'Yes' || echo 'No')"
|
|
echo ""
|
|
echo -e "${BLUE}Access DockMon:${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo -e "Web Interface: ${GREEN}http://$CONTAINER_IP:8001${NC}"
|
|
echo -e "SSH Access: ${GREEN}ssh root@$CONTAINER_IP${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}Container Management:${NC}"
|
|
echo "══════════════════════════════════════"
|
|
echo "Start: pct start $CONTAINER_ID"
|
|
echo "Stop: pct stop $CONTAINER_ID"
|
|
echo "Restart: pct restart $CONTAINER_ID"
|
|
echo "Console: pct console $CONTAINER_ID"
|
|
echo "Remove: pct destroy $CONTAINER_ID"
|
|
echo ""
|
|
echo -e "${YELLOW}Notes:${NC}"
|
|
echo "• Template used: $TEMPLATE (downloaded automatically)"
|
|
echo "• Frontend (nginx) serves on port 8001"
|
|
echo "• Backend API runs on port 8080"
|
|
echo "• Root password: (the password you set)"
|
|
echo "• Services: dockmon-backend, dockmon-frontend, nginx, supervisor"
|
|
echo "• To update DockMon, run inside container: update"
|
|
echo "• Backend logs: journalctl -u dockmon-backend -f"
|
|
echo "• Supervisor logs: tail -f /var/log/dockmon-backend.out.log"
|
|
echo ""
|
|
echo -e "${GREEN}Enjoy DockMon!${NC} 🐳" |