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.

471 lines
11 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. #!/bin/bash
  2. ## Bash wrap script to launch the ``compose`` docker with right options.
  3. ##
  4. ##
  5. ## Launcher
  6. ## - should need minimum requirement to run
  7. ## - no shell libs
  8. ##
  9. [[ "${BASH_SOURCE[0]}" != "${0}" ]] && SOURCED=true
  10. ANSI_ESC=$'\e['
  11. ansi_color() {
  12. local choice="$1"
  13. if [ "$choice" == "tty" ]; then
  14. if [ -t 1 ]; then
  15. choice="yes"
  16. else
  17. choice="no"
  18. fi
  19. fi
  20. if [ "$choice" != "no" ]; then
  21. SET_COL_CHAR="${ANSI_ESC}${COL_CHAR}G"
  22. SET_COL_STATUS="${ANSI_ESC}${COL_STATUS}G"
  23. SET_COL_INFO="${ANSI_ESC}${COL_INFO}G"
  24. SET_COL_ELT="${ANSI_ESC}${COL_ELT}G"
  25. SET_BEGINCOL="${ANSI_ESC}0G"
  26. UP="${ANSI_ESC}1A"
  27. DOWN="${ANSI_ESC}1B"
  28. LEFT="${ANSI_ESC}1D"
  29. RIGHT="${ANSI_ESC}1C"
  30. SAVE="${ANSI_ESC}7"
  31. RESTORE="${ANSI_ESC}8"
  32. NORMAL="${ANSI_ESC}0m"
  33. GRAY="${ANSI_ESC}1;30m"
  34. RED="${ANSI_ESC}1;31m"
  35. GREEN="${ANSI_ESC}1;32m"
  36. YELLOW="${ANSI_ESC}1;33m"
  37. BLUE="${ANSI_ESC}1;34m"
  38. PINK="${ANSI_ESC}1;35m"
  39. CYAN="${ANSI_ESC}1;36m"
  40. WHITE="${ANSI_ESC}1;37m"
  41. DARKGRAY="${ANSI_ESC}0;30m"
  42. DARKRED="${ANSI_ESC}0;31m"
  43. DARKGREEN="${ANSI_ESC}0;32m"
  44. DARKYELLOW="${ANSI_ESC}0;33m"
  45. DARKBLUE="${ANSI_ESC}0;34m"
  46. DARKPINK="${ANSI_ESC}0;35m"
  47. DARKCYAN="${ANSI_ESC}0;36m"
  48. DARKWHITE="${ANSI_ESC}0;37m"
  49. SUCCESS=$GREEN
  50. WARNING=$YELLOW
  51. FAILURE=$RED
  52. NOOP=$BLUE
  53. ON=$SUCCESS
  54. OFF=$FAILURE
  55. ERROR=$FAILURE
  56. else
  57. SET_COL_CHAR=
  58. SET_COL_STATUS=
  59. SET_COL_INFO=
  60. SET_COL_ELT=
  61. SET_BEGINCOL=
  62. NORMAL=
  63. RED=
  64. GREEN=
  65. YELLOW=
  66. BLUE=
  67. GRAY=
  68. WHITE=
  69. DARKGRAY=
  70. DARKRED=
  71. DARKGREEN=
  72. DARKYELLOW=
  73. DARKBLUE=
  74. DARKPINK=
  75. DARKCYAN=
  76. SUCCESS=
  77. WARNING=
  78. FAILURE=
  79. NOOP=
  80. ON=
  81. OFF=
  82. ERROR=
  83. fi
  84. ansi_color="$choice"
  85. export SET_COL_CHAR SET_COL_STATUS SET_COL_INFO SET_COL_ELT \
  86. SET_BEGINCOL UP DOWN LEFT RIGHT SAVE RESTORE NORMAL \
  87. GRAY RED GREEN YELLOW BLUE PINK CYAN WHITE DARKGRAY \
  88. DARKRED DARKGREEN DARKYELLOW DARKBLUE DARKPINK DARKCYAN \
  89. SUCCESS WARNING FAILURE NOOP ON OFF ERROR ansi_color
  90. }
  91. e() { printf "%s" "$*"; }
  92. warn() { e "${YELLOW}Warning:$NORMAL" "$*"$'\n' >&2 ; }
  93. info() { e "${BLUE}II$NORMAL" "$*"$'\n' >&2 ; }
  94. verb() { [ -z "$VERBOSE" ] || e "$*"$'\n' >&2; }
  95. debug() { [ -z "$DEBUG" ] || e "$*"$'\n' >&2; }
  96. err() { e "${RED}Error:$NORMAL $*"$'\n' >&2 ; }
  97. die() { err "$@" ; exit 1; }
  98. ansi_color "${ansi_color:-tty}"
  99. get_path() { (
  100. IFS=:
  101. for d in $PATH; do
  102. filename="$d/$1"
  103. [ -f "$filename" -a -x "$filename" ] && {
  104. echo "$d/$1"
  105. return 0
  106. }
  107. done
  108. return 1
  109. ) }
  110. depends() {
  111. ## Avoid colliding with variables that are created with depends.
  112. local __i __path __new_name
  113. for __i in "$@"; do
  114. if ! __path=$(get_path "$__i"); then
  115. __new_name="$(echo "${__i//-/_}")"
  116. if [ "$__new_name" != "$__i" ]; then
  117. depends "$__new_name"
  118. else
  119. err "dependency check: couldn't find '$__i' required command."
  120. exit 1
  121. fi
  122. else
  123. if ! test -z "$__path" ; then
  124. export "$(echo "${__i//- /__}")"="$__path"
  125. fi
  126. fi
  127. done
  128. }
  129. get_os() {
  130. local uname_output
  131. uname_output="$(uname -s)"
  132. case "${uname_output}" in
  133. Linux*) e linux;;
  134. Darwin*) e mac;;
  135. CYGWIN*) e cygwin;;
  136. MINGW*) e mingw;;
  137. *) e "UNKNOWN:${uname_output}";;
  138. esac
  139. }
  140. read-0() {
  141. local eof= IFS=''
  142. while [ "$1" ]; do
  143. read -r -d '' -- "$1" || eof=1
  144. shift
  145. done
  146. [ -z "$eof" ]
  147. }
  148. read-0a() {
  149. local eof= IFS=''
  150. while [ "$1" ]; do
  151. IFS='' read -r -d $'\n' -- "$1" || eof=1
  152. shift
  153. done
  154. [ -z "$eof" ]
  155. }
  156. p0() {
  157. printf "%s\0" "$@"
  158. }
  159. list_compose_vars() {
  160. while read-0a def; do
  161. def="${def##* }"
  162. def="${def%=*}"
  163. p0 "$def"
  164. done < <(declare -p | grep "^declare -x COMPOSE_[A-Z_]\+=\"")
  165. }
  166. get_running_compose_containers() {
  167. ## XXXvlab: docker bug: there will be a final newline anyway
  168. docker ps --filter label="compose.service" --format='{{.ID}}'
  169. }
  170. get_volumes_for_container() {
  171. local container="$1"
  172. docker inspect \
  173. --format '{{range $mount := .Mounts}}{{$mount.Source}}{{"\x00"}}{{$mount.Destination}}{{"\x00"}}{{end}}' \
  174. "$container"
  175. }
  176. is_volume_used() {
  177. local volume="$1"
  178. while read container_id; do
  179. while read-0 src dst; do
  180. [ "$src" == "$volume" ] && return 0
  181. done < <(get_volumes_for_container "$container_id")
  182. done < <(get_running_compose_containers)
  183. return 1
  184. }
  185. clean_unused_sessions() {
  186. for f in "$COMPOSE_VAR/sessions/"*; do
  187. [ -e "$f" ] || continue
  188. is_volume_used "$f" && continue
  189. rm -f "$f"
  190. done
  191. }
  192. relink_subdirs() {
  193. local dir
  194. for dir in "$@"; do
  195. [ -L "$dir" ] || continue
  196. target=$(realpath "$dir")
  197. [ -d "$target" ] || continue
  198. docker_run_opts+=("-v" "$target:$dir")
  199. [ -e "$dir/metadata.yml" ] && continue
  200. relink_subdirs "$dir"/*
  201. done
  202. }
  203. mk_docker_run_options() {
  204. docker_run_opts=("-v" "/var/run/docker.sock:/var/run/docker.sock")
  205. ## CACHE/DATA DIRS
  206. docker_run_opts+=("-v" "$COMPOSE_VAR:/var/lib/compose")
  207. docker_run_opts+=("-v" "$COMPOSE_CACHE:/var/cache/compose")
  208. docker_run_opts+=("-v" "$TZ_PATH:/etc/timezone:ro")
  209. ## current dir
  210. if parent=$(while true; do
  211. [ -e "./compose.yml" ] && {
  212. echo "$PWD"
  213. exit 0
  214. }
  215. [ "$PWD" == "/" ] && exit 1
  216. cd ..
  217. done
  218. ); then
  219. docker_path=$COMPOSE_VAR/root/$(basename "$parent")
  220. docker_run_opts+=("-v" "$parent:$docker_path:ro" \
  221. "-w" "$docker_path")
  222. fi
  223. ##
  224. ## Load config files
  225. ##
  226. if [ -z "$DISABLE_SYSTEM_CONFIG_FILE" ]; then
  227. ## XXXvlab: should provide YML config opportunities in possible parent dirs ?
  228. ## userdir ? and global /etc/compose.yml ?
  229. for cfgfile in "${compose_config_files[@]}"; do
  230. [ -e "$cfgfile" ] || continue
  231. docker_run_opts+=("-v" "$cfgfile:$cfgfile:ro")
  232. . "$cfgfile"
  233. done
  234. else
  235. docker_run_opts+=("-e" "DISABLE_SYSTEM_CONFIG_FILE=$DISABLE_SYSTEM_CONFIG_FILE")
  236. fi
  237. ##
  238. ## Checking vars
  239. ##
  240. ## CHARM_STORE
  241. CHARM_STORE=${CHARM_STORE:-/srv/charm-store}
  242. [ -L "$CHARM_STORE" ] && {
  243. CHARM_STORE=$(readlink -f "$CHARM_STORE") || exit 1
  244. }
  245. docker_run_opts+=(
  246. "-v" "$CHARM_STORE:/srv/charm-store:ro"
  247. "-e" "CHARM_STORE=/srv/charm-store"
  248. "-e" "HOST_CHARM_STORE=$CHARM_STORE"
  249. )
  250. relink_subdirs /srv/charm-store/*
  251. ## DEFAULT_COMPOSE_FILE
  252. if [ "${DEFAULT_COMPOSE_FILE+x}" ]; then
  253. DEFAULT_COMPOSE_FILE=$(realpath "$DEFAULT_COMPOSE_FILE")
  254. dirname=$(dirname "$DEFAULT_COMPOSE_FILE")/
  255. if [ -e "${DEFAULT_COMPOSE_FILE}" ]; then
  256. docker_run_opts+=("-v" "$dirname:$dirname:ro")
  257. fi
  258. fi
  259. ## COMPOSE_YML_FILE
  260. if [ "${COMPOSE_YML_FILE+x}" ]; then
  261. if [ -e "${COMPOSE_YML_FILE}" ]; then
  262. docker_run_opts+=(
  263. "-v" "$COMPOSE_YML_FILE:/tmp/compose.yml:ro"
  264. "-e" "COMPOSE_YML_FILE=/tmp/compose.yml"
  265. "-e" "HOST_COMPOSE_YML_FILE=/tmp/compose.yml"
  266. )
  267. fi
  268. fi
  269. ## DATASTORE
  270. DATASTORE=${DATASTORE:-/srv/datastore/data}
  271. docker_run_opts+=(
  272. "-v" "$DATASTORE:/srv/datastore/data:rw"
  273. "-e" "DATASTORE=/srv/datastore/data"
  274. "-e" "HOST_DATASTORE=$DATASTORE"
  275. )
  276. ## CONFIGSTORE
  277. CONFIGSTORE=${CONFIGSTORE:-/srv/datastore/config}
  278. docker_run_opts+=(
  279. "-v" "$CONFIGSTORE:/srv/datastore/config:rw"
  280. "-e" "CONFIGSTORE=/srv/datastore/config"
  281. "-e" "HOST_CONFIGSTORE=$CONFIGSTORE"
  282. )
  283. docker_run_opts+=("-v" "$HOME/.docker:/root/.docker")
  284. COMPOSE_DOCKER_IMAGE=${COMPOSE_DOCKER_IMAGE:-docker.0k.io/compose}
  285. docker_run_opts+=("-e" "COMPOSE_DOCKER_IMAGE=$COMPOSE_DOCKER_IMAGE")
  286. ## SSH config
  287. docker_run_opts+=(
  288. "-v" "/root/.ssh:/root/.ssh:ro"
  289. "-v" "/etc/ssh:/etc/ssh"
  290. )
  291. COMPOSE_LAUNCHER_BIN=$(readlink -f "${BASH_SOURCE[0]}")
  292. docker_run_opts+=("-v" "$COMPOSE_LAUNCHER_BIN:/usr/local/bin/compose")
  293. while read-0 var; do
  294. case "$var" in
  295. COMPOSE_YML_FILE|COMPOSE_LAUNCHER_BIN|COMPOSE_DOCKER_IMAGE|\
  296. COMPOSE_LAUNCHER_OPTS|COMPOSE_VAR|COMPOSE_CACHE)
  297. :
  298. ;;
  299. *)
  300. docker_run_opts+=("-e" "$var=${!var}")
  301. ;;
  302. esac
  303. done < <(list_compose_vars)
  304. filename=$(mktemp -p /tmp/ -t launch_opts-XXXXXXXXXXXXXXXX)
  305. {
  306. p0 "${docker_run_opts[@]}"
  307. } > "$filename"
  308. sha=$(sha256sum "$filename")
  309. sha=${sha:0:64}
  310. dest="$COMPOSE_VAR/sessions/$sha"
  311. {
  312. p0 "-v" "$dest:$dest"
  313. p0 "-e" "COMPOSE_LAUNCHER_OPTS=$dest"
  314. p0 "-e" "COMPOSE_LAUNCHER_BIN=$COMPOSE_LAUNCHER_BIN"
  315. } >> "$filename"
  316. mkdir -p "$COMPOSE_VAR"/sessions
  317. mv "$filename" "$dest"
  318. echo "$dest"
  319. }
  320. run() {
  321. local os docker_run_opts
  322. ## Order matters, files get to override vars
  323. compose_config_files=(
  324. /etc/default/charm
  325. /etc/default/datastore
  326. /etc/compose.conf
  327. /etc/compose.local.conf
  328. /etc/default/compose
  329. ~/.compose/etc/local.conf
  330. /etc/compose/local.conf
  331. )
  332. os=$(get_os)
  333. case "$os" in
  334. linux)
  335. COMPOSE_VAR=/var/lib/compose
  336. COMPOSE_CACHE=/var/cache/compose
  337. TZ_PATH=/etc/timezone
  338. ;;
  339. mac)
  340. if ! [ -e "$HOME/.compose/etc/timezone" ]; then
  341. TZ=${TZ:-"Europe/Paris"}
  342. echo "$TZ" > "$HOME"/.compose/etc/timezone
  343. fi
  344. TZ_PATH="$HOME"/.compose/etc/timezone
  345. COMPOSE_VAR="$HOME"/.compose/lib
  346. COMPOSE_CACHE="$HOME"/.compose/cache
  347. ;;
  348. *)
  349. echo "System '$os' not supported yet." >&2
  350. exit 1
  351. ;;
  352. esac
  353. docker_run_opts=()
  354. if [ -z "$COMPOSE_LAUNCHER_OPTS" ]; then
  355. clean_unused_sessions
  356. COMPOSE_LAUNCHER_OPTS="$(mk_docker_run_options)"
  357. fi
  358. while read-0 opt; do
  359. docker_run_opts+=("$opt")
  360. ## catch COMPOSE_DOCKER_IMAGE
  361. if [[ "$env" == "true" && "$opt" == "COMPOSE_DOCKER_IMAGE="* ]]; then
  362. COMPOSE_DOCKER_IMAGE=${opt##COMPOSE_DOCKER_IMAGE=}
  363. elif [ "$opt" == "-e" ]; then
  364. env=true
  365. else
  366. env=
  367. fi
  368. done < <(cat "$COMPOSE_LAUNCHER_OPTS")
  369. [ -t 0 ] && docker_run_opts+=("-i")
  370. [ -t 1 ] && docker_run_opts+=("-t")
  371. exec docker run --rm "${docker_run_opts[@]}" "${COMPOSE_DOCKER_IMAGE}" "$@"
  372. }
  373. [ "$SOURCED" ] && return 0
  374. ##
  375. ## Code
  376. ##
  377. depends readlink
  378. run "$@"