Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- Config, docs, scripts, and backup manifests - Submodule refs unchanged (m = modified content in submodules) Made-with: Cursor
198 lines
6.9 KiB
Bash
Executable File
198 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Phase 1: Create and configure secondary NPMplus container
|
|
|
|
set -euo pipefail
|
|
|
|
# Load IP configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
set +euo pipefail
|
|
source "$PROJECT_ROOT/.env" 2>/dev/null || true
|
|
set -euo pipefail
|
|
fi
|
|
|
|
SECONDARY_HOST="${SECONDARY_HOST:-192.168.11.12}"
|
|
SECONDARY_VMID="${SECONDARY_VMID:-10234}"
|
|
SECONDARY_IP="${SECONDARY_IP:-${IP_NPMPLUS:-192.168.11.167}}"
|
|
HOSTNAME="npmplus-secondary"
|
|
TZ="${TZ:-America/New_York}"
|
|
ACME_EMAIL="${NPM_EMAIL:-nsatoshi2007@hotmail.com}"
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
|
|
|
log_info "Creating secondary NPMplus container (VMID $SECONDARY_VMID)..."
|
|
|
|
# Check if container already exists
|
|
if ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct status $SECONDARY_VMID 2>/dev/null" >/dev/null 2>&1; then
|
|
log_warn "Container VMID $SECONDARY_VMID already exists"
|
|
read -p "Delete and recreate? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
log_info "Stopping and destroying existing container..."
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct stop $SECONDARY_VMID 2>/dev/null || true"
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct destroy $SECONDARY_VMID 2>/dev/null || true"
|
|
else
|
|
log_info "Skipping container creation"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Check for Alpine template
|
|
log_info "Checking for Alpine template..."
|
|
EXISTING_TEMPLATE=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pveam list local | grep -i 'alpine' | head -1 | awk '{print \$1}'" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$EXISTING_TEMPLATE" ]; then
|
|
# Extract just the template name (remove storage prefix if present)
|
|
TEMPLATE=$(echo "$EXISTING_TEMPLATE" | sed 's|.*/||' | sed 's|^local:vztmpl/||')
|
|
log_success "Using existing template: $TEMPLATE"
|
|
else
|
|
log_info "No Alpine template found locally, checking available templates..."
|
|
# Get template name (second column, skip header)
|
|
TEMPLATE_NAME=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pveam available | grep -i 'alpine.*3.22' | head -1 | awk '{print \$2}'" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$TEMPLATE_NAME" ] && [ "$TEMPLATE_NAME" != "template" ]; then
|
|
log_info "Downloading template: $TEMPLATE_NAME to local storage"
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pveam download local $TEMPLATE_NAME" || {
|
|
log_error "Failed to download template"
|
|
exit 1
|
|
}
|
|
TEMPLATE="$TEMPLATE_NAME"
|
|
else
|
|
# Try any Alpine template
|
|
TEMPLATE_NAME=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pveam available | grep -i alpine | head -1 | awk '{print \$2}'" 2>/dev/null || echo "")
|
|
if [ -n "$TEMPLATE_NAME" ] && [ "$TEMPLATE_NAME" != "template" ]; then
|
|
log_info "Downloading Alpine template: $TEMPLATE_NAME to local storage"
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pveam download local $TEMPLATE_NAME" || {
|
|
log_error "Failed to download template"
|
|
exit 1
|
|
}
|
|
TEMPLATE="$TEMPLATE_NAME"
|
|
else
|
|
log_error "No Alpine template found. Please download one manually."
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Determine storage (must be lvmthin for containers, and must be active)
|
|
STORAGE=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pvesm status | grep -E 'lvmthin.*active' | grep -v 'inactive' | awk '{print \$1}' | head -1" 2>/dev/null || echo "")
|
|
if [ -z "$STORAGE" ]; then
|
|
# Try specific thin pools that are known to be active
|
|
for thin in thin1-r630-02 thin5 thin6; do
|
|
STATUS=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pvesm status | grep '^$thin' | awk '{print \$3}'" 2>/dev/null || echo "")
|
|
if [ "$STATUS" = "active" ]; then
|
|
STORAGE="$thin"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -z "$STORAGE" ]; then
|
|
log_error "No suitable active LVM thin storage found"
|
|
log_info "Available storage:"
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pvesm status" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
|
|
ROOTFS_STORAGE="$STORAGE"
|
|
log_info "Using storage: $ROOTFS_STORAGE"
|
|
|
|
# Create container
|
|
log_info "Creating container..."
|
|
# Ensure template path is correct
|
|
if [[ "$TEMPLATE" == *"vztmpl/"* ]]; then
|
|
TEMPLATE_PATH="$TEMPLATE"
|
|
else
|
|
TEMPLATE_PATH="local:vztmpl/$TEMPLATE"
|
|
fi
|
|
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct create $SECONDARY_VMID \
|
|
$TEMPLATE_PATH \
|
|
--hostname $HOSTNAME \
|
|
--memory 1024 \
|
|
--cores 2 \
|
|
--rootfs $ROOTFS_STORAGE:5 \
|
|
--net0 name=eth0,bridge=vmbr0,ip=$SECONDARY_IP/24,gw=${NETWORK_GATEWAY:-192.168.11.1} \
|
|
--unprivileged 1 \
|
|
--features nesting=1" || {
|
|
log_error "Failed to create container"
|
|
exit 1
|
|
}
|
|
log_success "Container created"
|
|
|
|
# Start container
|
|
log_info "Starting container..."
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct start $SECONDARY_VMID" || {
|
|
log_error "Failed to start container"
|
|
exit 1
|
|
}
|
|
sleep 10
|
|
log_success "Container started"
|
|
|
|
# Install NPMplus
|
|
log_info "Installing NPMplus..."
|
|
ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct exec $SECONDARY_VMID -- ash" <<INSTALL_EOF
|
|
set -e
|
|
apk update
|
|
apk add --no-cache tzdata gawk yq docker docker-compose curl bash rsync
|
|
|
|
rc-service docker start
|
|
rc-update add docker default
|
|
sleep 5
|
|
|
|
cd /opt
|
|
curl -fsSL "https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml" -o compose.yaml
|
|
|
|
yq -i "
|
|
.services.npmplus.environment |=
|
|
(map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\")) +
|
|
[\"TZ=$TZ\", \"ACME_EMAIL=$ACME_EMAIL\"])
|
|
" compose.yaml
|
|
|
|
docker compose up -d
|
|
|
|
# Wait for NPMplus to be ready
|
|
for i in {1..60}; do
|
|
if docker ps --filter "name=npmplus" --format "{{.Status}}" | grep -qE "healthy|Up"; then
|
|
echo "NPMplus is ready"
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
INSTALL_EOF
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "NPMplus installed and running"
|
|
else
|
|
log_error "NPMplus installation failed"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify network
|
|
log_info "Verifying network configuration..."
|
|
ACTUAL_IP=$(ssh -o StrictHostKeyChecking=no root@"$SECONDARY_HOST" "pct exec $SECONDARY_VMID -- hostname -I | awk '{print \$1}'" || echo "")
|
|
if [ "$ACTUAL_IP" = "$SECONDARY_IP" ]; then
|
|
log_success "Network configured correctly: $ACTUAL_IP"
|
|
else
|
|
log_warn "IP mismatch: Expected $SECONDARY_IP, got $ACTUAL_IP"
|
|
fi
|
|
|
|
log_success "Phase 1 complete: Secondary container ready"
|