Refactor OCI deployment script for clarity and functionality
Refactor message functions and improve version checks.
This commit is contained in:
parent
513b096e6e
commit
ce1e3d76bf
@ -1,344 +1,348 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Maintainer: MickLesk (CanbiZ)
|
|
||||||
# Copyright (c) 2021-2025 community-scripts ORG
|
# Copyright (c) 2021-2025 community-scripts ORG
|
||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
# OCI Container Deployment Helper for Proxmox VE 9.1+
|
# Source: https://www.proxmox.com/
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
shopt -s inherit_errexit nullglob
|
|
||||||
|
|
||||||
# Color codes
|
|
||||||
YW=$(echo "\033[33m")
|
|
||||||
BL=$(echo "\033[36m")
|
|
||||||
RD=$(echo "\033[01;31m")
|
|
||||||
BGN=$(echo "\033[4;92m")
|
|
||||||
GN=$(echo "\033[1;92m")
|
|
||||||
DGN=$(echo "\033[32m")
|
|
||||||
CL=$(echo "\033[m")
|
|
||||||
BFR="\\r\\033[K"
|
|
||||||
HOLD="-"
|
|
||||||
CM="${GN}✓${CL}"
|
|
||||||
CROSS="${RD}✗${CL}"
|
|
||||||
|
|
||||||
msg_info() {
|
|
||||||
local msg="$1"
|
|
||||||
echo -ne " ${HOLD} ${YW}${msg}..."
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_ok() {
|
|
||||||
local msg="$1"
|
|
||||||
echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_error() {
|
|
||||||
local msg="$1"
|
|
||||||
echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if running on Proxmox VE 9.1+
|
|
||||||
check_proxmox_version() {
|
|
||||||
if ! command -v pveversion &>/dev/null; then
|
|
||||||
msg_error "This script must be run on Proxmox VE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local pve_version=$(pveversion | grep -oP 'pve-manager/\K[0-9.]+' | cut -d. -f1,2)
|
|
||||||
local major=$(echo "$pve_version" | cut -d. -f1)
|
|
||||||
local minor=$(echo "$pve_version" | cut -d. -f2)
|
|
||||||
|
|
||||||
if [[ "$major" -lt 9 ]] || { [[ "$major" -eq 9 ]] && [[ "$minor" -lt 1 ]]; }; then
|
|
||||||
msg_error "Proxmox VE 9.1 or higher required (current: $pve_version)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Parse OCI image reference
|
|
||||||
parse_image_ref() {
|
|
||||||
local image_ref="$1"
|
|
||||||
local registry=""
|
|
||||||
local image=""
|
|
||||||
local tag="latest"
|
|
||||||
|
|
||||||
# Handle different formats:
|
|
||||||
# - nginx:latest
|
|
||||||
# - docker.io/library/nginx:latest
|
|
||||||
# - ghcr.io/user/repo:tag
|
|
||||||
|
|
||||||
if [[ "$image_ref" =~ ^([^/]+\.[^/]+)/ ]]; then
|
|
||||||
# Has registry prefix (contains dot)
|
|
||||||
registry="${BASH_REMATCH[1]}"
|
|
||||||
image_ref="${image_ref#*/}"
|
|
||||||
else
|
|
||||||
# Use docker.io as default
|
|
||||||
registry="docker.io"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract tag if present
|
|
||||||
if [[ "$image_ref" =~ :([^:]+)$ ]]; then
|
|
||||||
tag="${BASH_REMATCH[1]}"
|
|
||||||
image="${image_ref%:*}"
|
|
||||||
else
|
|
||||||
image="$image_ref"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add library/ prefix for official docker hub images
|
|
||||||
if [[ "$registry" == "docker.io" ]] && [[ ! "$image" =~ / ]]; then
|
|
||||||
image="library/$image"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$registry/$image:$tag"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Show usage
|
|
||||||
usage() {
|
|
||||||
cat <<EOF
|
|
||||||
${GN}OCI Container Deployment Tool for Proxmox VE 9.1+${CL}
|
|
||||||
|
|
||||||
Usage: $(basename "$0") [OPTIONS] IMAGE
|
|
||||||
|
|
||||||
${BL}Arguments:${CL}
|
|
||||||
IMAGE OCI image reference (e.g., nginx:latest, ghcr.io/user/repo:tag)
|
|
||||||
|
|
||||||
${BL}Options:${CL}
|
|
||||||
-n, --name NAME Container name (default: derived from image)
|
|
||||||
-i, --vmid ID Container ID (default: next available)
|
|
||||||
-c, --cores NUM CPU cores (default: 2)
|
|
||||||
-m, --memory MB RAM in MB (default: 2048)
|
|
||||||
-d, --disk GB Root disk size in GB (default: 8)
|
|
||||||
-s, --storage NAME Storage name (default: local-zfs or local-lvm)
|
|
||||||
--network BRIDGE Network bridge (default: vmbr0)
|
|
||||||
--ip CIDR Static IP with CIDR (e.g., 192.168.1.100/24)
|
|
||||||
--gateway IP Gateway IP
|
|
||||||
-e, --env KEY=VALUE Environment variable (can be used multiple times)
|
|
||||||
-v, --volume PATH Mount point as PATH:SIZE (e.g., /data:10G)
|
|
||||||
--privileged Create privileged container
|
|
||||||
--start Start container after creation
|
|
||||||
-h, --help Show this help
|
|
||||||
|
|
||||||
${BL}Examples:${CL}
|
|
||||||
# Deploy nginx with defaults
|
|
||||||
$(basename "$0") nginx:latest
|
|
||||||
|
|
||||||
# Deploy with custom settings
|
|
||||||
$(basename "$0") -n my-app -c 4 -m 4096 -e DOMAIN=example.com ghcr.io/user/app:latest
|
|
||||||
|
|
||||||
# Deploy with volume and static IP
|
|
||||||
$(basename "$0") -v /data:20G --ip 192.168.1.100/24 --gateway 192.168.1.1 postgres:16
|
|
||||||
|
|
||||||
|
function header_info {
|
||||||
|
clear
|
||||||
|
cat <<"EOF"
|
||||||
|
____ ________ ______ __ _
|
||||||
|
/ __ \/ ____/ / / ____/___ ____ / /_____ _(_)___ ___ _____
|
||||||
|
/ / / / / / / / / / __ \/ __ \/ __/ __ `/ / __ \/ _ \/ ___/
|
||||||
|
/ /_/ / /___/ / / /___/ /_/ / / / / /_/ /_/ / / / / / __/ /
|
||||||
|
\____/\____/_/ \____/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main deployment function
|
YW=$(echo "\033[33m")
|
||||||
deploy_oci_container() {
|
GN=$(echo "\033[1;92m")
|
||||||
local image="$1"
|
RD=$(echo "\033[01;31m")
|
||||||
local name="${2:-}"
|
BL=$(echo "\033[36m")
|
||||||
local vmid="${3:-}"
|
CL=$(echo "\033[m")
|
||||||
local cores="${4:-2}"
|
CM="${GN}✔️${CL}"
|
||||||
local memory="${5:-2048}"
|
CROSS="${RD}✖️${CL}"
|
||||||
local disk="${6:-8}"
|
INFO="${BL}ℹ️${CL}"
|
||||||
local storage="${7:-}"
|
|
||||||
local network="${8:-vmbr0}"
|
|
||||||
local ip="${9:-dhcp}"
|
|
||||||
local gateway="${10:-}"
|
|
||||||
local privileged="${11:-0}"
|
|
||||||
local start_after="${12:-0}"
|
|
||||||
shift 12
|
|
||||||
local env_vars=("$@")
|
|
||||||
|
|
||||||
msg_info "Checking Proxmox version"
|
APP="OCI-Container"
|
||||||
check_proxmox_version
|
|
||||||
msg_ok "Proxmox version compatible"
|
|
||||||
|
|
||||||
# Parse image reference
|
header_info
|
||||||
local full_image=$(parse_image_ref "$image")
|
|
||||||
msg_info "Parsing image reference: $full_image"
|
|
||||||
msg_ok "Image reference parsed"
|
|
||||||
|
|
||||||
# Derive name from image if not specified
|
function msg_info() {
|
||||||
if [[ -z "$name" ]]; then
|
local msg="$1"
|
||||||
name=$(echo "$image" | sed 's/[^a-zA-Z0-9-]/-/g' | sed 's/:/-/g' | cut -c1-60)
|
echo -e "${INFO} ${YW}${msg}...${CL}"
|
||||||
fi
|
|
||||||
|
|
||||||
# Get next available VMID if not specified
|
|
||||||
if [[ -z "$vmid" ]]; then
|
|
||||||
vmid=$(pvesh get /cluster/nextid)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine storage
|
|
||||||
if [[ -z "$storage" ]]; then
|
|
||||||
if pvesm status | grep -q "local-zfs"; then
|
|
||||||
storage="local-zfs"
|
|
||||||
elif pvesm status | grep -q "local-lvm"; then
|
|
||||||
storage="local-lvm"
|
|
||||||
else
|
|
||||||
storage="local"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_info "Creating container $vmid ($name) from $full_image"
|
|
||||||
|
|
||||||
# Create container using pct
|
|
||||||
local pct_cmd="pct create $vmid"
|
|
||||||
pct_cmd+=" --ostemplate oci://$full_image"
|
|
||||||
pct_cmd+=" --hostname $name"
|
|
||||||
pct_cmd+=" --cores $cores"
|
|
||||||
pct_cmd+=" --memory $memory"
|
|
||||||
pct_cmd+=" --rootfs ${storage}:${disk}"
|
|
||||||
pct_cmd+=" --net0 name=eth0,bridge=$network"
|
|
||||||
|
|
||||||
if [[ "$ip" != "dhcp" ]]; then
|
|
||||||
pct_cmd+=",ip=$ip"
|
|
||||||
[[ -n "$gateway" ]] && pct_cmd+=",gw=$gateway"
|
|
||||||
else
|
|
||||||
pct_cmd+=",ip=dhcp"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ "$privileged" == "1" ]] && pct_cmd+=" --unprivileged 0" || pct_cmd+=" --unprivileged 1"
|
|
||||||
|
|
||||||
# Execute container creation
|
|
||||||
if ! eval "$pct_cmd" 2>/dev/null; then
|
|
||||||
msg_error "Failed to create container"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_ok "Container created (ID: $vmid)"
|
|
||||||
|
|
||||||
# Set environment variables if provided
|
|
||||||
if [[ ${#env_vars[@]} -gt 0 ]]; then
|
|
||||||
msg_info "Configuring environment variables"
|
|
||||||
for env_var in "${env_vars[@]}"; do
|
|
||||||
local key="${env_var%%=*}"
|
|
||||||
local value="${env_var#*=}"
|
|
||||||
pct set "$vmid" --env "$key=$value" >/dev/null 2>&1
|
|
||||||
done
|
|
||||||
msg_ok "Environment variables configured (${#env_vars[@]} variables)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start container if requested
|
|
||||||
if [[ "$start_after" == "1" ]]; then
|
|
||||||
msg_info "Starting container"
|
|
||||||
if pct start "$vmid" >/dev/null 2>&1; then
|
|
||||||
msg_ok "Container started successfully"
|
|
||||||
|
|
||||||
# Wait for network
|
|
||||||
sleep 3
|
|
||||||
local container_ip=$(pct exec "$vmid" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A")
|
|
||||||
|
|
||||||
echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
|
||||||
echo -e "${BL}Container Information:${CL}"
|
|
||||||
echo -e " ID: ${GN}$vmid${CL}"
|
|
||||||
echo -e " Name: ${GN}$name${CL}"
|
|
||||||
echo -e " Image: ${GN}$full_image${CL}"
|
|
||||||
echo -e " IP: ${GN}$container_ip${CL}"
|
|
||||||
echo -e " Status: ${GN}Running${CL}"
|
|
||||||
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n"
|
|
||||||
else
|
|
||||||
msg_error "Failed to start container"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
|
||||||
echo -e "${BL}Container Information:${CL}"
|
|
||||||
echo -e " ID: ${GN}$vmid${CL}"
|
|
||||||
echo -e " Name: ${GN}$name${CL}"
|
|
||||||
echo -e " Image: ${GN}$full_image${CL}"
|
|
||||||
echo -e " Status: ${YW}Stopped${CL}"
|
|
||||||
echo -e "\n${YW}Start with: pct start $vmid${CL}"
|
|
||||||
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse command line arguments
|
function msg_ok() {
|
||||||
IMAGE=""
|
local msg="$1"
|
||||||
NAME=""
|
echo -e "${CM} ${GN}${msg}${CL}"
|
||||||
VMID=""
|
}
|
||||||
CORES="2"
|
|
||||||
MEMORY="2048"
|
|
||||||
DISK="8"
|
|
||||||
STORAGE=""
|
|
||||||
NETWORK="vmbr0"
|
|
||||||
IP="dhcp"
|
|
||||||
GATEWAY=""
|
|
||||||
PRIVILEGED="0"
|
|
||||||
START="0"
|
|
||||||
ENV_VARS=()
|
|
||||||
VOLUMES=()
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
function msg_error() {
|
||||||
case $1 in
|
local msg="$1"
|
||||||
-h | --help)
|
echo -e "${CROSS} ${RD}${msg}${CL}"
|
||||||
usage
|
}
|
||||||
;;
|
|
||||||
-n | --name)
|
|
||||||
NAME="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-i | --vmid)
|
|
||||||
VMID="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-c | --cores)
|
|
||||||
CORES="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-m | --memory)
|
|
||||||
MEMORY="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-d | --disk)
|
|
||||||
DISK="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-s | --storage)
|
|
||||||
STORAGE="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--network)
|
|
||||||
NETWORK="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--ip)
|
|
||||||
IP="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--gateway)
|
|
||||||
GATEWAY="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-e | --env)
|
|
||||||
ENV_VARS+=("$2")
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-v | --volume)
|
|
||||||
VOLUMES+=("$2")
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--privileged)
|
|
||||||
PRIVILEGED="1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--start)
|
|
||||||
START="1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-*)
|
|
||||||
echo "Unknown option: $1"
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
IMAGE="$1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check if image is provided
|
# Check Proxmox version
|
||||||
if [[ -z "$IMAGE" ]]; then
|
if ! command -v pveversion &>/dev/null; then
|
||||||
msg_error "No image specified"
|
msg_error "This script must be run on Proxmox VE"
|
||||||
usage
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Deploy the container
|
PVE_VER=$(pveversion | grep -oP 'pve-manager/\K[0-9.]+' | cut -d. -f1,2)
|
||||||
deploy_oci_container "$IMAGE" "$NAME" "$VMID" "$CORES" "$MEMORY" "$DISK" "$STORAGE" "$NETWORK" "$IP" "$GATEWAY" "$PRIVILEGED" "$START" "${ENV_VARS[@]}"
|
MAJOR=$(echo "$PVE_VER" | cut -d. -f1)
|
||||||
|
MINOR=$(echo "$PVE_VER" | cut -d. -f2)
|
||||||
|
|
||||||
exit 0
|
if [[ "$MAJOR" -lt 9 ]] || { [[ "$MAJOR" -eq 9 ]] && [[ "$MINOR" -lt 1 ]]; }; then
|
||||||
|
msg_error "Proxmox VE 9.1+ required (current: $PVE_VER)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg_ok "Proxmox VE $PVE_VER detected"
|
||||||
|
|
||||||
|
# Parse OCI image
|
||||||
|
parse_image() {
|
||||||
|
local input="$1"
|
||||||
|
if [[ "$input" =~ ^([^/]+\.[^/]+)/ ]]; then
|
||||||
|
echo "$input"
|
||||||
|
elif [[ "$input" =~ / ]]; then
|
||||||
|
echo "docker.io/$input"
|
||||||
|
else
|
||||||
|
echo "docker.io/library/$input"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive image selection
|
||||||
|
if [[ -z "${OCI_IMAGE:-}" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e "${BL}Select OCI Image:${CL}"
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e " ${BL}1)${CL} nginx:alpine - Lightweight web server"
|
||||||
|
echo -e " ${BL}2)${CL} postgres:16-alpine - PostgreSQL database"
|
||||||
|
echo -e " ${BL}3)${CL} redis:alpine - Redis cache"
|
||||||
|
echo -e " ${BL}4)${CL} mariadb:latest - MariaDB database"
|
||||||
|
echo -e " ${BL}5)${CL} ghcr.io/linkwarden/linkwarden:latest"
|
||||||
|
echo -e " ${BL}6)${CL} Custom image"
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -r -p "Select option (1-6): " IMAGE_CHOICE
|
||||||
|
|
||||||
|
case $IMAGE_CHOICE in
|
||||||
|
1) OCI_IMAGE="nginx:alpine" ;;
|
||||||
|
2) OCI_IMAGE="postgres:16-alpine" ;;
|
||||||
|
3) OCI_IMAGE="redis:alpine" ;;
|
||||||
|
4) OCI_IMAGE="mariadb:latest" ;;
|
||||||
|
5) OCI_IMAGE="ghcr.io/linkwarden/linkwarden:latest" ;;
|
||||||
|
6)
|
||||||
|
read -r -p "Enter OCI image (e.g., ghcr.io/user/repo:tag): " OCI_IMAGE
|
||||||
|
[[ -z "$OCI_IMAGE" ]] && { msg_error "No image specified"; exit 1; }
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
msg_error "Invalid choice"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
FULL_IMAGE=$(parse_image "$OCI_IMAGE")
|
||||||
|
msg_ok "Selected: $FULL_IMAGE"
|
||||||
|
|
||||||
|
# Derive container name
|
||||||
|
if [[ -z "${CT_NAME:-}" ]]; then
|
||||||
|
DEFAULT_NAME=$(echo "$OCI_IMAGE" | sed 's|.*/||; s/:.*//; s/[^a-zA-Z0-9-]/-/g' | cut -c1-60)
|
||||||
|
read -r -p "Container name [${DEFAULT_NAME}]: " CT_NAME
|
||||||
|
CT_NAME=${CT_NAME:-$DEFAULT_NAME}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get next VMID
|
||||||
|
if [[ -z "${VMID:-}" ]]; then
|
||||||
|
NEXT_ID=$(pvesh get /cluster/nextid)
|
||||||
|
read -r -p "Container ID [${NEXT_ID}]: " VMID
|
||||||
|
VMID=${VMID:-$NEXT_ID}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Resources
|
||||||
|
if [[ -z "${CORES:-}" ]]; then
|
||||||
|
read -r -p "CPU cores [2]: " CORES
|
||||||
|
CORES=${CORES:-2}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${MEMORY:-}" ]]; then
|
||||||
|
read -r -p "Memory in MB [2048]: " MEMORY
|
||||||
|
MEMORY=${MEMORY:-2048}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${DISK:-}" ]]; then
|
||||||
|
read -r -p "Disk size in GB [8]: " DISK
|
||||||
|
DISK=${DISK:-8}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Storage
|
||||||
|
if [[ -z "${STORAGE:-}" ]]; then
|
||||||
|
AVAIL_STORAGE=$(pvesm status | awk '/^local-(zfs|lvm)/ {print $1; exit}')
|
||||||
|
[[ -z "$AVAIL_STORAGE" ]] && AVAIL_STORAGE="local"
|
||||||
|
read -r -p "Storage [${AVAIL_STORAGE}]: " STORAGE
|
||||||
|
STORAGE=${STORAGE:-$AVAIL_STORAGE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Network
|
||||||
|
if [[ -z "${BRIDGE:-}" ]]; then
|
||||||
|
read -r -p "Network bridge [vmbr0]: " BRIDGE
|
||||||
|
BRIDGE=${BRIDGE:-vmbr0}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${IP_MODE:-}" ]]; then
|
||||||
|
read -r -p "IP mode (dhcp/static) [dhcp]: " IP_MODE
|
||||||
|
IP_MODE=${IP_MODE:-dhcp}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$IP_MODE" == "static" ]]; then
|
||||||
|
read -r -p "Static IP (CIDR, e.g., 192.168.1.100/24): " STATIC_IP
|
||||||
|
read -r -p "Gateway IP: " GATEWAY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
declare -a ENV_VARS=()
|
||||||
|
|
||||||
|
case "$OCI_IMAGE" in
|
||||||
|
postgres*|postgresql*)
|
||||||
|
echo ""
|
||||||
|
msg_info "PostgreSQL requires environment variables"
|
||||||
|
read -r -p "PostgreSQL password: " -s PG_PASS
|
||||||
|
echo ""
|
||||||
|
ENV_VARS+=("POSTGRES_PASSWORD=$PG_PASS")
|
||||||
|
|
||||||
|
read -r -p "Create database (optional): " PG_DB
|
||||||
|
[[ -n "$PG_DB" ]] && ENV_VARS+=("POSTGRES_DB=$PG_DB")
|
||||||
|
|
||||||
|
read -r -p "PostgreSQL user (optional): " PG_USER
|
||||||
|
[[ -n "$PG_USER" ]] && ENV_VARS+=("POSTGRES_USER=$PG_USER")
|
||||||
|
;;
|
||||||
|
|
||||||
|
mariadb*|mysql*)
|
||||||
|
echo ""
|
||||||
|
msg_info "MariaDB/MySQL requires environment variables"
|
||||||
|
read -r -p "Root password: " -s MYSQL_PASS
|
||||||
|
echo ""
|
||||||
|
ENV_VARS+=("MYSQL_ROOT_PASSWORD=$MYSQL_PASS")
|
||||||
|
|
||||||
|
read -r -p "Create database (optional): " MYSQL_DB
|
||||||
|
[[ -n "$MYSQL_DB" ]] && ENV_VARS+=("MYSQL_DATABASE=$MYSQL_DB")
|
||||||
|
|
||||||
|
read -r -p "Create user (optional): " MYSQL_USER
|
||||||
|
if [[ -n "$MYSQL_USER" ]]; then
|
||||||
|
ENV_VARS+=("MYSQL_USER=$MYSQL_USER")
|
||||||
|
read -r -p "User password: " -s MYSQL_USER_PASS
|
||||||
|
echo ""
|
||||||
|
ENV_VARS+=("MYSQL_PASSWORD=$MYSQL_USER_PASS")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*linkwarden*)
|
||||||
|
echo ""
|
||||||
|
msg_info "Linkwarden configuration"
|
||||||
|
read -r -p "NEXTAUTH_SECRET (press Enter to generate): " NEXTAUTH_SECRET
|
||||||
|
if [[ -z "$NEXTAUTH_SECRET" ]]; then
|
||||||
|
NEXTAUTH_SECRET=$(openssl rand -base64 32)
|
||||||
|
fi
|
||||||
|
ENV_VARS+=("NEXTAUTH_SECRET=$NEXTAUTH_SECRET")
|
||||||
|
|
||||||
|
read -r -p "NEXTAUTH_URL [http://localhost:3000]: " NEXTAUTH_URL
|
||||||
|
NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000}
|
||||||
|
ENV_VARS+=("NEXTAUTH_URL=$NEXTAUTH_URL")
|
||||||
|
|
||||||
|
read -r -p "DATABASE_URL (PostgreSQL connection string): " DATABASE_URL
|
||||||
|
[[ -n "$DATABASE_URL" ]] && ENV_VARS+=("DATABASE_URL=$DATABASE_URL")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Additional env vars
|
||||||
|
read -r -p "Add custom environment variables? (y/N): " ADD_ENV
|
||||||
|
if [[ "${ADD_ENV,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
while true; do
|
||||||
|
read -r -p "Enter KEY=VALUE (or press Enter to finish): " CUSTOM_ENV
|
||||||
|
[[ -z "$CUSTOM_ENV" ]] && break
|
||||||
|
ENV_VARS+=("$CUSTOM_ENV")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Privileged mode
|
||||||
|
read -r -p "Run as privileged container? (y/N): " PRIV_MODE
|
||||||
|
if [[ "${PRIV_MODE,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
UNPRIVILEGED="0"
|
||||||
|
else
|
||||||
|
UNPRIVILEGED="1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Auto-start
|
||||||
|
read -r -p "Start container after creation? (Y/n): " AUTO_START
|
||||||
|
if [[ "${AUTO_START,,}" =~ ^(n|no)$ ]]; then
|
||||||
|
START_AFTER="no"
|
||||||
|
else
|
||||||
|
START_AFTER="yes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo ""
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e "${BL}Container Configuration Summary:${CL}"
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e " Image: $FULL_IMAGE"
|
||||||
|
echo -e " ID: $VMID"
|
||||||
|
echo -e " Name: $CT_NAME"
|
||||||
|
echo -e " CPUs: $CORES"
|
||||||
|
echo -e " Memory: ${MEMORY}MB"
|
||||||
|
echo -e " Disk: ${DISK}GB"
|
||||||
|
echo -e " Storage: $STORAGE"
|
||||||
|
echo -e " Network: $BRIDGE ($IP_MODE)"
|
||||||
|
[[ ${#ENV_VARS[@]} -gt 0 ]] && echo -e " Env vars: ${#ENV_VARS[@]} configured"
|
||||||
|
echo -e "${YW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -r -p "Proceed with creation? (Y/n): " CONFIRM
|
||||||
|
if [[ "${CONFIRM,,}" =~ ^(n|no)$ ]]; then
|
||||||
|
msg_error "Cancelled by user"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create container
|
||||||
|
msg_info "Creating container $VMID"
|
||||||
|
|
||||||
|
PCT_CMD="pct create $VMID oci://$FULL_IMAGE"
|
||||||
|
PCT_CMD+=" --hostname $CT_NAME"
|
||||||
|
PCT_CMD+=" --cores $CORES"
|
||||||
|
PCT_CMD+=" --memory $MEMORY"
|
||||||
|
PCT_CMD+=" --rootfs ${STORAGE}:${DISK}"
|
||||||
|
PCT_CMD+=" --unprivileged $UNPRIVILEGED"
|
||||||
|
|
||||||
|
if [[ "$IP_MODE" == "static" && -n "$STATIC_IP" ]]; then
|
||||||
|
PCT_CMD+=" --net0 name=eth0,bridge=$BRIDGE,ip=$STATIC_IP"
|
||||||
|
[[ -n "$GATEWAY" ]] && PCT_CMD+=",gw=$GATEWAY"
|
||||||
|
else
|
||||||
|
PCT_CMD+=" --net0 name=eth0,bridge=$BRIDGE,ip=dhcp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if eval "$PCT_CMD" 2>&1; then
|
||||||
|
msg_ok "Container created"
|
||||||
|
else
|
||||||
|
msg_error "Failed to create container"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
if [[ ${#ENV_VARS[@]} -gt 0 ]]; then
|
||||||
|
msg_info "Configuring environment variables"
|
||||||
|
for env_var in "${ENV_VARS[@]}"; do
|
||||||
|
if pct set "$VMID" -env "$env_var" &>/dev/null; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
msg_error "Failed to set: $env_var"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
msg_ok "Environment variables configured (${#ENV_VARS[@]} variables)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start container
|
||||||
|
if [[ "$START_AFTER" == "yes" ]]; then
|
||||||
|
msg_info "Starting container"
|
||||||
|
if pct start "$VMID" 2>&1; then
|
||||||
|
msg_ok "Container started"
|
||||||
|
|
||||||
|
# Wait for network
|
||||||
|
sleep 3
|
||||||
|
CT_IP=$(pct exec "$VMID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e "${BL}Container Information:${CL}"
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e " ID: ${GN}$VMID${CL}"
|
||||||
|
echo -e " Name: ${GN}$CT_NAME${CL}"
|
||||||
|
echo -e " Image: ${GN}$FULL_IMAGE${CL}"
|
||||||
|
echo -e " IP: ${GN}$CT_IP${CL}"
|
||||||
|
echo -e " Status: ${GN}Running${CL}"
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${INFO} ${YW}Access console:${CL} pct console $VMID"
|
||||||
|
echo -e "${INFO} ${YW}View logs:${CL} pct logs $VMID"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
msg_error "Failed to start container"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e "${BL}Container Information:${CL}"
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo -e " ID: ${GN}$VMID${CL}"
|
||||||
|
echo -e " Name: ${GN}$CT_NAME${CL}"
|
||||||
|
echo -e " Image: ${GN}$FULL_IMAGE${CL}"
|
||||||
|
echo -e " Status: ${YW}Stopped${CL}"
|
||||||
|
echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${INFO} ${YW}Start with:${CL} pct start $VMID"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user