1#!/usr/bin/env bash 2# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*- 3# SPDX-License-Identifier: AGPL-3.0-or-later 4# shellcheck disable=SC2059,SC1117 5 6# ubuntu, debian, arch, fedora, centos ... 7DIST_ID=$(source /etc/os-release; echo "$ID"); 8# shellcheck disable=SC2034 9DIST_VERS=$(source /etc/os-release; echo "$VERSION_ID"); 10 11ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}" 12ADMIN_NAME="${ADMIN_NAME:-$USER}" 13 14ADMIN_EMAIL="${ADMIN_EMAIL:-$(git config user.email)}" 15ADMIN_EMAIL="${ADMIN_EMAIL:-$USER@$(hostname)}" 16 17if [[ -z "${REPO_ROOT}" ]]; then 18 REPO_ROOT=$(dirname "${BASH_SOURCE[0]}") 19 while [ -h "${REPO_ROOT}" ] ; do 20 REPO_ROOT=$(readlink "${REPO_ROOT}") 21 done 22 REPO_ROOT=$(cd "${REPO_ROOT}/.." && pwd -P ) 23fi 24 25if [[ -z ${TEMPLATES} ]]; then 26 TEMPLATES="${REPO_ROOT}/utils/templates" 27fi 28 29if [[ -z "$CACHE" ]]; then 30 CACHE="${REPO_ROOT}/cache" 31fi 32 33if [[ -z ${DIFF_CMD} ]]; then 34 DIFF_CMD="diff -u" 35 if command -v colordiff >/dev/null; then 36 DIFF_CMD="colordiff -u" 37 fi 38fi 39 40DOT_CONFIG="${DOT_CONFIG:-${REPO_ROOT}/.config.sh}" 41 42source_dot_config() { 43 if [[ ! -e "${DOT_CONFIG}" ]]; then 44 err_msg "configuration does not exists at: ${DOT_CONFIG}" 45 return 42 46 fi 47 # shellcheck disable=SC1090 48 source "${DOT_CONFIG}" 49} 50 51sudo_or_exit() { 52 # usage: sudo_or_exit 53 54 if [ ! "$(id -u)" -eq 0 ]; then 55 err_msg "this command requires root (sudo) privilege!" >&2 56 exit 42 57 fi 58} 59 60required_commands() { 61 62 # usage: required_commands [cmd1 ...] 63 64 local exit_val=0 65 while [ -n "$1" ]; do 66 67 if ! command -v "$1" &>/dev/null; then 68 err_msg "missing command $1" 69 exit_val=42 70 fi 71 shift 72 done 73 return $exit_val 74} 75 76# colors 77# ------ 78 79# shellcheck disable=SC2034 80set_terminal_colors() { 81 _colors=8 82 _creset='\e[0m' # reset all attributes 83 84 _Black='\e[0;30m' 85 _White='\e[1;37m' 86 _Red='\e[0;31m' 87 _Green='\e[0;32m' 88 _Yellow='\e[0;33m' 89 _Blue='\e[0;34m' 90 _Violet='\e[0;35m' 91 _Cyan='\e[0;36m' 92 93 _BBlack='\e[1;30m' 94 _BWhite='\e[1;37m' 95 _BRed='\e[1;31m' 96 _BGreen='\e[1;32m' 97 _BYellow='\e[1;33m' 98 _BBlue='\e[1;34m' 99 _BPurple='\e[1;35m' 100 _BCyan='\e[1;36m' 101} 102 103if [ ! -p /dev/stdout ]; then 104 set_terminal_colors 105fi 106 107# reST 108# ---- 109 110if command -v fmt >/dev/null; then 111 export FMT="fmt -u" 112else 113 export FMT="cat" 114fi 115 116rst_title() { 117 # usage: rst_title <header-text> [part|chapter|section] 118 119 case ${2-chapter} in 120 part) printf "\n${_BGreen}${1//?/=}${_creset}\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";; 121 chapter) printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";; 122 section) printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/-}${_creset}\n";; 123 *) 124 err_msg "invalid argument '${2}' in line $(caller)" 125 return 42 126 ;; 127 esac 128} 129 130rst_para() { 131 # usage: RST_INDENT=1 rst_para "lorem ipsum ..." 132 local prefix='' 133 if [[ -n $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then 134 prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf " "; done)" 135 echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix" 136 else 137 echo -en "\n$*\n" | $FMT 138 fi 139} 140 141die() { 142 echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[1]}: line ${BASH_LINENO[0]}: ${2-died ${1-1}}" >&2; 143 exit "${1-1}" 144} 145 146die_caller() { 147 echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[2]}: line ${BASH_LINENO[1]}: ${FUNCNAME[1]}(): ${2-died ${1-1}}" >&2; 148 exit "${1-1}" 149} 150 151err_msg() { echo -e "${_BRed}ERROR:${_creset} $*" >&2; } 152warn_msg() { echo -e "${_BBlue}WARN:${_creset} $*" >&2; } 153info_msg() { echo -e "${_BYellow}INFO:${_creset} $*" >&2; } 154 155clean_stdin() { 156 if [[ $(uname -s) != 'Darwin' ]]; then 157 while read -r -n1 -t 0.1; do : ; done 158 fi 159} 160 161wait_key(){ 162 # usage: wait_key [<timeout in sec>] 163 164 clean_stdin 165 local _t=$1 166 local msg="${MSG}" 167 [[ -z "$msg" ]] && msg="${_Green}** press any [${_BCyan}KEY${_Green}] to continue **${_creset}" 168 169 [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT 170 [[ -n $_t ]] && _t="-t $_t" 171 printf "$msg" 172 # shellcheck disable=SC2086 173 read -r -s -n1 $_t 174 echo 175 clean_stdin 176} 177 178ask_yn() { 179 # usage: ask_yn <prompt-text> [Ny|Yn] [<timeout in sec>] 180 181 local EXIT_YES=0 # exit status 0 --> successful 182 local EXIT_NO=1 # exit status 1 --> error code 183 184 local _t=$3 185 [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT 186 [[ -n $_t ]] && _t="-t $_t" 187 case "${FORCE_SELECTION:-${2}}" in 188 Y) return ${EXIT_YES} ;; 189 N) return ${EXIT_NO} ;; 190 Yn) 191 local exit_val=${EXIT_YES} 192 local choice="[${_BGreen}YES${_creset}/no]" 193 local default="Yes" 194 ;; 195 *) 196 local exit_val=${EXIT_NO} 197 local choice="[${_BGreen}NO${_creset}/yes]" 198 local default="No" 199 ;; 200 esac 201 echo 202 while true; do 203 clean_stdin 204 printf "$1 ${choice} " 205 # shellcheck disable=SC2086 206 read -r -n1 $_t 207 if [[ -z $REPLY ]]; then 208 printf "$default\n"; break 209 elif [[ $REPLY =~ ^[Yy]$ ]]; then 210 exit_val=${EXIT_YES} 211 printf "\n" 212 break 213 elif [[ $REPLY =~ ^[Nn]$ ]]; then 214 exit_val=${EXIT_NO} 215 printf "\n" 216 break 217 fi 218 _t="" 219 err_msg "invalid choice" 220 done 221 clean_stdin 222 return $exit_val 223} 224 225tee_stderr () { 226 227 # usage:: 228 # tee_stderr 1 <<EOF | python -i 229 # print("hello") 230 # EOF 231 # ... 232 # >>> print("hello") 233 # hello 234 235 local _t="0"; 236 if [[ -n $1 ]] ; then _t="$1"; fi 237 238 (while read -r line; do 239 # shellcheck disable=SC2086 240 sleep $_t 241 echo -e "$line" >&2 242 echo "$line" 243 done) 244} 245 246prefix_stdout () { 247 # usage: <cmd> | prefix_stdout [prefix] 248 249 local prefix="${_BYellow}-->|${_creset}" 250 251 if [[ -n $1 ]] ; then prefix="$1"; fi 252 253 # shellcheck disable=SC2162 254 (while IFS= read line; do 255 echo -e "${prefix}$line" 256 done) 257} 258 259append_line() { 260 261 # usage: append_line <line> <file> 262 # 263 # Append line if not exists, create file if not exists. E.g:: 264 # 265 # append_line 'source ~/.foo' ~/bashrc 266 267 local LINE=$1 268 local FILE=$2 269 grep -qFs -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE" 270} 271 272cache_download() { 273 274 # usage: cache_download <url> <local-filename> 275 276 local exit_value=0 277 278 if [[ -n ${SUDO_USER} ]]; then 279 sudo -u "${SUDO_USER}" mkdir -p "${CACHE}" 280 else 281 mkdir -p "${CACHE}" 282 fi 283 284 if [[ -f "${CACHE}/$2" ]] ; then 285 info_msg "already cached: $1" 286 info_msg " --> ${CACHE}/$2" 287 fi 288 289 if [[ ! -f "${CACHE}/$2" ]]; then 290 info_msg "caching: $1" 291 info_msg " --> ${CACHE}/$2" 292 if [[ -n ${SUDO_USER} ]]; then 293 sudo -u "${SUDO_USER}" wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$? 294 else 295 wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$? 296 fi 297 if [[ ! $exit_value = 0 ]]; then 298 err_msg "failed to download: $1" 299 fi 300 fi 301} 302 303backup_file() { 304 305 # usage: backup_file /path/to/file.foo 306 307 local stamp 308 stamp=$(date +"_%Y%m%d_%H%M%S") 309 info_msg "create backup: ${1}${stamp}" 310 cp -a "${1}" "${1}${stamp}" 311} 312 313choose_one() { 314 315 # usage: 316 # 317 # DEFAULT_SELECT= 2 \ 318 # choose_one <name> "your selection?" "Coffee" "Coffee with milk" 319 320 local default=${DEFAULT_SELECT-1} 321 local REPLY 322 local env_name=$1 && shift 323 local choice=$1; 324 local max="${#@}" 325 local _t 326 [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT 327 [[ -n $_t ]] && _t="-t $_t" 328 329 list=("$@") 330 echo -e "${_BGreen}Menu::${_creset}" 331 for ((i=1; i<= $((max -1)); i++)); do 332 if [[ "$i" == "$default" ]]; then 333 echo -e " ${_BGreen}$i.${_creset}) ${list[$i]} [default]" 334 else 335 echo -e " $i.) ${list[$i]}" 336 fi 337 done 338 while true; do 339 clean_stdin 340 printf "$1 [${_BGreen}$default${_creset}] " 341 342 if (( 10 > max )); then 343 # shellcheck disable=SC2086 344 read -r -n1 $_t 345 else 346 # shellcheck disable=SC2086,SC2229 347 read -r $_t 348 fi 349 # selection fits 350 [[ $REPLY =~ ^-?[0-9]+$ ]] && (( REPLY > 0 )) && (( REPLY < max )) && break 351 352 # take default 353 [[ -z $REPLY ]] && REPLY=$default && break 354 355 _t="" 356 err_msg "invalid choice" 357 done 358 eval "$env_name"='${list[${REPLY}]}' 359 echo 360 clean_stdin 361} 362 363install_template() { 364 365 # usage: 366 # 367 # install_template [--no-eval] [--variant=<name>] \ 368 # {file} [{owner} [{group} [{chmod}]]] 369 # 370 # E.g. the origin of variant 'raw' of /etc/updatedb.conf is:: 371 # 372 # ${TEMPLATES}/etc/updatedb.conf:raw 373 # 374 # To install variant 'raw' of /etc/updatedb.conf without evaluated 375 # replacements you can use:: 376 # 377 # install_template --variant=raw --no-eval \ 378 # /etc/updatedb.conf root root 644 379 380 local _reply="" 381 local do_eval=1 382 local variant="" 383 local pos_args=("$0") 384 385 for i in "$@"; do 386 case $i in 387 --no-eval) do_eval=0; shift ;; 388 --variant=*) variant=":${i#*=}"; shift ;; 389 *) pos_args+=("$i") ;; 390 esac 391 done 392 393 local dst="${pos_args[1]}" 394 local template_origin="${TEMPLATES}${dst}${variant}" 395 local template_file="${TEMPLATES}${dst}" 396 397 local owner="${pos_args[2]-$(id -un)}" 398 local group="${pos_args[3]-$(id -gn)}" 399 local chmod="${pos_args[4]-644}" 400 401 info_msg "install (eval=$do_eval): ${dst}" 402 [[ -n $variant ]] && info_msg "variant --> ${variant}" 403 404 if [[ ! -f "${template_origin}" ]] ; then 405 err_msg "${template_origin} does not exists" 406 err_msg "... can't install $dst" 407 wait_key 30 408 return 42 409 fi 410 411 if [[ "$do_eval" == "1" ]]; then 412 template_file="${CACHE}${dst}${variant}" 413 info_msg "BUILD template ${template_file}" 414 if [[ -n ${SUDO_USER} ]]; then 415 sudo -u "${SUDO_USER}" mkdir -p "$(dirname "${template_file}")" 416 else 417 mkdir -p "$(dirname "${template_file}")" 418 fi 419 # shellcheck disable=SC2086 420 eval "echo \"$(cat ${template_origin})\"" > "${template_file}" 421 if [[ -n ${SUDO_USER} ]]; then 422 chown "${SUDO_USER}:${SUDO_USER}" "${template_file}" 423 fi 424 else 425 template_file=$template_origin 426 fi 427 428 mkdir -p "$(dirname "${dst}")" 429 430 if [[ ! -f "${dst}" ]]; then 431 info_msg "install: ${template_file}" 432 sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \ 433 "${template_file}" "${dst}" | prefix_stdout 434 return $? 435 fi 436 437 if [[ -f "${dst}" ]] && cmp --silent "${template_file}" "${dst}" ; then 438 info_msg "file ${dst} allready installed" 439 return 0 440 fi 441 442 info_msg "diffrent file ${dst} allready exists on this host" 443 444 while true; do 445 choose_one _reply "choose next step with file $dst" \ 446 "replace file" \ 447 "leave file unchanged" \ 448 "interactiv shell" \ 449 "diff files" 450 451 case $_reply in 452 "replace file") 453 info_msg "install: ${template_file}" 454 sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \ 455 "${template_file}" "${dst}" | prefix_stdout 456 break 457 ;; 458 "leave file unchanged") 459 break 460 ;; 461 "interactiv shell") 462 echo -e "// edit ${_Red}${dst}${_creset} to your needs" 463 echo -e "// exit with [${_BCyan}CTRL-D${_creset}]" 464 sudo -H -u "${owner}" -i 465 $DIFF_CMD "${dst}" "${template_file}" 466 echo 467 echo -e "// ${_BBlack}did you edit file ...${_creset}" 468 echo -en "// ${_Red}${dst}${_creset}" 469 if ask_yn "//${_BBlack}... to your needs?${_creset}"; then 470 break 471 fi 472 ;; 473 "diff files") 474 $DIFF_CMD "${dst}" "${template_file}" | prefix_stdout 475 esac 476 done 477} 478 479 480service_is_available() { 481 482 # usage: service_is_available <URL> 483 484 [[ -z $1 ]] && die_caller 42 "missing argument <URL>" 485 local URL="$1" 486 http_code=$(curl -H 'Cache-Control: no-cache' \ 487 --silent -o /dev/null --head --write-out '%{http_code}' --insecure \ 488 "${URL}") 489 exit_val=$? 490 if [[ $exit_val = 0 ]]; then 491 info_msg "got $http_code from ${URL}" 492 fi 493 case "$http_code" in 494 404|410|423) exit_val=$http_code;; 495 esac 496 return "$exit_val" 497} 498 499# golang 500# ------ 501 502go_is_available() { 503 504 # usage: go_is_available $SERVICE_USER && echo "go is installed!" 505 506 sudo -i -u "${1}" which go &>/dev/null 507} 508 509install_go() { 510 511 # usage: install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}" 512 513 local _svcpr=" ${_Yellow}|${3}|${_creset} " 514 515 rst_title "Install Go in user's HOME" section 516 517 rst_para "download and install go binary .." 518 cache_download "${1}" "${2}" 519 520 tee_stderr 0.1 <<EOF | sudo -i -u "${3}" | prefix_stdout "$_svcpr" 521echo \$PATH 522echo \$GOPATH 523mkdir -p \$HOME/local 524rm -rf \$HOME/local/go 525tar -C \$HOME/local -xzf ${CACHE}/${2} 526EOF 527 sudo -i -u "${3}" <<EOF | prefix_stdout 528! which go >/dev/null && echo "ERROR - Go Installation not found in PATH!?!" 529which go >/dev/null && go version && echo "congratulations -- Go installation OK :)" 530EOF 531} 532 533# system accounts 534# --------------- 535 536service_account_is_available() { 537 538 # usage: service_account_is_available "$SERVICE_USER" && echo "OK" 539 540 sudo -i -u "$1" echo \$HOME &>/dev/null 541} 542 543drop_service_account() { 544 545 # usage: drop_service_account "${SERVICE_USER}" 546 547 rst_title "Drop ${1} HOME" section 548 if ask_yn "Do you really want to drop ${1} home folder?"; then 549 userdel -r -f "${1}" 2>&1 | prefix_stdout 550 else 551 rst_para "Leave HOME folder $(du -sh "${1}") unchanged." 552 fi 553} 554 555interactive_shell(){ 556 557 # usage: interactive_shell "${SERVICE_USER}" 558 559 echo -e "// exit with [${_BCyan}CTRL-D${_creset}]" 560 sudo -H -u "${1}" -i 561} 562 563 564# systemd 565# ------- 566 567SYSTEMD_UNITS="${SYSTEMD_UNITS:-/lib/systemd/system}" 568 569systemd_install_service() { 570 571 # usage: systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}" 572 573 rst_title "Install System-D Unit ${1}" section 574 echo 575 install_template "${2}" root root 644 576 wait_key 577 systemd_activate_service "${1}" 578} 579 580systemd_remove_service() { 581 582 # usage: systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}" 583 584 if ! ask_yn "Do you really want to deinstall systemd unit ${1}?"; then 585 return 42 586 fi 587 systemd_deactivate_service "${1}" 588 rm "${2}" 2>&1 | prefix_stdout 589} 590 591systemd_activate_service() { 592 593 # usage: systemd_activate_service "${SERVICE_NAME}" 594 595 rst_title "Activate ${1} (service)" section 596 echo 597 tee_stderr <<EOF | bash 2>&1 598systemctl enable ${1}.service 599systemctl restart ${1}.service 600EOF 601 tee_stderr <<EOF | bash 2>&1 602systemctl status --no-pager ${1}.service 603EOF 604} 605 606systemd_deactivate_service() { 607 608 # usage: systemd_deactivate_service "${SERVICE_NAME}" 609 610 rst_title "De-Activate ${1} (service)" section 611 echo 612 tee_stderr <<EOF | bash 2>&1 | prefix_stdout 613systemctl stop ${1}.service 614systemctl disable ${1}.service 615EOF 616} 617 618systemd_restart_service() { 619 620 # usage: systemd_restart_service "${SERVICE_NAME}" 621 622 rst_title "Restart ${1} (service)" section 623 echo 624 tee_stderr <<EOF | bash 2>&1 625systemctl restart ${1}.service 626EOF 627 tee_stderr <<EOF | bash 2>&1 628systemctl status --no-pager ${1}.service 629EOF 630} 631 632 633# nginx 634# ----- 635 636nginx_distro_setup() { 637 # shellcheck disable=SC2034 638 639 NGINX_DEFAULT_SERVER=/etc/nginx/nginx.conf 640 641 # Including *location* directives from a dedicated config-folder into the 642 # server directive is, what fedora and centos (already) does. 643 NGINX_APPS_ENABLED="/etc/nginx/default.d" 644 645 # We add a apps-available folder and linking configurations into the 646 # NGINX_APPS_ENABLED folder. See also nginx_include_apps_enabled(). 647 NGINX_APPS_AVAILABLE="/etc/nginx/default.apps-available" 648 649 case $DIST_ID-$DIST_VERS in 650 ubuntu-*|debian-*) 651 NGINX_PACKAGES="nginx" 652 NGINX_DEFAULT_SERVER=/etc/nginx/sites-available/default 653 ;; 654 arch-*) 655 NGINX_PACKAGES="nginx-mainline" 656 ;; 657 fedora-*|centos-7) 658 NGINX_PACKAGES="nginx" 659 ;; 660 *) 661 err_msg "$DIST_ID-$DIST_VERS: nginx not yet implemented" 662 ;; 663 esac 664} 665nginx_distro_setup 666 667install_nginx(){ 668 info_msg "installing nginx ..." 669 pkg_install "${NGINX_PACKAGES}" 670 case $DIST_ID-$DIST_VERS in 671 arch-*|fedora-*|centos-7) 672 systemctl enable nginx 673 systemctl start nginx 674 ;; 675 esac 676} 677 678nginx_is_installed() { 679 command -v nginx &>/dev/null 680} 681 682nginx_reload() { 683 684 info_msg "reload nginx .." 685 echo 686 if ! nginx -t; then 687 err_msg "testing nginx configuration failed" 688 return 42 689 fi 690 systemctl restart nginx 691} 692 693nginx_install_app() { 694 695 # usage: nginx_install_app [<template option> ...] <myapp.conf> 696 # 697 # <template option>: see install_template 698 699 local template_opts=() 700 local pos_args=("$0") 701 702 for i in "$@"; do 703 case $i in 704 -*) template_opts+=("$i");; 705 *) pos_args+=("$i");; 706 esac 707 done 708 709 nginx_include_apps_enabled "${NGINX_DEFAULT_SERVER}" 710 711 install_template "${template_opts[@]}" \ 712 "${NGINX_APPS_AVAILABLE}/${pos_args[1]}" \ 713 root root 644 714 nginx_enable_app "${pos_args[1]}" 715 info_msg "installed nginx app: ${pos_args[1]}" 716} 717 718nginx_include_apps_enabled() { 719 720 # Add the *NGINX_APPS_ENABLED* infrastruture to a nginx server block. Such 721 # infrastruture is already known from fedora and centos, including apps (location 722 # directives) from the /etc/nginx/default.d folder into the *default* nginx 723 # server. 724 725 # usage: nginx_include_apps_enabled <config-file> 726 # 727 # config-file: Config file with server directive in. 728 729 [[ -z $1 ]] && die_caller 42 "missing argument <config-file>" 730 local server_conf="$1" 731 732 # include /etc/nginx/default.d/*.conf; 733 local include_directive="include ${NGINX_APPS_ENABLED}/*.conf;" 734 local include_directive_re="^\s*include ${NGINX_APPS_ENABLED}/\*\.conf;" 735 736 info_msg "checking existence: '${include_directive}' in file ${server_conf}" 737 if grep "${include_directive_re}" "${server_conf}"; then 738 info_msg "OK, already exists." 739 return 740 fi 741 742 info_msg "add missing directive: '${include_directive}'" 743 cp "${server_conf}" "${server_conf}.bak" 744 745 ( 746 local line 747 local stage=0 748 while IFS= read -r line 749 do 750 echo "$line" 751 if [[ $stage = 0 ]]; then 752 if [[ $line =~ ^[[:space:]]*server*[[:space:]]*\{ ]]; then 753 stage=1 754 fi 755 fi 756 757 if [[ $stage = 1 ]]; then 758 echo " # Load configuration files for the default server block." 759 echo " $include_directive" 760 echo "" 761 stage=2 762 fi 763 done < "${server_conf}.bak" 764 ) > "${server_conf}" 765 766} 767 768nginx_remove_app() { 769 770 # usage: nginx_remove_app <myapp.conf> 771 772 info_msg "remove nginx app: $1" 773 nginx_dissable_app "$1" 774 rm -f "${NGINX_APPS_AVAILABLE}/$1" 775} 776 777nginx_enable_app() { 778 779 # usage: nginx_enable_app <myapp.conf> 780 781 local CONF="$1" 782 783 info_msg "enable nginx app: ${CONF}" 784 mkdir -p "${NGINX_APPS_ENABLED}" 785 rm -f "${NGINX_APPS_ENABLED}/${CONF}" 786 ln -s "${NGINX_APPS_AVAILABLE}/${CONF}" "${NGINX_APPS_ENABLED}/${CONF}" 787 nginx_reload 788} 789 790nginx_dissable_app() { 791 792 # usage: nginx_disable_app <myapp.conf> 793 794 local CONF="$1" 795 796 info_msg "disable nginx app: ${CONF}" 797 rm -f "${NGINX_APPS_ENABLED}/${CONF}" 798 nginx_reload 799} 800 801 802# Apache 803# ------ 804 805apache_distro_setup() { 806 # shellcheck disable=SC2034 807 case $DIST_ID-$DIST_VERS in 808 ubuntu-*|debian-*) 809 # debian uses the /etc/apache2 path, while other distros use 810 # the apache default at /etc/httpd 811 APACHE_SITES_AVAILABLE="/etc/apache2/sites-available" 812 APACHE_SITES_ENABLED="/etc/apache2/sites-enabled" 813 APACHE_MODULES="/usr/lib/apache2/modules" 814 APACHE_PACKAGES="apache2" 815 ;; 816 arch-*) 817 APACHE_SITES_AVAILABLE="/etc/httpd/sites-available" 818 APACHE_SITES_ENABLED="/etc/httpd/sites-enabled" 819 APACHE_MODULES="modules" 820 APACHE_PACKAGES="apache" 821 ;; 822 fedora-*|centos-7) 823 APACHE_SITES_AVAILABLE="/etc/httpd/sites-available" 824 APACHE_SITES_ENABLED="/etc/httpd/sites-enabled" 825 APACHE_MODULES="modules" 826 APACHE_PACKAGES="httpd" 827 ;; 828 *) 829 err_msg "$DIST_ID-$DIST_VERS: apache not yet implemented" 830 ;; 831 esac 832} 833 834apache_distro_setup 835 836install_apache(){ 837 info_msg "installing apache ..." 838 pkg_install "$APACHE_PACKAGES" 839 case $DIST_ID-$DIST_VERS in 840 arch-*|fedora-*|centos-7) 841 if ! grep "IncludeOptional sites-enabled" "/etc/httpd/conf/httpd.conf"; then 842 echo "IncludeOptional sites-enabled/*.conf" >> "/etc/httpd/conf/httpd.conf" 843 fi 844 systemctl enable httpd 845 systemctl start httpd 846 ;; 847 esac 848} 849 850apache_is_installed() { 851 case $DIST_ID-$DIST_VERS in 852 ubuntu-*|debian-*) (command -v apachectl) &>/dev/null;; 853 arch-*) (command -v httpd) &>/dev/null;; 854 fedora-*|centos-7) (command -v httpd) &>/dev/null;; 855 esac 856} 857 858apache_reload() { 859 860 info_msg "reload apache .." 861 echo 862 case $DIST_ID-$DIST_VERS in 863 ubuntu-*|debian-*) 864 sudo -H apachectl configtest 865 sudo -H systemctl force-reload apache2 866 ;; 867 arch-*|fedora-*|centos-7) 868 sudo -H httpd -t 869 sudo -H systemctl force-reload httpd 870 ;; 871 esac 872} 873 874apache_install_site() { 875 876 # usage: apache_install_site [<template option> ...] <mysite.conf> 877 # 878 # <template option>: see install_template 879 880 local template_opts=() 881 local pos_args=("$0") 882 883 for i in "$@"; do 884 case $i in 885 -*) template_opts+=("$i");; 886 *) pos_args+=("$i");; 887 esac 888 done 889 890 install_template "${template_opts[@]}" \ 891 "${APACHE_SITES_AVAILABLE}/${pos_args[1]}" \ 892 root root 644 893 apache_enable_site "${pos_args[1]}" 894 info_msg "installed apache site: ${pos_args[1]}" 895} 896 897apache_remove_site() { 898 899 # usage: apache_remove_site <mysite.conf> 900 901 info_msg "remove apache site: $1" 902 apache_dissable_site "$1" 903 rm -f "${APACHE_SITES_AVAILABLE}/$1" 904} 905 906apache_enable_site() { 907 908 # usage: apache_enable_site <mysite.conf> 909 910 local CONF="$1" 911 912 info_msg "enable apache site: ${CONF}" 913 914 case $DIST_ID-$DIST_VERS in 915 ubuntu-*|debian-*) 916 sudo -H a2ensite -q "${CONF}" 917 ;; 918 arch-*) 919 mkdir -p "${APACHE_SITES_ENABLED}" 920 rm -f "${APACHE_SITES_ENABLED}/${CONF}" 921 ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}" 922 ;; 923 fedora-*|centos-7) 924 mkdir -p "${APACHE_SITES_ENABLED}" 925 rm -f "${APACHE_SITES_ENABLED}/${CONF}" 926 ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}" 927 ;; 928 esac 929 apache_reload 930} 931 932apache_dissable_site() { 933 934 # usage: apache_disable_site <mysite.conf> 935 936 local CONF="$1" 937 938 info_msg "disable apache site: ${CONF}" 939 940 case $DIST_ID-$DIST_VERS in 941 ubuntu-*|debian-*) 942 sudo -H a2dissite -q "${CONF}" 943 ;; 944 arch-*) 945 rm -f "${APACHE_SITES_ENABLED}/${CONF}" 946 ;; 947 fedora-*|centos-7) 948 rm -f "${APACHE_SITES_ENABLED}/${CONF}" 949 ;; 950 esac 951 apache_reload 952} 953 954# uWSGI 955# ----- 956 957uWSGI_SETUP="${uWSGI_SETUP:=/etc/uwsgi}" 958uWSGI_USER= 959uWSGI_GROUP= 960 961# How distros manage uWSGI apps is very different. From uWSGI POV read: 962# - https://uwsgi-docs.readthedocs.io/en/latest/Management.html 963 964uWSGI_distro_setup() { 965 case $DIST_ID-$DIST_VERS in 966 ubuntu-*|debian-*) 967 # init.d --> /usr/share/doc/uwsgi/README.Debian.gz 968 # For uWSGI debian uses the LSB init process, this might be changed 969 # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067 970 uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available" 971 uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled" 972 uWSGI_PACKAGES="uwsgi" 973 ;; 974 arch-*) 975 # systemd --> /usr/lib/systemd/system/uwsgi@.service 976 # For uWSGI archlinux uses systemd template units, see 977 # - http://0pointer.de/blog/projects/instances.html 978 # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd 979 uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux" 980 uWSGI_APPS_ENABLED="${uWSGI_SETUP}" 981 uWSGI_PACKAGES="uwsgi" 982 ;; 983 fedora-*|centos-7) 984 # systemd --> /usr/lib/systemd/system/uwsgi.service 985 # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see 986 # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html 987 uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available" 988 uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d" 989 uWSGI_PACKAGES="uwsgi" 990 uWSGI_USER="uwsgi" 991 uWSGI_GROUP="uwsgi" 992 ;; 993 *) 994 err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented" 995 ;; 996esac 997} 998 999uWSGI_distro_setup 1000 1001install_uwsgi(){ 1002 info_msg "installing uwsgi ..." 1003 pkg_install "$uWSGI_PACKAGES" 1004 case $DIST_ID-$DIST_VERS in 1005 fedora-*|centos-7) 1006 # enable & start should be called once at uWSGI installation time 1007 systemctl enable uwsgi 1008 systemctl restart uwsgi 1009 ;; 1010 esac 1011} 1012 1013uWSGI_restart() { 1014 1015 # usage: uWSGI_restart() <myapp.ini> 1016 1017 local CONF="$1" 1018 1019 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1020 info_msg "restart uWSGI service" 1021 case $DIST_ID-$DIST_VERS in 1022 ubuntu-*|debian-*) 1023 # the 'service' method seems broken in that way, that it (re-)starts 1024 # the whole uwsgi process. 1025 service uwsgi restart "${CONF%.*}" 1026 ;; 1027 arch-*) 1028 # restart systemd template instance 1029 if uWSGI_app_available "${CONF}"; then 1030 systemctl restart "uwsgi@${CONF%.*}" 1031 else 1032 info_msg "[uWSGI:systemd-template] ${CONF} not installed (no need to restart)" 1033 fi 1034 ;; 1035 fedora-*|centos-7) 1036 # in emperor mode, just touch the file to restart 1037 if uWSGI_app_enabled "${CONF}"; then 1038 touch "${uWSGI_APPS_ENABLED}/${CONF}" 1039 # it seems, there is a polling time in between touch and restart 1040 # of the service. 1041 sleep 3 1042 else 1043 info_msg "[uWSGI:emperor] ${CONF} not installed (no need to restart)" 1044 fi 1045 ;; 1046 *) 1047 err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented" 1048 return 42 1049 ;; 1050 esac 1051} 1052 1053uWSGI_prepare_app() { 1054 1055 # usage: uWSGI_prepare_app <myapp.ini> 1056 1057 [[ -z $1 ]] && die_caller 42 "missing argument <myapp.ini>" 1058 1059 local APP="${1%.*}" 1060 1061 case $DIST_ID-$DIST_VERS in 1062 fedora-*|centos-7) 1063 # in emperor mode, the uwsgi user is the owner of the sockets 1064 info_msg "prepare (uwsgi:uwsgi) /run/uwsgi/app/${APP}" 1065 mkdir -p "/run/uwsgi/app/${APP}" 1066 chown -R "uwsgi:uwsgi" "/run/uwsgi/app/${APP}" 1067 ;; 1068 *) 1069 info_msg "prepare (${SERVICE_USER}:${SERVICE_GROUP}) /run/uwsgi/app/${APP}" 1070 mkdir -p "/run/uwsgi/app/${APP}" 1071 chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "/run/uwsgi/app/${APP}" 1072 ;; 1073 esac 1074} 1075 1076 1077uWSGI_app_available() { 1078 # usage: uWSGI_app_available <myapp.ini> 1079 local CONF="$1" 1080 1081 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1082 [[ -f "${uWSGI_APPS_AVAILABLE}/${CONF}" ]] 1083} 1084 1085uWSGI_install_app() { 1086 1087 # usage: uWSGI_install_app [<template option> ...] <myapp.ini> 1088 # 1089 # <template option>: see install_template 1090 1091 local pos_args=("$0") 1092 1093 for i in "$@"; do 1094 case $i in 1095 -*) template_opts+=("$i");; 1096 *) pos_args+=("$i");; 1097 esac 1098 done 1099 uWSGI_prepare_app "${pos_args[1]}" 1100 mkdir -p "${uWSGI_APPS_AVAILABLE}" 1101 install_template "${template_opts[@]}" \ 1102 "${uWSGI_APPS_AVAILABLE}/${pos_args[1]}" \ 1103 root root 644 1104 uWSGI_enable_app "${pos_args[1]}" 1105 uWSGI_restart "${pos_args[1]}" 1106 info_msg "uWSGI app: ${pos_args[1]} is installed" 1107} 1108 1109uWSGI_remove_app() { 1110 1111 # usage: uWSGI_remove_app <myapp.ini> 1112 1113 local CONF="$1" 1114 1115 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1116 info_msg "remove uWSGI app: ${CONF}" 1117 uWSGI_disable_app "${CONF}" 1118 uWSGI_restart "${CONF}" 1119 rm -f "${uWSGI_APPS_AVAILABLE}/${CONF}" 1120} 1121 1122uWSGI_app_enabled() { 1123 # usage: uWSGI_app_enabled <myapp.ini> 1124 1125 local exit_val=0 1126 local CONF="$1" 1127 1128 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1129 case $DIST_ID-$DIST_VERS in 1130 ubuntu-*|debian-*) 1131 [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]] 1132 exit_val=$? 1133 ;; 1134 arch-*) 1135 systemctl -q is-enabled "uwsgi@${CONF%.*}" 1136 exit_val=$? 1137 ;; 1138 fedora-*|centos-7) 1139 [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]] 1140 exit_val=$? 1141 ;; 1142 *) 1143 # FIXME 1144 err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented" 1145 exit_val=1 1146 ;; 1147 esac 1148 return $exit_val 1149} 1150 1151# shellcheck disable=SC2164 1152uWSGI_enable_app() { 1153 1154 # usage: uWSGI_enable_app <myapp.ini> 1155 1156 local CONF="$1" 1157 1158 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1159 case $DIST_ID-$DIST_VERS in 1160 ubuntu-*|debian-*) 1161 mkdir -p "${uWSGI_APPS_ENABLED}" 1162 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1163 ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}" 1164 info_msg "enabled uWSGI app: ${CONF} (restart required)" 1165 ;; 1166 arch-*) 1167 mkdir -p "${uWSGI_APPS_ENABLED}" 1168 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1169 ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}" 1170 systemctl enable "uwsgi@${CONF%.*}" 1171 info_msg "enabled uWSGI app: ${CONF} (restart required)" 1172 ;; 1173 fedora-*|centos-7) 1174 mkdir -p "${uWSGI_APPS_ENABLED}" 1175 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1176 ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}" 1177 chown "${uWSGI_USER}:${uWSGI_GROUP}" "${uWSGI_APPS_ENABLED}/${CONF}" 1178 info_msg "enabled uWSGI app: ${CONF}" 1179 ;; 1180 *) 1181 # FIXME 1182 err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented" 1183 ;; 1184 esac 1185} 1186 1187uWSGI_disable_app() { 1188 1189 # usage: uWSGI_disable_app <myapp.ini> 1190 1191 local CONF="$1" 1192 1193 [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>" 1194 case $DIST_ID-$DIST_VERS in 1195 ubuntu-*|debian-*) 1196 service uwsgi stop "${CONF%.*}" 1197 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1198 info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)" 1199 ;; 1200 arch-*) 1201 systemctl stop "uwsgi@${CONF%.*}" 1202 systemctl disable "uwsgi@${CONF%.*}" 1203 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1204 ;; 1205 fedora-*|centos-7) 1206 # in emperor mode, just remove the app.ini file 1207 rm -f "${uWSGI_APPS_ENABLED}/${CONF}" 1208 ;; 1209 *) 1210 # FIXME 1211 err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented" 1212 ;; 1213 esac 1214} 1215 1216# distro's package manager 1217# ------------------------ 1218 1219_apt_pkg_info_is_updated=0 1220 1221pkg_install() { 1222 1223 # usage: TITEL='install foobar' pkg_install foopkg barpkg 1224 1225 rst_title "${TITLE:-installation of packages}" section 1226 echo -e "\npackage(s)::\n" 1227 # shellcheck disable=SC2068 1228 echo " " $@ | $FMT 1229 1230 if ! ask_yn "Should packages be installed?" Yn 30; then 1231 return 42 1232 fi 1233 case $DIST_ID in 1234 ubuntu|debian) 1235 if [[ $_apt_pkg_info_is_updated == 0 ]]; then 1236 export _apt_pkg_info_is_updated=1 1237 apt update 1238 fi 1239 # shellcheck disable=SC2068 1240 apt-get install -m -y $@ 1241 ;; 1242 arch) 1243 # shellcheck disable=SC2068 1244 pacman -Sy --noconfirm $@ 1245 ;; 1246 fedora) 1247 # shellcheck disable=SC2068 1248 dnf install -y $@ 1249 ;; 1250 centos) 1251 # shellcheck disable=SC2068 1252 yum install -y $@ 1253 ;; 1254 esac 1255} 1256 1257pkg_remove() { 1258 1259 # usage: TITEL='remove foobar' pkg_remove foopkg barpkg 1260 1261 rst_title "${TITLE:-remove packages}" section 1262 echo -e "\npackage(s)::\n" 1263 # shellcheck disable=SC2068 1264 echo " " $@ | $FMT 1265 1266 if ! ask_yn "Should packages be removed (purge)?" Yn 30; then 1267 return 42 1268 fi 1269 case $DIST_ID in 1270 ubuntu|debian) 1271 # shellcheck disable=SC2068 1272 apt-get purge --autoremove --ignore-missing -y $@ 1273 ;; 1274 arch) 1275 # shellcheck disable=SC2068 1276 pacman -R --noconfirm $@ 1277 ;; 1278 fedora) 1279 # shellcheck disable=SC2068 1280 dnf remove -y $@ 1281 ;; 1282 centos) 1283 # shellcheck disable=SC2068 1284 yum remove -y $@ 1285 ;; 1286 esac 1287} 1288 1289pkg_is_installed() { 1290 1291 # usage: pkg_is_install foopkg || pkg_install foopkg 1292 1293 case $DIST_ID in 1294 ubuntu|debian) 1295 dpkg -l "$1" &> /dev/null 1296 return $? 1297 ;; 1298 arch) 1299 pacman -Qsq "$1" &> /dev/null 1300 return $? 1301 ;; 1302 fedora) 1303 dnf list -q --installed "$1" &> /dev/null 1304 return $? 1305 ;; 1306 centos) 1307 yum list -q --installed "$1" &> /dev/null 1308 return $? 1309 ;; 1310 esac 1311} 1312 1313# git tooling 1314# ----------- 1315 1316# shellcheck disable=SC2164 1317git_clone() { 1318 1319 # usage: 1320 # 1321 # git_clone <url> <name> [<branch> [<user>]] 1322 # git_clone <url> <path> [<branch> [<user>]] 1323 # 1324 # First form uses $CACHE/<name> as destination folder, second form clones 1325 # into <path>. If repository is allready cloned, pull from <branch> and 1326 # update working tree (if needed, the caller has to stash local changes). 1327 # 1328 # git clone https://github.com/searx/searx searx-src origin/master searxlogin 1329 # 1330 1331 local url="$1" 1332 local dest="$2" 1333 local branch="$3" 1334 local user="$4" 1335 local bash_cmd="bash" 1336 local remote="origin" 1337 1338 if [[ ! "${dest:0:1}" = "/" ]]; then 1339 dest="$CACHE/$dest" 1340 fi 1341 1342 [[ -z $branch ]] && branch=master 1343 [[ -z $user ]] && [[ -n "${SUDO_USER}" ]] && user="${SUDO_USER}" 1344 [[ -n $user ]] && bash_cmd="sudo -H -u $user -i" 1345 1346 if [[ -d "${dest}" ]] ; then 1347 info_msg "already cloned: $dest" 1348 tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 | prefix_stdout " ${_Yellow}|$user|${_creset} " 1349cd "${dest}" 1350git checkout -m -B "$branch" --track "$remote/$branch" 1351git pull --all 1352EOF 1353 else 1354 info_msg "clone into: $dest" 1355 tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 | prefix_stdout " ${_Yellow}|$user|${_creset} " 1356mkdir -p "$(dirname "$dest")" 1357cd "$(dirname "$dest")" 1358git clone --branch "$branch" --origin "$remote" "$url" "$(basename "$dest")" 1359EOF 1360 fi 1361} 1362 1363# containers 1364# ---------- 1365 1366in_container() { 1367 # Test if shell runs in a container. 1368 # 1369 # usage: in_container && echo "process running inside a LXC container" 1370 # in_container || echo "process is not running inside a LXC container" 1371 # 1372 # sudo_or_exit 1373 # hint: Reads init process environment, therefore root access is required! 1374 # to be safe, take a look at the environment of process 1 (/sbin/init) 1375 # grep -qa 'container=lxc' /proc/1/environ 1376 1377 # see lxc_init_container_env 1378 [[ -f /.lxcenv ]] 1379} 1380 1381LXC_ENV_FOLDER= 1382if in_container; then 1383 # shellcheck disable=SC2034 1384 LXC_ENV_FOLDER="lxc-env/$(hostname)/" 1385fi 1386 1387lxc_init_container_env() { 1388 1389 # usage: lxc_init_container_env <name> 1390 1391 # Create a /.lxcenv file in the root folder. Call this once after the 1392 # container is inital started and before installing any boilerplate stuff. 1393 1394 info_msg "create /.lxcenv in container $1" 1395 cat <<EOF | lxc exec "${1}" -- bash | prefix_stdout "[${_BBlue}${1}${_creset}] " 1396touch "/.lxcenv" 1397ls -l "/.lxcenv" 1398EOF 1399} 1400 1401# apt packages 1402LXC_BASE_PACKAGES_debian="bash git build-essential python3 python3-venv" 1403 1404# pacman packages 1405LXC_BASE_PACKAGES_arch="bash git base-devel python" 1406 1407# dnf packages 1408LXC_BASE_PACKAGES_fedora="bash git @development-tools python" 1409 1410# yum packages 1411LXC_BASE_PACKAGES_centos="bash git python3" 1412 1413case $DIST_ID in 1414 ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;; 1415 arch) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_arch}" ;; 1416 fedora) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_fedora}" ;; 1417 centos) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_centos}" ;; 1418 *) err_msg "$DIST_ID-$DIST_VERS: pkg_install LXC_BASE_PACKAGES not yet implemented" ;; 1419esac 1420 1421lxc_install_base_packages() { 1422 info_msg "install LXC_BASE_PACKAGES in container $1" 1423 case $DIST_ID in 1424 centos) yum groupinstall "Development Tools" -y ;; 1425 esac 1426 pkg_install "${LXC_BASE_PACKAGES}" 1427} 1428 1429 1430lxc_image_copy() { 1431 1432 # usage: lxc_image_copy <remote image> <local image> 1433 # 1434 # lxc_image_copy "images:ubuntu/20.04" "ubu2004" 1435 1436 if lxc_image_exists "local:${LXC_SUITE[i+1]}"; then 1437 info_msg "image ${LXC_SUITE[i]} already copied --> ${LXC_SUITE[i+1]}" 1438 else 1439 info_msg "copy image locally ${LXC_SUITE[i]} --> ${LXC_SUITE[i+1]}" 1440 lxc image copy "${LXC_SUITE[i]}" local: \ 1441 --alias "${LXC_SUITE[i+1]}" | prefix_stdout 1442 fi 1443} 1444 1445lxc_init_container() { 1446 1447 # usage: lxc_init_container <image name> <container name> 1448 1449 local image_name="$1" 1450 local container_name="$2" 1451 1452 if lxc info "${container_name}" &>/dev/null; then 1453 info_msg "container '${container_name}' already exists" 1454 else 1455 info_msg "create container instance: ${container_name}" 1456 lxc init "local:${image_name}" "${container_name}" 1457 fi 1458} 1459 1460lxc_exists(){ 1461 1462 # usage: lxc_exists <name> || echo "container <name> does not exists" 1463 1464 lxc info "$1" &>/dev/null 1465} 1466 1467lxc_image_exists(){ 1468 # usage: lxc_image_exists <alias> || echo "image <alias> does locally not exists" 1469 1470 lxc image info "local:$1" &>/dev/null 1471 1472} 1473 1474lxc_delete_container() { 1475 1476 # usage: lxc_delete_container <container-name> 1477 1478 if lxc info "$1" &>/dev/null; then 1479 info_msg "stop & delete instance ${_BBlue}${1}${_creset}" 1480 lxc stop "$1" &>/dev/null 1481 lxc delete "$1" | prefix_stdout 1482 else 1483 warn_msg "instance '$1' does not exist / can't delete :o" 1484 fi 1485} 1486 1487lxc_delete_local_image() { 1488 1489 # usage: lxc_delete_local_image <container-name> 1490 1491 info_msg "delete image 'local:$i'" 1492 lxc image delete "local:$i" 1493} 1494 1495 1496# IP 1497# -- 1498 1499global_IPs(){ 1500 # usage: global_IPS 1501 # 1502 # print list of host's SCOPE global addresses and adapters e.g:: 1503 # 1504 # $ global_IPs 1505 # enp4s0|192.168.1.127 1506 # lxdbr0|10.246.86.1 1507 # lxdbr0|fd42:8c58:2cd:b73f::1 1508 1509 ip -o addr show | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\1|\2/p' 1510} 1511 1512primary_ip() { 1513 1514 case $DIST_ID in 1515 arch) 1516 ip -o addr show \ 1517 | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\2/p' \ 1518 | head -n 1 1519 ;; 1520 *) hostname -I | cut -d' ' -f1 ;; 1521 esac 1522} 1523 1524# URL 1525# --- 1526 1527url_replace_hostname(){ 1528 1529 # usage: url_replace_hostname <url> <new hostname> 1530 1531 # to replace hostname by primary IP:: 1532 # 1533 # url_replace_hostname http://searx-ubu1604/morty $(primary_ip) 1534 # http://10.246.86.250/morty 1535 1536 # shellcheck disable=SC2001 1537 echo "$1" | sed "s|\(http[s]*://\)[^/]*\(.*\)|\1$2\2|" 1538} 1539