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.

708 lines
21 KiB

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