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.

442 lines
13 KiB

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
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
9 months ago
  1. #!/bin/bash
  2. MIAOU_BASEDIR=$(readlink -f "$(dirname "$0")/..")
  3. # shellcheck source=/dev/null
  4. . "$MIAOU_BASEDIR/lib/functions.sh"
  5. readonly MIAOU_BASEDIR
  6. miaou_init
  7. EXPANDED_CONF="$MIAOU_CONFIGDIR/miaou.expanded.yaml"
  8. NEW_GROUP=lxd
  9. readonly NEW_GROUP EXPANDED_CONF
  10. on_exit() {
  11. if [[ "$SESSION_RELOAD_REQUIRED" == true ]]; then
  12. echo "======================================================"
  13. echo "Session Reload is required (due to new group <$NEW_GROUP>)"
  14. echo "======================================================"
  15. fi
  16. if [ -n "${1:-}" ]; then
  17. echo "Aborted by $1"
  18. elif [ "${status:-}" -ne 0 ]; then
  19. echo "Failure (status $status)"
  20. fi
  21. }
  22. function prepare_lxd {
  23. local PREFIX="lxd:prepare"
  24. # test group lxd assign to current user
  25. if ! groups | grep -q lxd; then
  26. echo "define lxd and assign to user <$USER>"
  27. sudo groupadd --force "$NEW_GROUP"
  28. sudo usermod --append --groups "$NEW_GROUP" "$(whoami)"
  29. exec sg "$NEW_GROUP" -c "$0 SESSION_RELOAD_REQUIRED $TARGET $@"
  30. # no further processing because exec has been called!
  31. else
  32. echo "user <$USER> already belongs to group <lxd>!"
  33. fi
  34. sudo /opt/miaou-bash/tools/idem_apt_install lxd btrfs-progs
  35. override_lxd_service
  36. # test lxdbr0
  37. if ! lxc network info lxdbr0 &>/dev/null; then
  38. echo "bridge <lxdbr0> down, so initialization will use default preseed..."
  39. if [[ $(printenv container) == 'lxc' ]]; then
  40. echo "nested configuration applying..."
  41. cat <<EOF | sg $NEW_GROUP -c 'lxd init --preseed'
  42. config: {}
  43. networks:
  44. - name: lxdbr0
  45. type: bridge
  46. config:
  47. ipv4.address: auto
  48. ipv6.address: none
  49. storage_pools:
  50. - config: {}
  51. description: ""
  52. name: default
  53. driver: dir
  54. profiles:
  55. - config:
  56. security.privileged: "true"
  57. description: ""
  58. devices:
  59. eth0:
  60. name: eth0
  61. nictype: bridged
  62. parent: lxdbr0
  63. type: nic
  64. root:
  65. path: /
  66. pool: default
  67. type: disk
  68. name: default
  69. projects: []
  70. cluster: null
  71. EOF
  72. else
  73. empty_block_partition=$(lsblk -o NAME,FSTYPE,GROUP --noheadings -p | grep -E ^.─ | grep disk | awk '{if($3=="") print $1}' | cut -d'/' -f3)
  74. if [[ -n "$empty_block_partition" ]]; then
  75. echo "use empty block partition /dev/$empty_block_partition for speed and optimization"
  76. cat <<EOF | sudo lxd init --preseed
  77. config: {}
  78. networks:
  79. - config:
  80. ipv4.address: auto
  81. ipv6.address: none
  82. description: ""
  83. name: lxdbr0
  84. type: ""
  85. project: default
  86. storage_pools:
  87. - config:
  88. source: /dev/$empty_block_partition
  89. description: ""
  90. name: default
  91. driver: btrfs
  92. profiles:
  93. - config: {}
  94. description: ""
  95. devices:
  96. eth0:
  97. name: eth0
  98. network: lxdbr0
  99. type: nic
  100. root:
  101. path: /
  102. pool: default
  103. type: disk
  104. name: default
  105. projects: []
  106. cluster: null
  107. EOF
  108. echo OK
  109. else
  110. echo "use dir partition for development purpose"
  111. cat <<EOF | lxd init --preseed
  112. config: {}
  113. networks:
  114. - name: lxdbr0
  115. type: bridge
  116. config:
  117. ipv4.address: auto
  118. ipv6.address: none
  119. EOF
  120. fi
  121. fi
  122. else
  123. echo "bridge <lxdbr0> found implies it has been already initialized!"
  124. fi
  125. set_alias 'sameuser' "exec @ARG1@ -- su --whitelist-environment container,container_hostname - $(whoami)"
  126. set_alias 'login' 'exec @ARGS@ --mode interactive -- /bin/bash -c $@${user:-root} - exec su --whitelist-environment container,container_hostname - '
  127. set_alias 'll' 'list -c ns4mDN'
  128. # test environment container hostname
  129. local env_container_hostname
  130. env_container_hostname=$(sg $NEW_GROUP -c 'lxc profile get default environment.container_hostname')
  131. if [[ -z "$env_container_hostname" ]]; then
  132. env_container_hostname=$(hostname -s)
  133. if env | grep -q container_hostname; then
  134. local previous_container_hostname=$(env | grep container_hostname | cut -d '=' -f2)
  135. env_container_hostname="$previous_container_hostname $env_container_hostname"
  136. fi
  137. echo -n "set environment container_hostname to <$env_container_hostname> ... "
  138. sg $NEW_GROUP -c "lxc profile set default environment.container_hostname \"$env_container_hostname\""
  139. PREFIX="" echoinfo OK
  140. else
  141. echo "environment container_hostname <$env_container_hostname> already defined!"
  142. fi
  143. if ! grep -q "root:$(id -u):1" /etc/subuid; then
  144. echo -n "subuid, subgid allowing <$(whoami)> ..."
  145. printf "root:$(id -u):1\n" | sudo tee -a /etc/subuid /etc/subgid
  146. PREFIX="" echoinfo DONE
  147. else
  148. echo "subuid, subgid allowing <$(whoami)> already done!"
  149. fi
  150. if [[ ! -d "$HOME/LXD/SHARED" ]]; then
  151. echo -n "$HOME/LXD/SHARED creating ... "
  152. mkdir "$HOME/LXD/SHARED" -p
  153. PREFIX="" echoinfo DONE
  154. else
  155. echo "folder <$HOME/LXD/SHARED> already created!"
  156. fi
  157. if [[ ! -d "$HOME/LXD/BACKUP" ]]; then
  158. echo -n "$HOME/LXD/BACKUP creating ... "
  159. mkdir "$HOME/LXD/BACKUP" -p
  160. PREFIX="" echoinfo DONE
  161. else
  162. echo "folder <$HOME/LXD/BACKUP> already created!"
  163. fi
  164. }
  165. function set_alias {
  166. local name="$1"
  167. local command="$2"
  168. if ! lxc alias list -f csv | grep -q "^$name,"; then
  169. echo -n "defining new lxc alias <$name> ..."
  170. lxc alias add "$name" "$command"
  171. PREFIX="" echoinfo OK
  172. else
  173. echo "lxc alias <$name> already defined!"
  174. fi
  175. }
  176. function miaou_evalfrombashrc() {
  177. local PREFIX="miaou:bashrc"
  178. output=$(
  179. /opt/miaou-bash/tools/append_or_replace \
  180. "^eval \"\\$\($MIAOU_BASEDIR/lib/install.sh shellenv\)\"$" \
  181. "eval \"\$($MIAOU_BASEDIR/lib/install.sh shellenv)\"" \
  182. "$HOME/.bashrc"
  183. )
  184. if [[ "$output" == "appended" ]]; then
  185. echo "new path <$MIAOU_BASEDIR> created!"
  186. SESSION_RELOAD_REQUIRED=true
  187. else
  188. echo "path <$MIAOU_BASEDIR> already loaded!"
  189. fi
  190. }
  191. function ask_target() {
  192. PS3='Choose miaou target purpose: '
  193. foods=("Dev" "Beta" "Prod")
  194. select ans in "${foods[@]}"; do
  195. builtin echo "${ans^^}"
  196. break
  197. done
  198. }
  199. function check_credential {
  200. local PREFIX="check:credential"
  201. check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.username' &&
  202. check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.shadow' &&
  203. check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.email'
  204. }
  205. function check_target() {
  206. case "${TARGET^^}" in
  207. DEV) ;;
  208. BETA) ;;
  209. PROD) ;;
  210. *)
  211. if [[ -f /etc/miaou/defaults.yaml ]]; then
  212. # load already defined target in expanded conf
  213. TARGET=$(grep -Es "^target:" /etc/miaou/defaults.yaml | cut -d ' ' -f2)
  214. else
  215. TARGET=$(ask_target)
  216. fi
  217. ;;
  218. esac
  219. TARGET=${TARGET,,} # downcase
  220. return 0
  221. }
  222. function miaou_configfiles() {
  223. local PREFIX="miaou:config"
  224. if [[ ! -d /etc/miaou ]]; then
  225. echo -n "configuration initializing ..."
  226. sudo mkdir -p /etc/miaou
  227. sudo chown "$USER" /etc/miaou
  228. PREFIX="" echoinfo OK
  229. fi
  230. if [[ ! -f /etc/miaou/defaults.yaml ]]; then
  231. echo -n "building /etc/miaou/defaults.yaml for the first time..."
  232. shadow_passwd=$(sudo grep "$CURRENT_USER" /etc/shadow | cut -d ':' -f2)
  233. env current_user="$CURRENT_USER" shadow_passwd="$shadow_passwd" tera -e --env-key env --env-only -t "$MIAOU_BASEDIR/templates/etc/defaults.yaml.j2" -o /etc/miaou/defaults.yaml >/dev/null
  234. yq ".target=\"$TARGET\"" /etc/miaou/defaults.yaml -i
  235. PREFIX="" echoinfo OK
  236. fi
  237. if [[ ! -f /etc/miaou/miaou.yaml ]]; then
  238. echo -n "building /etc/miaou/miaou.yaml for the first time..."
  239. cp "$MIAOU_BASEDIR/templates/etc/miaou.yaml.j2" /etc/miaou/miaou.yaml
  240. PREFIX="" echoinfo OK
  241. fi
  242. PREVIOUS_TARGET=""
  243. echo "expanded configuration stored in <$MIAOU_CONFIGDIR>!"
  244. [[ -f "$EXPANDED_CONF" ]] && PREVIOUS_TARGET=$(grep -Es "^target:" "$EXPANDED_CONF" | cut -d ' ' -f2)
  245. if [[ "$PREVIOUS_TARGET" != "$TARGET" ]]; then
  246. if [[ -z "$PREVIOUS_TARGET" ]]; then
  247. echo "new target defined <$TARGET>"
  248. else
  249. echowarnn "TARGET has changed from <$PREVIOUS_TARGET> to <$TARGET>, do you agree?"
  250. if askConfirmation N; then
  251. echowarn "removing previous settings, please restart <miaou> to apply changes"
  252. rm "$MIAOU_CONFIGDIR" -rf
  253. else
  254. echoerr "TARGET not accepted, exit"
  255. exit 102
  256. fi
  257. fi
  258. yq ".target=\"$TARGET\"" /etc/miaou/defaults.yaml -i
  259. else
  260. echo "target <$TARGET> already defined!"
  261. fi
  262. }
  263. function opt_link() {
  264. if [[ $MIAOU_BASEDIR != '/opt/miaou' ]]; then
  265. if [[ -L '/opt/miaou' && -d '/opt/miaou' && $(readlink /opt/miaou) == "$MIAOU_BASEDIR" ]]; then
  266. echo "symbolic link /opt/miaou already set up!"
  267. else
  268. sudo rm -f /opt/miaou
  269. sudo ln -s "$MIAOU_BASEDIR" /opt/miaou
  270. echo "symbolic link /opt/miaou successfully defined!"
  271. fi
  272. else
  273. echo "real path /opt/miaou already set up!"
  274. fi
  275. }
  276. function miaou_resolver() {
  277. local PREFIX="miaou:resolver"
  278. bridge=$(ip addr show lxdbr0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
  279. gateway=$(ip route | grep default | cut -d' ' -f3)
  280. if command -v nmcli &>/dev/null; then
  281. if [[ ! -f /etc/NetworkManager/dispatcher.d/50-miaou-resolver ]]; then
  282. echo -n "use NetworkManager dispatcher to deal with LXD bridge automatically..."
  283. sudo cp "$MIAOU_BASEDIR/templates/network-manager/50-miaou-resolver" /etc/NetworkManager/dispatcher.d/
  284. sudo chmod +x /etc/NetworkManager/dispatcher.d/50-miaou-resolver
  285. ACTIVE_CONNECTION=$(nmcli -g NAME connection show --active | head -n1)
  286. sudo nmcli connection up "$ACTIVE_CONNECTION" &>/dev/null
  287. PREFIX="" echoinfo OK
  288. else
  289. echo "miaou-resolver in NetworkManager dispatcher already initialized!"
  290. fi
  291. else
  292. if ! grep -q "nameserver $bridge" /etc/resolv.conf; then
  293. echo "customize resolv.conf from scratch (SERVER)..."
  294. sudo tee /etc/resolv.conf &>/dev/null <<EOF
  295. nameserver $bridge
  296. nameserver $gateway
  297. EOF
  298. PREFIX="" echoinfo OK
  299. else
  300. echo "customize resolv.conf already already defined!"
  301. fi
  302. fi
  303. }
  304. function extra_dev_desktop {
  305. # detect if DEV
  306. # detect if DESKTOP
  307. :
  308. }
  309. function override_lxd_service {
  310. local PREFIX="lxd:override"
  311. if [[ ! -d /etc/systemd/system/lxd.service.d ]]; then
  312. echo -n "override lxd service..."
  313. sudo mkdir -p /etc/systemd/system/lxd.service.d
  314. cat <<EOF | sudo tee /etc/systemd/system/lxd.service.d/override.conf
  315. [Service]
  316. ExecStartPost=systemctl reload nftables.service
  317. Environment=LANGUAGE=en:en_US
  318. EOF
  319. sudo systemctl daemon-reload
  320. sudo systemctl restart lxd.service
  321. PREFIX="" echo "OK"
  322. else
  323. echo "lxd service already overridden!"
  324. fi
  325. }
  326. function ask_for_credential {
  327. local PREFIX="ask:credential"
  328. if ! check_credential 2>/dev/null; then
  329. echo "further details required, please replace any <TO BE DEFINED> by a proper value ...press any key to open editor"
  330. read -rn1
  331. editor /etc/miaou/defaults.yaml
  332. fi
  333. check_credential
  334. echo "successfully checked!"
  335. }
  336. function preload_bookworm_image {
  337. local PREFIX="preload:bookworm"
  338. if [[ $(lxc image list debian/12/cloud -f csv | wc -l) -lt 1 ]]; then
  339. echo -n "downloading images from public remote, please hold on..."
  340. sg $NEW_GROUP -c 'lxc image copy images:debian/12/cloud local: --copy-aliases --quiet'
  341. PREFIX="" echoinfo OK
  342. else
  343. echo -n "refreshing images from public remote..."
  344. sg $NEW_GROUP -c 'lxc image refresh debian/12/cloud --quiet'
  345. PREFIX="" echoinfo DONE
  346. fi
  347. }
  348. ### MAIN
  349. if [[ "${1:-}" == "SESSION_RELOAD_REQUIRED" ]]; then
  350. SESSION_RELOAD_REQUIRED=true
  351. shift
  352. else
  353. SESSION_RELOAD_REQUIRED=false
  354. fi
  355. if [[ "${1:-}" == "shellenv" ]]; then
  356. unset PREFIX
  357. echo "export MIAOU_BASEDIR=$MIAOU_BASEDIR"
  358. echo "export PATH=\"\$MIAOU_BASEDIR/scripts\":\$PATH"
  359. else
  360. . "$MIAOU_BASEDIR/lib/init.sh"
  361. trap 'status=$?; on_exit; exit $status' EXIT
  362. trap 'trap - HUP; on_exit SIGHUP; kill -HUP $$' HUP
  363. trap 'trap - INT; on_exit SIGINT; kill -INT $$' INT
  364. trap 'trap - TERM; on_exit SIGTERM; kill -TERM $$' TERM
  365. PREFIX="miaou"
  366. : $PREFIX
  367. TARGET=${1:-}
  368. CURRENT_USER=$(id -un)
  369. check_target
  370. sudo_required
  371. install_miaou_bash
  372. install_mandatory_commands
  373. prepare_toolbox
  374. add_toolbox_sudoers
  375. prepare_nftables
  376. prepare_lxd "$@"
  377. preload_bookworm_image
  378. miaou_resolver
  379. miaou_evalfrombashrc
  380. miaou_configfiles
  381. ask_for_credential
  382. opt_link
  383. extra_dev_desktop
  384. if [[ "$SESSION_RELOAD_REQUIRED" == true ]]; then
  385. echowarn "current session has been reloaded, due to new group <$NEW_GROUP>."
  386. echowarn "be aware to exit twice or fully disconnect/reconnect to take advantage of new group <$NEW_GROUP>!"
  387. echoinfo "successful session installation "
  388. sg $NEW_GROUP -c bash
  389. else
  390. echoinfo "successful installation"
  391. fi
  392. fi