Browse Source

big fat changes (to separate)

raw-remaining-args
Valentin Lab 6 years ago
parent
commit
0e0754a864
  1. 424
      bin/compose
  2. 285
      test/test

424
bin/compose

@ -23,6 +23,14 @@
## This will allow to cache completely without issues function in time. ## This will allow to cache completely without issues function in time.
## - would probably need instrospection in charm custom action to know if these need ## - would probably need instrospection in charm custom action to know if these need
## init or relations to be set up. ## init or relations to be set up.
## - Be clear about when the SERVICE name is used and the CHARM name is used.
## - in case of service contained in another container
## - in normal case
## - in docker-compose, can't use charm name: if we want 2 instances of the same charm
## we are stuck. What will be unique is the name of the service.
## - some relations are configured in compose.yml but should not trigger the loading
## of necessary component (for instance, apache --> log-rotate), if log-rotate is
## not there, this link should considered optional.
#:- #:-
[ -e /etc/shlib ] && . /etc/shlib || { [ -e /etc/shlib ] && . /etc/shlib || {
@ -75,17 +83,30 @@ ${WHITE}$exname Options${NORMAL}:
" "
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && SOURCED=true
## XXXvlab: this doesn't seem to work when 'compose' is called in
## a hook of a charm.
#[[ "${BASH_SOURCE[0]}" == "" ]] && SOURCED=true
$(return >/dev/null 2>&1) && SOURCED=true
export CACHEDIR=/var/cache/compose
export VARDIR=/var/lib/compose
if [ "$UID" == 0 ]; then
CACHEDIR=${CACHEDIR:-/var/cache/compose}
VARDIR=${VARDIR:-/var/lib/compose}
else
[ "$XDG_CONFIG_HOME" ] && CACHEDIR=${CACHEDIR:-$XDG_CONFIG_HOME/compose}
[ "$XDG_DATA_HOME" ] && VARDIR=${VARDIR:-$XDG_DATA_HOME/compose}
CACHEDIR=${CACHEDIR:-$HOME/.cache/compose}
VARDIR=${VARDIR:-$HOME/.local/share/compose}
fi
export VARDIR CACHEDIR
md5_compat() { md5sum | cut -c -32; } md5_compat() { md5sum | cut -c -32; }
quick_cat_file() { quick_cat_stdin < "$1"; } quick_cat_file() { quick_cat_stdin < "$1"; }
quick_cat_stdin() { local IFS=''; while read -r line; do echo "$line"; done ; } quick_cat_stdin() { local IFS=''; while read -r line; do echo "$line"; done ; }
export -f quick_cat_file quick_cat_stdin md5_compat export -f quick_cat_file quick_cat_stdin md5_compat
clean_cache() { clean_cache() {
local i=0 local i=0
for f in $(ls -t "$CACHEDIR/"*.cache.* 2>/dev/null | tail -n +500); do for f in $(ls -t "$CACHEDIR/"*.cache.* 2>/dev/null | tail -n +500); do
@ -294,6 +315,7 @@ cached_cmd_on_base_image() {
} }
export -f cached_cmd_on_base_image export -f cached_cmd_on_base_image
image_exposed_ports_0() { image_exposed_ports_0() {
local image="$1" local image="$1"
docker inspect --format='{{range $p, $conf := .Config.ExposedPorts}}{{$p}}{{"\x00"}}{{end}}' "$image" docker inspect --format='{{range $p, $conf := .Config.ExposedPorts}}{{$p}}{{"\x00"}}{{end}}' "$image"
@ -542,10 +564,11 @@ ensure_db_docker_running () {
_set_db_params "$DOCKER_IP" "$DOCKER_NETWORK" _set_db_params "$DOCKER_IP" "$DOCKER_NETWORK"
return 0 return 0
else else
err "Db not found. Docker logs follows:"
errlvl="$?"
err "Db not found (errlvl: $errlvl). Tail of docker logs follows:"
docker logs --tail=5 "$container_id" 2>&1 | prefix " | " >&2 docker logs --tail=5 "$container_id" 2>&1 | prefix " | " >&2
rm "/tmp/${_DB_NAME}.working" rm "/tmp/${_DB_NAME}.working"
return 1
return "$errlvl"
fi fi
} }
export -f ensure_db_docker_running export -f ensure_db_docker_running
@ -575,16 +598,16 @@ export -f _dcmd
## Executes code through db ## Executes code through db
dcmd () {
dcmd() {
local fun
[ "$DB_NAME" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_NAME." [ "$DB_NAME" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_NAME."
[ "$DB_DATADIR" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_DATADIR." [ "$DB_DATADIR" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_DATADIR."
[ "$DB_PASSFILE" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_PASSFILE."
# [ "$DB_PASSFILE" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_PASSFILE."
[ "$_PID" ] || print_syntax_error "$FUNCNAME: You must provide \$_PID." [ "$_PID" ] || print_syntax_error "$FUNCNAME: You must provide \$_PID."
[ "$(type -t is_db_locked)" == "function" ] || print_syntax_error "$FUNCNAME: You must provide function 'is_db_locked'."
[ "$(type -t _set_db_params)" == "function" ] || print_syntax_error "$FUNCNAME: You must provide function '_set_db_params'."
[ "$(type -t ddb)" == "function" ] || print_syntax_error "$FUNCNAME: You must provide function 'ddb'."
for fun in is_db_locked _set_db_params ddb; do
[ "$(type -t "$fun")" == "function" ] ||
print_syntax_error "$FUNCNAME: You must provide function '$fun'."
done
ensure_db_docker_running </dev/null || return 1 ensure_db_docker_running </dev/null || return 1
_dcmd "$@" _dcmd "$@"
@ -634,31 +657,47 @@ wait_docker_ip() {
export -f wait_docker_ip export -f wait_docker_ip
wait_for_tcp_port() {
local host_port=$1 timeout=30 start=$SECONDS
verb "Trying to connect to $host_port"
while true; do
timeout 1 bash -c "</dev/tcp/${host_port/://}" >/dev/null 2>&1 && break
sleep 0.2
if [ "$((SECONDS - start))" -gt "$timeout" ]; then
err "${RED}timeout error${NORMAL}(${timeout}s):"\
"Could not connect to $host_port."
return 1
fi
done
return 0
}
export -f wait_for_tcp_port
## Warning: requires a ``ddb`` matching current database to be checked ## Warning: requires a ``ddb`` matching current database to be checked
wait_for_docker_ip() { wait_for_docker_ip() {
local name=$1 timeout=15 timeout_count=0 DOCKER_IP= DOCKER_NETWORK= docker_ips= docker_ip= DB_OK=
local name=$1 DOCKER_IP= DOCKER_NETWORK= docker_ips= docker_ip=
docker_ip=$(wait_docker_ip "$name" 5) || return 1 docker_ip=$(wait_docker_ip "$name" 5) || return 1
IFS=: read DOCKER_NETWORK DOCKER_IP <<<"$docker_ip" IFS=: read DOCKER_NETWORK DOCKER_IP <<<"$docker_ip"
if ! str_is_ipv4 "$DOCKER_IP"; then if ! str_is_ipv4 "$DOCKER_IP"; then
err "internal 'wait_docker_ip' did not return a valid IP. Returned IP is '$DOCKER_IP'." err "internal 'wait_docker_ip' did not return a valid IP. Returned IP is '$DOCKER_IP'."
return 1 return 1
fi fi
timeout_count=0
DB_OK=
_set_db_params "$DOCKER_IP" "$DOCKER_NETWORK" _set_db_params "$DOCKER_IP" "$DOCKER_NETWORK"
while [ -z "$DB_OK" ]; do
sleep 1
echo "SELECT 1;" | ddb >/dev/null && DB_OK=1
verb "[2/2] Waiting for db service from docker $name... ($[timeout_count + 1]/$timeout)"
((timeout_count++)) || true
if [ "$timeout_count" == "$timeout" ]; then
err "${RED}timeout error${NORMAL}(${timeout}s):"\
while read-0 port; do
IFS="/" read port type <<<"$port"
[ "$type" == "tcp" ] || continue
wait_for_tcp_port "$DOCKER_IP:${port}" || return 17
verb "Port $DOCKER_IP:${port} checked open."
done < <(image_exposed_ports_0 "$container_id")
## Checking direct connection
if ! echo "SELECT 1;" | ddb >/dev/null; then
err "${RED}db connection error${NORMAL}:"\
"Could not connect to db on $DOCKER_IP" \ "Could not connect to db on $DOCKER_IP" \
"container's IP."
return 1
"container's IP. (IP up, TCP ports is(are) open)"
return 18
fi fi
done
verb "[2/2] Db is ready !"
echo "${DOCKER_NETWORK}:${DOCKER_IP}" echo "${DOCKER_NETWORK}:${DOCKER_IP}"
return 0 return 0
} }
@ -711,19 +750,25 @@ get_docker_compose_links() {
fi fi
master_service=$(get_top_master_service_for_service "$service") || return 1 master_service=$(get_top_master_service_for_service "$service") || return 1
## XXXvlab: yuck, this make the assumption that next function is cached,
## and leverage the fact that the result is stored in a file. All this only
## to catch a failure of ``get_compose_relations``, that would go out silently.
get_compose_relations "$service" >/dev/null || return 1 ## fetch cache and fail if necessary
deps=() deps=()
while read-0 _relation_name target_service _relation_config tech_dep; do while read-0 _relation_name target_service _relation_config tech_dep; do
master_target_service="$(get_top_master_service_for_service "$target_service")"
master_target_service="$(get_top_master_service_for_service "$target_service")" || return 1
[ "$master_service" == "$master_target_service" ] && continue [ "$master_service" == "$master_target_service" ] && continue
if [ "$tech_dep" == "reversed" ]; then if [ "$tech_dep" == "reversed" ]; then
deps+=("$(echo -en "$master_target_service:\n links:\n - $master_service")") deps+=("$(echo -en "$master_target_service:\n links:\n - $master_service")")
elif [ "$tech_dep" == "True" ]; then elif [ "$tech_dep" == "True" ]; then
deps+=("$(echo -en "$master_service:\n links:\n - $master_target_service")") deps+=("$(echo -en "$master_service:\n links:\n - $master_target_service")")
fi fi
done < <(get_compose_relations "$service") || return 1
merge_yaml_str "${deps[@]}" | tee "$cache_file"
## XXXvlab: an attempt to add depends_on, but this doesn't work well actually
## as there's a circular dependency issue. We don't really want the full feature
## of depends_on, but just to add it as targets when doing an 'up'
# deps+=("$(echo -en "$master_service:\n depends_on:\n - $master_target_service")")
done < <(get_compose_relations "$service")
merge_yaml_str "${deps[@]}" | tee "$cache_file" || return 1
if [ "${PIPESTATUS[0]}" != 0 ]; then if [ "${PIPESTATUS[0]}" != 0 ]; then
rm "$cache_file" rm "$cache_file"
return 1 return 1
@ -777,7 +822,6 @@ _get_docker_compose_service_mixin() {
cat "$cache_file" cat "$cache_file"
return 0 return 0
fi fi
master_service=$(get_top_master_service_for_service "$service") || { master_service=$(get_top_master_service_for_service "$service") || {
err "Failed to get top master service for service $DARKYELLOW$service$NORMAL" err "Failed to get top master service for service $DARKYELLOW$service$NORMAL"
return 1 return 1
@ -787,17 +831,15 @@ _get_docker_compose_service_mixin() {
base_mixin="$master_service: base_mixin="$master_service:
labels: labels:
- \"compose.service=$service\"
- \"compose.master-service=${master_service}\"
- \"compose.project=$(get_default_project_name)\""
- compose.service=$service
- compose.master-service=${master_service}
- compose.project=$(get_default_project_name)"
links_yaml=$(get_docker_compose_links "$service") || return 1 links_yaml=$(get_docker_compose_links "$service") || return 1
docker_compose_options=$(_get_docker_compose_opts "$service") || return 1 docker_compose_options=$(_get_docker_compose_opts "$service") || return 1
## the charm part ## the charm part
#debug "Get charm name from service name $DARKYELLOW$service$NORMAL."
charm=$(get_service_charm "$service") || return 1
charm_part=$(get_docker_compose_mixin_from_metadata "$service" "$charm") || return 1
charm_part=$(get_docker_compose_mixin_from_metadata "$service") || return 1
## Merge results ## Merge results
if [ "$charm_part" ]; then if [ "$charm_part" ]; then
@ -822,7 +864,8 @@ export -f _get_docker_compose_service_mixin
## @export ## @export
## @cache: !system !nofail +stdout ## @cache: !system !nofail +stdout
get_docker_compose () { get_docker_compose () {
local cache_file="$state_tmpdir/$FUNCNAME.cache.$(echo "$*" | md5_compat)" entries services service start
local cache_file="$state_tmpdir/$FUNCNAME.cache.$(echo "$*" | md5_compat)" \
entries services service start docker_compose_services
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: cache hit ($*)" # debug "$FUNCNAME: cache hit ($*)"
cat "$cache_file" cat "$cache_file"
@ -892,19 +935,31 @@ _get_compose_service_def_cached () {
local service="$1" docker_compose="$2" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)" local service="$1" docker_compose="$2" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)"
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: STATIC cache hit" #debug "$FUNCNAME: STATIC cache hit"
cat "$cache_file"
touch "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
service_def_base="charm: $service"
value=$(echo "$docker_compose" | shyaml get-value "$service" 2>/dev/null) value=$(echo "$docker_compose" | shyaml get-value "$service" 2>/dev/null)
merge_yaml <(echo "$service_def_base") <(echo "$value") | tee "$cache_file"
if [ "${PIPESTATUS[0]}" != 0 ]; then
rm "$cache_file"
if ! echo "$value" | shyaml get-value "charm" >/dev/null 2>&1; then
if charm.exists "$service"; then
value=$(merge_yaml <(echo "charm: $service") <(echo "$value")) || return 1
else
err "No ${WHITE}charm${NORMAL} value for service $DARKYELLOW$service$NORMAL" \
"in compose, nor same name charm found."
return 1 return 1
fi fi
fi
echo "$value" | tee "$cache_file" || return 1
# if [ "${PIPESTATUS[0]}" != 0 ]; then
# rm "$cache_file"
# return 1
# fi
return 0
# if [ "${PIPESTATUS[0]}" != 0 -o \! -s "$cache_file" ]; then
# rm "$cache_file"
# err "PAS OK $service: $value"
# return 1
# fi
} }
export -f _get_compose_service_def_cached export -f _get_compose_service_def_cached
@ -914,15 +969,15 @@ get_compose_service_def () {
result result
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: SESSION cache hit" #debug "$FUNCNAME: SESSION cache hit"
cat "$cache_file"
cat "$cache_file" || return 1
return 0 return 0
fi fi
[ -z "$service" ] && print_syntax_error "Missing service as first argument." [ -z "$service" ] && print_syntax_error "Missing service as first argument."
docker_compose=$(cat "$COMPOSE_YML_FILE") || return 1
docker_compose=$([ "$COMPOSE_YML_FILE" -a -e "$COMPOSE_YML_FILE" ] && cat "$COMPOSE_YML_FILE")
result=$(_get_compose_service_def_cached "$service" "$docker_compose") || return 1 result=$(_get_compose_service_def_cached "$service" "$docker_compose") || return 1
echo "$result" | tee "$cache_file"
echo "$result" | tee "$cache_file" || return 1
} }
export -f get_compose_service_def export -f get_compose_service_def
@ -931,8 +986,8 @@ _get_service_charm_cached () {
local service="$1" service_def="$2" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)" local service="$1" service_def="$2" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)"
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: cache hit $1" # debug "$FUNCNAME: cache hit $1"
cat "$cache_file"
touch "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
charm=$(echo "$service_def" | shyaml get-value charm 2>/dev/null) charm=$(echo "$service_def" | shyaml get-value charm 2>/dev/null)
@ -940,13 +995,14 @@ _get_service_charm_cached () {
err "Missing ${WHITE}charm${NORMAL} value in service $DARKYELLOW$service$NORMAL definition." err "Missing ${WHITE}charm${NORMAL} value in service $DARKYELLOW$service$NORMAL definition."
return 1 return 1
fi fi
echo "$charm" | tee "$cache_file"
echo "$charm" | tee "$cache_file" || return 1
} }
export -f _get_service_charm_cached export -f _get_service_charm_cached
get_service_charm () { get_service_charm () {
local service="$1" local service="$1"
if [ -z "$service" ]; then if [ -z "$service" ]; then
echo ${FUNCNAME[@]} >&2
print_syntax_error "$FUNCNAME: Please specify a service as first argument." print_syntax_error "$FUNCNAME: Please specify a service as first argument."
return 1 return 1
fi fi
@ -1111,8 +1167,9 @@ get_ordered_service_dependencies() {
#debug "Figuring ordered deps of $DARKYELLOW$services$NORMAL" #debug "Figuring ordered deps of $DARKYELLOW$services$NORMAL"
if [ -z "${services[*]}" ]; then if [ -z "${services[*]}" ]; then
print_syntax_error "$FUNCNAME: no arguments"
return 1
return 0
# print_syntax_error "$FUNCNAME: no arguments"
# return 1
fi fi
declare -A depths declare -A depths
@ -1138,11 +1195,12 @@ get_ordered_service_dependencies() {
export -f get_ordered_service_dependencies export -f get_ordered_service_dependencies
run_service_hook () { run_service_hook () {
local services="$1" action="$2" loaded
local services="$1" action="$2" subservices loaded
declare -A loaded declare -A loaded
for service in $services; do for service in $services; do
for subservice in $(get_ordered_service_dependencies "$service"); do
subservices=$(get_ordered_service_dependencies "$service") || return 1
for subservice in $subservices; do
if [ "${loaded[$subservice]}" ]; then if [ "${loaded[$subservice]}" ]; then
## Prevent double inclusion of same service if this ## Prevent double inclusion of same service if this
## service is deps of two or more of your ## service is deps of two or more of your
@ -1254,11 +1312,12 @@ export -f setup_host_resource
setup_host_resources () { setup_host_resources () {
local services="$1" loaded
local services="$1" subservices loaded
declare -A loaded declare -A loaded
for service in $services; do for service in $services; do
for subservice in $(get_ordered_service_dependencies "$service"); do
subservices=$(get_ordered_service_dependencies "$service") || return 1
for subservice in $subservices; do
if [ "${loaded[$subservice]}" ]; then if [ "${loaded[$subservice]}" ]; then
## Prevent double inclusion of same service if this ## Prevent double inclusion of same service if this
## service is deps of two or more of your ## service is deps of two or more of your
@ -1469,8 +1528,8 @@ _get_compose_relations_cached () {
relation_name relation_def target_service relation_name relation_def target_service
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: STATIC cache hit $1" #debug "$FUNCNAME: STATIC cache hit $1"
cat "$cache_file"
touch "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
@ -1478,30 +1537,31 @@ _get_compose_relations_cached () {
set -o pipefail set -o pipefail
if [ "$compose_service_def" ]; then if [ "$compose_service_def" ]; then
while read-0 relation_name relation_def; do while read-0 relation_name relation_def; do
## XXXvlab: could we use braces here instead of parenthesis ?
( (
case "$(echo "$relation_def" | shyaml get-type 2>/dev/null)" in case "$(echo "$relation_def" | shyaml get-type 2>/dev/null)" in
"str") "str")
target_service="$(echo "$relation_def" | shyaml get-value 2>/dev/null)" target_service="$(echo "$relation_def" | shyaml get-value 2>/dev/null)"
target_charm=$(get_service_charm "$target_service")
target_charm=$(get_service_charm "$target_service") || return 1
tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")" tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")"
echo -en "$relation_name\0$target_service\0\0$tech_dep\0" echo -en "$relation_name\0$target_service\0\0$tech_dep\0"
;; ;;
"sequence") "sequence")
while read-0 target_service; do while read-0 target_service; do
target_charm=$(get_service_charm "$target_service")
target_charm=$(get_service_charm "$target_service") || return 1
tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")" tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")"
echo -en "$relation_name\0$target_service\0\0$tech_dep\0" echo -en "$relation_name\0$target_service\0\0$tech_dep\0"
done < <(echo "$relation_def" | shyaml get-values-0 2>/dev/null) done < <(echo "$relation_def" | shyaml get-values-0 2>/dev/null)
;; ;;
"struct") "struct")
while read-0 target_service relation_config; do while read-0 target_service relation_config; do
target_charm=$(get_service_charm "$target_service")
target_charm=$(get_service_charm "$target_service") || return 1
tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")" tech_dep="$(get_charm_tech_dep_orientation_for_relation "$target_charm" "$relation_name")"
echo -en "$relation_name\0$target_service\0$relation_config\0$tech_dep\0" echo -en "$relation_name\0$target_service\0$relation_config\0$tech_dep\0"
done < <(echo "$relation_def" | shyaml key-values-0 2>/dev/null) done < <(echo "$relation_def" | shyaml key-values-0 2>/dev/null)
;; ;;
esac esac
) </dev/null >> "$cache_file"
) </dev/null >> "$cache_file" || return 1
done < <(echo "$compose_service_def" | shyaml key-values-0 relations 2>/dev/null) done < <(echo "$compose_service_def" | shyaml key-values-0 relations 2>/dev/null)
fi fi
) )
@ -1528,7 +1588,6 @@ get_compose_relations () {
compose_def="$(get_compose_service_def "$service")" || return 1 compose_def="$(get_compose_service_def "$service")" || return 1
_get_compose_relations_cached "$compose_def" > "$cache_file" _get_compose_relations_cached "$compose_def" > "$cache_file"
if [ "$?" != 0 ]; then if [ "$?" != 0 ]; then
err "Error while looking for compose relations."
rm -f "$cache_file" ## no cache rm -f "$cache_file" ## no cache
return 1 return 1
fi fi
@ -1536,15 +1595,27 @@ get_compose_relations () {
} }
export -f get_compose_relations export -f get_compose_relations
get_compose_relation_def() {
local service="$1" relation="$2" relation_name target_service relation_config tech_dep
while read-0 relation_name target_service relation_config tech_dep; do
[ "$relation_name" == "$relation" ] || continue
printf "%s\0%s\0%s\0" "$target_service" "$relation_config" "$tech_dep"
done < <(get_compose_relations "$service") || return 1
}
export -f get_compose_relation_def
run_service_relations () { run_service_relations () {
local services="$1" loaded
local services="$1" loaded subservices
PROJECT_NAME=$(get_default_project_name) || return 1 PROJECT_NAME=$(get_default_project_name) || return 1
export PROJECT_NAME export PROJECT_NAME
declare -A loaded declare -A loaded
for service in $(get_ordered_service_dependencies $services); do
subservices=$(get_ordered_service_dependencies $services) || return 1
for service in $subservices; do
# debug "Upping dep's relations of ${DARKYELLOW}$service${NORMAL}:" # debug "Upping dep's relations of ${DARKYELLOW}$service${NORMAL}:"
for subservice in $(get_service_deps "$service") "$service"; do for subservice in $(get_service_deps "$service") "$service"; do
@ -1805,8 +1876,8 @@ _get_master_service_for_service_cached () {
charm requires master_charm target_charm target_service service_def found charm requires master_charm target_charm target_service service_def found
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: STATIC cache hit ($1)" # debug "$FUNCNAME: STATIC cache hit ($1)"
cat "$cache_file"
touch "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
@ -1864,17 +1935,16 @@ get_master_service_for_service() {
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
# debug "$FUNCNAME: SESSION cache hit ($*)" # debug "$FUNCNAME: SESSION cache hit ($*)"
cat "$cache_file"
cat "$cache_file" || return 1
return 0 return 0
fi fi
charm=$(get_service_charm "$service") || return 1 charm=$(get_service_charm "$service") || return 1
metadata=$(charm.metadata "$charm" 2>/dev/null) || { metadata=$(charm.metadata "$charm" 2>/dev/null) || {
metadata="" metadata=""
warn "No charm $DARKPINK$charm$NORMAL found." warn "No charm $DARKPINK$charm$NORMAL found."
} }
result=$(_get_master_service_for_service_cached "$service" "$charm" "$metadata") || return 1 result=$(_get_master_service_for_service_cached "$service" "$charm" "$metadata") || return 1
echo "$result" | tee "$cache_file"
echo "$result" | tee "$cache_file" || return 1
} }
export -f get_master_service_for_service export -f get_master_service_for_service
@ -1906,16 +1976,16 @@ export -f get_top_master_service_for_service
## docker-compose entry (thinking of subordinates). The result ## docker-compose entry (thinking of subordinates). The result
## will be merge with master charms. ## will be merge with master charms.
_get_docker_compose_mixin_from_metadata_cached() { _get_docker_compose_mixin_from_metadata_cached() {
local service="$1" charm="$2" metadata="$3" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)" \
local service="$1" charm="$2" metadata="$3" has_build_dir="$4" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$*" | md5_compat)" \
metadata_file metadata volumes docker_compose subordinate image metadata_file metadata volumes docker_compose subordinate image
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: STATIC cache hit $1" #debug "$FUNCNAME: STATIC cache hit $1"
cat "$cache_file"
touch "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
mixin=$(echo -en "labels:\n - \"compose.charm=$charm\"")
mixin=$(echo -en "labels:\n- compose.charm=$charm")
if [ "$metadata" ]; then if [ "$metadata" ]; then
## resources to volumes ## resources to volumes
volumes=$( volumes=$(
@ -1925,10 +1995,16 @@ _get_docker_compose_mixin_from_metadata_cached() {
done < <(echo "$metadata" | shyaml get-values-0 "${resource_type}-resources" 2>/dev/null) done < <(echo "$metadata" | shyaml get-values-0 "${resource_type}-resources" 2>/dev/null)
done done
while read-0 resource; do while read-0 resource; do
if [[ "$resource" == *:* ]]; then
if [[ "$resource" == /*:/*:* ]]; then
echo " - $resource"
elif [[ "$resource" == /*:/* ]]; then
echo " - $resource:rw" echo " - $resource:rw"
else
elif [[ "$resource" == /*:* ]]; then
echo " - ${resource%%:*}:$resource"
elif [[ "$resource" =~ ^/[^:]+$ ]]; then
echo " - $resource:$resource:rw" echo " - $resource:$resource:rw"
else
die "Invalid host-resource specified in 'metadata.yml'."
fi fi
done < <(echo "$metadata" | shyaml get-values-0 "host-resources" 2>/dev/null) done < <(echo "$metadata" | shyaml get-values-0 "host-resources" 2>/dev/null)
while read-0 resource; do while read-0 resource; do
@ -1969,7 +2045,7 @@ _get_docker_compose_mixin_from_metadata_cached() {
return 1 return 1
fi fi
image_or_build_statement="image: $image" image_or_build_statement="image: $image"
elif [ -d "$(charm.get_dir "$charm")/build" ]; then
elif [ "$has_build_dir" ]; then
if [ "$subordinate" ]; then if [ "$subordinate" ]; then
err "Subordinate charm can not have a 'build' sub directory." err "Subordinate charm can not have a 'build' sub directory."
return 1 return 1
@ -1985,15 +2061,17 @@ export -f _get_docker_compose_mixin_from_metadata_cached
get_docker_compose_mixin_from_metadata() { get_docker_compose_mixin_from_metadata() {
local service="$1" charm="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$1"
local service="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$1"
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
#debug "$FUNCNAME: SESSION cache hit ($*)" #debug "$FUNCNAME: SESSION cache hit ($*)"
cat "$cache_file" cat "$cache_file"
return 0 return 0
fi fi
metadata="$(charm.metadata "$charm" 2>/dev/null)"
mixin=$(_get_docker_compose_mixin_from_metadata_cached "$service" "$charm" "$metadata") || return 1
charm=$(get_service_charm "$service") || return 1
metadata="$(charm.metadata "$charm" 2>/dev/null)" || return 1
has_build_dir=
[ -d "$(charm.get_dir "$charm")/build" ] && has_build_dir=true
mixin=$(_get_docker_compose_mixin_from_metadata_cached "$service" "$charm" "$metadata" "$has_build_dir") || return 1
echo "$mixin" | tee "$cache_file" echo "$mixin" | tee "$cache_file"
} }
export -f get_docker_compose_mixin_from_metadata export -f get_docker_compose_mixin_from_metadata
@ -2024,7 +2102,7 @@ export -f get_default_project_name
launch_docker_compose() { launch_docker_compose() {
local charm
local charm docker_compose_tmpdir docker_compose_dir
docker_compose_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX) docker_compose_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
#debug "Creating temporary docker-compose directory in '$docker_compose_tmpdir'." #debug "Creating temporary docker-compose directory in '$docker_compose_tmpdir'."
# trap_add EXIT "debug \"Removing temporary docker-compose directory in $docker_compose_tmpdir.\";\ # trap_add EXIT "debug \"Removing temporary docker-compose directory in $docker_compose_tmpdir.\";\
@ -2034,9 +2112,9 @@ launch_docker_compose() {
mkdir -p "$docker_compose_tmpdir/$project" mkdir -p "$docker_compose_tmpdir/$project"
docker_compose_dir="$docker_compose_tmpdir/$project" docker_compose_dir="$docker_compose_tmpdir/$project"
if [ -z "$SERVICE_PACK" ]; then
export SERVICE_PACK=$(get_default_target_services $SERVICE_PACK)
fi
# if [ -z "$SERVICE_PACK" ]; then
# export SERVICE_PACK=$(get_default_target_services $SERVICE_PACK)
# fi
get_docker_compose $SERVICE_PACK > "$docker_compose_dir/docker-compose.yml" || return 1 get_docker_compose $SERVICE_PACK > "$docker_compose_dir/docker-compose.yml" || return 1
if [ -e "$state_tmpdir/to-merge-in-docker-compose.yml" ]; then if [ -e "$state_tmpdir/to-merge-in-docker-compose.yml" ]; then
# debug "Merging some config data in docker-compose.yml:" # debug "Merging some config data in docker-compose.yml:"
@ -2063,7 +2141,11 @@ launch_docker_compose() {
debug "${WHITE}docker-compose.yml$NORMAL for $DARKYELLOW$SERVICE_PACK$NORMAL:" debug "${WHITE}docker-compose.yml$NORMAL for $DARKYELLOW$SERVICE_PACK$NORMAL:"
debug "$(cat "$docker_compose_dir/docker-compose.yml" | prefix " $GRAY|$NORMAL ")" debug "$(cat "$docker_compose_dir/docker-compose.yml" | prefix " $GRAY|$NORMAL ")"
debug "${WHITE}Launching$NORMAL: docker-compose $@" debug "${WHITE}Launching$NORMAL: docker-compose $@"
if [ "$DRY_COMPOSE_RUN" ]; then
echo docker-compose "$@"
else
docker-compose "$@" docker-compose "$@"
fi
echo "$?" > "$docker_compose_dir/.data/errlvl" echo "$?" > "$docker_compose_dir/.data/errlvl"
} | _save stdout } | _save stdout
} 3>&1 1>&2 2>&3 | _save stderr 3>&1 1>&2 2>&3 } 3>&1 1>&2 2>&3 | _save stderr 3>&1 1>&2 2>&3
@ -2094,7 +2176,8 @@ get_compose_yml_location() {
if [ "$DEFAULT_COMPOSE_FILE" ]; then if [ "$DEFAULT_COMPOSE_FILE" ]; then
if ! [ -e "$DEFAULT_COMPOSE_FILE" ]; then if ! [ -e "$DEFAULT_COMPOSE_FILE" ]; then
err "No 'compose.yml' was found in current or parent dirs," \ err "No 'compose.yml' was found in current or parent dirs," \
"and \$DEFAULT_COMPOSE_FILE points to an unexistent file."
"and \$DEFAULT_COMPOSE_FILE points to an unexistent file." \
"(${DEFAULT_COMPOSE_FILE})"
die "Please provide a 'compose.yml' file." die "Please provide a 'compose.yml' file."
fi fi
echo "$DEFAULT_COMPOSE_FILE" echo "$DEFAULT_COMPOSE_FILE"
@ -2135,6 +2218,7 @@ get_master_services() {
echo "$master_service" echo "$master_service"
loaded["$master_service"]=1 loaded["$master_service"]=1
done | xargs echo done | xargs echo
return "${PIPESTATUS[0]}"
} }
export -f get_master_services export -f get_master_services
@ -2148,18 +2232,50 @@ _setup_state_dir() {
} }
get_docker_compose_opts_list() {
local action="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$action"; cat "$(which docker-compose)" | md5_compat)"
get_docker_compose_action_help_msg() {
local action="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$action"; cat "$(which docker-compose)" | md5_compat)" \
docker_compose_help_msg
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
cat "$cache_file"
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0 return 0
fi fi
docker_compose_help_msg=$(docker-compose "$action" --help 2>/dev/null) || return 1 docker_compose_help_msg=$(docker-compose "$action" --help 2>/dev/null) || return 1
echo "$docker_compose_help_msg" |
tee "$cache_file" || return 1
}
get_docker_compose_action_usage() {
local action="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$action"; cat "$(which docker-compose)" | md5_compat)" \
docker_compose_help_msg
if [ -e "$cache_file" ]; then
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0
fi
docker_compose_help_msg=$(get_docker_compose_action_help_msg "$action") || return 1
echo "$docker_compose_help_msg" |
grep -m 1 "^Usage:" -A 10000 |
egrep -m 1 "^\$" -B 10000 |
xargs echo |
sed -r 's/^Usage: //g' |
tee "$cache_file" || return 1
}
get_docker_compose_opts_list() {
local action="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(echo "$action"; cat "$(which docker-compose)" | md5_compat)" \
docker_compose_help_msg
if [ -e "$cache_file" ]; then
cat "$cache_file" &&
touch "$cache_file" || return 1
return 0
fi
docker_compose_help_msg=$(get_docker_compose_action_help_msg "$action") || return 1
echo "$docker_compose_help_msg" | grep '^Options:' -A 20000 | echo "$docker_compose_help_msg" | grep '^Options:' -A 20000 |
tail -n +2 | tail -n +2 |
egrep "^\s+-" | egrep "^\s+-" |
sed -r 's/\s+((((-[a-zA-Z]|--[a-zA-Z0-9-]+)( [A-Z=]+|=[^ ]+)?)(, )?)+)\s+.*$/\1/g' | sed -r 's/\s+((((-[a-zA-Z]|--[a-zA-Z0-9-]+)( [A-Z=]+|=[^ ]+)?)(, )?)+)\s+.*$/\1/g' |
tee "$cache_file"
tee "$cache_file" || return 1
} }
_MULTIOPTION_REGEX='^((-[a-zA-Z]|--[a-zA-Z0-9-]+)(, )?)+' _MULTIOPTION_REGEX='^((-[a-zA-Z]|--[a-zA-Z0-9-]+)(, )?)+'
@ -2312,7 +2428,7 @@ digraph g {
ratio = auto; ratio = auto;
EOF EOF
for target_service in "$@"; do for target_service in "$@"; do
services=$(get_ordered_service_dependencies "$target_service")
services=$(get_ordered_service_dependencies "$target_service") || return 1
for service in $services; do for service in $services; do
[ "${entries[$service]}" ] && continue || entries[$service]=1 [ "${entries[$service]}" ] && continue || entries[$service]=1
if cla_contains "$service" "${services[@]}"; then if cla_contains "$service" "${services[@]}"; then
@ -2329,6 +2445,11 @@ EOF
[ "$SOURCED" ] && return 0 [ "$SOURCED" ] && return 0
# if [[ "$UID" != 0 ]]; then
# die "'$exname' requires root permissions (for now). Please run as root."
# fi
if [ -z "$DISABLE_SYSTEM_CONFIG_FILE" ]; then if [ -z "$DISABLE_SYSTEM_CONFIG_FILE" ]; then
if [ -r /etc/default/charm ]; then if [ -r /etc/default/charm ]; then
. /etc/default/charm . /etc/default/charm
@ -2362,9 +2483,12 @@ export DOCKER_HOST_NET DOCKER_HOST_IP
## Argument parsing ## Argument parsing
## ##
services=()
remainder_args=() remainder_args=()
compose_opts=() compose_opts=()
action_opts=() action_opts=()
services_args=()
pos_arg_ct=0
no_hooks= no_hooks=
no_init= no_init=
action= action=
@ -2410,22 +2534,41 @@ while [ "$#" != 0 ]; do
export DEBUG=true export DEBUG=true
export VERBOSE=true export VERBOSE=true
;; ;;
--dirs)
echo "CACHEDIR: $CACHEDIR"
echo "VARDIR: $VARDIR"
exit 0
;;
--dry-compose-run)
export DRY_COMPOSE_RUN=true
;;
--*|-*) --*|-*)
compose_opts+=("$1") compose_opts+=("$1")
;; ;;
*) *)
action="$1" action="$1"
stage="action" stage="action"
if DC_USAGE=$(get_docker_compose_action_usage "$action"); then
is_docker_compose_action=true
DC_MATCH_MULTI=$(get_docker_compose_multi_opts_list "$action") && DC_MATCH_MULTI=$(get_docker_compose_multi_opts_list "$action") &&
DC_MATCH_SINGLE="$(get_docker_compose_single_opts_list "$action")" DC_MATCH_SINGLE="$(get_docker_compose_single_opts_list "$action")"
if [ "$DC_MATCH_MULTI" ]; then if [ "$DC_MATCH_MULTI" ]; then
DC_MATCH_SINGLE="$DC_MATCH_SINGLE $(echo "$DC_MATCH_MULTI" | sed -r 's/( |$)/=\* /g')" DC_MATCH_SINGLE="$DC_MATCH_SINGLE $(echo "$DC_MATCH_MULTI" | sed -r 's/( |$)/=\* /g')"
fi fi
is_docker_compose_action=true
pos_args=($(echo "$DC_USAGE" | sed -r 's/\[-[^]]+\] ?//g;s/\[options\] ?//g'))
pos_args=("${pos_args[@]:1}")
# echo "USAGE: $DC_USAGE"
# echo "pos_args: ${pos_args[@]}"
# echo "MULTI: $DC_MATCH_MULTI"
# echo "SINGLE: $DC_MATCH_SINGLE"
# exit 1
else
stage="remainder"
fi
;; ;;
esac esac
;; ;;
"action")
"action") ## Only for docker-compose actions
case "$1" in case "$1" in
--help|-h) --help|-h)
no_init=true ; no_hooks=true ; no_relations=true no_init=true ; no_hooks=true ; no_relations=true
@ -2434,10 +2577,10 @@ while [ "$#" != 0 ]; do
--*|-*) --*|-*)
if [ "$is_docker_compose_action" ]; then if [ "$is_docker_compose_action" ]; then
if str_matches "$1" $DC_MATCH_MULTI; then if str_matches "$1" $DC_MATCH_MULTI; then
action_opts=("$1" "$2")
action_opts+=("$1" "$2")
shift; shift;
elif str_matches "$1" $DC_MATCH_SINGLE; then elif str_matches "$1" $DC_MATCH_SINGLE; then
action_opts=("$1")
action_opts+=("$1")
else else
err "Unknown option '$1'. Please check help." err "Unknown option '$1'. Please check help."
docker-compose "$action" --help >&2 docker-compose "$action" --help >&2
@ -2446,21 +2589,33 @@ while [ "$#" != 0 ]; do
fi fi
;; ;;
*) *)
action_posargs+=("$1")
# echo "LOOP $1 : pos_arg: $pos_arg_ct // ${pos_args[$pos_arg_ct]}"
if [[ "${pos_args[$pos_arg_ct]}" == "[SERVICE...]" ]]; then
services_args+=("$1")
elif [[ "${pos_args[$pos_arg_ct]}" == "SERVICE" ]]; then
services_args=("$1") || exit 1
stage="remainder" stage="remainder"
else
action_posargs+=("$1")
((pos_arg_ct++))
fi
;; ;;
esac esac
;; ;;
"remainder") "remainder")
remainder_args+=("$@") remainder_args+=("$@")
break 3;;
break 3
;;
esac esac
shift shift
done done
[ "${compose_opts[*]}" ] && debug "Main docker-compose opts: ${compose_opts[*]}"
[ "${action_opts[*]}" ] && debug "Action $DARKCYAN$action$NORMAL with opts: ${action_opts[*]}"
[ "${remainder_args[*]}" ] && debug "Remainder args: ${remainder_args[*]}"
[ "${services[*]}" ] && debug " ${DARKWHITE}Services:$NORMAL ${services[*]}"
[ "${compose_opts[*]}" ] && debug " ${DARKWHITE}Main docker-compose opts:$NORMAL ${compose_opts[*]}"
[ "${action_posargs[*]}" ] && debug " ${DARKWHITE}Main docker-compose pos args:$NORMAL ${action_posargs[*]}"
[ "${action_opts[*]}" ] && debug " ${DARKWHITE}Action $DARKCYAN$action$NORMAL with opts:$NORMAL ${action_opts[*]}"
[ "${remainder_args[*]}" ] && debug " ${DARKWHITE}Remainder args:$NORMAL ${remainder_args[*]}"
## ##
@ -2496,19 +2651,8 @@ charm.sanity_checks || die "Sanity checks about charm-store failed. Please corre
## Get services in command line. ## Get services in command line.
## ##
is_service_action=
case "$action" in
up|build|start|stop|config|graph)
services="$(get_default_target_services "${action_posargs[@]}" "${remainder_args[@]}")" || exit 1
orig_services="${action_posargs[@]:1}"
;;
run)
services="${action_posargs[0]}"
;;
""|down)
services=
;;
*)
if [ -z "$is_docker_compose_action" -a "$action" ]; then
if is_service_action=$(has_service_action "${action_posargs[0]}" "$action"); then if is_service_action=$(has_service_action "${action_posargs[0]}" "$action"); then
{ {
read-0 action_type read-0 action_type
@ -2522,15 +2666,25 @@ case "$action" in
;; ;;
esac esac
} < <(has_service_action "${action_posargs[0]}" "$action") } < <(has_service_action "${action_posargs[0]}" "$action")
services="${action_posargs[0]}"
services=("${action_posargs[0]}")
else else
services="$(get_default_target_services "${action_posargs[@]}")"
die "Unknown command: It doesn't match any docker-compose commands nor inner charm actions."
fi fi
;;
esac
else
if [ "$action" == "up" -a "${#services_args[@]}" == 0 ]; then
services_args=($(shyaml keys <"$COMPOSE_YML_FILE"))
fi
if [ "$action" == "config" ]; then
services_args=("${action_posargs[@]}")
fi
if [ "$is_docker_compose_action" -a "${#services_args[@]}" -gt 0 ]; then
services=($(get_master_services "${services_args[@]}")) || exit 1
action_posargs+=("${services[@]}")
fi
fi
get_docker_compose $services >/dev/null || { ## precalculate variable \$_current_docker_compose
get_docker_compose "${services[@]}" >/dev/null || { ## precalculate variable \$_current_docker_compose
err "Fails to compile base 'docker-compose.yml'" err "Fails to compile base 'docker-compose.yml'"
exit 1 exit 1
} }
@ -2546,7 +2700,7 @@ case "$action" in
full_init=true full_init=true
post_hook=true post_hook=true
;; ;;
""|down)
""|down|restart|logs|config)
full_init= full_init=
;; ;;
*) *)
@ -2572,24 +2726,22 @@ if [ "$full_init" ]; then
fi fi
export SERVICE_PACK="$services"
export SERVICE_PACK="${services[*]}"
## ##
## Docker-compose ## Docker-compose
## ##
case "$action" in case "$action" in
up|start|stop|build)
master_services=$(get_master_services $SERVICE_PACK) || exit 1
up|start|stop|build|run)
if [[ "$action" == "up" ]] && ! array_member action_opts -d; then ## force daemon mode for up if [[ "$action" == "up" ]] && ! array_member action_opts -d; then ## force daemon mode for up
action_opts=("-d" "${action_opts[@]}") action_opts=("-d" "${action_opts[@]}")
fi fi
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" $master_services
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${action_posargs[@]}" "${remainder_args[@]}"
;; ;;
run)
master_service=$(get_master_services $SERVICE_PACK) || exit 1
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "$master_service" "${remainder_args[@]}"
"")
launch_docker_compose "${compose_opts[@]}"
;; ;;
# enter) # enter)
# master_service=$(get_master_services $SERVICE_PACK) || exit 1 # master_service=$(get_master_services $SERVICE_PACK) || exit 1
@ -2601,23 +2753,27 @@ case "$action" in
;; ;;
config) config)
## removing the services ## removing the services
services=($(get_master_services "${action_posargs[@]}")) || exit 1
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${remainder_args[@]}" launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${remainder_args[@]}"
warn "Runtime configuration modification (from relations) are not included here." warn "Runtime configuration modification (from relations) are not included here."
;; ;;
down) down)
remainder_args+=("--remove-orphans")
if ! array_member action_opts --remove-orphans; then ## force daemon mode for up
debug "Adding a default argument of '--remove-orphans'"
action_opts+=("--remove-orphans")
fi
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${remainder_args[@]}" launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${remainder_args[@]}"
;; ;;
*) *)
if [ "$is_service_action" ]; then if [ "$is_service_action" ]; then
run_service_action "$SERVICE_PACK" "$action" "${remainder_args[@]}" run_service_action "$SERVICE_PACK" "$action" "${remainder_args[@]}"
else else
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${remainder_args[@]}"
launch_docker_compose "${compose_opts[@]}" "$action" "${action_opts[@]}" "${action_posargs[@]}" "${remainder_args[@]}"
fi fi
;; ;;
esac esac
if [ "$post_hook" ]; then
run_service_hook "$services" post_deploy || exit 1
if [ "$post_hook" -a "${#services[@]}" != 0 ]; then
run_service_hook "${services[@]}" post_deploy || exit 1
fi fi

285
test/test

@ -1,8 +1,5 @@
#!/bin/bash
#!- Library include
. /etc/shlib
#!-
#!/usr/bin/env bash-shlib
# -*- mode: shell-script -*-
include shunit include shunit
@ -19,18 +16,22 @@ tprog=$(readlink -f $tprog)
export PATH=".:$PATH" export PATH=".:$PATH"
short_tprog=$(basename "$tprog") short_tprog=$(basename "$tprog")
## ##
## Convenience function ## Convenience function
## ##
function init_test() {
init_test() {
test_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX) test_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
cd "$test_tmpdir" cd "$test_tmpdir"
export CACHEDIR="$test_tmpdir/.cache"
export VARDIR="$test_tmpdir/.var"
mkdir -p "$CACHEDIR"
} }
function tear_test() {
tear_test() {
rm -rf "$test_tmpdir" rm -rf "$test_tmpdir"
} }
@ -41,7 +42,7 @@ function tear_test() {
## ##
# Checking arguments # Checking arguments
function test_calling_sourcing {
test_calling_sourcing() {
assert_list <<EOF assert_list <<EOF
@ -60,7 +61,7 @@ EOF
} }
function test_mixin_functions {
test_mixin_functions() {
init_test init_test
@ -79,7 +80,14 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
test -z "\$(get_docker_compose_mixin_from_metadata testcharm)"
out="\$(get_docker_compose_mixin_from_metadata testcharm)"
expected='\
labels:
- compose.charm=testcharm'
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1
}
## -- volumes ## -- volumes
@ -99,10 +107,19 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
test "\$(get_docker_compose_mixin_from_metadata testcharm)" == "volumes:
- /tmp/DATA/testcharm/a:/a:rw
- /tmp/CONFIG/testcharm/b:/b:rw
- /tmp:/tmp:rw"
out=\$(get_docker_compose_mixin_from_metadata testcharm) || exit 1
expected="\
labels:
- compose.charm=testcharm
volumes:
- /tmp/DATA/testcharm/a:/a:rw
- /tmp/CONFIG/testcharm/b:/b:rw
- /tmp:/tmp:rw"
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1
}
## -- docker-compose ## -- docker-compose
@ -118,11 +135,15 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
out="\$(get_docker_compose_mixin_from_metadata testcharm)"
test "\$out" == "volumes:
- /any:/vol
entrypoint: any" || {
echo -e "** get_docker_compose_mixin_from_metadata testcharm:\n\$out"
out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1
expected="\
entrypoint: any
labels:
- compose.charm=testcharm
volumes:
- /any:/vol"
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1 exit 1
} }
@ -137,25 +158,39 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
test "\$(get_docker_compose_mixin_from_metadata testcharm)" == "image: toto"
out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1
expected="\
image: toto
labels:
- compose.charm=testcharm"
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1
}
## -- build ## -- build
export CHARM_STORE=$test_tmpdir export CHARM_STORE=$test_tmpdir
mkdir -p $test_tmpdir/testcharm/build mkdir -p $test_tmpdir/testcharm/build
cat <<EOF2 > $test_tmpdir/testcharm/metadata.yml cat <<EOF2 > $test_tmpdir/testcharm/metadata.yml
# XXX new content to invalidate cache
EOF2 EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
out="\$(get_docker_compose_mixin_from_metadata testcharm)" || {
echo "Failed"
out="\$(get_docker_compose_mixin_from_metadata testcharm)" || exit 1
expected="\
build: $test_tmpdir/testcharm/build
labels:
- compose.charm=testcharm"
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1 exit 1
} }
echo "\$out"
test "\$out" == "build: testcharm/build"
## -- subordinate with image ## -- subordinate with image
@ -413,8 +448,7 @@ EOF
} }
function test_get_compose_service_def {
test_get_compose_service_def() {
init_test init_test
@ -426,11 +460,17 @@ function test_get_compose_service_def {
export CHARM_STORE=$test_tmpdir export CHARM_STORE=$test_tmpdir
mkdir $test_tmpdir/www mkdir $test_tmpdir/www
touch $test_tmpdir/www/metadata.yml
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
test "\$(get_compose_service_def www)" == "charm: www"
out="\$(get_compose_service_def www)"
test "\$out" == "charm: www" || {
echo OUTPUT:
echo "\$out"
false
}
## -- Simple (no docker-compose, no charm dir) ## -- Simple (no docker-compose, no charm dir)
@ -488,13 +528,13 @@ EOF
## ##
## ##
## ##
function test_get_master_charm_for_service() {
function test_get_master_service_for_service() {
init_test init_test
assert_list <<EOF assert_list <<EOF
### Testing get_master_charm_for_service
### Testing get_master_service_for_service
## -- Simple (no subordinate) ## -- Simple (no subordinate)
@ -506,12 +546,13 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
test "\$(_get_master_charm_for_service www)" == "www"
test "\$(get_master_service_for_service www)" == "www"
## -- subordinate ## -- subordinate
export CHARM_STORE=$test_tmpdir export CHARM_STORE=$test_tmpdir
mkdir -p $test_tmpdir/{www,mysql} mkdir -p $test_tmpdir/{www,mysql}
touch $test_tmpdir/mysql/metadata.yml
cat <<EOF2 > $test_tmpdir/www/metadata.yml cat <<EOF2 > $test_tmpdir/www/metadata.yml
subordinate: true subordinate: true
requires: requires:
@ -534,7 +575,7 @@ EOF2
_setup_state_dir _setup_state_dir
COMPOSE_YML_FILE=$test_tmpdir/compose.yml COMPOSE_YML_FILE=$test_tmpdir/compose.yml
test "\$(_get_master_charm_for_service www)" == "mysql"
test "\$(get_master_service_for_service www)" == "mysql"
EOF EOF
} }
@ -567,11 +608,11 @@ EOF2
. "$tprog" . "$tprog"
_setup_state_dir _setup_state_dir
out=\$(_get_docker_compose_service_mixin www)
test "\$out" == "www:
volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" || {
out=\$(_get_docker_compose_service_mixin www | shyaml get-value www.volumes)
[[ "\$out" == "\
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" ]] || {
echo -e "** _get_docker_compose_service_mixin www:\n\$out"; exit 1 echo -e "** _get_docker_compose_service_mixin www:\n\$out"; exit 1
} }
@ -587,6 +628,8 @@ config-resources:
- /tmp/b - /tmp/b
EOF2 EOF2
touch $test_tmpdir/mysql/metadata.yml
cat <<EOF2 > $test_tmpdir/compose.yml cat <<EOF2 > $test_tmpdir/compose.yml
www: www:
charm: www charm: www
@ -600,12 +643,22 @@ EOF2
_setup_state_dir _setup_state_dir
COMPOSE_YML_FILE=$test_tmpdir/compose.yml COMPOSE_YML_FILE=$test_tmpdir/compose.yml
test "\$(_get_docker_compose_service_mixin www)" == "www:
out="\$(_get_docker_compose_service_mixin www)" || exit 1
[ "\$out" == "www:
labels:
- compose.service=www
- compose.master-service=www
- compose.project=\$(basename "$test_tmpdir")
- compose.charm=www
links: links:
- mysql - mysql
volumes: volumes:
- /www/tmp/a:/tmp/a:rw - /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw"
- /www/tmp/b:/tmp/b:rw" ] || {
echo -e "OUT:\n\$out"
exit 1
}
## -- compose, subordinate ## -- compose, subordinate
@ -636,10 +689,20 @@ EOF2
_setup_state_dir _setup_state_dir
COMPOSE_YML_FILE=$test_tmpdir/compose.yml COMPOSE_YML_FILE=$test_tmpdir/compose.yml
test "\$(_get_docker_compose_service_mixin www)" == "mysql:
out="\$(_get_docker_compose_service_mixin www)" || exit 1
expected="mysql:
labels:
- compose.service=www
- compose.master-service=mysql
- compose.project=$(basename "$test_tmpdir")
- compose.charm=www
volumes: volumes:
- /www/tmp/a:/tmp/a:rw - /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" - /www/tmp/b:/tmp/b:rw"
[ "\$out" == "\$expected" ] || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1
}
EOF EOF
} }
@ -669,10 +732,13 @@ _setup_state_dir
out=\$(get_docker_compose www) out=\$(get_docker_compose www)
echo "OUT:" echo "OUT:"
echo "\$out" echo "\$out"
test "\$out" == "www:
volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw"
out=\$(echo "\$out" | shyaml get-value services.www.volumes)
echo "OUT volumes:"
echo "\$out"
test "\$out" == "\\
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw"
## -- simple with docker-compose ## -- simple with docker-compose
@ -709,36 +775,44 @@ COMPOSE_YML_FILE=$test_tmpdir/compose.yml
_setup_state_dir _setup_state_dir
out=\$(get_docker_compose www)
test "\$out" == "www:
volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" || {
out=\$(get_docker_compose www | shyaml get-value services.www.volumes)
test "\$out" == "\\
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" || {
echo -e "** get_docker_compose www:\n\$out" echo -e "** get_docker_compose www:\n\$out"
exit 1 exit 1
} }
out=\$(_get_docker_compose_links web_site)
test "\$out" == "www:
links:
- mysql" || {
echo -e "** _get_docker_compose_links web_site:\n\$out"
out=\$(get_docker_compose_links web_site | shyaml get-value web_site.links)
test "\$out" == "- mysql" || {
echo -e "** get_docker_compose_links web_site:\n\$out"
exit 1 exit 1
} }
out=\$(get_docker_compose web_site)
test "\$out" == "\
out=\$(get_docker_compose web_site | shyaml get-value services)
expected="\
mysql: mysql:
labels:
- compose.service=mysql
- compose.master-service=mysql
- compose.project=$(basename "$test_tmpdir")
- compose.charm=mysql
volumes: volumes:
- /mysql/tmp/c:/tmp/c:rw - /mysql/tmp/c:/tmp/c:rw
- /mysql/tmp/d:/tmp/d:rw - /mysql/tmp/d:/tmp/d:rw
www:
web_site:
labels:
- compose.service=web_site
- compose.master-service=web_site
- compose.project=$(basename "$test_tmpdir")
- compose.charm=www
links: links:
- mysql - mysql
volumes: volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw" || {
echo -e "** get_docker_compose web_site:\n\$out"
- /web_site/tmp/a:/tmp/a:rw
- /web_site/tmp/b:/tmp/b:rw"
test "\$out" == "\$expected" || {
echo -e "** get_docker_compose web_site:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1 exit 1
} }
@ -786,12 +860,16 @@ _setup_state_dir
! get_docker_compose www || exit 1 ! get_docker_compose www || exit 1
# volumes gets mixed # volumes gets mixed
test "\$(get_docker_compose web_site)" == "mysql:
volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw
- /mysql/tmp/c:/tmp/c:rw
- /mysql/tmp/d:/tmp/d:rw"
out="\$(get_docker_compose web_site | shyaml get-value services.mysql.volumes)"
test "\$out" == "\
- /web_site/tmp/a:/tmp/a:rw
- /web_site/tmp/b:/tmp/b:rw
- /mysql/tmp/c:/tmp/c:rw
- /mysql/tmp/d:/tmp/d:rw" || {
echo -e "OUT:\n\$out"
exit 1
}
## -- subordinate with complex features ## -- subordinate with complex features
@ -844,15 +922,30 @@ _setup_state_dir
#! get_docker_compose www || exit 1 #! get_docker_compose www || exit 1
# volumes gets mixed # volumes gets mixed
test "\$(get_docker_compose web_site)" == "mysql:
entrypoint: custom-entrypoint
volumes:
- /www/tmp/a:/tmp/a:rw
- /www/tmp/b:/tmp/b:rw
- /special-volume-from-www:/special-volume-from-www
- /mysql/tmp/c:/tmp/c:rw
- /mysql/tmp/d:/tmp/d:rw
- /special-volume-from-mysql:/special-volume-from-mysql"
out="\$(get_docker_compose web_site | shyaml get-value services.mysql)"
expected="\
entrypoint: custom-entrypoint
labels:
- compose.service=web_site
- compose.charm=www
- compose.service=mysql
- compose.master-service=mysql
- compose.project=$(basename "$test_tmpdir")
- compose.charm=mysql
volumes:
- /web_site/tmp/a:/tmp/a:rw
- /web_site/tmp/b:/tmp/b:rw
- /special-volume-from-www:/special-volume-from-www
- /mysql/tmp/c:/tmp/c:rw
- /mysql/tmp/d:/tmp/d:rw
- /special-volume-from-mysql:/special-volume-from-mysql"
test "\$out" == "\$expected" || {
echo -e "DIFF:\n\$(diff <(echo "\$out") <(echo "\$expected"))"
exit 1
}
EOF EOF
tear_test tear_test
@ -1032,12 +1125,12 @@ EOF2
COMPOSE_YML_FILE=$test_tmpdir/compose.yml COMPOSE_YML_FILE=$test_tmpdir/compose.yml
_setup_state_dir _setup_state_dir
out=\$(_get_docker_compose_links "app")
out=\$(get_docker_compose_links "app")
test "\$out" == "app: test "\$out" == "app:
links: links:
- www - www
- mysql" || { - mysql" || {
echo -e "** _get_docker_compose_links:\n\$out"; exit 1
echo -e "** get_docker_compose_links:\n\$out"; exit 1
} }
@ -1048,7 +1141,7 @@ mkdir -p $test_tmpdir/{www,mysql}
cat <<EOF2 > $test_tmpdir/www/metadata.yml cat <<EOF2 > $test_tmpdir/www/metadata.yml
provides: provides:
web-proxy: web-proxy:
reverse-tech-dep: true
tech-dep: reversed
EOF2 EOF2
touch $test_tmpdir/mysql/metadata.yml touch $test_tmpdir/mysql/metadata.yml
@ -1066,26 +1159,25 @@ COMPOSE_YML_FILE=$test_tmpdir/compose.yml
_setup_state_dir _setup_state_dir
out=\$(get_charm_relation_def "www" "web-proxy") || exit 1 out=\$(get_charm_relation_def "www" "web-proxy") || exit 1
test "\$out" == "reverse-tech-dep: true" || {
test "\$out" == "tech-dep: reversed" || {
echo -e "** get_charm_relation_def:\n\$out"; exit 1 echo -e "** get_charm_relation_def:\n\$out"; exit 1
} }
out=\$(get_charm_reverse_tech_dep_relation "www" "web-proxy")
test "\$out" == "True" || {
echo -e "** get_charm_reverse_tech_dep_relation:\n\$out"; exit 1
out=\$(get_charm_tech_dep_orientation_for_relation "www" "web-proxy")
test "\$out" == "reversed" || {
echo -e "** get_charm_tech_dep_orientation_for_relation:\n\$out"; exit 1
} }
out=\$(_get_docker_compose_links "web_site")
test "\$out" == "www:
out=\$(get_docker_compose_links "web_site")
expected="www:
links: links:
- mysql" || {
echo -e "** _get_docker_compose_links:\n\$out"; exit 1
- web_site"
test "\$out" == "\$expected" || {
echo -e "** get_docker_compose_links:\n\$out\nExpected:\n\$expected"; exit 1
} }
out=\$(get_docker_compose web_site)
test "\$out" == "www:
links:
- mysql" || {
out=\$(get_docker_compose web_site | shyaml get-value services.www.links)
test "\$out" == "- web_site" || {
echo -e "** get_docker_compose:\n\$out"; exit 1 echo -e "** get_docker_compose:\n\$out"; exit 1
} }
@ -1144,23 +1236,23 @@ EOF2
assert_list <<EOF assert_list <<EOF
### Testing get_compose_config
### Testing get_compose_config - syntax validation from docker-compose
## -- no service provided (syntax validation from docker-compose)
## -- no service provided
cd "$test_tmpdir" cd "$test_tmpdir"
export DISABLE_SYSTEM_CONFIG_FILE=true export DISABLE_SYSTEM_CONFIG_FILE=true
! "$tprog" config
"$tprog" config
## -- simple service provided (syntax validation from docker-compose)
## -- simple service provided
cd "$test_tmpdir" cd "$test_tmpdir"
export DISABLE_SYSTEM_CONFIG_FILE=true export DISABLE_SYSTEM_CONFIG_FILE=true
"$tprog" config mysql "$tprog" config mysql
## -- complex service provided (syntax validation from docker-compose)
## -- complex service provided
cd "$test_tmpdir" cd "$test_tmpdir"
@ -1170,9 +1262,6 @@ export DISABLE_SYSTEM_CONFIG_FILE=true
EOF EOF
tear_test tear_test
} }
continue_on_error="0" testbench $* continue_on_error="0" testbench $*
Loading…
Cancel
Save