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.

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