From 8da80586e83323aa39b9259abdaa748f515bbbcc Mon Sep 17 00:00:00 2001 From: m8in Date: Wed, 17 Jun 2026 22:00:10 +0200 Subject: [PATCH] startOwn composition script including composition module and a smal fix in base,module --- composition/startOwn.sh | 15 +++++++ core/base.module.sh | 3 +- module/composition.module.sh | 84 +++++++++++++++++++++++++++--------- 3 files changed, 81 insertions(+), 21 deletions(-) create mode 100755 composition/startOwn.sh diff --git a/composition/startOwn.sh b/composition/startOwn.sh new file mode 100755 index 0000000..32a634c --- /dev/null +++ b/composition/startOwn.sh @@ -0,0 +1,15 @@ +#!/bin/bash +source /cis/core/base.module.sh +base.loadModule composition + + + +function startOwn() { + local _COMPOSITION + composition.printAll | while read -r _COMPOSITION; do + composition.start "${_COMPOSITION}" + done +} + +startOwn && exit 0 +exit 1 diff --git a/core/base.module.sh b/core/base.module.sh index 593bce9..d9e937d 100755 --- a/core/base.module.sh +++ b/core/base.module.sh @@ -194,6 +194,7 @@ function prepare.setREGEX() { REGEX[COMMAND]='^([]a-zA-Z0-9[|/_:,." -]+)$' #WARNING: Escaping does not work properly here, so we need to position the special characters in a clever way. REGEX[COMPOSITION]='^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$' REGEX[DOMAIN]='^([a-zA-Z][a-zA-Z0-9\.-]*)?[a-zA-Z]{2,}$' + REGEX[FULLDIRPATH]='^/([a-zA-Z0-9\._-]+/)*$' REGEX[SNAPSHOT]='^@[a-zA-Z]([a-zA-Z0-9\.:_-]*[a-zA-Z0-9])?$' REGEX[SYNCSNAPSHOT]='^@SYNC_[a-zA-Z0-9\.:_-]*[a-zA-Z0-9]$' REGEX[USER]='^[a-zA-Z]([-a-zA-Z0-9\._]*[a-zA-Z0-9])?$' @@ -254,7 +255,7 @@ function base.filterComments() { function base.loadModule() { local _MODULENAME _MODULEFULLNAME _MODULENAME="${1:?"Function base.loadModule(): Missing parameter MODULENAME."}" - _MODULEFULLNAME="${CIS[MODULEROOT]:?"Function base.loadModule(): Missing CISMODULEDIR."}/${_MODULENAME}.module.sh" + _MODULEFULLNAME="${CIS[MODULEROOT]:?"Function base.loadModule(): Missing CIS_MODULEROOT."}${_MODULENAME}.module.sh" readonly _MODULENAME _MODULEFULLNAME #module already is loaded => return diff --git a/module/composition.module.sh b/module/composition.module.sh index f55f319..275465e 100755 --- a/module/composition.module.sh +++ b/module/composition.module.sh @@ -4,29 +4,29 @@ source /cis/core/base.module.sh function composition.isRunningOnThisHost() { - local _COMPOSITION _COMPOSITIONS _COMPOSITION_PATH _CURRENTHOST_FILE - _COMPOSITIONS="${CIS[DOMAINDEFINITIONS]:?"Missing CIS_DOMAINDEFINITIONS"}compositions/" + local _COMPOSITION _CURRENTHOST_FILE _COMPOSITION="${1:?"Missing first parameter COMPOSITION"}" - _COMPOSITION_PATH="${_COMPOSITIONS}${_COMPOSITION}/" - _CURRENTHOST_FILE="current-host" - readonly _COMPOSITION _COMPOSITIONS _COMPOSITION_PATH _CURRENTHOST_FILE + _CURRENTHOST_FILE="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}${_COMPOSITION}/current-host" + readonly _COMPOSITION _CURRENTHOST_FILE + + ! [ -f "${_CURRENTHOST_FILE}" ] \ + && echo "FAILURE: Missing file current-host for composition: '${_COMPOSITION}'" >&2 \ + && return 1 [ -n "${CIS[HOST]}" ] \ - && [ -f "${_COMPOSITION_PATH}${_CURRENTHOST_FILE}" ] \ - && head -n 1 -- "${_COMPOSITION_PATH}${_CURRENTHOST_FILE}" | grep -q -E -- "^${CIS[HOST]}" \ + && head -n 1 -- "${_CURRENTHOST_FILE}" | grep -q -E -- "^${CIS[HOST]}" \ && return 0 return 1 } function composition.isSyncedByThisHost() { - local _COMPOSITION _COMPOSITIONS _COMPOSITION_PATH _CURRENTHOST_FILE _SYNCHOSTS_FILE - _COMPOSITIONS="${CIS[DOMAINDEFINITIONS]:?"Missing CIS_DOMAINDEFINITIONS"}compositions/" + local _COMPOSITION _COMPOSITION_PATH _CURRENTHOST_FILE _SYNCHOSTS_FILE _COMPOSITION="${1:?"Missing first parameter COMPOSITION"}" - _COMPOSITION_PATH="${_COMPOSITIONS}${_COMPOSITION}/" + _COMPOSITION_PATH="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}${_COMPOSITION}/" _CURRENTHOST_FILE="current-host" _SYNCHOSTS_FILE="composition-sync-hosts" - readonly _COMPOSITION _COMPOSITIONS _COMPOSITION_PATH _CURRENTHOST_FILE _SYNCHOSTS_FILE + readonly _COMPOSITION _COMPOSITION_PATH _CURRENTHOST_FILE _SYNCHOSTS_FILE # This host either runs the composition or syncs it. # If there is no CURRENTHOST_FILE than the definition is invalid and should not be synced. @@ -40,14 +40,14 @@ function composition.isSyncedByThisHost() { } function composition.printAll() { - local _COMPOSITIONS _CURRENTHOST_FILE - _COMPOSITIONS="${CIS[DOMAINDEFINITIONS]:?"Missing CIS_DOMAINDEFINITIONS"}compositions/" - _CURRENTHOST_FILE="current-host" - readonly _COMPOSITIONS _CURRENTHOST_FILE + local _COMPOSITIONS + _COMPOSITIONS="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}" + readonly _COMPOSITIONS - ls -1 "${_COMPOSITIONS}"*/"${_CURRENTHOST_FILE}" | while read -r _CURRENTHOST_FILE_PATH; do - # Like dirname: removes tailing '/${_CURRENTHOST_FILE}' - local _COMPOSITION="${_CURRENTHOST_FILE_PATH%/${_CURRENTHOST_FILE}}" + local _COMPOSITION_PATH + ls -d1 "${_COMPOSITIONS}"*/ | while read -r _COMPOSITION_PATH; do + # Like dirname: removes tailing '/' + local _COMPOSITION="${_COMPOSITION_PATH%/}" # Like basename echo "${_COMPOSITION##*/}" done @@ -55,7 +55,7 @@ function composition.printAll() { function composition.printAllRunningOnThisHost() { local _COMPOSITIONS _CURRENTHOST_FILE - _COMPOSITIONS="${CIS[DOMAINDEFINITIONS]:?"Missing CIS_DOMAINDEFINITIONS"}compositions/" + _COMPOSITIONS="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}" _CURRENTHOST_FILE="current-host" readonly _COMPOSITIONS _CURRENTHOST_FILE @@ -71,7 +71,7 @@ function composition.printAllRunningOnThisHost() { function composition.printAllSyncedByThisHost() { local _COMPOSITIONS - _COMPOSITIONS="${CIS[DOMAINDEFINITIONS]:?"Missing CIS_DOMAINDEFINITIONS"}compositions/" + _COMPOSITIONS="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}" readonly _COMPOSITIONS for _COMPOSITION_DIR in "${_COMPOSITIONS}"*; do @@ -84,6 +84,50 @@ function composition.printAllSyncedByThisHost() { done } +function composition.start() { + local _COMPOSITION _COMPOSITION_FILE_BACKUP _COMPOSITION_HOME _COMPOSITION_HOME_FILE + _COMPOSITION="${1:?"composition.start(): Missing first parameter COMPOSITION"}" + _COMPOSITION_FILE_BACKUP="/persistent/${_COMPOSITION}/docker-compose.yml" + _COMPOSITION_HOME_FILE="${CIS[COMPOSITIONS]:?"Missing CIS_COMPOSITIONS"}${_COMPOSITION}/home" + + # The regex should ensure a path starts and ends with a '/' and it should limit the allowed set of characters + base.set _COMPOSITION_HOME "$(head -n 1 "${_COMPOSITION_HOME_FILE}" 2> /dev/null)" '^/([a-zA-Z0-9\._-]+/)*$' optional + readonly _COMPOSITION _COMPOSITION_FILE_BACKUP _COMPOSITION_HOME _COMPOSITION_HOME_FILE + + local _COMPOSITION_FILE + if [ -n "${_COMPOSITION_HOME}" ]; then + readonly _COMPOSITION_FILE="${_COMPOSITION_HOME}docker-compose.yml" + [ ! -f "${_COMPOSITION_FILE}" ] \ + && echo "FAILURE: No composition file found, using the information from file 'home': '${_COMPOSITION_FILE}'" >&2 \ + && return 1 + else + readonly _COMPOSITION_FILE="${_COMPOSITION_FILE_BACKUP}" + [ ! -f "${_COMPOSITION_FILE}" ] \ + && echo "FAILURE: No composition file found, falling back to convention: '${_COMPOSITION_FILE}'" >&2 \ + && return 1 + fi + + printf -- "Starting composition: '%s'\n" "${_COMPOSITION}" >&2 + + ! composition.isRunningOnThisHost "${_COMPOSITION}" \ + && echo "SKIPPED: This composition does not run on this host" >&2 \ + && return 0 + + if [ "$(docker compose version 2> /dev/null)" ]; then + docker compose --file "${_COMPOSITION_FILE}" start \ + && echo "SUCCESS" >&2 \ + && return 0 + elif [ "$(docker-compose version 2> /dev/null)" ]; then + docker-compose --file "${_COMPOSITION_FILE}" start \ + && echo "SUCCESS" >&2 \ + && return 0 + fi + + echo "FAILURE: Missing command: 'docker compose'" >&2 + echo " (maybe you have to install it via: 'apt install docker-compose-v2')" >&2 + return 1 +} + # Check if this module was started correctly using source