1
0

Compare commits

..

22 Commits

Author SHA1 Message Date
matmoul 40670bd1f7 chore: replace legacy repo state docs with project memory 2026-05-02 01:21:37 +02:00
matmoul 8229b06cc1 refactor: stop marking reboot command failures in netupgrade 2026-05-02 01:12:12 +02:00
matmoul 5728e8769a fix: simplify reboot to direct SSH invocation 2026-05-01 22:00:32 +02:00
matmoul 0360323c10 docs: update project state overview 2026-04-26 03:09:30 +02:00
matmoul faeac57bf4 feat: add dnf action support 2026-04-26 02:57:22 +02:00
matmoul 8b566f0793 docs: reorganize TODO priorities 2026-04-26 02:43:51 +02:00
matmoul 76101e77c9 chore: rename repository state file to STATE.md 2026-04-26 02:35:38 +02:00
matmoul c138d9201e fix: detach reboot command to avoid false SSH failures 2026-04-26 02:08:34 +02:00
matmoul ba5a8c9397 docs: update dependency and sample config notes 2026-04-26 01:36:02 +02:00
matmoul a08efd54c4 fix: stop passing -y to apk upgrade
The Alpine apk upgrade command does not accept -y, so the flag is no longer appended there. The CLI -y option remains available for other package managers.
2026-04-26 01:17:50 +02:00
matmoul 3e78f8afe6 fix: simplify pacman orphan cleanup command construction 2026-04-26 01:08:09 +02:00
matmoul afea447887 docs: update roadmap with review findings and next priorities 2026-04-26 00:45:03 +02:00
matmoul e2b3a0a88d fix: correct package cleanup commands in netupgrade
Use apt-get autoremove --purge instead of a separate empty purge step, and pass the CLI yes flag through to apk upgrade so the logged commands match actual behavior.
2026-04-26 00:40:24 +02:00
matmoul 3fe7959850 fix: avoid sed interpolation when prepending log summary
Use a temporary file to write the summary header and existing log content, then replace the original log atomically. This removes the dependency on sed for summary insertion and avoids unsafe string interpolation.
2026-04-26 00:36:01 +02:00
matmoul 46f42c8893 fix: harden checklist selection parsing and clarify -f help text 2026-04-26 00:33:27 +02:00
matmoul f9af0f4823 docs: update roadmap with next hardening priorities 2026-04-26 00:26:09 +02:00
matmoul a25caf6f3d feat: make SSH user configurable and harden remote actions 2026-04-26 00:19:39 +02:00
matmoul f3c649341a fix: make log viewer optional and document runtime dependencies 2026-04-26 00:08:02 +02:00
matmoul 2ed34b97be docs: expand netupgrade usage and configuration docs
Update the README with installation, requirements, supported actions, config format, and usage details. Align the CLI help text with current behavior and add startup checks for required runtime dependencies.
2026-04-25 23:59:28 +02:00
matmoul 9de65f9aa5 chore: add repository guidance and state context files 2026-04-25 23:51:29 +02:00
matmoul 09d4815a88 Add archlinux-keyring update and system upgrade steps to netupgrade script 2026-02-14 01:33:19 +01:00
matmoul 1d4a884195 Fix ssh command by removing -f flag and add shellcheck disable comment 2026-02-14 01:29:40 +01:00
4 changed files with 341 additions and 93 deletions
+30
View File
@@ -0,0 +1,30 @@
# Project memory
- Repo: `netupgrade`
- Language: Bash
- Entry point: `bin/netupgrade`
- Purpose: interactive SSH-based maintenance/upgrade CLI
## Rules
- Keep it lightweight and shell-based
- Preserve backward compatibility when possible
- Treat config files as trusted shell input
- Be careful with SSH quoting and remote command construction
- Prefer small, reviewable changes
- Keep docs aligned with runtime behavior
- Avoid introducing `set -euo pipefail` without validating failure semantics
## Current behavior
- Loads configs from `~/.config/netupgrade/*.cfg`
- Uses `NODES` entries: `host;display-name;action1;action2;...`
- Runs selected actions sequentially over SSH
- Defaults to `root@host`, or `SSH_USER@host`
- Logs to `~/netupgrade.log`
- Opens the log with `$EDITOR`, then `nano`, `vi`, or `less`
## Sensitive areas
- SSH quoting
- `cmd:<...>` execution
- `docker-stacks:<...>` handling
- package-manager cleanup behavior
- error propagation
+98 -12
View File
@@ -1,32 +1,118 @@
# netupgrade
Servers full upgrade script
Interactive CLI tool to run upgrade and maintenance actions on multiple remote hosts over SSH.
## Where to use
- On a dedicated server (bastion, ...)
- On a dedicated server (bastion, jump host, ...)
- On your computer with an alias to your dedicated server
- On your computer (not recommended)
- On your computer directly (not recommended)
## Features
- Select one or more hosts from an interactive checklist
- Run predefined actions on each selected host
- Write execution logs to `~/netupgrade.log`
Supported actions:
- `apt`
- `yum`
- `dnf`
- `pkg`
- `pacman`
- `apk`
- `reboot`
- `cmd:<remote command>`
- `docker-stacks:<directory>`
## Requirements
Required locally:
- `bash`
- `ssh`
- `whiptail`
- core utilities such as `cat`, `tee`, `rm`, `touch`, and `mv`
Optional log viewer:
- `$EDITOR` if it points to an installed command
- otherwise one of: `nano`, `vi`, `less`
Remote hosts must also provide the commands needed by the configured actions, such as:
`apt-get`, `yum`, `dnf`, `pkg`, `pacman`, `apk`, `docker`, `docker compose`, or `reboot`.
## Install
### Bin as root
### Install the executable
``` bash
cp bin/netupgrade to /usr/local/bin
```bash README.md
cp bin/netupgrade /usr/local/bin/netupgrade
chmod +x /usr/local/bin/netupgrade
```
### Config as user
### Create the config directory
``` bash
mkdir -p ~/.config/netuprade
touch ~/.config/netuprade/index.cfg
```bash README.md
mkdir -p ~/.config/netupgrade
touch ~/.config/netupgrade/index.cfg
```
### Alias on your computer with a dedicated server
You can save it in your .bashrc
You can save it in your `.bashrc`
``` bash
```bash README.md
alias netupgrade='ssh -t user@10.0.0.10 netupgrade'
```
## Configuration
The default config file is:
```text README.md
~/.config/netupgrade/index.cfg
```
The script sources this file as Bash code. It must define a `NODES` array.
Each entry uses this format:
```text README.md
host;display-name;action1;action2;...
```
Example:
```bash README.md
SSH_USER="root"
NODES=(
"192.168.1.10;web-01;apt;reboot"
"192.168.1.11;db-01;apt;cmd:systemctl restart postgresql"
"192.168.1.12;docker-01;docker-stacks:/opt/stacks"
)
```
## Usage
```bash README.md
netupgrade [--help] [-f] [-y] [configfilename]
```
Options:
- `--help`: show help
- `-f`: preselect all nodes in the interactive checklist
- `-y`: pass non-interactive confirmation flags to supported package managers
- `configfilename`: path to a config file
## Notes
- SSH connections use `root@host` by default and can be changed with `SSH_USER` in the config file
- `cmd:<remote command>` is executed through a remote shell, so shell operators such as pipes, redirections, `&&`, and `||` are supported
- The tool is interactive and intended for manual administration workflows
- After execution, the log file is opened with `$EDITOR` when available, otherwise with `nano`, `vi`, or `less`
- If no supported log viewer is available, the script keeps running and prints the log file path
- The configuration file is sourced as shell code, so only use trusted config files
+209 -78
View File
@@ -1,12 +1,47 @@
#!/bin/bash
showHelp() {
echo "netupgrade [-f] [-y] [configfilename]"
echo "netupgrade [--help] [-f] [-y] [configfilename]"
echo ""
echo " -f : Select all nodes"
echo " -y : No confirmation"
echo " -b : Breack on error"
echo " configfilename : a cfg filename"
echo " --help Show this help message"
echo " -f Preselect all nodes in the checklist"
echo " -y No confirmation for supported package managers"
echo " configfilename Path to a cfg file"
}
checkDependencies() {
local -a REQUIRED_CMDS=(ssh whiptail cat tee rm touch mv)
local -a MISSING_CMDS=()
local CMD
for CMD in "${REQUIRED_CMDS[@]}"; do
if ! command -v "${CMD}" >/dev/null 2>&1; then
MISSING_CMDS+=("${CMD}")
fi
done
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
echo "Error: missing required dependencies: ${MISSING_CMDS[*]}"
exit 1
fi
}
resolveLogViewer() {
if [ -n "${EDITOR}" ] && command -v "${EDITOR}" >/dev/null 2>&1; then
LOGVIEWER="${EDITOR}"
return
fi
local -a CANDIDATES=(nano vi less)
local CANDIDATE
for CANDIDATE in "${CANDIDATES[@]}"; do
if command -v "${CANDIDATE}" >/dev/null 2>&1; then
LOGVIEWER="${CANDIDATE}"
return
fi
done
LOGVIEWER=""
}
pressAnyKey(){
@@ -23,25 +58,76 @@ loadConfig(){
fi
}
parseNode() {
local NODE_VALUE="${1}"
local -n NODE_FIELDS_REF="${2}"
IFS=';' read -r -a NODE_FIELDS_REF <<< "${NODE_VALUE}"
}
parseSelection() {
local SELECTION_RAW="${1}"
local -n SELECTION_REF="${2}"
local ITEM_INDEX=0
SELECTION_REF=()
read -r -a SELECTION_REF <<< "${SELECTION_RAW}"
for ITEM_INDEX in "${!SELECTION_REF[@]}"; do
SELECTION_REF[ITEM_INDEX]="${SELECTION_REF[ITEM_INDEX]%\"}"
SELECTION_REF[ITEM_INDEX]="${SELECTION_REF[ITEM_INDEX]#\"}"
done
}
runSSH() {
local HOST="${1}"
shift
ssh "${SSH_USER}@${HOST}" "$@"
}
prependLogSummary() {
local SUMMARY_CONTENT="${1}"
local TEMP_LOGFILENAME="${LOGFILENAME}.tmp"
{
echo "Results :"
echo "---------"
echo ""
echo -e "${SUMMARY_CONTENT}"
echo ""
echo ""
cat "${LOGFILENAME}"
} > "${TEMP_LOGFILENAME}"
mv "${TEMP_LOGFILENAME}" "${LOGFILENAME}"
}
selectNodes(){
local -a OPTIONS=()
local -a SELECTED_ITEMS=()
local -i INDEX=1
for NODE in "${NODES[@]}"; do
# shellcheck disable=SC2206
local FIELDS=(${NODE//;/ })
local DESKSKIP=0
local -i I=0
local -a FIELDS=()
local DESC=""
for FIELD in "${FIELDS[@]}"; do
if [ ${DESKSKIP} -gt 1 ]; then
if [ "${DESC}" == "" ]; then
DESC="${FIELD/:*/}"
local FIELD=""
local SEL=""
local DEFAULT_STATE="OFF"
if [ "${FULL}" -eq 1 ]; then
DEFAULT_STATE="ON"
fi
for NODE in "${NODES[@]}"; do
FIELDS=()
DESC=""
parseNode "${NODE}" FIELDS
for ((I = 2; I < ${#FIELDS[@]}; ++I)); do
FIELD="${FIELDS[I]}"
if [ -z "${DESC}" ]; then
DESC="${FIELD%%:*}"
else
DESC="${DESC}|${FIELD/:*/}"
DESC="${DESC}|${FIELD%%:*}"
fi
fi
DESKSKIP=$(( DESKSKIP + 1))
done
OPTIONS+=("${INDEX}:${FIELDS[0]}" "${FIELDS[1]} [${DESC}]" "${FULL}")
OPTIONS+=("${INDEX}:${FIELDS[0]}" "${FIELDS[1]} [${DESC}]" "${DEFAULT_STATE}")
INDEX+=1
done
if ! SEL=$(whiptail --title "NetUpgrade" --checklist "" 0 0 0 \
@@ -49,7 +135,12 @@ selectNodes(){
3>&1 1>&2 2>&3); then
exit 0
fi
if [ ${#SEL} == 0 ]; then
if [ -z "${SEL}" ]; then
exit 0
fi
parseSelection "${SEL}" SELECTED_ITEMS
if [ ${#SELECTED_ITEMS[@]} -eq 0 ]; then
exit 0
fi
@@ -59,17 +150,17 @@ selectNodes(){
touch "${LOGFILENAME}"
local RESULT="\n"
for ITM in ${SEL}; do
for ITM in "${SELECTED_ITEMS[@]}"; do
INDEX=1
for NODE in "${NODES[@]}"; do
# shellcheck disable=SC2206
local FIELDS=(${NODE//;/ })
if [ "${ITM}" = "\"${INDEX}:${FIELDS[0]}\"" ]; then
FIELDS=()
parseNode "${NODE}" FIELDS
if [ "${ITM}" = "${INDEX}:${FIELDS[0]}" ]; then
for ((I = 2; I < ${#FIELDS[@]}; ++I)); do
if runCmd "${FIELDS[0]}" "${FIELDS[1]}" "${FIELDS[${I}]}"; then
RESULT+="Ok: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[${I}]}\n"
if runCmd "${FIELDS[0]}" "${FIELDS[1]}" "${FIELDS[I]}"; then
RESULT+="Ok: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[I]}\n"
else
RESULT+="Error: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[${I}]}\n"
RESULT+="Error: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[I]}\n"
fi
done
fi
@@ -77,10 +168,12 @@ selectNodes(){
done
done
sed -i "1s/^/${RESULT//\//\\\/}\n\n\n\n/" "${LOGFILENAME}"
sed -i "1s/^/---------\n\n/" "${LOGFILENAME}"
sed -i "1s/^/Results :\n/" "${LOGFILENAME}"
nano "${LOGFILENAME}"
prependLogSummary "${RESULT}"
if [ -n "${LOGVIEWER}" ]; then
"${LOGVIEWER}" "${LOGFILENAME}"
else
echo "Warning: no log viewer found, showing log path instead: ${LOGFILENAME}"
fi
rm -i "${LOGFILENAME}"
echo ""
echo "Results :"
@@ -88,131 +181,165 @@ selectNodes(){
echo -e "${RESULT}"
}
runCmd() { #$1=host $2=name #3=cmd
local -r HOST=${1}
local -r NAME=${2}
local -r CMD=${3//:*}
local -r CMDVAL=${3//*:}
runCmd() { # $1=host $2=name $3=cmd
local -r HOST="${1}"
local -r NAME="${2}"
local -r ACTION="${3}"
local -r CMD="${ACTION%%:*}"
local -r CMDVAL="${ACTION#*:}"
local -i ERROR=0
echo "${NAME} @ ${HOST} : ${CMD}" | tee -a "${LOGFILENAME}"
local TITLELENGTH=$((${#NAME} + ${#HOST} + ${#CMD} + 6))
local TITLELENGTH=0
local SUBTITLE="-----------------------------------------------------------------------------"
echo ${SUBTITLE:0:${TITLELENGTH}} | tee -a "${LOGFILENAME}"
local YESARG=""
echo "${NAME} @ ${HOST} : ${CMD}" | tee -a "${LOGFILENAME}"
TITLELENGTH=$((${#NAME} + ${#HOST} + ${#CMD} + 6))
echo "${SUBTITLE:0:${TITLELENGTH}}" | tee -a "${LOGFILENAME}"
date +'%Y-%m-%d %H:%M:%S %A' | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
set -o pipefail
local YESARG=""
case ${CMD} in
reboot)
ssh root@"${HOST}" reboot | tee -a "${LOGFILENAME}"
echo "reboot" | tee -a "${LOGFILENAME}"
runSSH "${HOST}" reboot | tee -a "${LOGFILENAME}"
;;
apt)
if [ ${YES} == 1 ]; then
if [ "${YES}" -eq 1 ]; then
YESARG="-y"
fi
echo "apt-get ${YESARG} update" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" apt-get ${YESARG} update | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" apt-get ${YESARG} update | tee -a "${LOGFILENAME}"; then
ERROR=1
else
echo "" | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} dist-upgrade" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" apt-get ${YESARG} dist-upgrade | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" apt-get ${YESARG} dist-upgrade | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "" | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} autoremove" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" apt-get ${YESARG} autoremove | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} autoremove --purge" | tee -a "${LOGFILENAME}"
runSSH "${HOST}" apt-get ${YESARG} autoremove --purge | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} autoclean" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" apt-get ${YESARG} autoclean | tee -a "${LOGFILENAME}"
runSSH "${HOST}" apt-get ${YESARG} autoclean | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} clean" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" apt-get ${YESARG} clean | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
echo "apt-get ${YESARG} purge" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" apt-get ${YESARG} purge | tee -a "${LOGFILENAME}"
runSSH "${HOST}" apt-get ${YESARG} clean | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
fi
;;
yum)
if [ ${YES} == 1 ]; then
if [ "${YES}" -eq 1 ]; then
YESARG="-y"
fi
echo "yum ${YESARG} update" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" yum ${YESARG} update | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" yum ${YESARG} update | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
;;
dnf)
if [ "${YES}" -eq 1 ]; then
YESARG="-y"
fi
echo "dnf ${YESARG} upgrade --refresh" | tee -a "${LOGFILENAME}"
if ! runSSH "${HOST}" dnf ${YESARG} upgrade --refresh | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
;;
pkg)
if [ ${YES} == 1 ]; then
if [ "${YES}" -eq 1 ]; then
YESARG="-y"
fi
echo "pkg ${YESARG} update" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" pkg ${YESARG} update | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" pkg ${YESARG} update | tee -a "${LOGFILENAME}"; then
ERROR=1
else
echo "" | tee -a "${LOGFILENAME}"
echo "pkg upgrade ${YESARG}" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" pkg upgrade ${YESARG} | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" pkg upgrade ${YESARG} | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "" | tee -a "${LOGFILENAME}"
echo "pkg autoremove ${YESARG}" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" pkg autoremove ${YESARG} | tee -a "${LOGFILENAME}"
runSSH "${HOST}" pkg autoremove ${YESARG} | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
echo "pkg clean ${YESARG}" | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" pkg clean ${YESARG} | tee -a "${LOGFILENAME}"
runSSH "${HOST}" pkg clean ${YESARG} | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
fi
;;
pacman)
if [ ${YES} == 1 ]; then
if [ "${YES}" -eq 1 ]; then
YESARG="--noconfirm"
fi
echo "pacman -Syu ${YESARG}" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" pacman -Syu ${YESARG} | tee -a "${LOGFILENAME}"; then
echo "pacman -Sy ${YESARG} archlinux-keyring" | tee -a "${LOGFILENAME}"
if ! runSSH "${HOST}" pacman -Sy ${YESARG} archlinux-keyring | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "pacman -Syu ${YESARG}" | tee -a "${LOGFILENAME}"
if ! runSSH "${HOST}" pacman -Syu ${YESARG} | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "pacman orphan cleanup" | tee -a "${LOGFILENAME}"
if [ -n "${YESARG}" ]; then
if ! runSSH "${HOST}" sh -c 'orphans=$(pacman -Qqtd 2>/dev/null || true); if [ -n "$orphans" ]; then pacman -Rns --noconfirm $orphans; fi' | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
else
if ! runSSH "${HOST}" sh -c 'orphans=$(pacman -Qqtd 2>/dev/null || true); if [ -n "$orphans" ]; then pacman -Rns $orphans; fi' | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
fi
echo "pacman -Sc ${YESARG}" | tee -a "${LOGFILENAME}"
if ! runSSH "${HOST}" pacman -Sc ${YESARG} | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
# shellcheck disable=SC2046
ssh root@"${HOST}" pacman -Rns $(pacman -Qqtd) ${YESARG} | tee -a "${LOGFILENAME}"
ssh root@"${HOST}" pacman -Sc ${YESARG} | tee -a "${LOGFILENAME}"
;;
apk)
if [ ${YES} == 1 ]; then
YESARG="-y"
fi
echo "apk update" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" apk update | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" apk update | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "apk upgrade" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" apk upgrade | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" apk upgrade | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
;;
cmd)
echo "cmd: ${CMDVAL}" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" "${CMDVAL}" -f | tee -a "${LOGFILENAME}"; then
if ! runSSH "${HOST}" sh -c "${CMDVAL}" | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
;;
docker-stacks)
echo "docker stacks update" | tee -a "${LOGFILENAME}"
echo "for each" | tee -a "${LOGFILENAME}"
echo " docker compose pull; docker compose up -d" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" 'for dir in '"${CMDVAL}"'/*; do (cd "${dir}"; docker compose pull; docker compose up -d); done; docker image prune -f' | tee -a "${LOGFILENAME}"; then
ERROR=1
fi
echo "docker image prune -a -f" | tee -a "${LOGFILENAME}"
if ! ssh root@"${HOST}" docker image prune -f | tee -a "${LOGFILENAME}"; then
echo "docker stacks update in ${CMDVAL}" | tee -a "${LOGFILENAME}"
if ! ssh "${SSH_USER}@${HOST}" "STACK_ROOT=$(printf '%q' "${CMDVAL}") bash -s" <<'EOF' | tee -a "${LOGFILENAME}"
stack_root="${STACK_ROOT}"
for dir in "$stack_root"/*; do
[ -d "$dir" ] || continue
(
cd "$dir" || exit 1
docker compose pull
docker compose up -d
) || exit 1
done
docker image prune -f
EOF
then
ERROR=1
fi
;;
*) echo "Error: Command ${CMD} unknown" | tee -a "${LOGFILENAME}";;
*)
echo "Error: Command ${CMD} unknown" | tee -a "${LOGFILENAME}"
ERROR=1
;;
esac
echo "" | tee -a "${LOGFILENAME}"
echo "" | tee -a "${LOGFILENAME}"
if [ ${ERROR} == 1 ]; then
if [ "${ERROR}" -eq 1 ]; then
return 1
fi
}
@@ -222,6 +349,8 @@ declare -i YES=0
declare -i FULL=0
declare CONFIGFILENAME="${HOME}/.config/netupgrade/index.cfg"
declare LOGFILENAME="${HOME}/netupgrade.log"
declare LOGVIEWER=""
declare SSH_USER="root"
declare -a NODES=()
while [[ ${#} -gt 0 ]]; do
@@ -233,6 +362,8 @@ while [[ ${#} -gt 0 ]]; do
esac
done
checkDependencies
resolveLogViewer
loadConfig
selectNodes
+2 -1
View File
@@ -4,6 +4,7 @@ NODES+=("10.0.0.101;debian-01;apt;reboot")
NODES+=("10.0.0.102;archlinux-01;pacman;reboot")
NODES+=("10.0.0.103;alpine-01;apk;reboot")
NODES+=("10.0.0.104;redhat-01;yum;reboot")
NODES+=("10.0.0.106;rocky-01;dnf;reboot")
NODES+=("10.0.0.105;freebsd-01;pkg;reboot")
NODES+=("10.0.0.211;docker-01;docker-stacks:/srv/stacks")
NODES+=("10.0.0.105;docker-01;cmd:reboot")
#NODES+=("10.0.0.211;docker-01;cmd:reboot")