| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 | #!/usr/bin/env bash# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-# SPDX-License-Identifier: AGPL-3.0-or-later# shellcheck source=utils/lib.shsource "$(dirname "${BASH_SOURCE[0]}")/lib.sh"# shellcheck source=utils/brand.envsource "${REPO_ROOT}/utils/brand.env"# load environment of the LXC suiteLXC_ENV="${LXC_ENV:-${REPO_ROOT}/utils/lxc-searxng.env}"source "$LXC_ENV"lxc_set_suite_env# ----------------------------------------------------------------------------# config# ----------------------------------------------------------------------------## read also:# - https://lxd.readthedocs.io/en/latest/LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-test}"# Location in the container where all folders from HOST are mountedLXC_SHARE_FOLDER="/share"LXC_REPO_ROOT="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")"# shellcheck disable=SC2034ubu2004_boilerplate="export DEBIAN_FRONTEND=noninteractiveapt-get update -yapt-get upgrade -yapt-get install -y git curl wgetecho 'Set disable_coredump false' >> /etc/sudo.conf"# shellcheck disable=SC2034ubu2204_boilerplate="$ubu2004_boilerplate"# shellcheck disable=SC2034archlinux_boilerplate="pacman --noprogressbar -Syu --noconfirmpacman --noprogressbar -S --noconfirm inetutils git curl wget sudoecho 'Set disable_coredump false' >> /etc/sudo.conf"# shellcheck disable=SC2034fedora35_boilerplate="dnf update -ydnf install -y git curl wget hostnameecho 'Set disable_coredump false' >> /etc/sudo.conf"# shellcheck disable=SC2034centos7_boilerplate="yum update -yyum install -y git curl wget hostname sudo whichecho 'Set disable_coredump false' >> /etc/sudo.conf"REMOTE_IMAGES=()CONTAINERS=()LOCAL_IMAGES=()for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do    REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${LXC_SUITE[i]}")    CONTAINERS=("${CONTAINERS[@]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}")    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${LXC_SUITE[i+1]}")doneHOST_USER="${SUDO_USER:-$USER}"HOST_USER_ID=$(id -u "${HOST_USER}")HOST_GROUP_ID=$(id -g "${HOST_USER}")# ----------------------------------------------------------------------------usage() {# ----------------------------------------------------------------------------    _cmd="$(basename "$0")"    cat <<EOFusage::  $_cmd build        [containers|<name>]  $_cmd copy         [images]  $_cmd remove       [containers|<name>|images]  $_cmd [start|stop] [containers|<name>]  $_cmd show         [images|suite|info|config [<name>]]  $_cmd cmd          [--|<name>] '...'  $_cmd install      [suite|base [<name>]]build  :containers:   build, launch all containers and 'install base' packages  :<name>:       build, launch container <name>  and 'install base' packagescopy:  :images:       copy remote images of the suite into local storageremove  :containers:   delete all 'containers' or only <container-name>  :images:       delete local images of the suitestart/stop  :containers:   start/stop all 'containers' from the suite  :<name>:       start/stop container <name> from suiteshow  :info:         show info of all (or <name>) containers from LXC suite  :config:       show config of all (or <name>) containers from the LXC suite  :suite:        show services of all (or <name>) containers from the LXC suite  :images:       show information of local imagescmd  use single quotes to evaluate in container's bash, e.g.: 'echo \$(hostname)'  --             run command '...' in all containers of the LXC suite  :<name>:       run command '...' in container <name>install  :base:         prepare LXC; install basic packages  :suite:        install LXC ${LXC_SUITE_NAME} suite into all (or <name>) containersEOF    usage_containers    [ -n "${1+x}" ] &&  err_msg "$1"}usage_containers() {    lxc_suite_install_info    [ -n "${1+x}" ] &&  err_msg "$1"}lxd_info() {    cat <<EOFLXD is needed, to install run::  snap install lxd  lxd init --autoEOF}main() {    local exit_val    local _usage="unknown or missing $1 command $2"    # don't check prerequisite when in recursion    if [[ ! $1 == __* ]] && [[ ! $1 == --help  ]]; then        if ! in_container; then            ! required_commands lxc && lxd_info && exit 42        fi        [[ -z $LXC_SUITE ]] && err_msg "missing LXC_SUITE" && exit 42    fi    case $1 in        --getenv)  var="$2"; echo "${!var}"; exit 0;;        -h|--help) usage; exit 0;;        build)            sudo_or_exit            case $2 in                ${LXC_HOST_PREFIX}-*) build_container "$2" ;;                ''|--|containers) build_all_containers ;;                *) usage "$_usage"; exit 42;;            esac            ;;        copy)            case $2 in                ''|images) lxc_copy_images_localy;;                *) usage "$_usage"; exit 42;;            esac            ;;        remove)            sudo_or_exit            case $2 in                ''|--|containers) remove_containers ;;                images) lxc_delete_images_localy ;;                ${LXC_HOST_PREFIX}-*)                    ! lxc_exists "$2" && warn_msg "container not yet exists: $2" && exit 0                    if ask_yn "Do you really want to delete container $2"; then                        lxc_delete_container "$2"                    fi                    ;;                *) usage "unknown or missing container <name> $2"; exit 42;;            esac            ;;        start|stop)            sudo_or_exit            case $2 in                ''|--|containers)  lxc_cmd "$1" ;;                ${LXC_HOST_PREFIX}-*)                    ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42                    info_msg "lxc $1 $2"                    lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "                    ;;                *) usage "unknown or missing container <name> $2"; exit 42;;            esac            ;;        show)            sudo_or_exit            case $2 in                suite)                    case $3 in                        ${LXC_HOST_PREFIX}-*)                            lxc exec -t "$3" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \                                | prefix_stdout "[${_BBlue}$3${_creset}]  "                        ;;                        *) show_suite;;                    esac                    ;;                images) show_images ;;                config)                    case $3 in                        ${LXC_HOST_PREFIX}-*)                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42                            lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "                        ;;                        *)                            rst_title "container configurations"                            echo                            lxc list "$LXC_HOST_PREFIX-"                            echo                            lxc_cmd config show                            ;;                    esac                    ;;                info)                    case $3 in                        ${LXC_HOST_PREFIX}-*)                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42                            lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "                            ;;                        *)                            rst_title "container info"                            echo                            lxc_cmd info                            ;;                    esac                    ;;                *) usage "$_usage"; exit 42;;            esac            ;;        __show)            # wrapped show commands, called once in each container            case $2 in                suite) lxc_suite_info ;;            esac            ;;        cmd)            sudo_or_exit            shift            case $1 in                --) shift; lxc_exec "$@" ;;                ${LXC_HOST_PREFIX}-*)                    ! lxc_exists "$1" && usage_containers "unknown container: $1" && exit 42                    local name=$1                    shift                    lxc_exec_cmd "${name}" "$@"                    ;;                *) usage_containers "unknown container: $1" && exit 42           esac            ;;        install)            sudo_or_exit            case $2 in                suite|base)                    case $3 in                        ${LXC_HOST_PREFIX}-*)                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42                            lxc_exec_cmd "$3" "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"                            ;;                        ''|--) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2" ;;                        *) usage_containers "unknown container: $3" && exit 42                    esac                    ;;                *) usage "$_usage"; exit 42 ;;            esac            ;;        __install)            # wrapped install commands, called once in each container            # shellcheck disable=SC2119            case $2 in                suite) lxc_suite_install ;;                base) FORCE_TIMEOUT=0 lxc_install_base_packages ;;            esac            ;;        doc)            echo            echo ".. generic utils/lxc.sh documentation"            ;;        -*) usage "unknown option $1"; exit 42;;        *)  usage "unknown or missing command $1"; exit 42;;    esac}build_all_containers() {    rst_title "Build all LXC containers of suite"    echo    usage_containers    lxc_copy_images_localy    lxc_init_all_containers    lxc_config_all_containers    lxc_boilerplate_all_containers    rst_title "install LXC base packages" section    echo    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install base    echo    lxc list "$LXC_HOST_PREFIX"}build_container() {    rst_title "Build container $1"    local remote_image    local container    local image    local boilerplate_script    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do        if [ "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}" = "$1" ]; then            remote_image="${LXC_SUITE[i]}"            container="${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"            image="${LXC_SUITE[i+1]}"            boilerplate_script="${image}_boilerplate"            boilerplate_script="${!boilerplate_script}"            break        fi    done    echo    if [ -z "$container" ]; then        err_msg "container $1 unknown"        usage_containers        return 42    fi    lxc_image_copy "${remote_image}" "${image}"    rst_title "init container" section    lxc_init_container "${image}" "${container}"    rst_title "configure container" section    lxc_config_container "${container}"    rst_title "run LXC boilerplate scripts" section    lxc_install_boilerplate "${container}" "$boilerplate_script"    echo    rst_title "install LXC base packages" section    lxc_exec_cmd "${container}" "${LXC_REPO_ROOT}/utils/lxc.sh" __install base \        | prefix_stdout "[${_BBlue}${container}${_creset}] "    echo    lxc list "$container"}remove_containers() {    rst_title "Remove all LXC containers of suite"    rst_para "existing containers matching ${_BGreen}$LXC_HOST_PREFIX-*${_creset}"    echo    lxc list "$LXC_HOST_PREFIX-"    echo -en "\\n${_BRed}LXC containers to delete::${_creset}\\n\\n  ${CONTAINERS[*]}\\n" | $FMT    local default=Ny    [[ $FORCE_TIMEOUT = 0 ]] && default=Yn    if ask_yn "Do you really want to delete these containers" $default; then        for i in "${CONTAINERS[@]}"; do            lxc_delete_container "$i"        done    fi    echo    lxc list "$LXC_HOST_PREFIX-"}# images# ------lxc_copy_images_localy() {    rst_title "copy images" section    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do        lxc_image_copy "${LXC_SUITE[i]}" "${LXC_SUITE[i+1]}"    done    # lxc image list local: && wait_key}lxc_delete_images_localy() {    rst_title "Delete LXC images"    rst_para "local existing images"    echo    lxc image list local:    echo -en "\\n${_BRed}LXC images to delete::${_creset}\\n\\n  ${LOCAL_IMAGES[*]}\\n"    if ask_yn "Do you really want to delete these images"; then        for i in "${LOCAL_IMAGES[@]}"; do            lxc_delete_local_image "$i"        done    fi    for i in $(lxc image list --format csv | grep '^,' | sed 's/,\([^,]*\).*$/\1/'); do        if ask_yn "Image $i has no alias, do you want to delete the image?" Yn; then            lxc_delete_local_image "$i"        fi    done    echo    lxc image list local:}show_images(){    rst_title "local images"    echo    lxc image list local:    echo -en "\\n${_Green}LXC suite images::${_creset}\\n\\n  ${LOCAL_IMAGES[*]}\\n"    wait_key    for i in "${LOCAL_IMAGES[@]}"; do        if lxc_image_exists "$i"; then            info_msg "lxc image info ${_BBlue}${i}${_creset}"            lxc image info "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "        else            warn_msg "image ${_BBlue}$i${_creset} does not yet exists"        fi    done}# container# ---------show_suite(){    rst_title "LXC suite ($LXC_HOST_PREFIX-*)"    echo    lxc list "$LXC_HOST_PREFIX-"    echo    for i in "${CONTAINERS[@]}"; do        if ! lxc_exists "$i"; then            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"        else            lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \                | prefix_stdout "[${_BBlue}${i}${_creset}]  "            echo        fi    done}lxc_cmd() {    for i in "${CONTAINERS[@]}"; do        if ! lxc_exists "$i"; then            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"        else            info_msg "lxc $* $i"            lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "        fi    done}lxc_exec_cmd() {    local name="$1"    shift    exit_val=    info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"    lxc exec -t --cwd "${LXC_REPO_ROOT}" "${name}" -- bash -c "$*"    exit_val=$?    if [[ $exit_val -ne 0 ]]; then        warn_msg "[${_BBlue}${name}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"    else        info_msg "[${_BBlue}${name}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"    fi}lxc_exec() {    for i in "${CONTAINERS[@]}"; do        if ! lxc_exists "$i"; then            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"        else            lxc_exec_cmd "${i}" "$@" | prefix_stdout "[${_BBlue}${i}${_creset}] "        fi    done}lxc_init_all_containers() {    rst_title "init all containers" section    local image_name    local container_name    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do        lxc_init_container "${LXC_SUITE[i+1]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"    done}lxc_config_all_containers() {    rst_title "configure all containers" section    for i in "${CONTAINERS[@]}"; do        lxc_config_container "${i}"    done}lxc_config_container() {    info_msg "[${_BBlue}$1${_creset}] configure container ..."    info_msg "[${_BBlue}$1${_creset}] map uid/gid from host to container"    # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps    echo -e -n "uid $HOST_USER_ID 0\\ngid $HOST_GROUP_ID 0"\        | lxc config set "$1" raw.idmap -    info_msg "[${_BBlue}$1${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"    # https://lxd.readthedocs.io/en/latest/instances/#type-disk    lxc config device add "$1" repo_share disk \        source="${REPO_ROOT}" \        path="${LXC_REPO_ROOT}" &>/dev/null    # lxc config show "$1" && wait_key}lxc_boilerplate_all_containers() {    rst_title "run LXC boilerplate scripts" section    local boilerplate_script    local image_name    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do        image_name="${LXC_SUITE[i+1]}"        boilerplate_script="${image_name}_boilerplate"        boilerplate_script="${!boilerplate_script}"        lxc_install_boilerplate "${LXC_HOST_PREFIX}-${image_name}" "$boilerplate_script"        if [[ -z "${boilerplate_script}" ]]; then            err_msg "[${_BBlue}${container_name}${_creset}] no boilerplate for image '${image_name}'"        fi    done}lxc_install_boilerplate() {    # usage:  lxc_install_boilerplate <container-name> <string: shell commands ..>    #    # usage:  lxc_install_boilerplate searx-archlinux "${archlinux_boilerplate}"    local container_name="$1"    local boilerplate_script="$2"    info_msg "[${_BBlue}${container_name}${_creset}] init .."    if lxc start -q "${container_name}" &>/dev/null; then        sleep 5 # guest needs some time to come up and get an IP    fi    if ! check_connectivity "${container_name}"; then        die 42 "Container ${container_name} has no internet connectivity!"    fi    lxc_init_container_env "${container_name}"    info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."    cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "rm -f "/.lxcenv.mk"ln -s "${LXC_REPO_ROOT}/utils/makefile.lxc" "/.lxcenv.mk"ls -l "/.lxcenv.mk"EOF    info_msg "[${_BBlue}${container_name}${_creset}] run LXC boilerplate scripts .."    if lxc start -q "${container_name}" &>/dev/null; then        sleep 5 # guest needs some time to come up and get an IP    fi    if [[ -n "${boilerplate_script}" ]]; then        echo "${boilerplate_script}" \            | lxc exec "${container_name}" -- bash \            | prefix_stdout "[${_BBlue}${container_name}${_creset}] "    fi}check_connectivity() {    local ret_val=0    info_msg "check internet connectivity ..."    if ! lxc exec "${1}" -- ping -c 1 8.8.8.8 &>/dev/null; then        ret_val=1        err_msg "no internet connectivity!"        info_msg "Most often the connectivity is blocked by a docker installation:"        info_msg "Whenever docker is started (reboot) it sets the iptables policy "        info_msg "for the FORWARD chain to DROP, see:"        info_msg "    https://docs.searxng.org/utils/lxc.sh.html#internet-connectivity-docker"        iptables-save | grep ":FORWARD"    fi    return $ret_val}# ----------------------------------------------------------------------------main "$@"# ----------------------------------------------------------------------------
 |