From 7140291080c92a3c821f32603f57d965e9e3fc0e Mon Sep 17 00:00:00 2001 From: casjay Date: Fri, 10 Jul 2026 12:28:21 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Migrate=20/var/lib/srv=20docker?= =?UTF-8?q?=20paths=20to=20/srv=20in=20README=20=F0=9F=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update volume mount examples and dockerHome variable from the old /var/lib/srv/$USER/docker layout to /srv/$USER/docker. - README.md: update volume path references to /srv/$USER/docker README.md rootfs/usr/local/bin/copy rootfs/usr/local/bin/healthcheck rootfs/usr/local/bin/symlink --- README.md | 8 +- rootfs/usr/local/bin/copy | 78 ++++++++++ rootfs/usr/local/bin/healthcheck | 249 +++++++++++++++++++++++++++++++ rootfs/usr/local/bin/symlink | 77 ++++++++++ 4 files changed, 408 insertions(+), 4 deletions(-) create mode 100755 rootfs/usr/local/bin/copy create mode 100755 rootfs/usr/local/bin/healthcheck create mode 100755 rootfs/usr/local/bin/symlink diff --git a/README.md b/README.md index 795f76a..de8cf22 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ dockermgr update vim ## Install and run container ```shell -dockerHome="/var/lib/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs" -mkdir -p "/var/lib/srv/$USER/docker/vim/rootfs" +dockerHome="/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs" +mkdir -p "/srv/$USER/docker/vim/rootfs" git clone "https://github.com/dockermgr/vim" "$HOME/.local/share/CasjaysDev/dockermgr/vim" cp -Rfva "$HOME/.local/share/CasjaysDev/dockermgr/vim/rootfs/." "$dockerHome/" docker run -d \ @@ -47,8 +47,8 @@ services: - TZ=America/New_York - HOSTNAME=vim volumes: - - "/var/lib/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs/data:/data:z" - - "/var/lib/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs/config:/config:z" + - "/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs/data:/data:z" + - "/srv/$USER/docker/casjaysdevdocker/vim/vim/latest/rootfs/config:/config:z" ports: - 80:80 restart: always diff --git a/rootfs/usr/local/bin/copy b/rootfs/usr/local/bin/copy new file mode 100755 index 0000000..e9878d8 --- /dev/null +++ b/rootfs/usr/local/bin/copy @@ -0,0 +1,78 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051306-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : copy --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 13:06 EDT +# @@File : copy +# @@Description : copies a file and shows progress +# @@Changelog : Refactored for self-contained operation +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# colorization +if [ -n "$NO_COLOR" ]; then + __printf_color() { printf '%b' "$1\n" | tr -d '\t' | sed '/^%b$/d;s,\x1B\[ 0-9;]*[a-zA-Z],,g'; } +else + __printf_color() { { [ -z "$2" ] || DEFAULT_COLOR=$2; } && printf "%b" "$(tput setaf "$DEFAULT_COLOR" 2>/dev/null)" "$1\n" "$(tput sgr0 2>/dev/null)"; } +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +__unlink() { [ -L "$1" ] && rm -f -- "$1" >/dev/null; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +# custom functions +__copy() { + exitCode=0 + if [ -d "$1" ]; then + __printf_color "Copying $1/* to $2/" + __unlink "$2" + mkdir -p "$2" + for f in "$1"/* "$1"/.[!.]* "$1"/..?*; do + [ -e "$f" ] || [ -L "$f" ] || continue + base=$(basename -- "$f") + __copy "$f" "$2/$base" || exitCode=$? + done + elif [ -f "$1" ] || [ -L "$1" ]; then + __printf_color "Copying $1 to $2" + __unlink "$2" + cp -Rf "$1" "$2" + exitCode=$? + fi + return $exitCode +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Define variables +DEFAULT_COLOR="254" +COPY_EXIT_STATUS=0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Main application +if [ $# -ne 2 ]; then + __printf_color "USAGE: $APPNAME to from" "1" >&2 + COPY_EXIT_STATUS=1 +elif [ ! -e "$1" ]; then + __printf_color "$1 does not exist" >&2 + COPY_EXIT_STATUS=2 +else + __printf_color "Copying $1 to $2" "4" + __copy "$1" "$2" >/dev/null + COPY_EXIT_STATUS=$? +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# End application +# - - - - - - - - - - - - - - - - - - - - - - - - - +# lets exit with code +# - - - - - - - - - - - - - - - - - - - - - - - - - +exit $COPY_EXIT_STATUS +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh diff --git a/rootfs/usr/local/bin/healthcheck b/rootfs/usr/local/bin/healthcheck new file mode 100755 index 0000000..8dab380 --- /dev/null +++ b/rootfs/usr/local/bin/healthcheck @@ -0,0 +1,249 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051654-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : healthcheck --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 16:54 EDT +# @@File : healthcheck +# @@Description : Docker container healthcheck — HTTP/TCP/process/file checks +# @@Changelog : Rewrote as a real Docker HEALTHCHECK probe +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +VERSION="202605051654-git" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Defaults (env vars override built-ins, CLI flags override env vars) +HEALTHCHECK_URL="${HEALTHCHECK_URL:-}" +HEALTHCHECK_HTTP_STATUS="${HEALTHCHECK_HTTP_STATUS:-2,3}" +HEALTHCHECK_HOST="${HEALTHCHECK_HOST:-127.0.0.1}" +HEALTHCHECK_PORT="${HEALTHCHECK_PORT:-}" +HEALTHCHECK_PROCESS="${HEALTHCHECK_PROCESS:-}" +HEALTHCHECK_FILE="${HEALTHCHECK_FILE:-}" +HEALTHCHECK_FILE_MAX_AGE="${HEALTHCHECK_FILE_MAX_AGE:-}" +HEALTHCHECK_TIMEOUT="${HEALTHCHECK_TIMEOUT:-5}" +HEALTHCHECK_VERBOSE="${HEALTHCHECK_VERBOSE:-}" +# - - - - - - - - - - - - - - - - - - - - - - - - - +__cmd_exists() { command -v "$1" >/dev/null 2>&1; } +__log() { [ -n "$HEALTHCHECK_VERBOSE" ] && printf '%s\n' "$*" >&2; return 0; } +__fail() { printf 'UNHEALTHY: %s\n' "$*" >&2; exit 1; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +__usage() { + cat <&2; __usage >&2; exit 1 ;; + *) printf 'Unexpected argument: %s\n' "$1" >&2; exit 1 ;; + esac +done +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Individual checks — each prints why it failed and exits 1 on failure +__trim() { printf '%s' "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'; } + +__check_one_http() { + url="$1"; accepted="$2"; timeout="$3" + if __cmd_exists curl; then + code="$(curl -ksSL -o /dev/null -w '%{http_code}' --max-time "$timeout" "$url" 2>/dev/null)" \ + || __fail "HTTP request to $url failed (curl error)" + elif __cmd_exists wget; then + code="$(wget -q -S --spider --timeout="$timeout" --tries=1 "$url" 2>&1 \ + | awk '/^ HTTP\// {c=$2} END {print c+0}')" + [ "$code" -gt 0 ] 2>/dev/null || __fail "HTTP request to $url failed (wget error)" + else + __fail "HTTP check requires curl or wget" + fi + IFS=',' + for prefix in $accepted; do + case "$code" in + "$prefix"*) unset IFS; __log "HTTP ok: $url -> $code"; return 0 ;; + esac + done + unset IFS + __fail "HTTP $url returned $code (expected prefix in: $accepted)" +} + +__check_http() { + urls="$1"; accepted="$2"; timeout="$3" + __log "HTTP: urls=$urls (timeout=${timeout}s, accept=${accepted})" + IFS=',' + for u in $urls; do + unset IFS + u="$(__trim "$u")" + [ -n "$u" ] || { IFS=','; continue; } + __check_one_http "$u" "$accepted" "$timeout" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_tcp() { + host="$1"; port="$2"; timeout="$3" + if __cmd_exists nc; then + nc -z -w "$timeout" "$host" "$port" >/dev/null 2>&1 && { __log "TCP ok: $host:$port"; return 0; } + fi + if __cmd_exists ncat; then + ncat -z -w "${timeout}s" "$host" "$port" >/dev/null 2>&1 && { __log "TCP ok (ncat): $host:$port"; return 0; } + fi + # Last resort: bash /dev/tcp (only if bash is available; sh-only systems skip) + if __cmd_exists bash; then + bash -c "exec 3<>/dev/tcp/$host/$port" >/dev/null 2>&1 && { __log "TCP ok (bash): $host:$port"; return 0; } + fi + return 1 +} + +__check_tcp() { + host="$1"; ports="$2"; timeout="$3" + __log "TCP: host=$host ports=$ports (timeout=${timeout}s)" + IFS=',' + for p in $ports; do + unset IFS + p="$(__trim "$p")" + [ -n "$p" ] || { IFS=','; continue; } + __check_one_tcp "$host" "$p" "$timeout" || __fail "TCP $host:$p not reachable" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_process() { + pattern="$1" + if __cmd_exists pgrep; then + # Match against process name (not full cmdline) so our own argv doesn't self-match + pgrep -- "$pattern" >/dev/null 2>&1 && return 0 + else + # Portable fallback: ps -o comm= prints just the command name + ps -e -o comm= 2>/dev/null | grep -v -e "^grep$" -e "^$APPNAME$" | grep -q -- "$pattern" && return 0 + fi + return 1 +} + +__check_process() { + patterns="$1" + __log "Process: patterns=$patterns" + IFS=',' + for p in $patterns; do + unset IFS + p="$(__trim "$p")" + [ -n "$p" ] || { IFS=','; continue; } + __check_one_process "$p" || __fail "Process not running: $p" + __log "Process ok: $p" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_file() { + path="$1"; max_age="$2" + [ -e "$path" ] || __fail "File not found: $path" + if [ -n "$max_age" ]; then + now="$(date +%s)" + mtime="$(stat -c %Y "$path" 2>/dev/null || stat -f %m "$path" 2>/dev/null \ + || perl -e 'print((stat(shift))[9])' "$path" 2>/dev/null)" + [ -n "$mtime" ] || __fail "Cannot determine mtime of $path" + age=$(( now - mtime )) + [ "$age" -le "$max_age" ] || __fail "File $path is stale (age=${age}s, max=${max_age}s)" + fi + __log "File ok: $path" + return 0 +} + +__check_file() { + paths="$1"; max_age="$2" + __log "File: paths=$paths max_age=${max_age:-none}" + IFS=',' + for f in $paths; do + unset IFS + f="$(__trim "$f")" + [ -n "$f" ] || { IFS=','; continue; } + __check_one_file "$f" "$max_age" + IFS=',' + done + unset IFS + return 0 +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Run checks +ran_any=0 +[ -n "$HEALTHCHECK_URL" ] && { __check_http "$HEALTHCHECK_URL" "$HEALTHCHECK_HTTP_STATUS" "$HEALTHCHECK_TIMEOUT"; ran_any=1; } +[ -n "$HEALTHCHECK_PORT" ] && { __check_tcp "$HEALTHCHECK_HOST" "$HEALTHCHECK_PORT" "$HEALTHCHECK_TIMEOUT"; ran_any=1; } +[ -n "$HEALTHCHECK_PROCESS" ] && { __check_process "$HEALTHCHECK_PROCESS"; ran_any=1; } +[ -n "$HEALTHCHECK_FILE" ] && { __check_file "$HEALTHCHECK_FILE" "$HEALTHCHECK_FILE_MAX_AGE"; ran_any=1; } + +[ "$ran_any" -eq 1 ] || __fail "no checks configured (set HEALTHCHECK_URL/PORT/PROCESS/FILE or pass --url/--port/--process/--file)" + +__log "All checks passed" +exit 0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh diff --git a/rootfs/usr/local/bin/symlink b/rootfs/usr/local/bin/symlink new file mode 100755 index 0000000..ac2db15 --- /dev/null +++ b/rootfs/usr/local/bin/symlink @@ -0,0 +1,77 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051306-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : symlink --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 13:06 EDT +# @@File : symlink +# @@Description : +# @@Changelog : New script +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# colorization +if [ -n "$NO_COLOR" ]; then + __printf_color() { printf '%b' "$1\n" | tr -d '\t' | sed '/^%b$/d;s,\x1B\[ 0-9;]*[a-zA-Z],,g'; } +else + __printf_color() { { [ -z "$2" ] || DEFAULT_COLOR=$2; } && printf "%b" "$(tput setaf "$DEFAULT_COLOR" 2>/dev/null)" "$1\n" "$(tput sgr0 2>/dev/null)"; } +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +__unlink() { [ -L "$1" ] && rm -f -- "$1" >/dev/null; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +# custom functions +__ln_sf() { + exitCode=0 + if [ -d "$1" ] && [ ! -L "$1" ]; then + __printf_color "symlinking contents of $1 into $2/" "4" + __unlink "$2" + mkdir -p "$2" + for f in "$1"/* "$1"/.[!.]* "$1"/..?*; do + [ -e "$f" ] || [ -L "$f" ] || continue + base=$(basename -- "$f") + __ln_sf "$f" "$2/$base" || exitCode=$? + done + else + __printf_color "symlinking $2 to $1" "4" + __unlink "$2" + ln -sf "$1" "$2" + exitCode=$? + fi + return $exitCode +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Define variables +DEFAULT_COLOR="254" +SYMLINK_EXIT_STATUS=0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Main application +if [ $# -ne 2 ]; then + __printf_color "USAGE: $APPNAME from to" "2" >&2 + SYMLINK_EXIT_STATUS=1 +elif [ ! -e "$1" ]; then + __printf_color "$1 does not exist" >&2 + SYMLINK_EXIT_STATUS=2 +else + __ln_sf "$1" "$2" >/dev/null + SYMLINK_EXIT_STATUS=$? +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# End application +# - - - - - - - - - - - - - - - - - - - - - - - - - +# lets exit with code +# - - - - - - - - - - - - - - - - - - - - - - - - - +exit $SYMLINK_EXIT_STATUS +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh