provisioning tool for building opinionated architecture
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

716 lines
22 KiB

9 months ago
9 months ago
9 months ago
7 months ago
9 months ago
7 months ago
9 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
7 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
  1. #!/bin/bash
  2. RED='\e[0;41m\e[1;37m'
  3. GREEN='\033[0;32m'
  4. YELLOW='\033[0;33m'
  5. PURPLE='\033[0;35m'
  6. DARK='\e[100m'
  7. NC='\033[0m' # No Color
  8. # BOLD='\033[1m'
  9. # DIM='\e[2m\e[0;90m'
  10. TO_BE_DEFINED="TO BE DEFINED"
  11. FDN_DOMAINNAME=fdn.fr
  12. FDN_NAMESERVER="ns0.$FDN_DOMAINNAME"
  13. FDN_RESOLVER=80.67.169.12
  14. : $FDN_DOMAINNAME $FDN_NAMESERVER $FDN_RESOLVER
  15. function echo() {
  16. [[ -n ${PREFIX:-} ]] && printf "${DARK}%25.25s${NC} " "${PREFIX}"
  17. builtin echo "$@"
  18. }
  19. function check_normal_user() {
  20. [[ $(id -u) -lt 1000 ]] && echoerr "normal user (>1000) expected, please connect as a normal user then call again!" && exit 100
  21. return 0
  22. }
  23. function sudo_required() {
  24. check_normal_user
  25. command -v sudo &>/dev/null &&
  26. id -G | grep -q sudo && echoerr "command <sudo> not found, please install as so: \`apt install -y sudo\`" && exit 1
  27. if ! sudo -n true &>/dev/null; then
  28. if [[ -n "${1:-}" ]]; then
  29. echowarnn "[sudo] requiring authorized access for: [ $1 ]"
  30. else
  31. echowarnn "[sudo] requiring authorized access for further processing"
  32. fi
  33. fi
  34. sudo -vp ' : '
  35. }
  36. # idempotent cargo install <package1 package2 ...>
  37. function idem_cargo_install() {
  38. for i in "$@"; do
  39. if [ ! -f ~/.cargo/bin/"$i" ]; then
  40. cargo install "$i"
  41. fi
  42. done
  43. }
  44. # display error in red
  45. function echoerr() {
  46. echo -e "${RED}$*${NC}" >&2
  47. }
  48. function echoerrn() {
  49. echo -en "${RED}$*${NC}" >&2
  50. }
  51. # display warn in yellow
  52. function echowarn() {
  53. echo -e "${YELLOW}$*${NC}" >&2
  54. }
  55. function echowarnn() {
  56. echo -en "${YELLOW}$*${NC}" >&2
  57. }
  58. # display error in green
  59. function echoinfo() {
  60. echo -e "${GREEN}$*${NC}" >&2
  61. }
  62. function echoinfon() {
  63. echo -en "${GREEN}$*${NC}" >&2
  64. }
  65. # test whether <ip> is a valid ipv4 address?
  66. function valid_ipv4() {
  67. local ip="$1"
  68. if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
  69. IFS='.' read -ra ADDR <<<"$ip"
  70. [[ ${ADDR[0]} -le 255 && ${ADDR[1]} -le 255 && ${ADDR[2]} -le 255 && ${ADDR[3]} -le 255 ]]
  71. return $?
  72. fi
  73. return 1
  74. }
  75. function enable_trace() {
  76. trap 'trap_error $? ${LINENO:-0} ${BASH_LINENO:-0} ${BASH_COMMAND:-empty} $(printf "::%s" ${FUNCNAME[@]})' ERR
  77. }
  78. function disable_trace() {
  79. trap - ERR
  80. }
  81. function disable_all_signals {
  82. trap - ERR
  83. trap - HUP
  84. trap - INT
  85. trap - TERM
  86. }
  87. function prepare_nftables() {
  88. local PREFIX="miaou:nftables"
  89. if ! [[ -f /etc/nftables.rules.d/firewall.table ]]; then
  90. echo "installing nftables ..."
  91. sudo apt install -y nftables
  92. sudo cp -f "$MIAOU_BASEDIR/templates/hardened/nftables.conf" /etc/
  93. sudo mkdir -p /etc/nftables.rules.d
  94. sudo systemctl enable nftables
  95. else
  96. echo "nftables already installed!"
  97. fi
  98. current_target="${TARGET:-not_defined_yet}"
  99. desktop=false
  100. samba=false
  101. transmission_daemon=false
  102. if [[ $current_target == not_defined_yet ]]; then
  103. echo -n "generating new firewall table first time... "
  104. else
  105. if [[ $current_target == 'dev' ]]; then
  106. if exist_command 'xprop'; then
  107. desktop=true
  108. fi
  109. if exist_command 'smbstatus'; then
  110. samba=true
  111. fi
  112. if exist_command 'transmission-daemon'; then
  113. transmission_daemon=true
  114. fi
  115. echo -n "generating new firewall table according to target=<${current_target}>, desktop=<$desktop>, samba=<$samba>, transmission_daemon=<$transmission_daemon> ..."
  116. else
  117. echo -n "generating new firewall table according to target=<${current_target}>..."
  118. fi
  119. fi
  120. sudo env target="$current_target" desktop="$desktop" samba="$samba" transmission_daemon="$transmission_daemon" tera -e --env-only --env-key env -t "$MIAOU_BASEDIR/templates/nftables/firewall.table.j2" -o /etc/nftables.rules.d/firewall.table &>/dev/null
  121. sudo systemctl reload nftables
  122. PREFIX="" echoinfo "DONE"
  123. }
  124. function miaou_init() {
  125. # shellcheck source=/dev/null
  126. [[ -f /opt/miaou-bash/lib/functions.sh ]] && source /opt/miaou-bash/lib/functions.sh
  127. # shellcheck source=/dev/null
  128. . "$MIAOU_BASEDIR/lib/functions.sh"
  129. export MIAOU_CONFIGDIR="$HOME/.config/miaou"
  130. set -Eeuo pipefail
  131. enable_trace
  132. trap 'ctrl_c $? ${LINENO:-0} ${BASH_LINENO:-0} ${BASH_COMMAND:-empty} $(printf "::%s" ${FUNCNAME[@]})' INT
  133. }
  134. function ctrl_c() {
  135. PREFIX="miaou:trap" echoerr "Ctrl + C happened, exiting!!! $*"
  136. exit 125
  137. }
  138. # extract source code error triggered on trap error <error_code> <error_line>
  139. function trap_error() {
  140. ERRORS_COUNT=0
  141. if [[ -f "$MIAOU_CONFIGDIR"/error_count ]]; then
  142. ERRORS_COUNT=$(cat "$MIAOU_CONFIGDIR"/error_count)
  143. else
  144. mkdir -p "$MIAOU_CONFIGDIR"
  145. printf 0 >"$MIAOU_CONFIGDIR"/error_count
  146. fi
  147. ERRORS_COUNT=$((ERRORS_COUNT + 1))
  148. printf '%s' $ERRORS_COUNT >"$MIAOU_CONFIGDIR"/error_count
  149. local PREFIX=""
  150. # local file="${0:-}"
  151. local err=$1 # error status
  152. local line=$2 # LINENO
  153. # local linecallfunc=${3:-}
  154. local command="${4:-}"
  155. local funcstack="${5:-}"
  156. local caller
  157. caller=$(caller | cut -d' ' -f2)
  158. # echo >&2
  159. # if [ "$funcstack" != "::" ]; then
  160. # echo -e "${RED}ERROR <$err>, due to command <$command> at line $line from <$caller>, stack=${funcstack}${NC}" >&2
  161. # else
  162. # echo >&2 "ERROR DETECTED"
  163. # fi
  164. # echo
  165. # echo -e "${PURPLE}$caller:$line ${NC}EXIT ${RED}<$err>${NC}" >&2
  166. # echo -e "${PURPLE}------------------------------------------ ${NC}" >&2
  167. if [[ $ERRORS_COUNT == 1 ]]; then
  168. echo
  169. echo -e "${RED}ERROR <$err>, due to command <$command $funcstack>${NC}" >&2
  170. fi
  171. echo -e "${PURPLE}$ERRORS_COUNT: $caller:$line ${RED}$command $funcstack${NC}" >&2
  172. # echo -e "${PURPLE}----------------------------- ${PURPLE}EXIT CODE ${PURPLE}--------------${PURPLE} $err ${NC}" >&2
  173. # if [[ $line -gt 2 ]]; then
  174. # sed "$((line - 2))q;d" "$caller" >&2
  175. # sed "$((line - 1))q;d" "$caller" >&2
  176. # fi
  177. # echo -ne "${BOLD}" >&2
  178. # sed "${line}q;d" "$caller" >&2
  179. # echo -e "${PURPLE}------------------------------------------ ${NC}" >&2
  180. }
  181. # exist_command(cmd1, ...)
  182. # test all commands exist, else fail
  183. function exist_command() {
  184. for i in "$@"; do
  185. command -v "$i" >/dev/null || return 50
  186. done
  187. }
  188. # test whether container <ct> is up and running?
  189. function container_running() {
  190. arg1_required "$@"
  191. container_exists "$1" && lxc list "$1" -c ns -f csv | head -n1 | grep -q "$1,RUNNING"
  192. lxc exec "$1" -- bash <<EOF
  193. set -Eeuo pipefail
  194. if [[ ! -f /root/cloud-status.json ]]; then
  195. cloud-init status --wait >/dev/null
  196. fi
  197. EOF
  198. }
  199. # test arg1 required
  200. function arg1_required() {
  201. [[ -z "${1:-}" ]] && echoerr "ERROR: arg#1 expected!" && return 125
  202. return 0
  203. }
  204. # test arg2 required
  205. function arg2_required() {
  206. [[ -z "${2:-}" ]] && echoerr "ERROR: arg#2 expected!" && return 125
  207. return 0
  208. }
  209. # test arg3 required
  210. function arg3_required() {
  211. [[ -z "${3:-}" ]] && echoerr "ERROR: arg#3 expected!" && return 125 || return 0
  212. }
  213. # test arg4 required
  214. function arg4_required() {
  215. [[ -z "${4:-}" ]] && echoerr "ERROR: arg#4 expected!" && return 125 || return 0
  216. }
  217. # test whether container <ct> exists yet?
  218. function container_exists() {
  219. arg1_required "$@"
  220. lxc list "$1" -c n -f csv | grep -q "^$1\$"
  221. }
  222. # 3 args expected: <commmand> <delay in s, example: 0.2> <max_attempts>
  223. function wait_for_command {
  224. arg3_required "$@"
  225. command=$1
  226. delay=$2
  227. max_attempt=$3
  228. attempt=0
  229. while ! eval "$command"; do
  230. attempt=$((attempt + 1))
  231. if [[ $attempt -gt $max_attempt ]]; then
  232. echoerr "command <$command> failed after a delay of $(bc <<<"$max_attempt * $delay")s and $max_attempt attempts"
  233. return 1
  234. else
  235. sleep "$delay"
  236. fi
  237. done
  238. echo SUCCESS
  239. }
  240. function wait_for_container_full_initialization {
  241. arg1_required "$@"
  242. wait_for_command "lxc exec $1 -- test -f /root/cloud-status.json" 0.2 40
  243. }
  244. # build debian image with prebuild miaou-bash and various useful settings
  245. # ARG1=release [bullseye, buster]
  246. function build_miaou_image() {
  247. local RELEASE="$1"
  248. local IMAGE_LABEL="$RELEASE-miaou"
  249. local PREFIX="miaou:image"
  250. local DEB_REPOSITORY
  251. DEB_REPOSITORY=$(grep ^deb /etc/apt/sources.list | head -n1 | cut -d ' ' -f2 | cut -d '/' -f3)
  252. if ! lxc image -cl list -f csv | grep -q "$IMAGE_LABEL"; then
  253. echo "building lxc image <$IMAGE_LABEL> ... "
  254. echo "image will reuse same local repository <$DEB_REPOSITORY>"
  255. creation_date=$(date +%s)
  256. sudo /opt/miaou-bash/tools/idem_apt_install debootstrap
  257. cat <<EOF1 | sudo bash
  258. set -euo pipefail
  259. rm -rf /tmp/$IMAGE_LABEL{,-image}
  260. mkdir -p /tmp/$IMAGE_LABEL{,-image}
  261. debootstrap $RELEASE /tmp/$IMAGE_LABEL http://$DEB_REPOSITORY/debian
  262. echo
  263. echo "DEBOOTSTRAP ... OK"
  264. echo
  265. cat <<EOF2 | chroot /tmp/$IMAGE_LABEL
  266. set -euo pipefail
  267. echo "image prepare source.list from $DEB_REPOSITORY"
  268. if [[ "$RELEASE" == "buster" ]]; then
  269. cat <<EOF3 >/etc/apt/sources.list
  270. deb http://$DEB_REPOSITORY/debian $RELEASE main contrib
  271. deb http://$DEB_REPOSITORY/debian $RELEASE-updates main contrib
  272. deb http://$DEB_REPOSITORY/debian-security/ $RELEASE/updates main contrib
  273. EOF3
  274. else
  275. cat <<EOF3 >/etc/apt/sources.list
  276. deb http://$DEB_REPOSITORY/debian $RELEASE main contrib
  277. deb http://$DEB_REPOSITORY/debian $RELEASE-updates main contrib
  278. deb http://$DEB_REPOSITORY/debian-security/ $RELEASE-security main contrib
  279. EOF3
  280. fi
  281. echo APT UPDATE
  282. apt update && apt dist-upgrade -y
  283. apt install -y curl wget file git sudo bash-completion
  284. curl https://git.artcode.re/miaou/miaou-bash/raw/branch/main/install.sh | sudo bash -s -- --host
  285. # TODO: remove line below
  286. # ln -sf /usr/share/zoneinfo/Indian/Reunion /etc/localtime
  287. cat <<EOF3 >/etc/network/interfaces
  288. # This file describes the network interfaces available on your system
  289. # and how to activate them. For more information, see interfaces(5).
  290. # The loopback network interface
  291. auto lo
  292. iface lo inet loopback
  293. auto eth0
  294. iface eth0 inet dhcp
  295. source /etc/network/interfaces.d/*
  296. EOF3
  297. echo "deboostrap ready!"
  298. EOF2
  299. cd /tmp/$IMAGE_LABEL-image
  300. tar -czf rootfs.tar.gz -C /tmp/$IMAGE_LABEL .
  301. cat <<EOF2 >metadata.yaml
  302. architecture: "x86_64"
  303. creation_date: $creation_date
  304. properties:
  305. architecture: "x86_64"
  306. description: "Debian $RELEASE for miaou instances"
  307. os: "debian"
  308. release: "$RELEASE"
  309. EOF2
  310. tar -czf metadata.tar.gz metadata.yaml
  311. EOF1
  312. lxc image import "/tmp/$IMAGE_LABEL-image/metadata.tar.gz" "/tmp/$IMAGE_LABEL-image/rootfs.tar.gz" --alias "$IMAGE_LABEL"
  313. echo "image <$IMAGE_LABEL> successfully built!"
  314. echo DONE
  315. else
  316. echo "image <$IMAGE_LABEL> already built!"
  317. fi
  318. }
  319. # convert array to string according to IFS arg1
  320. # example: join "," "${MY_ARRAY[@]}" => one,two,three
  321. function join() {
  322. local IFS="$1"
  323. shift
  324. builtin echo "$*"
  325. }
  326. # execute remote scripting onto one LXC container <CONTAINER> [COMMANDS, ...]
  327. # may use one command like: `lxc_exec ct1 uname -a`
  328. # or pipe like so: `
  329. # cat <<EOF | lxc_exec ct1
  330. # ls -l
  331. # uname -a
  332. # echo [\$0] [\$1] [\$2] # toto titi tata
  333. # EOF
  334. # `
  335. function lxc_exec() {
  336. arg1_required "$@"
  337. container="$1"
  338. shift
  339. declare -a ARGUMENTS
  340. ARGUMENTS=(toto titi tata) # might be overriden with interesting stuff!
  341. if ((${#} == 0)); then
  342. multiline=""
  343. while read -r line; do
  344. if [[ ! "$line" =~ ^\# ]] && [[ ! "$line" =~ ^[[:space:]]*$ ]]; then
  345. if [[ "$line" =~ .*\;$ ]] || [[ "$line" =~ do$ ]] || [[ "$line" =~ then$ ]] || [[ "$line" =~ else$ ]]; then
  346. multiline+="${line} " # append space in case of ending with either '; do then else'
  347. else
  348. multiline+="${line};" # append ; for multiple commands
  349. fi
  350. fi
  351. done
  352. # echo "DEBUG: multiline = [$multiline]"
  353. # echo DEBUG: lxc exec "$container" -- bash -lc "$multiline" "${ARGUMENTS[@]}"
  354. lxc exec "$container" -- bash -lc "$multiline" "${ARGUMENTS[@]}"
  355. else
  356. lxc exec "$container" -- bash -lc "$*" "${ARGUMENTS[@]}"
  357. fi
  358. }
  359. # check container exist and running
  360. function check_container() {
  361. arg1_required "$@"
  362. local CT="$1"
  363. container_exists "$CT"
  364. container_running "$CT"
  365. }
  366. function launch_container() {
  367. arg1_required "$@"
  368. local ct="$1"
  369. if ! container_exists "$ct"; then
  370. echo "container <$ct> about to be created ..."
  371. local extra_release="${2:-}"
  372. if [[ -n "$extra_release" ]] && ! lxc image info "${extra_release}-miaou" >/dev/null; then
  373. echoerrn "unknown extra_release <${extra_release}-miaou>!\nHINT : please add it into /etc/miaou/defaults.yaml, then re-install miaou!"
  374. exit 128
  375. fi
  376. if [[ -n "$extra_release" ]]; then
  377. echoerrn "FIXME: lxc-miaou-create -o release=bookworm should be implemented ...."
  378. lxc-miaou-create "$ct" "$extra_release"
  379. else
  380. lxc-miaou-create "$ct"
  381. fi
  382. echo "DONE"
  383. fi
  384. if ! container_running "$ct"; then
  385. echowarn "container <$ct> seems to be asleep, starting ..."
  386. lxc start "$ct"
  387. echowarn DONE
  388. fi
  389. }
  390. function load_yaml_from_expanded {
  391. arg1_required "$@"
  392. yaml_key="$1"
  393. yaml_file="$MIAOU_CONFIGDIR/miaou.expanded.yaml"
  394. yaml_value=$(yq ".$yaml_key" "$yaml_file")
  395. if [[ -n "$yaml_value" ]] && [[ "$yaml_value" != "null" ]] && [[ "$yaml_value" != "$TO_BE_DEFINED" ]]; then
  396. PREFIX="" echo "$yaml_value"
  397. else
  398. echoerr "undefined value for key: <$yaml_key> from file: <$yaml_file>"
  399. return 98
  400. fi
  401. }
  402. function check_yaml_defined_value {
  403. yaml_file="$1"
  404. yaml_key="$2"
  405. yaml_value=$(yq ".$yaml_key" "$yaml_file")
  406. if [[ -n "$yaml_value" ]] && [[ "$yaml_value" != "null" ]] && [[ "$yaml_value" != "$TO_BE_DEFINED" ]]; then
  407. return 0
  408. else
  409. echoerr "undefined value for key: <$yaml_key> from file: <$yaml_file>"
  410. return 99
  411. fi
  412. }
  413. # halt unless current user is root
  414. function root_required() {
  415. [[ $(id -u) == 0 ]] || (echoerr "root required" && return 1)
  416. }
  417. # arg#1: environment variable
  418. # read from environment or ask entry before exporting new variable
  419. function env_or_ask {
  420. if [[ -n ${1+x} ]]; then
  421. if printenv "$1" >/dev/null; then
  422. echo "value defined as $(printenv "$1")"
  423. else
  424. printf "Please define %20s: " "$1"
  425. read -r
  426. export "$1=\"$REPLY\"" >/dev/null
  427. fi
  428. else
  429. echoerr "env_or_ask requires one argument: <VARIABLE_NAME>" && exit 5
  430. fi
  431. }
  432. # grab and install related project
  433. function install_miaou_bash() {
  434. local PREFIX="miaou-bash:install"
  435. if [[ ! -d /opt/miaou-bash ]]; then
  436. echo "installing curl wget commands ..."
  437. sudo apt install -y curl wget
  438. echo "installing miaou-bash..."
  439. curl https://git.artcode.re/miaou/miaou-bash/raw/branch/main/install.sh | sudo bash -s
  440. echo "OK"
  441. else
  442. echo "addon <miaou-bash> already installed!"
  443. fi
  444. # shellcheck source=/dev/null
  445. source /etc/bash.bashrc
  446. sudo /opt/miaou-bash/tools/idem_apt_install bash-completion
  447. }
  448. function add_toolbox_sudoers {
  449. local PREFIX="toolbox:sudoers"
  450. echo -n "creating sudoers file to allow sudo as command from /TOOLBOX... "
  451. sudo mkdir -p /etc/sudoers.d
  452. if [[ ! -f /etc/sudoers.d/add_TOOLBOX_to_PATH ]]; then
  453. sudo tee /etc/sudoers.d/add_TOOLBOX_to_PATH &>/dev/null <<EOF
  454. Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/TOOLBOX"
  455. EOF
  456. PREFIX="" echo "updated!"
  457. else
  458. PREFIX="" echo "already done!"
  459. fi
  460. }
  461. function prepare_toolbox() {
  462. local PREFIX="toolbox:prepare"
  463. sudo mkdir -p /TOOLBOX
  464. if ! command -v cargo &>/dev/null; then
  465. echo -n "installing <cargo> ... "
  466. curl -sSf https://sh.rustup.rs | sh -s -- -y
  467. # shellcheck source=/dev/null
  468. source "$HOME/.cargo/env"
  469. /opt/miaou-bash/tools/append_or_replace "^PATH=\$PATH:\$HOME/\\.cargo/bin" "PATH=\$PATH:\$HOME/.cargo/bin" ~/.bashrc
  470. PREFIX="" echo "OK"
  471. else
  472. echo "command <cargo> already installed!"
  473. fi
  474. echo -n "installing <fd> ... "
  475. if [ ! -f "/TOOLBOX/fd" ]; then
  476. idem_cargo_install fd-find
  477. sudo cp "$HOME"/.cargo/bin/fd /TOOLBOX/fd
  478. PREFIX="" echo "successfully installed!"
  479. else
  480. PREFIX="" echo "already done!"
  481. fi
  482. echo -n "installing <viu> ... "
  483. if [ ! -f "/TOOLBOX/viu" ]; then
  484. idem_cargo_install viu
  485. sudo cp "$HOME"/.cargo/bin/viu /TOOLBOX/
  486. PREFIX="" echo "successfully installed!"
  487. else
  488. PREFIX="" echo "already done!"
  489. fi
  490. echo -n "installing <rg> alias <ripgrep> ... "
  491. if [ ! -f "/TOOLBOX/rg" ]; then
  492. sudo /opt/miaou-bash/tools/idem_apt_install ripgrep
  493. sudo ln /usr/bin/rg /TOOLBOX/
  494. PREFIX="" echo "successfully installed"
  495. else
  496. PREFIX="" echo "already done!"
  497. fi
  498. echo -n "installing <ag> alias <silversearcher-ag> ... "
  499. if [ ! -f "/TOOLBOX/ag" ]; then
  500. sudo /opt/miaou-bash/tools/idem_apt_install silversearcher-ag
  501. sudo ln /usr/bin/ag /TOOLBOX/
  502. PREFIX="" echo "successfully installed"
  503. else
  504. PREFIX="" echo "already done!"
  505. fi
  506. echo -n "installing <bandwhich> ... "
  507. if [ ! -f "/TOOLBOX/bandwhich" ]; then
  508. idem_cargo_install bandwhich
  509. sudo cp "$HOME"/.cargo/bin/bandwhich /TOOLBOX/bandwhich
  510. PREFIX="" echo "successfully installed"
  511. else
  512. PREFIX="" echo "already done!"
  513. fi
  514. echo -n "installing <btm> alias <bottom> ... "
  515. if [ ! -f "/TOOLBOX/btm" ]; then
  516. VERSION=$(wget_semver github ClementTsang/bottom)
  517. cd /tmp
  518. wget "https://github.com/ClementTsang/bottom/releases/download/$VERSION/bottom_x86_64-unknown-linux-musl.tar.gz"
  519. tar -xzvf bottom_x86_64-unknown-linux-musl.tar.gz
  520. sudo cp btm /usr/local/bin/
  521. sudo ln /usr/local/bin/btm /TOOLBOX/
  522. PREFIX="" echo "successfully installed"
  523. else
  524. PREFIX="" echo "already done!"
  525. fi
  526. echo -n "installing <micro> ... "
  527. if [ ! -f "/TOOLBOX/micro" ]; then
  528. cd /tmp || (echoerr "/tmp wrong permission" && exit 101)
  529. curl -q https://getmic.ro | GETMICRO_REGISTER=n sh
  530. sudo mv micro /TOOLBOX/micro
  531. sudo chown root:root /TOOLBOX/micro
  532. PREFIX="" echo "successfully installed"
  533. else
  534. PREFIX="" echo "already done!"
  535. fi
  536. echo -n "installing <ncdu> ... "
  537. if [ ! -f "/TOOLBOX/ncdu" ]; then
  538. sudo /opt/miaou-bash/tools/idem_apt_install ncdu
  539. sudo cp /usr/bin/ncdu /TOOLBOX/ncdu
  540. PREFIX="" echo "successfully installed"
  541. else
  542. PREFIX="" echo "already done!"
  543. fi
  544. echo -n "installing <unzip> ... "
  545. if [ ! -f "/TOOLBOX/unzip" ]; then
  546. sudo /opt/miaou-bash/tools/idem_apt_install unzip
  547. sudo cp /usr/bin/unzip /TOOLBOX/unzip
  548. PREFIX="" echo "successfully installed"
  549. else
  550. PREFIX="" echo "already done!"
  551. fi
  552. echo -n "installing <tree> ... "
  553. if [ ! -f "/TOOLBOX/tree" ]; then
  554. sudo /opt/miaou-bash/tools/idem_apt_install tree
  555. sudo cp /bin/tree /TOOLBOX/tree
  556. PREFIX="" echo "successfully installed"
  557. else
  558. PREFIX="" echo "already done!"
  559. fi
  560. echo -n "installing <duf> ... "
  561. if [ ! -f "/TOOLBOX/duf" ]; then
  562. VERSION=$(/opt/miaou-bash/tools/wget_semver github muesli/duf)
  563. VERSION_WITHOUT_V=${VERSION#v}
  564. wget -O /tmp/duf.deb "https://github.com/muesli/duf/releases/download/${VERSION}/duf_${VERSION_WITHOUT_V}_linux_amd64.deb"
  565. sudo dpkg -i /tmp/duf.deb
  566. sudo cp /bin/duf /TOOLBOX/duf
  567. PREFIX="" echo "successfully installed"
  568. else
  569. PREFIX="" echo "already done!"
  570. fi
  571. echo -n "installing <curl> ... "
  572. if [ ! -f "/TOOLBOX/curl" ]; then
  573. sudo wget -O /TOOLBOX/curl "https://github.com/moparisthebest/static-curl/releases/latest/download/curl-amd64"
  574. sudo chmod +x /TOOLBOX/curl
  575. PREFIX="" echo "successfully installed"
  576. else
  577. PREFIX="" echo "already done!"
  578. fi
  579. echo -n "installing <wget> ... "
  580. if [ ! -f "/TOOLBOX/wget" ]; then
  581. sudo ln -f /usr/bin/wget /TOOLBOX/wget
  582. PREFIX="" echo "successfully installed"
  583. else
  584. PREFIX="" echo "already done!"
  585. fi
  586. }
  587. # install_mandatory_commands
  588. function install_mandatory_commands() {
  589. local PREFIX="mandatory:commands"
  590. echo "installing various mandatory commands"
  591. sudo /opt/miaou-bash/tools/idem_apt_install dnsutils build-essential curl mariadb-client postgresql-client
  592. if ! exist_command tera; then
  593. echo "installing <tera> ..."
  594. local version=v0.2.4
  595. wget -q "https://github.com/chevdor/tera-cli/releases/download/${version}/tera-cli_linux_amd64.deb" -O /tmp/tera-cli_linux_amd64.deb
  596. sudo dpkg -i /tmp/tera-cli_linux_amd64.deb
  597. else
  598. echo "command <tera> already installed!"
  599. fi
  600. if ! exist_command yq; then
  601. local version binary
  602. version='v4.35.2'
  603. binary='yq_linux_amd64'
  604. sudo sh -c "wget https://github.com/mikefarah/yq/releases/download/${version}/${binary}.tar.gz -O - |\
  605. tar -xz ./${binary} && sudo mv ${binary} /usr/bin/yq"
  606. else
  607. echo "command <yq> already installed!"
  608. fi
  609. }
  610. # flatten array, aka remove duplicated elements in array
  611. # return: `mapfile -t OUTPUT_ARRAY < <(sort_array "${INPUT_ARRAY[@]}")`
  612. function flatten_array {
  613. declare -a array=("$@")
  614. IFS=" " read -r -a array <<<"$(tr ' ' '\n' <<<"${array[@]}" | sort -u | tr '\n' ' ')"
  615. printf '%s\n' "${array[@]}"
  616. }