forked from forkanization/Proxmox-arm64
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -16,6 +16,7 @@ color() {
|
||||
BFR="\\r\\033[K"
|
||||
BOLD=$(echo "\033[1m")
|
||||
TAB=" "
|
||||
TAB3=" "
|
||||
|
||||
# System
|
||||
RETRY_NUM=10
|
||||
|
||||
+99
-308
@@ -31,6 +31,7 @@ color() {
|
||||
BOLD=$(echo "\033[1m")
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
TAB3=" "
|
||||
|
||||
# Icons
|
||||
CM="${TAB}✔️${TAB}"
|
||||
@@ -360,6 +361,76 @@ base_settings() {
|
||||
var_version="12"
|
||||
fi
|
||||
}
|
||||
write_config() {
|
||||
# This function writes the configuration to a file.
|
||||
if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then
|
||||
FILEPATH="/opt/community-scripts/${NSAPP}.conf"
|
||||
if [[ ! -f $FILEPATH ]]; then
|
||||
cat <<EOF >"$FILEPATH"
|
||||
# ${NSAPP} Configuration File
|
||||
# Generated on $(date)
|
||||
|
||||
CT_TYPE="${CT_TYPE}"
|
||||
DISK_SIZE="${DISK_SIZE}"
|
||||
CORE_COUNT="${DISK_SIZE}"
|
||||
RAM_SIZE="${RAM_SIZE}"
|
||||
HN="${HN}"
|
||||
BRG="${BRG}"
|
||||
APT_CACHER_IP="${APT_CACHER_IP:-none}"
|
||||
DISABLEIP6="${DISABLEIP6}"
|
||||
PW="${PW:-none}"
|
||||
SSH="${SSH}"
|
||||
SSH_AUTHORIZED_KEY="${SSH_AUTHORIZED_KEY}"
|
||||
VERBOSE="${VERBOSE}"
|
||||
TAGS="${TAGS:-none}"
|
||||
VLAN="${VLAN:-none}"
|
||||
MTU="${MTU:-1500}"
|
||||
GATE="${GATE:-none}"
|
||||
SD="${SD:-none}"
|
||||
MAC="${MAC:-none}"
|
||||
NS="${NS:-none}"
|
||||
NET="${NET}"
|
||||
|
||||
EOF
|
||||
echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}"
|
||||
else
|
||||
echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}"
|
||||
if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then
|
||||
rm -f "$FILEPATH"
|
||||
cat <<EOF >"$FILEPATH"
|
||||
# ${NSAPP} Configuration File
|
||||
# Generated on $(date)
|
||||
|
||||
CT_TYPE="${CT_TYPE}"
|
||||
DISK_SIZE="${DISK_SIZE}"
|
||||
CORE_COUNT="${DISK_SIZE}"
|
||||
RAM_SIZE="${RAM_SIZE}"
|
||||
HN="${HN}"
|
||||
BRG="${BRG}"
|
||||
APT_CACHER_IP="${APT_CACHER_IP:-none}"
|
||||
DISABLEIP6="${DISABLEIP6}"
|
||||
PW="${PW:-none}"
|
||||
SSH="${SSH}"
|
||||
SSH_AUTHORIZED_KEY="${SSH_AUTHORIZED_KEY}"
|
||||
VERBOSE="${VERBOSE}"
|
||||
TAGS="${TAGS:-none}"
|
||||
VLAN="${VLAN:-none}"
|
||||
MTU="${MTU:-1500}"
|
||||
GATE="${GATE:-none}"
|
||||
SD="${SD:-none}"
|
||||
MAC="${MAC:-none}"
|
||||
NS="${NS:-none}"
|
||||
NET="${NET}"
|
||||
|
||||
EOF
|
||||
echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}"
|
||||
else
|
||||
echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# This function displays the default values for various settings.
|
||||
echo_default() {
|
||||
@@ -527,29 +598,29 @@ advanced_settings() {
|
||||
exit_script
|
||||
fi
|
||||
|
||||
|
||||
BRIDGES=""
|
||||
IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f)
|
||||
OLD_IFS=$IFS; IFS=$'\n'
|
||||
OLD_IFS=$IFS
|
||||
IFS=$'\n'
|
||||
|
||||
for iface_filepath in ${IFACE_FILEPATH_LIST}; do
|
||||
iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
|
||||
|
||||
( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | \
|
||||
awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}"
|
||||
(grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) |
|
||||
awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true
|
||||
|
||||
if [ -f "${iface_indexes_tmpfile}" ]; then
|
||||
while read -r pair; do
|
||||
start=$(echo "${pair}" | cut -d':' -f1)
|
||||
end=$(echo "${pair}" | cut -d':' -f2)
|
||||
while read -r pair; do
|
||||
start=$(echo "${pair}" | cut -d':' -f1)
|
||||
end=$(echo "${pair}" | cut -d':' -f2)
|
||||
|
||||
if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*bridge[-_](ports|stp|fd|vlan-aware|vids)\s+'; then
|
||||
iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
|
||||
BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
|
||||
fi
|
||||
if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
|
||||
iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
|
||||
BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
|
||||
fi
|
||||
|
||||
done < "${iface_indexes_tmpfile}"
|
||||
rm -f "${iface_indexes_tmpfile}"
|
||||
done <"${iface_indexes_tmpfile}"
|
||||
rm -f "${iface_indexes_tmpfile}"
|
||||
fi
|
||||
|
||||
done
|
||||
@@ -563,11 +634,11 @@ advanced_settings() {
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3)
|
||||
if [ -z "$BRG" ]; then
|
||||
exit_script
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
if [ -z "$BRG" ]; then
|
||||
exit_script
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
while true; do
|
||||
@@ -700,8 +771,14 @@ advanced_settings() {
|
||||
exit_script
|
||||
fi
|
||||
|
||||
if [[ "$PW" == -password* ]]; then
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then
|
||||
SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)"
|
||||
|
||||
if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
SSH_AUTHORIZED_KEY=""
|
||||
fi
|
||||
|
||||
if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then
|
||||
SSH="yes"
|
||||
else
|
||||
SSH="no"
|
||||
@@ -712,15 +789,7 @@ advanced_settings() {
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
fi
|
||||
|
||||
if [[ "${SSH}" == "yes" ]]; then
|
||||
SSH_AUTHORIZED_KEY="$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)"
|
||||
|
||||
if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
echo "Warning: No SSH key provided."
|
||||
fi
|
||||
else
|
||||
SSH_AUTHORIZED_KEY=""
|
||||
fi
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
|
||||
VERB="yes"
|
||||
else
|
||||
@@ -730,6 +799,7 @@ advanced_settings() {
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
|
||||
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
|
||||
|
||||
else
|
||||
clear
|
||||
header_info
|
||||
@@ -806,285 +876,6 @@ EOF
|
||||
|
||||
}
|
||||
|
||||
config_file() {
|
||||
|
||||
CONFIG_FILE="/opt/community-scripts/.settings"
|
||||
|
||||
if [[ -f "/opt/community-scripts/${NSAPP}.conf" ]]; then
|
||||
CONFIG_FILE="/opt/community-scripts/${NSAPP}.conf"
|
||||
fi
|
||||
|
||||
if CONFIG_FILE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set absolute path to config file" 8 58 "$CONFIG_FILE" --title "CONFIG FILE" 3>&1 1>&2 2>&3); then
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo -e "${CROSS}${RD}Config file not found, exiting script!.${CL}"
|
||||
exit
|
||||
else
|
||||
echo -e "${INFO}${BOLD}${DGN}Using config File: ${BGN}$CONFIG_FILE${CL}"
|
||||
base_settings
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
fi
|
||||
if [[ -n "$CT_ID" ]]; then
|
||||
if [[ "$CT_ID" =~ ^([0-9]{3,4})-([0-9]{3,4})$ ]]; then
|
||||
MIN_ID=${BASH_REMATCH[1]}
|
||||
MAX_ID=${BASH_REMATCH[2]}
|
||||
if ((MIN_ID >= MAX_ID)); then
|
||||
msg_error "Invalid Container ID range. The first number must be smaller than the second number, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
for ((ID = MIN_ID; ID <= MAX_ID; ID++)); do
|
||||
if ! grep -q "^$ID$" <<<"$LIST_OF_IDS"; then
|
||||
CT_ID=$ID
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
|
||||
elif [[ "$CT_ID" =~ ^[0-9]+$ ]]; then
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
|
||||
if ! grep -q "^$CT_ID$" <<<"$LIST_OF_IDS"; then
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
else
|
||||
msg_error "Container ID $CT_ID already exists"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid Container ID format. Needs to be 0000-9999 or 0-9999, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$CT_TYPE" -eq 0 ]]; then
|
||||
CT_TYPE_DESC="Privileged"
|
||||
elif [[ "$CT_TYPE" -eq 1 ]]; then
|
||||
CT_TYPE_DESC="Unprivileged"
|
||||
else
|
||||
msg_error "Unknown setting for CT_TYPE, should be 1 or 0, was ${CT_TYPE}"
|
||||
exit
|
||||
fi
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
|
||||
if [[ ! -z "$PW" ]]; then
|
||||
|
||||
if [[ "$PW" == *" "* ]]; then
|
||||
msg_error "Password cannot be empty"
|
||||
exit
|
||||
elif [[ ${#PW} -lt 5 ]]; then
|
||||
msg_error "Password must be at least 5 characters long"
|
||||
exit
|
||||
else
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
|
||||
fi
|
||||
PW="-password $PW"
|
||||
else
|
||||
PW=""
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}Automatic Login${CL}"
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
|
||||
if [[ ! -z "$HN" ]]; then
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
msg_error "Hostname cannot be empty"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ! -z "$DISK_SIZE" ]]; then
|
||||
if [[ "$DISK_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
else
|
||||
msg_error "DISK_SIZE must be an integer, was ${DISK_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "DISK_SIZE cannot be empty"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ! -z "$CORE_COUNT" ]]; then
|
||||
if [[ "$CORE_COUNT" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
else
|
||||
msg_error "CORE_COUNT must be an integer, was ${CORE_COUNT}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "CORE_COUNT cannot be empty"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ! -z "$RAM_SIZE" ]]; then
|
||||
if [[ "$RAM_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
else
|
||||
msg_error "RAM_SIZE must be an integer, was ${RAM_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "RAM_SIZE cannot be empty"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ! -z "$BRG" ]]; then
|
||||
if grep -q "^iface ${BRG}" /etc/network/interfaces; then
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "Bridge cannot be empty"
|
||||
exit
|
||||
fi
|
||||
|
||||
local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||
local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
|
||||
if [[ ! -z $NET ]]; then
|
||||
if [ "$NET" == "dhcp" ]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}DHCP${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
|
||||
elif [[ "$NET" =~ $ip_cidr_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
if [ ! -z "$GATE" ]; then
|
||||
if [[ "$GATE" =~ $ip_regex ]]; then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}"
|
||||
GATE=",gw=$GATE"
|
||||
else
|
||||
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "Gateway IP Address cannot be empty"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0, was ${NET}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -z "$APT_CACHER_IP" ]]; then
|
||||
if [[ "$APT_CACHER_IP" =~ $ip_regex ]]; then
|
||||
APT_CACHER="yes"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}"
|
||||
else
|
||||
msg_error "Invalid IP Address format for APT-Cacher. Needs to be 0.0.0.0, was ${APT_CACHER_IP}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$DISABLEIP6" == "yes" ]]; then
|
||||
echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}Yes${CL}"
|
||||
elif [[ "$DISABLEIP6" == "no" ]]; then
|
||||
echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Disable IPv6 needs to be 'yes' or 'no'"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ ! -z "$MTU" ]]; then
|
||||
if [[ "$MTU" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU${CL}"
|
||||
MTU=",mtu=$MTU"
|
||||
else
|
||||
msg_error "MTU must be an integer, was ${MTU}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
MTU=""
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
||||
|
||||
fi
|
||||
|
||||
if [[ ! -z "$SD" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SD${CL}"
|
||||
SD="-searchdomain=$SD"
|
||||
else
|
||||
SD=""
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}HOST${CL}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$NS" ]]; then
|
||||
if [[ "$NS" =~ $ip_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NS${CL}"
|
||||
NS="-nameserver=$NS"
|
||||
else
|
||||
msg_error "Invalid IP Address format for DNS Server. Needs to be 0.0.0.0, was ${NS}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
NS=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}HOST${CL}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$MAC" ]]; then
|
||||
if [[ "$MAC" =~ ^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$ ]]; then
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
MAC=",hwaddr=$MAC"
|
||||
else
|
||||
msg_error "MAC Address must be in the format xx:xx:xx:xx:xx:xx, was ${MAC}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -z "$VLAN" ]]; then
|
||||
if [[ "$VLAN" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN${CL}"
|
||||
VLAN=",tag=$VLAN"
|
||||
else
|
||||
msg_error "VLAN must be an integer, was ${VLAN}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -z "$TAGS" ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
|
||||
fi
|
||||
|
||||
if [[ "$SSH" == "yes" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
if [[ ! -z "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}********************${CL}"
|
||||
else
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}None${CL}"
|
||||
fi
|
||||
elif [[ "$SSH" == "no" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
else
|
||||
msg_error "SSH needs to be 'yes' or 'no', was ${SSH}"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$VERB" == "yes" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}"
|
||||
elif [[ "$VERB" == "no" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Verbose Mode needs to be 'yes' or 'no', was ${VERB}"
|
||||
exit
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS WITH CONFIG FILE COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
|
||||
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above settings${CL}"
|
||||
else
|
||||
clear
|
||||
header_info
|
||||
echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}"
|
||||
config_file
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_script() {
|
||||
pve_check
|
||||
@@ -1147,7 +938,7 @@ install_script() {
|
||||
header_info
|
||||
echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}"
|
||||
METHOD="advanced"
|
||||
base_settings
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/config-file.func)
|
||||
config_file
|
||||
break
|
||||
;;
|
||||
|
||||
@@ -0,0 +1,576 @@
|
||||
config_file() {
|
||||
CONFIG_FILE="/opt/community-scripts/.settings"
|
||||
|
||||
if [[ -f "/opt/community-scripts/${NSAPP}.conf" ]]; then
|
||||
CONFIG_FILE="/opt/community-scripts/${NSAPP}.conf"
|
||||
fi
|
||||
|
||||
if CONFIG_FILE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set absolute path to config file" 8 58 "$CONFIG_FILE" --title "CONFIG FILE" 3>&1 1>&2 2>&3); then
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo -e "${CROSS}${RD}Config file not found, exiting script!.${CL}"
|
||||
exit
|
||||
else
|
||||
echo -e "${INFO}${BOLD}${DGN}Using config File: ${BGN}$CONFIG_FILE${CL}"
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
fi
|
||||
if [[ -n "${CT_ID-}" ]]; then
|
||||
if [[ "$CT_ID" =~ ^([0-9]{3,4})-([0-9]{3,4})$ ]]; then
|
||||
MIN_ID=${BASH_REMATCH[1]}
|
||||
MAX_ID=${BASH_REMATCH[2]}
|
||||
if ((MIN_ID >= MAX_ID)); then
|
||||
msg_error "Invalid Container ID range. The first number must be smaller than the second number, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
for ((ID = MIN_ID; ID <= MAX_ID; ID++)); do
|
||||
if ! grep -q "^$ID$" <<<"$LIST_OF_IDS"; then
|
||||
CT_ID=$ID
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
|
||||
elif [[ "$CT_ID" =~ ^[0-9]+$ ]]; then
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
|
||||
if ! grep -q "^$CT_ID$" <<<"$LIST_OF_IDS"; then
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
else
|
||||
msg_error "Container ID $CT_ID already exists"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid Container ID format. Needs to be 0000-9999 or 0-9999, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CT_ID" ]; then
|
||||
CT_ID="$NEXTID"
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
else
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
|
||||
fi
|
||||
if [[ -n "${CT_TYPE-}" ]]; then
|
||||
if [[ "$CT_TYPE" -eq 0 ]]; then
|
||||
CT_TYPE_DESC="Privileged"
|
||||
elif [[ "$CT_TYPE" -eq 1 ]]; then
|
||||
CT_TYPE_DESC="Unprivileged"
|
||||
else
|
||||
msg_error "Unknown setting for CT_TYPE, should be 1 or 0, was ${CT_TYPE}"
|
||||
exit
|
||||
fi
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
else
|
||||
if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \
|
||||
"1" "Unprivileged" ON \
|
||||
"0" "Privileged" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ -n "$CT_TYPE" ]; then
|
||||
CT_TYPE_DESC="Unprivileged"
|
||||
if [ "$CT_TYPE" -eq 0 ]; then
|
||||
CT_TYPE_DESC="Privileged"
|
||||
fi
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [[ -n "${PW-}" ]]; then
|
||||
if [[ "$PW" == "none" ]]; then
|
||||
PW=""
|
||||
else
|
||||
if [[ "$PW" == *" "* ]]; then
|
||||
msg_error "Password cannot be empty"
|
||||
exit
|
||||
elif [[ ${#PW} -lt 5 ]]; then
|
||||
msg_error "Password must be at least 5 characters long"
|
||||
exit
|
||||
else
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
|
||||
fi
|
||||
PW="-password $PW"
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then
|
||||
if [[ -n "$PW1" ]]; then
|
||||
if [[ "$PW1" == *" "* ]]; then
|
||||
whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58
|
||||
elif [ ${#PW1} -lt 5 ]; then
|
||||
whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58
|
||||
else
|
||||
if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
|
||||
if [[ "$PW1" == "$PW2" ]]; then
|
||||
PW="-password $PW1"
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
|
||||
break
|
||||
else
|
||||
whiptail --msgbox "Passwords do not match. Please try again." 8 58
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
else
|
||||
PW1="Automatic Login"
|
||||
PW=""
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
|
||||
break
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -n "${HN-}" ]]; then
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CT_NAME" ]; then
|
||||
HN="$NSAPP"
|
||||
else
|
||||
HN=$(echo "${CT_NAME,,}" | tr -d ' ')
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${DISK_SIZE-}" ]]; then
|
||||
if [[ "$DISK_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
else
|
||||
msg_error "DISK_SIZE must be an integer, was ${DISK_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$DISK_SIZE" ]; then
|
||||
DISK_SIZE="$var_disk"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
else
|
||||
if ! [[ $DISK_SIZE =~ $INTEGER ]]; then
|
||||
echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${CORE_COUNT-}" ]]; then
|
||||
if [[ "$CORE_COUNT" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
else
|
||||
msg_error "CORE_COUNT must be an integer, was ${CORE_COUNT}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then
|
||||
CORE_COUNT="$var_cpu"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
else
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${RAM_SIZE-}" ]]; then
|
||||
if [[ "$RAM_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
else
|
||||
msg_error "RAM_SIZE must be an integer, was ${RAM_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then
|
||||
RAM_SIZE="$var_ram"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
else
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f)
|
||||
BRIDGES=""
|
||||
OLD_IFS=$IFS
|
||||
IFS=$'\n'
|
||||
|
||||
for iface_filepath in ${IFACE_FILEPATH_LIST}; do
|
||||
|
||||
iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
|
||||
( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true
|
||||
|
||||
if [ -f "${iface_indexes_tmpfile}" ]; then
|
||||
|
||||
while read -r pair; do
|
||||
start=$(echo "${pair}" | cut -d':' -f1)
|
||||
end=$(echo "${pair}" | cut -d':' -f2)
|
||||
if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
|
||||
iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
|
||||
BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
|
||||
fi
|
||||
|
||||
done < "${iface_indexes_tmpfile}"
|
||||
rm -f "${iface_indexes_tmpfile}"
|
||||
fi
|
||||
|
||||
done
|
||||
IFS=$OLD_IFS
|
||||
BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq)
|
||||
|
||||
if [[ -n "${BRG-}" ]]; then
|
||||
if echo "$BRIDGES" | grep -q "${BRG}"; then
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces or /etc/network/interfaces.d/sdn"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3)
|
||||
if [ -z "$BRG" ]; then
|
||||
exit_script
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||
local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
|
||||
if [[ -n ${NET-} ]]; then
|
||||
if [ "$NET" == "dhcp" ]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}DHCP${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
|
||||
elif [[ "$NET" =~ $ip_cidr_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
if [ ! -z "$GATE" ]; then
|
||||
if [[ "$GATE" =~ $ip_regex ]]; then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}"
|
||||
GATE=",gw=$GATE"
|
||||
else
|
||||
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "Gateway IP Address cannot be empty"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0, was ${NET}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3)
|
||||
exit_status=$?
|
||||
if [ $exit_status -eq 0 ]; then
|
||||
if [ "$NET" = "dhcp" ]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
break
|
||||
else
|
||||
if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
break
|
||||
else
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58
|
||||
fi
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
if [ "$NET" != "dhcp" ]; then
|
||||
while true; do
|
||||
GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3)
|
||||
if [ -z "$GATE1" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58
|
||||
elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58
|
||||
else
|
||||
GATE=",gw=$GATE1"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
GATE=""
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$var_os" == "alpine" ]; then
|
||||
APT_CACHER=""
|
||||
APT_CACHER_IP=""
|
||||
else
|
||||
if [[ -n "${APT_CACHER_IP-}" ]]; then
|
||||
if [[ ! $APT_CACHER_IP == "none" ]]; then
|
||||
if [[ "$APT_CACHER_IP" =~ $ip_regex ]]; then
|
||||
APT_CACHER="yes"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}"
|
||||
else
|
||||
msg_error "Invalid IP Address format for APT-Cacher. Needs to be 0.0.0.0, was ${APT_CACHER_IP}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
APT_CACHER=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}"
|
||||
fi
|
||||
else
|
||||
if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then
|
||||
APT_CACHER="${APT_CACHER_IP:+yes}"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}"
|
||||
if [[ -n $APT_CACHER_IP ]]; then
|
||||
APT_CACHER_IP="none"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${DISABLEIP6-}" == "yes" ]]; then
|
||||
echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}Yes${CL}"
|
||||
elif [[ "${DISABLEIP6-}" == "no" ]]; then
|
||||
echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}No${CL}"
|
||||
else
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then
|
||||
DISABLEIP6="yes"
|
||||
else
|
||||
DISABLEIP6="no"
|
||||
fi
|
||||
echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}"
|
||||
fi
|
||||
|
||||
if [[ -n "${MTU-}" ]]; then
|
||||
if [[ "$MTU" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU${CL}"
|
||||
MTU=",mtu=$MTU"
|
||||
else
|
||||
msg_error "MTU must be an integer, was ${MTU}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
else
|
||||
MTU=",mtu=$MTU1"
|
||||
fi
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${SD-}" ]]; then
|
||||
if [[ "$SD" == "none" ]]; then
|
||||
SD=""
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}Host${CL}"
|
||||
else
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SD${CL}"
|
||||
SD="-searchdomain=$SD"
|
||||
fi
|
||||
else
|
||||
if SD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$SD" ]; then
|
||||
SX=Host
|
||||
SD=""
|
||||
else
|
||||
SX=$SD
|
||||
SD="-searchdomain=$SD"
|
||||
fi
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${NS-}" ]]; then
|
||||
if [[ $NS == "none" ]]; then
|
||||
NS=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}Host${CL}"
|
||||
else
|
||||
if [[ "$NS" =~ $ip_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NS${CL}"
|
||||
NS="-nameserver=$NS"
|
||||
else
|
||||
msg_error "Invalid IP Address format for DNS Server. Needs to be 0.0.0.0, was ${NS}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if NX=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$NX" ]; then
|
||||
NX=Host
|
||||
NS=""
|
||||
else
|
||||
NS="-nameserver=$NX"
|
||||
fi
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${MAC-}" ]]; then
|
||||
if [[ "$MAC" == "none" ]]; then
|
||||
MAC=""
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}Host${CL}"
|
||||
else
|
||||
if [[ "$MAC" =~ ^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$ ]]; then
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
MAC=",hwaddr=$MAC"
|
||||
else
|
||||
msg_error "MAC Address must be in the format xx:xx:xx:xx:xx:xx, was ${MAC}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MAC1" ]; then
|
||||
MAC1="Default"
|
||||
MAC=""
|
||||
else
|
||||
MAC=",hwaddr=$MAC1"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${VLAN-}" ]]; then
|
||||
if [[ "$VLAN" == "none" ]]; then
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}Host${CL}"
|
||||
else
|
||||
if [[ "$VLAN" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN${CL}"
|
||||
VLAN=",tag=$VLAN"
|
||||
else
|
||||
msg_error "VLAN must be an integer, was ${VLAN}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
else
|
||||
VLAN=",tag=$VLAN1"
|
||||
fi
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${TAGS-}" ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
|
||||
else
|
||||
TAGS="community-scripts;"
|
||||
if ADV_TAGS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then
|
||||
if [ -n "${ADV_TAGS}" ]; then
|
||||
ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]')
|
||||
TAGS="${ADV_TAGS}"
|
||||
else
|
||||
TAGS=";"
|
||||
fi
|
||||
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${SSH-}" ]]; then
|
||||
if [[ "$SSH" == "yes" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
if [[ ! -z "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}********************${CL}"
|
||||
else
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}None${CL}"
|
||||
fi
|
||||
elif [[ "$SSH" == "no" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
else
|
||||
msg_error "SSH needs to be 'yes' or 'no', was ${SSH}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)"
|
||||
if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
SSH_AUTHORIZED_KEY=""
|
||||
fi
|
||||
if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then
|
||||
SSH="yes"
|
||||
else
|
||||
SSH="no"
|
||||
fi
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
else
|
||||
SSH="no"
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${VERBOSE-}" ]]; then
|
||||
if [[ "$VERBOSE" == "yes" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
||||
elif [[ "$VERBOSE" == "no" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Verbose Mode needs to be 'yes' or 'no', was ${VERBOSE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
|
||||
VERBOSE="yes"
|
||||
else
|
||||
VERBOSE="no"
|
||||
fi
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS WITH CONFIG FILE COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
|
||||
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above settings${CL}"
|
||||
write_config
|
||||
else
|
||||
clear
|
||||
header_info
|
||||
echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}"
|
||||
config_file
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ color() {
|
||||
BOLD=$(echo "\033[1m")
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
TAB3=" "
|
||||
|
||||
# System
|
||||
RETRY_NUM=10
|
||||
|
||||
+308
-48
@@ -1,4 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs Node.js and optional global modules.
|
||||
#
|
||||
# Description:
|
||||
# - Installs specified Node.js version using NodeSource APT repo
|
||||
# - Optionally installs or updates global npm modules
|
||||
#
|
||||
# Variables:
|
||||
# NODE_VERSION - Node.js version to install (default: 22)
|
||||
# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0")
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_node_and_modules() {
|
||||
local NODE_VERSION="${NODE_VERSION:-22}"
|
||||
local NODE_MODULE="${NODE_MODULE:-}"
|
||||
@@ -99,6 +112,19 @@ install_node_and_modules() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or upgrades PostgreSQL and performs data migration.
|
||||
#
|
||||
# Description:
|
||||
# - Detects existing PostgreSQL version
|
||||
# - Dumps all databases before upgrade
|
||||
# - Adds PGDG repo and installs specified version
|
||||
# - Restores dumped data post-upgrade
|
||||
#
|
||||
# Variables:
|
||||
# PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_postgresql() {
|
||||
local PG_VERSION="${PG_VERSION:-16}"
|
||||
local CURRENT_PG_VERSION=""
|
||||
@@ -115,7 +141,7 @@ install_postgresql() {
|
||||
msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION"
|
||||
NEED_PG_INSTALL=true
|
||||
else
|
||||
msg_info "PostgreSQL not installed, proceeding with fresh install of $PG_VERSION"
|
||||
msg_info "Setup PostgreSQL $PG_VERSION"
|
||||
NEED_PG_INSTALL=true
|
||||
fi
|
||||
|
||||
@@ -123,10 +149,10 @@ install_postgresql() {
|
||||
if [[ -n "$CURRENT_PG_VERSION" ]]; then
|
||||
msg_info "Dumping all PostgreSQL data from version $CURRENT_PG_VERSION"
|
||||
su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql"
|
||||
fi
|
||||
|
||||
msg_info "Stopping PostgreSQL service"
|
||||
systemctl stop postgresql || true
|
||||
msg_info "Stopping PostgreSQL service"
|
||||
systemctl stop postgresql
|
||||
fi
|
||||
|
||||
msg_info "Removing pgdg repo and old GPG key"
|
||||
rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg
|
||||
@@ -149,7 +175,7 @@ install_postgresql() {
|
||||
fi
|
||||
|
||||
$STD msg_info "Starting PostgreSQL $PG_VERSION"
|
||||
systemctl enable --now postgresql
|
||||
systemctl enable -q --now postgresql
|
||||
|
||||
if [[ -n "$CURRENT_PG_VERSION" ]]; then
|
||||
$STD msg_info "Restoring dumped data"
|
||||
@@ -160,6 +186,18 @@ install_postgresql() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or updates MariaDB from official repo.
|
||||
#
|
||||
# Description:
|
||||
# - Detects current MariaDB version and replaces it if necessary
|
||||
# - Preserves existing database data
|
||||
# - Dynamically determines latest GA version if "latest" is given
|
||||
#
|
||||
# Variables:
|
||||
# MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_mariadb() {
|
||||
local MARIADB_VERSION="${MARIADB_VERSION:-latest}"
|
||||
local DISTRO_CODENAME
|
||||
@@ -167,13 +205,18 @@ install_mariadb() {
|
||||
|
||||
# grab dynamic latest LTS version
|
||||
if [[ "$MARIADB_VERSION" == "latest" ]]; then
|
||||
msg_info "Resolving latest MariaDB version"
|
||||
MARIADB_VERSION=$(curl -fsSL https://mariadb.org | grep -oP 'MariaDB \K10\.[0-9]+' | head -n1)
|
||||
$STD msg_info "Resolving latest GA MariaDB version"
|
||||
MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ |
|
||||
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' |
|
||||
grep -vE 'rc/|rolling/' |
|
||||
sed 's|/||' |
|
||||
sort -Vr |
|
||||
head -n1)
|
||||
if [[ -z "$MARIADB_VERSION" ]]; then
|
||||
msg_error "Could not determine latest MariaDB version"
|
||||
msg_error "Could not determine latest GA MariaDB version"
|
||||
return 1
|
||||
fi
|
||||
msg_ok "Latest MariaDB version is $MARIADB_VERSION"
|
||||
$STD msg_ok "Latest GA MariaDB version is $MARIADB_VERSION"
|
||||
fi
|
||||
|
||||
local CURRENT_VERSION=""
|
||||
@@ -182,23 +225,23 @@ install_mariadb() {
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then
|
||||
msg_info "MariaDB $MARIADB_VERSION already installed, checking for upgrade"
|
||||
$STD msg_info "MariaDB $MARIADB_VERSION, upgrading"
|
||||
$STD apt-get update
|
||||
$STD apt-get install --only-upgrade -y mariadb-server mariadb-client
|
||||
msg_ok "MariaDB $MARIADB_VERSION upgraded if applicable"
|
||||
$STD msg_ok "MariaDB upgraded to $MARIADB_VERSION"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "$CURRENT_VERSION" ]]; then
|
||||
msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)"
|
||||
$STD msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)"
|
||||
$STD systemctl stop mariadb >/dev/null 2>&1 || true
|
||||
$STD apt-get purge -y 'mariadb*' || true
|
||||
rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg
|
||||
else
|
||||
msg_info "Installing MariaDB $MARIADB_VERSION"
|
||||
msg_info "Setup MariaDB $MARIADB_VERSION"
|
||||
fi
|
||||
|
||||
msg_info "Setting up MariaDB Repository"
|
||||
$STD msg_info "Setting up MariaDB Repository"
|
||||
curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" |
|
||||
gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg
|
||||
|
||||
@@ -208,9 +251,21 @@ install_mariadb() {
|
||||
$STD apt-get update
|
||||
$STD apt-get install -y mariadb-server mariadb-client
|
||||
|
||||
msg_ok "Installed MariaDB $MARIADB_VERSION"
|
||||
msg_ok "Setup MariaDB $MARIADB_VERSION"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or upgrades MySQL and configures APT repo.
|
||||
#
|
||||
# Description:
|
||||
# - Detects existing MySQL installation
|
||||
# - Purges conflicting packages before installation
|
||||
# - Supports clean upgrade
|
||||
#
|
||||
# Variables:
|
||||
# MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_mysql() {
|
||||
local MYSQL_VERSION="${MYSQL_VERSION:-8.0}"
|
||||
local CURRENT_VERSION=""
|
||||
@@ -248,11 +303,33 @@ install_mysql() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs PHP with selected modules and configures Apache/FPM support.
|
||||
#
|
||||
# Description:
|
||||
# - Adds Sury PHP repo if needed
|
||||
# - Installs default and user-defined modules
|
||||
# - Patches php.ini for CLI, Apache, and FPM as needed
|
||||
#
|
||||
# Variables:
|
||||
# PHP_VERSION - PHP version to install (default: 8.4)
|
||||
# PHP_MODULE - Additional comma-separated modules
|
||||
# PHP_APACHE - Set YES to enable PHP with Apache
|
||||
# PHP_FPM - Set YES to enable PHP-FPM
|
||||
# PHP_MEMORY_LIMIT - (default: 512M)
|
||||
# PHP_UPLOAD_MAX_FILESIZE - (default: 128M)
|
||||
# PHP_POST_MAX_SIZE - (default: 128M)
|
||||
# PHP_MAX_EXECUTION_TIME - (default: 300)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_php() {
|
||||
local PHP_VERSION="${PHP_VERSION:-8.4}"
|
||||
local PHP_MODULE="${PHP_MODULE:-}"
|
||||
local PHP_APACHE="${PHP_APACHE:-NO}"
|
||||
local PHP_FPM="${PHP_FPM:-NO}"
|
||||
local DISTRO_CODENAME
|
||||
DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)
|
||||
|
||||
local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip"
|
||||
local COMBINED_MODULES
|
||||
|
||||
@@ -279,11 +356,11 @@ install_php() {
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then
|
||||
$STD echo "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION"
|
||||
$STD msg_info "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION"
|
||||
if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then
|
||||
$STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
|
||||
$STD dpkg -i /tmp/debsuryorg-archive-keyring.deb
|
||||
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" \
|
||||
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \
|
||||
>/etc/apt/sources.list.d/php.list
|
||||
$STD apt-get update
|
||||
fi
|
||||
@@ -329,16 +406,24 @@ install_php() {
|
||||
|
||||
for ini in "${PHP_INI_PATHS[@]}"; do
|
||||
if [[ -f "$ini" ]]; then
|
||||
msg_info "Patching $ini"
|
||||
$STD msg_info "Patching $ini"
|
||||
sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini"
|
||||
sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini"
|
||||
sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini"
|
||||
sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini"
|
||||
msg_ok "Patched $ini"
|
||||
$STD msg_ok "Patched $ini"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or updates Composer globally.
|
||||
#
|
||||
# Description:
|
||||
# - Downloads latest version from getcomposer.org
|
||||
# - Installs to /usr/local/bin/composer
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_composer() {
|
||||
local COMPOSER_BIN="/usr/local/bin/composer"
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
@@ -347,14 +432,14 @@ install_composer() {
|
||||
if [[ -x "$COMPOSER_BIN" ]]; then
|
||||
local CURRENT_VERSION
|
||||
CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}')
|
||||
msg_info "Composer $CURRENT_VERSION found, updating to latest"
|
||||
$STD msg_info "Composer $CURRENT_VERSION found, updating to latest"
|
||||
else
|
||||
msg_info "Composer not found, installing latest version"
|
||||
msg_info "Setup Composer"
|
||||
fi
|
||||
|
||||
# Download and install latest composer
|
||||
curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php
|
||||
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1
|
||||
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer &>/dev/null
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
msg_error "Failed to install Composer"
|
||||
@@ -365,6 +450,17 @@ install_composer() {
|
||||
msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs Go (Golang) from official tarball.
|
||||
#
|
||||
# Description:
|
||||
# - Determines system architecture
|
||||
# - Downloads latest version if GO_VERSION not set
|
||||
#
|
||||
# Variables:
|
||||
# GO_VERSION - Version to install (e.g. 1.22.2 or latest)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_go() {
|
||||
local ARCH
|
||||
case "$(uname -m)" in
|
||||
@@ -420,6 +516,17 @@ install_go() {
|
||||
msg_ok "Installed Go $GO_VERSION"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs Temurin JDK via Adoptium APT repository.
|
||||
#
|
||||
# Description:
|
||||
# - Removes previous JDK if version mismatch
|
||||
# - Installs or upgrades to specified JAVA_VERSION
|
||||
#
|
||||
# Variables:
|
||||
# JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_java() {
|
||||
local JAVA_VERSION="${JAVA_VERSION:-21}"
|
||||
local DISTRO_CODENAME
|
||||
@@ -460,6 +567,17 @@ install_java() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or updates MongoDB to specified major version.
|
||||
#
|
||||
# Description:
|
||||
# - Preserves data across installations
|
||||
# - Adds official MongoDB repo
|
||||
#
|
||||
# Variables:
|
||||
# MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
install_mongodb() {
|
||||
local MONGO_VERSION="${MONGO_VERSION:-8.0}"
|
||||
local DISTRO_CODENAME
|
||||
@@ -508,6 +626,19 @@ install_mongodb() {
|
||||
msg_ok "MongoDB $MONGO_VERSION installed and started"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Downloads and deploys latest GitHub release tarball.
|
||||
#
|
||||
# Description:
|
||||
# - Fetches latest release from GitHub API
|
||||
# - Detects matching asset by architecture
|
||||
# - Extracts to /opt/<app> and saves version
|
||||
#
|
||||
# Variables:
|
||||
# APP - Override default application name (optional)
|
||||
# GITHUB_TOKEN - (optional) GitHub token for private rate limits
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
fetch_and_deploy_gh_release() {
|
||||
local repo="$1"
|
||||
local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')}
|
||||
@@ -518,13 +649,11 @@ fetch_and_deploy_gh_release() {
|
||||
local api_response tag http_code
|
||||
local current_version=""
|
||||
local curl_timeout="--connect-timeout 10 --max-time 30"
|
||||
|
||||
# Check if the app directory exists and if there's a version file
|
||||
if [[ -f "/opt/${app}_version.txt" ]]; then
|
||||
current_version=$(cat "/opt/${app}_version.txt")
|
||||
$STD msg_info "Current version: $current_version"
|
||||
fi
|
||||
|
||||
# ensure that jq is installed
|
||||
if ! command -v jq &>/dev/null; then
|
||||
$STD msg_info "Installing jq..."
|
||||
@@ -534,58 +663,45 @@ fetch_and_deploy_gh_release() {
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
[[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN")
|
||||
|
||||
until [[ $attempt -ge $max_attempts ]]; do
|
||||
((attempt++)) || true
|
||||
$STD msg_info "[$attempt/$max_attempts] Fetching GitHub release for $repo...\n"
|
||||
|
||||
api_response=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_resp.json "${header[@]}" "$api_url")
|
||||
http_code="${api_response:(-3)}"
|
||||
|
||||
if [[ "$http_code" == "404" ]]; then
|
||||
msg_error "Repository $repo has no Release candidate (404)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
$STD msg_info "Request failed with HTTP $http_code, retrying...\n"
|
||||
sleep $((attempt * 2))
|
||||
continue
|
||||
fi
|
||||
|
||||
api_response=$(</tmp/gh_resp.json)
|
||||
|
||||
if echo "$api_response" | grep -q "API rate limit exceeded"; then
|
||||
msg_error "GitHub API rate limit exceeded."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if echo "$api_response" | jq -e '.message == "Not Found"' &>/dev/null; then
|
||||
msg_error "Repository not found: $repo"
|
||||
return 1
|
||||
fi
|
||||
|
||||
tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty')
|
||||
[[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}"
|
||||
version="${tag#v}"
|
||||
|
||||
if [[ -z "$tag" ]]; then
|
||||
$STD msg_info "Empty tag received, retrying...\n"
|
||||
sleep $((attempt * 2))
|
||||
continue
|
||||
fi
|
||||
|
||||
$STD msg_ok "Found release: $tag for $repo"
|
||||
break
|
||||
done
|
||||
|
||||
if [[ -z "$tag" ]]; then
|
||||
msg_error "Failed to fetch release for $repo after $max_attempts attempts."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Version comparison (if we already have this version, skip)
|
||||
if [[ "$current_version" == "$tag" ]]; then
|
||||
$STD msg_info "Already running the latest version ($tag). Skipping update."
|
||||
@@ -595,11 +711,9 @@ fetch_and_deploy_gh_release() {
|
||||
local base_url="https://github.com/$repo/releases/download/v$tag"
|
||||
local tmpdir
|
||||
tmpdir=$(mktemp -d) || return 1
|
||||
|
||||
# Extract list of assets from the Release API
|
||||
local assets urls
|
||||
assets=$(echo "$api_response" | jq -r '.assets[].browser_download_url') || true
|
||||
|
||||
# Detect current architecture
|
||||
local arch
|
||||
if command -v dpkg &>/dev/null; then
|
||||
@@ -616,7 +730,6 @@ fetch_and_deploy_gh_release() {
|
||||
arch="unknown"
|
||||
fi
|
||||
$STD msg_info "Detected system architecture: $arch"
|
||||
|
||||
# Try to find a matching asset for our architecture
|
||||
local url=""
|
||||
for u in $assets; do
|
||||
@@ -626,7 +739,6 @@ fetch_and_deploy_gh_release() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Fallback to other architectures if our specific one isn't found
|
||||
if [[ -z "$url" ]]; then
|
||||
for u in $assets; do
|
||||
@@ -637,7 +749,6 @@ fetch_and_deploy_gh_release() {
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Fallback to any tar.gz
|
||||
if [[ -z "$url" ]]; then
|
||||
for u in $assets; do
|
||||
@@ -648,7 +759,6 @@ fetch_and_deploy_gh_release() {
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Final fallback to GitHub source tarball
|
||||
if [[ -z "$url" ]]; then
|
||||
# Use tarball_url directly from API response instead of constructing our own URL
|
||||
@@ -661,18 +771,14 @@ fetch_and_deploy_gh_release() {
|
||||
|
||||
$STD msg_info "Using GitHub source tarball: $url"
|
||||
fi
|
||||
|
||||
local filename="${url##*/}"
|
||||
$STD msg_info "Downloading $url"
|
||||
|
||||
if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then
|
||||
msg_error "Failed to download release asset from $url"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mkdir -p "/opt/$app"
|
||||
|
||||
tar -xzf "$tmpdir/$filename" -C "$tmpdir"
|
||||
local content_root
|
||||
content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d)
|
||||
@@ -681,12 +787,19 @@ fetch_and_deploy_gh_release() {
|
||||
else
|
||||
cp -r "$tmpdir"/* "/opt/$app/"
|
||||
fi
|
||||
|
||||
echo "$version" >"/opt/${app}_version.txt"
|
||||
$STD msg_ok "Deployed $app v$version to /opt/$app"
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs a local IP updater script using networkd-dispatcher.
|
||||
#
|
||||
# Description:
|
||||
# - Stores current IP in /run/local-ip.env
|
||||
# - Automatically runs on network changes
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
setup_local_ip_helper() {
|
||||
local BASE_DIR="/usr/local/community-scripts/ip-management"
|
||||
local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh"
|
||||
@@ -759,6 +872,13 @@ EOF
|
||||
$STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Loads LOCAL_IP from persistent store or detects if missing.
|
||||
#
|
||||
# Description:
|
||||
# - Loads from /run/local-ip.env or performs runtime lookup
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import_local_ip() {
|
||||
local IP_FILE="/run/local-ip.env"
|
||||
if [[ -f "$IP_FILE" ]]; then
|
||||
@@ -796,6 +916,14 @@ import_local_ip() {
|
||||
export LOCAL_IP
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Downloads file with optional progress indicator using pv.
|
||||
#
|
||||
# Arguments:
|
||||
# $1 - URL
|
||||
# $2 - Destination path
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function download_with_progress() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
@@ -824,6 +952,14 @@ function download_with_progress() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or upgrades uv (Python package manager) from GitHub releases.
|
||||
#
|
||||
# Description:
|
||||
# - Downloads architecture-specific tarball
|
||||
# - Places binary in /usr/local/bin
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function setup_uv() {
|
||||
$STD msg_info "Checking uv installation..."
|
||||
UV_BIN="/usr/local/bin/uv"
|
||||
@@ -877,6 +1013,13 @@ function setup_uv() {
|
||||
msg_ok "uv installed/updated to $LATEST_VERSION"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Ensures /usr/local/bin is permanently in system PATH.
|
||||
#
|
||||
# Description:
|
||||
# - Adds to /etc/profile.d if not present
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function ensure_usr_local_bin_persist() {
|
||||
local PROFILE_FILE="/etc/profile.d/custom_path.sh"
|
||||
|
||||
@@ -886,6 +1029,14 @@ function ensure_usr_local_bin_persist() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs or updates Ghostscript (gs) from source.
|
||||
#
|
||||
# Description:
|
||||
# - Fetches latest release
|
||||
# - Builds and installs system-wide
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function setup_gs() {
|
||||
msg_info "Setup Ghostscript"
|
||||
mkdir -p /tmp
|
||||
@@ -939,3 +1090,112 @@ function setup_gs() {
|
||||
msg_error "Ghostscript installation failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs rbenv and ruby-build, installs Ruby and optionally Rails.
|
||||
#
|
||||
# Description:
|
||||
# - Downloads rbenv and ruby-build from GitHub
|
||||
# - Compiles and installs target Ruby version
|
||||
# - Optionally installs Rails via gem
|
||||
#
|
||||
# Variables:
|
||||
# RUBY_VERSION - Ruby version to install (default: 3.4.4)
|
||||
# RUBY_INSTALL_RAILS - true/false to install Rails (default: true)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
setup_rbenv_stack() {
|
||||
local RUBY_VERSION="${RUBY_VERSION:-3.4.4}"
|
||||
local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}"
|
||||
|
||||
local RBENV_DIR="$HOME/.rbenv"
|
||||
local RBENV_BIN="$RBENV_DIR/bin/rbenv"
|
||||
local PROFILE_FILE="$HOME/.profile"
|
||||
local TMP_DIR
|
||||
TMP_DIR=$(mktemp -d)
|
||||
|
||||
$STD msg_info "Installing rbenv + ruby-build + Ruby $RUBY_VERSION"
|
||||
|
||||
# Fetch latest rbenv release tag from GitHub (e.g. v1.3.2 → 1.3.2)
|
||||
local RBENV_RELEASE
|
||||
RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//')
|
||||
if [[ -z "$RBENV_RELEASE" ]]; then
|
||||
msg_error "Failed to fetch latest rbenv version"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Download and extract rbenv release
|
||||
curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz"
|
||||
tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR"
|
||||
mkdir -p "$RBENV_DIR"
|
||||
cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/"
|
||||
cd "$RBENV_DIR" && src/configure && make -C src
|
||||
|
||||
# Fetch latest ruby-build plugin release tag (e.g. v20250507 → 20250507)
|
||||
local RUBY_BUILD_RELEASE
|
||||
RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//')
|
||||
if [[ -z "$RUBY_BUILD_RELEASE" ]]; then
|
||||
msg_error "Failed to fetch latest ruby-build version"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Download and install ruby-build plugin
|
||||
curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz"
|
||||
tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR"
|
||||
mkdir -p "$RBENV_DIR/plugins/ruby-build"
|
||||
cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/"
|
||||
echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt"
|
||||
|
||||
# Persist rbenv init to user's profile
|
||||
if ! grep -q 'rbenv init' "$PROFILE_FILE"; then
|
||||
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE"
|
||||
echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE"
|
||||
fi
|
||||
|
||||
# Activate rbenv in current shell
|
||||
export PATH="$RBENV_DIR/bin:$PATH"
|
||||
eval "$("$RBENV_BIN" init - bash)"
|
||||
|
||||
# Install Ruby version if not already present
|
||||
if "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then
|
||||
msg_ok "Ruby $RUBY_VERSION already installed"
|
||||
else
|
||||
$STD msg_info "Installing Ruby $RUBY_VERSION"
|
||||
$STD "$RBENV_BIN" install "$RUBY_VERSION"
|
||||
fi
|
||||
|
||||
# Set Ruby version globally
|
||||
"$RBENV_BIN" global "$RUBY_VERSION"
|
||||
hash -r
|
||||
|
||||
# Optionally install Rails via gem
|
||||
if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then
|
||||
$STD msg_info "Installing latest Rails via gem"
|
||||
gem install rails
|
||||
msg_ok "Rails $(rails -v) installed"
|
||||
fi
|
||||
|
||||
rm -rf "$TMP_DIR"
|
||||
msg_ok "rbenv stack ready (Ruby $RUBY_VERSION)"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Creates and installs self-signed certificates.
|
||||
#
|
||||
# Description:
|
||||
# - Create a self-signed certificate with option to override application name
|
||||
#
|
||||
# Variables:
|
||||
# APP - Application name (default: $APPLICATION variable)
|
||||
# ------------------------------------------------------------------------------
|
||||
create_selfsigned_certs() {
|
||||
local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')}
|
||||
$STD msg_info "Creating Self-Signed Certificate"
|
||||
$STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 \
|
||||
-keyout /etc/ssl/private/"$app"-selfsigned.key \
|
||||
-out /etc/ssl/certs/"$app"-selfsigned.crt \
|
||||
-subj "/C=US/O=$app/OU=Domain Control Validated/CN=localhost"
|
||||
$STD msg_ok "Created Self-Signed Certificate"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user