|
|
@ -82,6 +82,9 @@ $WHITE$exname$NORMAL reads '/etc/compose.conf' for global variables, and |
|
|
|
" |
|
|
|
|
|
|
|
|
|
|
|
time_now() { date +%s.%3N; } |
|
|
|
time_elapsed() { echo "scale=3; $2 - $1" | bc; } |
|
|
|
|
|
|
|
## XXXvlab: this doesn't seem to work when 'compose' is called in |
|
|
|
## a hook of a charm. |
|
|
|
#[[ "${BASH_SOURCE[0]}" == "" ]] && SOURCED=true |
|
|
@ -101,6 +104,8 @@ else |
|
|
|
fi |
|
|
|
export VARDIR CACHEDIR |
|
|
|
|
|
|
|
export SERVICE_STATE_PATH=${SERVICE_STATE_PATH:-/var/lib/compose/state} |
|
|
|
|
|
|
|
|
|
|
|
md5_compat() { md5sum | cut -c -32; } |
|
|
|
quick_cat_file() { quick_cat_stdin < "$1"; } |
|
|
@ -159,6 +164,82 @@ wyq-r() { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
err-d () { |
|
|
|
local msg="$*" |
|
|
|
shift |
|
|
|
err "$msg" |
|
|
|
print:traceback 1 |
|
|
|
} |
|
|
|
|
|
|
|
print:traceback() { |
|
|
|
local omit_level="${1:-0}" |
|
|
|
if [ -z "$DEBUG" ]; then |
|
|
|
echo " Note: traceback available if you provide {--debug|-d} option." >&2 |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
echo "${WHITE}Traceback (most recent call last):${NORMAL}" >&2 |
|
|
|
local i |
|
|
|
for ((i=${#FUNCNAME[@]} - 1; i > "$omit_level"; i--)); do |
|
|
|
local file="${BASH_SOURCE[$i]}" |
|
|
|
local line="${BASH_LINENO[$i - 1]}" |
|
|
|
local func="${FUNCNAME[$i]}" |
|
|
|
|
|
|
|
if [[ -f "$file" ]]; then |
|
|
|
# Get total number of lines in the file |
|
|
|
local total_lines |
|
|
|
total_lines=$(wc -l < "$file") |
|
|
|
|
|
|
|
# Calculate start and end lines for context |
|
|
|
local start_line=$((line - 2)) |
|
|
|
local end_line=$((line + 2)) |
|
|
|
[[ $start_line -lt 1 ]] && start_line=1 |
|
|
|
[[ $end_line -gt $total_lines ]] && end_line=$total_lines |
|
|
|
|
|
|
|
# Extract context lines |
|
|
|
mapfile -s $((start_line - 1)) -n $((end_line - start_line + 1)) context_lines < "$file" |
|
|
|
|
|
|
|
# Calculate minimal indentation |
|
|
|
local min_indent=9999 |
|
|
|
for line_text in "${context_lines[@]}"; do |
|
|
|
if [[ -n "$line_text" ]]; then |
|
|
|
# Get leading whitespace |
|
|
|
local leading_whitespace="${line_text%%[![:space:]]*}" |
|
|
|
local indent=${#leading_whitespace} |
|
|
|
if [[ $indent -lt $min_indent ]]; then |
|
|
|
min_indent=$indent |
|
|
|
fi |
|
|
|
fi |
|
|
|
done |
|
|
|
|
|
|
|
# Remove minimal indentation from each line |
|
|
|
for idx in "${!context_lines[@]}"; do |
|
|
|
context_lines[$idx]="${context_lines[$idx]:$min_indent}" |
|
|
|
done |
|
|
|
else |
|
|
|
context_lines=("<source unavailable>") |
|
|
|
min_indent=0 |
|
|
|
start_line=1 |
|
|
|
end_line=1 |
|
|
|
fi |
|
|
|
|
|
|
|
# Print the traceback frame |
|
|
|
echo " File \"$file\", line $line, in ${WHITE}$func${NORMAL}:" |
|
|
|
|
|
|
|
# Print the context with line numbers |
|
|
|
local current_line=$start_line |
|
|
|
for context_line in "${context_lines[@]}"; do |
|
|
|
context_line="${context_line%$'\n'}" |
|
|
|
if [[ $current_line -eq $line ]]; then |
|
|
|
echo " ${DARKYELLOW}$current_line${NORMAL} ${context_line}" |
|
|
|
else |
|
|
|
echo " ${DARKGRAY}$current_line${NORMAL} ${context_line}" |
|
|
|
fi |
|
|
|
((current_line++)) |
|
|
|
done |
|
|
|
done >&2 |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clean_cache() { |
|
|
|
local i=0 |
|
|
|
for f in $(ls -t "$CACHEDIR/"*.cache.* 2>/dev/null | tail -n +500); do |
|
|
@ -1378,7 +1459,6 @@ export -f _get_service_charm_cached |
|
|
|
get_service_charm () { |
|
|
|
local service="$1" |
|
|
|
if [ -z "$service" ]; then |
|
|
|
echo ${FUNCNAME[@]} >&2 |
|
|
|
print_syntax_error "$FUNCNAME: Please specify a service as first argument." |
|
|
|
return 1 |
|
|
|
fi |
|
|
@ -2398,16 +2478,21 @@ export -f get_compose_relations |
|
|
|
|
|
|
|
|
|
|
|
get_all_services() { |
|
|
|
local cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$GLOBAL_ALL_RELATIONS_HASH")" \ |
|
|
|
s rn ts rc td services |
|
|
|
local services compose_yml_services service |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS_HASH" ]; then |
|
|
|
err-d "Can't access global \$GLOBAL_ALL_RELATIONS_HASH" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$GLOBAL_ALL_RELATIONS_HASH" "$(declare -f "$FUNCNAME")")" \ |
|
|
|
s rn ts rc td services service |
|
|
|
if [ -e "$cache_file" ]; then |
|
|
|
#debug "$FUNCNAME: SESSION cache hit $1" |
|
|
|
#debug "$FUNCNAME: cache hit $1" |
|
|
|
cat "$cache_file" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
err "Can't access global \$GLOBAL_ALL_RELATIONS" |
|
|
|
err-d "Can't access global \$GLOBAL_ALL_RELATIONS" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
|
|
|
@ -2418,8 +2503,17 @@ get_all_services() { |
|
|
|
services["$service"]=1 |
|
|
|
echo "$service" |
|
|
|
done |
|
|
|
done < <(cat "$GLOBAL_ALL_RELATIONS") > "$cache_file" |
|
|
|
done < "$GLOBAL_ALL_RELATIONS" > "$cache_file.wip" |
|
|
|
|
|
|
|
compose_yml_services=($(compose:yml:root:services)) || return 1 |
|
|
|
|
|
|
|
for service in "${compose_yml_services[@]}"; do |
|
|
|
[ "${services[$service]}" ] && continue |
|
|
|
services["$service"]=1 |
|
|
|
echo "$service" |
|
|
|
done >> "$cache_file.wip" |
|
|
|
|
|
|
|
mv "$cache_file"{.wip,} || return 1 |
|
|
|
cat "$cache_file" |
|
|
|
} |
|
|
|
export -f get_all_services |
|
|
@ -2435,7 +2529,7 @@ get_service_relations () { |
|
|
|
fi |
|
|
|
|
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
err "Can't access global \$GLOBAL_ALL_RELATIONS" |
|
|
|
err-d "Can't access global \$GLOBAL_ALL_RELATIONS" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
|
|
|
@ -2483,25 +2577,26 @@ export -f get_service_relation |
|
|
|
## (base_service, relation_config) |
|
|
|
## |
|
|
|
get_service_incoming_relations() { |
|
|
|
local service="$1" relation="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$GLOBAL_ALL_RELATIONS_HASH")" \ |
|
|
|
if [ -z "$SUBSET_ALL_RELATIONS_HASH" ]; then |
|
|
|
err-d "Expected \$SUBSET_ALL_RELATIONS_HASH to be set." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
local service="$1" relation="$2" \ |
|
|
|
cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$SUBSET_ALL_RELATIONS_HASH")" \ |
|
|
|
s rn ts rc td |
|
|
|
if [ -e "$cache_file" ]; then |
|
|
|
#debug "$FUNCNAME: SESSION cache hit $1" |
|
|
|
debug "$FUNCNAME: SESSION cache hit $1" |
|
|
|
cat "$cache_file" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
err "Can't access global \$GLOBAL_ALL_RELATIONS" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
|
|
|
|
while read-0 s rn ts rc _td; do |
|
|
|
[[ "$ts" == "$service" ]] || continue |
|
|
|
[[ "$rn" == "$relation" ]] || continue |
|
|
|
relation_data_file=$(get_relation_data_file "$s" "$ts" "$rn" "$rc") || return 1 |
|
|
|
printf "%s\0" "$s" "$(cat "$relation_data_file")" |
|
|
|
done < "$GLOBAL_ALL_RELATIONS" > "$cache_file" |
|
|
|
|
|
|
|
printf "%s\0" "$s" "$(cat "$relation_data_file")" || return 1 |
|
|
|
debug "Found relation $rn from $s to $ts" >&2 |
|
|
|
done < "$SUBSET_ALL_RELATIONS" > "$cache_file.wip" |
|
|
|
mv "$cache_file"{.wip,} || return 1 |
|
|
|
cat "$cache_file" |
|
|
|
} |
|
|
|
export -f get_service_incoming_relations |
|
|
@ -2841,14 +2936,170 @@ _out_after_value_from_def() { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
get_all_relations () { |
|
|
|
local cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$(declare -p without_relations)")" \ |
|
|
|
services |
|
|
|
get_all_compose_yml_service() { |
|
|
|
if [ -z "$COMPOSE_YML_CONTENT_HASH" ]; then |
|
|
|
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || { |
|
|
|
err "Failed to get compose yml hash" |
|
|
|
return 1 |
|
|
|
} |
|
|
|
fi |
|
|
|
local cache_file="$CACHEDIR/$FUNCNAME.cache.$COMPOSE_YML_CONTENT_HASH" |
|
|
|
if [ -e "${cache_file}" ]; then |
|
|
|
#debug "$FUNCNAME: cache hit: ${cache_file}" |
|
|
|
cat "${cache_file}" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
compose_yml_content=$(get_compose_yml_content) || return 1 |
|
|
|
printf "%s" "${compose_yml_content}" | shyaml keys-0 2>/dev/null > "${cache_file}.wip" || { |
|
|
|
err "Failed to get keys of compose content." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
mv "${cache_file}"{.wip,} || return 1 |
|
|
|
cat "${cache_file}" |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
## Outputs all relations array. |
|
|
|
_service:all:relations_cached() { |
|
|
|
local services service E |
|
|
|
services=($(compose:yml:root:services)) || return 1 |
|
|
|
get_all_relations "${services[@]}" || return 1 |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
## Outputs all relations array. |
|
|
|
service:all:relations() { |
|
|
|
if [ -z "$COMPOSE_YML_CONTENT_HASH" ]; then |
|
|
|
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || { |
|
|
|
err "Failed to get compose yml hash." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
fi |
|
|
|
local cache_file="$CACHEDIR/$FUNCNAME.cache.$COMPOSE_YML_CONTENT_HASH" |
|
|
|
if [ -e "${cache_file}" ]; then |
|
|
|
# debug "$FUNCNAME: SESSION cache hit $1" |
|
|
|
cat "${cache_file}" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
_service:all:relations_cached > "${cache_file}.wip" || { |
|
|
|
err-d "Failed to compute all relations." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
mv "${cache_file}"{.wip,} || return 1 |
|
|
|
cat "${cache_file}" |
|
|
|
} |
|
|
|
|
|
|
|
_service:all:relations_hash_cached() { |
|
|
|
if [ -z "$COMPOSE_YML_CONTENT_HASH" ]; then |
|
|
|
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || { |
|
|
|
err "Failed to get compose yml hash." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
fi |
|
|
|
local cache_file="$CACHEDIR/$FUNCNAME.cache.$COMPOSE_YML_CONTENT_HASH" \ |
|
|
|
hash |
|
|
|
if [ -e "${cache_file}" ]; then |
|
|
|
# debug "$FUNCNAME: SESSION cache hit $1" |
|
|
|
cat "${cache_file}" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
service:all:relations > "${cache_file}.pre" || { |
|
|
|
err-d "Failed to get all relations." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
{ |
|
|
|
p0 "$(hash_get < "${cache_file}.pre")" || return 1 |
|
|
|
cat "${cache_file}.pre" |
|
|
|
rm "${cache_file}.pre" |
|
|
|
} > "${cache_file}".wip || return 1 |
|
|
|
|
|
|
|
mv "${cache_file}"{.wip,} || return 1 |
|
|
|
cat "${cache_file}" |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
## Get all relations from all services in the current compose file. |
|
|
|
## Sets GLOBAL_ALL_RELATIONS_HASH and returns all relations array. |
|
|
|
service:all:set_relations_hash() { |
|
|
|
if [ -n "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS_HASH" ]; then |
|
|
|
err "Can't access global \$GLOBAL_ALL_RELATIONS_HASH" |
|
|
|
echo " (despite \$GLOBAL_ALL_RELATIONS being set)" >&2 |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
## sets COMPOSE_YML_CONTENT_HASH |
|
|
|
_service:all:relations_hash_cached >/dev/null || return 1 |
|
|
|
{ |
|
|
|
read-0 GLOBAL_ALL_RELATIONS_HASH || return 1 |
|
|
|
export GLOBAL_ALL_RELATIONS_HASH |
|
|
|
## transfer to statedir |
|
|
|
export GLOBAL_ALL_RELATIONS="$state_tmpdir/$FUNCNAME.cache.$COMPOSE_YML_CONTENT_HASH" |
|
|
|
cat > "$GLOBAL_ALL_RELATIONS" |
|
|
|
} < <(_service:all:relations_hash_cached) |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
err "Failed to set \$GLOBAL_ALL_RELATIONS." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS_HASH" ]; then |
|
|
|
err "Failed to set \$GLOBAL_ALL_RELATIONS_HASH." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
get_subset_relations () { |
|
|
|
local service all_services services start |
|
|
|
if [ -n "$SUBSET_ALL_RELATIONS" ]; then |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
if [ -z "$GLOBAL_ALL_RELATIONS_HASH" ]; then |
|
|
|
err-d "Can't access global \$GLOBAL_ALL_RELATIONS_HASH" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
cache_hash=$(H "$@" "$GLOBAL_ALL_RELATIONS_HASH" "$(declare -f get_subset_relations)") |
|
|
|
local cache_file="$CACHEDIR/$FUNCNAME.cache.$cache_hash" |
|
|
|
if [ -e "${cache_file}" ]; then |
|
|
|
export SUBSET_ALL_RELATIONS="$cache_file" |
|
|
|
hash=$(hash_get < "$cache_file") || return 1 |
|
|
|
export SUBSET_ALL_RELATIONS_HASH="$hash" |
|
|
|
cat "${cache_file}" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
## collect all connected services first |
|
|
|
all_services=("$@") |
|
|
|
while [ "${#all_services[@]}" != 0 ]; do |
|
|
|
array_pop all_services service |
|
|
|
#debug " Getting relations for $DARKYELLOW$service$NORMAL" |
|
|
|
while read-0 s rn ts rc td; do |
|
|
|
[[ "$s" == "$service" ]] || continue |
|
|
|
#debug " adding relation $DARKBLUE$rn$NORMAL to $DARKYELLOW$ts$NORMAL" |
|
|
|
p0 "$service" "$rn" "$ts" "$rc" "$td" |
|
|
|
[ -n "${services[$ts]}" ] || all_services+=("$ts") |
|
|
|
done < "$GLOBAL_ALL_RELATIONS" |
|
|
|
services["$service"]=1 |
|
|
|
done > "$cache_file.wip" |
|
|
|
mv "$cache_file"{.wip,} || return 1 |
|
|
|
export SUBSET_ALL_RELATIONS="$cache_file" |
|
|
|
hash=$(hash_get < "$cache_file") || return 1 |
|
|
|
export SUBSET_ALL_RELATIONS_HASH="$hash" |
|
|
|
cat "$cache_file" |
|
|
|
} |
|
|
|
|
|
|
|
get_all_relations () { |
|
|
|
if [ -z "$COMPOSE_YML_CONTENT_HASH" ]; then |
|
|
|
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || return 1 |
|
|
|
fi |
|
|
|
if [ -n "$GLOBAL_ALL_RELATIONS" ]; then |
|
|
|
cat "$GLOBAL_ALL_RELATIONS" || return 1 |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
local cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$COMPOSE_YML_CONTENT_HASH" "$(declare -p without_relations)")" \ |
|
|
|
services all_services service services_uses services_provides \ |
|
|
|
changed summon required recommended optional |
|
|
|
|
|
|
|
if [ -e "${cache_file}" ]; then |
|
|
|
#debug "$FUNCNAME: SESSION cache hit $1" |
|
|
@ -2860,11 +3111,11 @@ get_all_relations () { |
|
|
|
declare -A services |
|
|
|
services_uses=() |
|
|
|
## XXXvlab: bwerk, leveraging cache to be able to get the errorlevel here. |
|
|
|
_get_services_uses "$@" || return 1 |
|
|
|
_get_services_uses "$@" >/dev/null || return 1 |
|
|
|
array_read-0 services_uses < <(_get_services_uses "$@") |
|
|
|
services_provides=() |
|
|
|
## XXXvlab: bwerk, leveraging cache to be able to get the errorlevel here. |
|
|
|
_get_services_provides "$@" || return 1 |
|
|
|
_get_services_provides "$@" >/dev/null || return 1 |
|
|
|
array_read-0 services_provides < <(_get_services_provides "$@") |
|
|
|
|
|
|
|
for service in "$@"; do |
|
|
@ -3096,7 +3347,7 @@ get_all_relations () { |
|
|
|
while read-0 p s rn ts rc td; do |
|
|
|
if [ -z "$p" ] || [ "$p" == "," ]; then |
|
|
|
relation_done["$s:$rn"]=1 |
|
|
|
# printf " .. %-30s %-30s %-30s\n" "--" "$s" "$rn" >&2 |
|
|
|
# printf " .. %-30s %-30s %-30s\n" "$s" "$ts" "$rn" >&2 |
|
|
|
printf "%s\0" "$s" "$rn" "$ts" "$rc" "$td" >> "${cache_file}.wip.final" |
|
|
|
had_new_relation=1 |
|
|
|
else |
|
|
@ -3823,25 +4074,10 @@ clean_unused_docker_compose() { |
|
|
|
export -f clean_unused_docker_compose |
|
|
|
|
|
|
|
|
|
|
|
stdin_get_hash() { |
|
|
|
local sha |
|
|
|
sha=$(sha256sum) || return 1 |
|
|
|
sha=${sha:0:64} |
|
|
|
echo "$sha" |
|
|
|
} |
|
|
|
export -f stdin_get_hash |
|
|
|
|
|
|
|
|
|
|
|
file_get_hash() { |
|
|
|
stdin_get_hash < "$1" || return 1 |
|
|
|
} |
|
|
|
export -f file_get_hash |
|
|
|
|
|
|
|
|
|
|
|
docker_compose_store() { |
|
|
|
local file="$1" sha |
|
|
|
|
|
|
|
sha=$(file_get_hash "$file") || return 1 |
|
|
|
sha=$(hash_get 64 < "$file") || return 1 |
|
|
|
project=$(get_default_project_name) || return 1 |
|
|
|
dst="/var/lib/compose/docker-compose/$sha/$project" |
|
|
|
mkdir -p "$dst" || return 1 |
|
|
@ -4015,7 +4251,7 @@ get_compose_yml_content() { |
|
|
|
COMPOSE_YML_FILE=$(get_compose_yml_location) || return 1 |
|
|
|
fi |
|
|
|
if [ -e "$COMPOSE_YML_FILE" ]; then |
|
|
|
debug "Found $WHITE$exname$NORMAL YAML file in '$COMPOSE_YML_FILE'." |
|
|
|
# debug "Found $WHITE$exname$NORMAL YAML file in '$COMPOSE_YML_FILE'." |
|
|
|
COMPOSE_YML_CONTENT=$(cat "$COMPOSE_YML_FILE") || { |
|
|
|
err "Could not read '$COMPOSE_YML_FILE'." |
|
|
|
return 1 |
|
|
@ -4046,6 +4282,37 @@ get_compose_yml_content() { |
|
|
|
} |
|
|
|
export -f get_compose_yml_content |
|
|
|
|
|
|
|
compose:yml:hash() { |
|
|
|
local cache_file="$state_tmpdir/$FUNCNAME.cache" |
|
|
|
if [ -e "$cache_file" ]; then |
|
|
|
cat "$cache_file" && |
|
|
|
touch "$cache_file" || return 1 |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
compose_yml_content=$(get_compose_yml_content) || return 1 |
|
|
|
compose_yml_hash=$(echo "$compose_yml_content" | hash_get) || return 1 |
|
|
|
|
|
|
|
e "$compose_yml_hash" | tee "$cache_file" || return 1 |
|
|
|
} |
|
|
|
export -f compose:yml:hash |
|
|
|
|
|
|
|
|
|
|
|
compose:yml:root:services() { |
|
|
|
local cache_file="$state_tmpdir/$FUNCNAME.cache" services compose_yml_content |
|
|
|
if [ -e "$cache_file" ]; then |
|
|
|
cat "$cache_file" && |
|
|
|
touch "$cache_file" || return 1 |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
compose_yml_content=$(get_compose_yml_content) || return 1 |
|
|
|
services=($(e "$compose_yml_content" | shyaml keys)) || return 1 |
|
|
|
|
|
|
|
e "${services[*]}" | tee "$cache_file" || return 1 |
|
|
|
} |
|
|
|
export -f compose:yml:root:services |
|
|
|
|
|
|
|
|
|
|
|
get_default_target_services() { |
|
|
|
local services=("$@") |
|
|
@ -4284,9 +4551,12 @@ display_commands_help() { |
|
|
|
|
|
|
|
get_docker_charm_action() { |
|
|
|
local services service charm relation_name target_service relation_config \ |
|
|
|
target_charm |
|
|
|
services=($(get_compose_yml_content | yq -r 'keys().[]' 2>/dev/null)) || return 1 |
|
|
|
NO_CONSTRAINT_CHECK=1 get_all_relations "${services[@]}" >/dev/null || return 1 |
|
|
|
target_charm services |
|
|
|
## XXXvlab: this is for get_service_relations |
|
|
|
NO_CONSTRAINT_CHECK=True service:all:set_relations_hash || { |
|
|
|
err-d "Failed to set relations hash." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
services=($(get_all_services)) || return 1 |
|
|
|
for service in "${services[@]}"; do |
|
|
|
printf "%s:\n" "$service" |
|
|
@ -4310,8 +4580,12 @@ export -f get_docker_charm_action |
|
|
|
get_docker_charm_action_help() { |
|
|
|
local services service charm relation_name target_service relation_config \ |
|
|
|
target_charm |
|
|
|
services=($(get_compose_yml_content | shyaml keys 2>/dev/null)) |
|
|
|
NO_CONSTRAINT_CHECK=1 get_all_relations "${services[@]}" >/dev/null || return 1 |
|
|
|
## XXXvlab: this is for get_service_relations |
|
|
|
NO_CONSTRAINT_CHECK=True service:all:set_relations_hash || { |
|
|
|
err-d "Failed to set relations hash." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
services=($(get_all_services)) || return 1 |
|
|
|
for service in "${services[@]}"; do |
|
|
|
out=$( |
|
|
|
charm=$(get_service_charm "$service") || return 1 |
|
|
@ -4602,6 +4876,7 @@ no_init= |
|
|
|
action= |
|
|
|
stage="main" ## switches from 'main', to 'action', 'remainder' |
|
|
|
is_docker_compose_action= |
|
|
|
is_docker_compose_action_multi_service= |
|
|
|
rebuild_relations_to_service=() |
|
|
|
color= |
|
|
|
declare -A without_relations |
|
|
@ -4741,6 +5016,12 @@ while read-0 arg; do |
|
|
|
fi |
|
|
|
pos_args=($(echo "$DC_USAGE" | sed -r 's/\[-[^]]+\] ?//g;s/\[options\] ?//g')) |
|
|
|
pos_args=("${pos_args[@]:1}") |
|
|
|
if [[ "${pos_args[0]}" == "[SERVICE...]" ]]; then |
|
|
|
is_docker_compose_action_multi_service=1 |
|
|
|
elif [[ "${pos_args[0]}" == "SERVICE" ]]; then |
|
|
|
is_docker_compose_action_multi_service=0 |
|
|
|
fi |
|
|
|
|
|
|
|
# echo "USAGE: $DC_USAGE" |
|
|
|
# echo "pos_args: ${pos_args[@]}" |
|
|
|
# echo "MULTI: $DC_MATCH_MULTI" |
|
|
@ -4799,8 +5080,22 @@ while read-0 arg; do |
|
|
|
shift |
|
|
|
done < <(cla.normalize "$@") |
|
|
|
|
|
|
|
|
|
|
|
more_actions=(status) |
|
|
|
|
|
|
|
if [[ "$action" == *" "* ]]; then |
|
|
|
err "Invalid action name containing spaces: ${DARKCYAN}$action${NORMAL}" |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
is_more_action= |
|
|
|
[[ " ${more_actions[*]} " == *" $action "* ]] && is_more_action=true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[ -n "$CACHEDIR" ] || die "No cache directory defined." |
|
|
|
[ -d "$CACHEDIR" ] || die "Cache directory '$CACHEDIR' doesn't exists." |
|
|
|
|
|
|
|
case "$action" in |
|
|
|
cache) |
|
|
|
case "${remainder_args[0]}" in |
|
|
@ -4851,8 +5146,8 @@ aexport remainder_args |
|
|
|
|
|
|
|
COMPOSE_YML_FILE=$(get_compose_yml_location) || exit 1 |
|
|
|
COMPOSE_YML_CONTENT=$(get_compose_yml_content) || exit 1 |
|
|
|
export COMPOSE_YML_FILE COMPOSE_YML_CONTENT |
|
|
|
|
|
|
|
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || exit 1 |
|
|
|
export COMPOSE_YML_FILE COMPOSE_YML_CONTENT COMPOSE_YML_CONTENT_HASH |
|
|
|
|
|
|
|
charm.sanity_checks || die "Sanity checks about charm-store failed. Please correct." |
|
|
|
|
|
|
@ -4860,7 +5155,8 @@ charm.sanity_checks || die "Sanity checks about charm-store failed. Please corre |
|
|
|
## Get services in command line. |
|
|
|
## |
|
|
|
|
|
|
|
if [ -z "$is_docker_compose_action" -a "$action" ]; then |
|
|
|
|
|
|
|
if [ -z "$is_docker_compose_action" ] && [ -z "$is_more_action" ] && [ -n "$action" ]; then |
|
|
|
action_service=${remainder_args[0]} |
|
|
|
if [ -z "$action_service" ]; then |
|
|
|
err "No such command or action: ${DARKCYAN}$action${NORMAL}" |
|
|
@ -4868,10 +5164,9 @@ if [ -z "$is_docker_compose_action" -a "$action" ]; then |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
services_args=($(compose:yml:root:services)) || return 1 |
|
|
|
## Required by has_service_action |
|
|
|
array_read-0 services_args < <(printf "%s" "$COMPOSE_YML_CONTENT" | shyaml keys-0 2>/dev/null) |
|
|
|
NO_CONSTRAINT_CHECK=1 get_all_relations "${services_args[@]}" >/dev/null || exit 1 |
|
|
|
service:all:set_relations_hash |
|
|
|
|
|
|
|
remainder_args=("${remainder_args[@]:1}") |
|
|
|
if has_service_action "$action_service" "$action" >/dev/null; then |
|
|
@ -4906,7 +5201,13 @@ else |
|
|
|
case "$action" in |
|
|
|
ps|up) |
|
|
|
if [ "${#services_args[@]}" == 0 ]; then |
|
|
|
array_read-0 services_args < <(printf "%s" "$COMPOSE_YML_CONTENT" | shyaml keys-0 2>/dev/null) |
|
|
|
services_args=($(compose:yml:root:services)) || return 1 |
|
|
|
fi |
|
|
|
;; |
|
|
|
status) |
|
|
|
services_args=("${remainder_args[@]}") |
|
|
|
if [ "${#services_args[@]}" == 0 ]; then |
|
|
|
services_args=($(compose:yml:root:services)) || return 1 |
|
|
|
fi |
|
|
|
;; |
|
|
|
config) |
|
|
@ -4918,19 +5219,117 @@ fi |
|
|
|
export COMPOSE_ACTION="$action" |
|
|
|
NO_CONSTRAINT_CHECK=True |
|
|
|
case "$action" in |
|
|
|
up) |
|
|
|
up|status) |
|
|
|
NO_CONSTRAINT_CHECK= |
|
|
|
if [ -n "$DEBUG" ]; then |
|
|
|
Elt "solve all relations" |
|
|
|
start=$(time_now) |
|
|
|
fi |
|
|
|
service:all:set_relations_hash || exit 1 |
|
|
|
|
|
|
|
if [ -n "$DEBUG" ]; then |
|
|
|
elapsed="$(time_elapsed $start "$(time_now)")" || exit 1 |
|
|
|
print_info "$(printf "%.3fs" "$elapsed")" |
|
|
|
Feedback |
|
|
|
fi |
|
|
|
|
|
|
|
all_services=($(get_all_services)) || exit 1 |
|
|
|
## check that services_args is a subset of all_services |
|
|
|
for service in "${services_args[@]}"; do |
|
|
|
[[ " ${all_services[*]} " == *" $service "* ]] || { |
|
|
|
err "Service ${DARKYELLOW}$service${NORMAL} is not defined in the current compose file." |
|
|
|
echo " Neither is is a dependency of a service in the compose file." >&2 |
|
|
|
echo " These are the services directly or indirectly available from current compose file:" >&2 |
|
|
|
for service in "${all_services[@]}"; do |
|
|
|
echo " - ${DARKYELLOW}$service${NORMAL}" >&2 |
|
|
|
done |
|
|
|
exit 1 |
|
|
|
} |
|
|
|
done |
|
|
|
;; |
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
case "$action" in |
|
|
|
up) |
|
|
|
PROJECT_NAME=$(get_default_project_name) || exit 1 |
|
|
|
|
|
|
|
## Required by get_ordered_service_dependencies |
|
|
|
array_read-0 services_args < <(printf "%s" "$COMPOSE_YML_CONTENT" | shyaml keys-0 2>/dev/null) |
|
|
|
NO_CONSTRAINT_CHECK=1 get_all_relations "${services_args[@]}" >/dev/null || exit 1 |
|
|
|
|
|
|
|
## Remove all intents (*ing states) |
|
|
|
rm -f "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/*ing || true |
|
|
|
|
|
|
|
## Notify that we have the intent to bring up all these |
|
|
|
## This will be use in inner or concurrent 'run' to include the |
|
|
|
## services that are supposed to be up. |
|
|
|
mkdir -p "$SERVICE_STATE_PATH/$PROJECT_NAME" || exit 1 |
|
|
|
services_args_deps=($(get_ordered_service_dependencies "${services_args[@]}")) || exit 1 |
|
|
|
for service in "${services_args_deps[@]}"; do |
|
|
|
mkdir -p "$SERVICE_STATE_PATH/$PROJECT_NAME"/"$service" || exit 1 |
|
|
|
[ -e "$SERVICE_STATE_PATH/$PROJECT_NAME"/"$service"/up ] || { |
|
|
|
touch "$SERVICE_STATE_PATH/$PROJECT_NAME"/"$service"/deploying || exit 1 |
|
|
|
} |
|
|
|
done |
|
|
|
## remove services not included in compose.yml anymore |
|
|
|
array_read-0 all_services < <(printf "%s" "$COMPOSE_YML_CONTENT" | shyaml keys-0 2>/dev/null) |
|
|
|
all_services_deps=($(get_ordered_service_dependencies "${all_services[@]}")) || exit 1 |
|
|
|
for service in "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/up; do |
|
|
|
[ -e "$service" ] || continue |
|
|
|
state=${service##*/} |
|
|
|
service=${service%/$state} |
|
|
|
service=${service##*/} |
|
|
|
|
|
|
|
if [[ " ${all_services_deps[*]} " != *" ${service} "* ]]; then |
|
|
|
touch "$SERVICE_STATE_PATH/$PROJECT_NAME"/"${service}"/orphaning || exit 1 |
|
|
|
fi |
|
|
|
done |
|
|
|
;; |
|
|
|
|
|
|
|
run) |
|
|
|
PROJECT_NAME=$(get_default_project_name) || return 1 |
|
|
|
if [ -d "$SERVICE_STATE_PATH/$PROJECT_NAME" ]; then |
|
|
|
## Notify that we have the intent to bring up all these |
|
|
|
## This will be use in inner or concurrent 'run' to include the |
|
|
|
## services that are supposed to be up. |
|
|
|
for service in "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/{up,deploying}; do |
|
|
|
[ -e "$service" ] || continue |
|
|
|
state=${service##*/} |
|
|
|
service=${service%/$state} |
|
|
|
service=${service##*/} |
|
|
|
## don't add if orphaning |
|
|
|
[ -e "$SERVICE_STATE_PATH/$PROJECT_NAME"/"${service}"/orphaning ] && continue |
|
|
|
## add in ${services_args[@]} if not already there |
|
|
|
if [[ " ${services_args[*]} " != *" ${service} "* ]]; then |
|
|
|
services_args+=("$service") |
|
|
|
fi |
|
|
|
done |
|
|
|
fi |
|
|
|
;; |
|
|
|
esac |
|
|
|
|
|
|
|
if [ -n "$is_docker_compose_action_multi_service" ]; then |
|
|
|
if [ -n "$DEBUG" ]; then |
|
|
|
Elt "get relation subet" |
|
|
|
start=$(time_now) |
|
|
|
fi |
|
|
|
|
|
|
|
get_all_relations "${services_args[@]}" >/dev/null || exit 1 |
|
|
|
get_subset_relations "${services_args[@]}" >/dev/null || exit 1 |
|
|
|
|
|
|
|
if [ -n "$DEBUG" ]; then |
|
|
|
elapsed="$(time_elapsed $start "$(time_now)")" || exit 1 |
|
|
|
print_info "$(printf "%.3fs" "$elapsed")" |
|
|
|
Feedback |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
if [ "$is_docker_compose_action" -a "${#services_args[@]}" -gt 0 ]; then |
|
|
|
if [ -n "$is_docker_compose_action" ] && [ "${#services_args[@]}" -gt 0 ]; then |
|
|
|
services=($(get_master_services "${services_args[@]}")) || exit 1 |
|
|
|
if [ "$action" == "up" ]; then |
|
|
|
declare -A seen |
|
|
|
for service in $(get_ordered_service_dependencies "${services_args[@]}"); do |
|
|
|
services=($(get_ordered_service_dependencies "${services_args[@]}")) || exit 1 |
|
|
|
for service in "${services[@]}"; do |
|
|
|
mservice=$(get_master_service_for_service "$service") || exit 1 |
|
|
|
[ "${seen[$mservice]}" ] && continue |
|
|
|
type="$(get_service_type "$mservice")" || exit 1 |
|
|
@ -4940,18 +5339,23 @@ if [ "$is_docker_compose_action" -a "${#services_args[@]}" -gt 0 ]; then |
|
|
|
seen[$mservice]=1 |
|
|
|
action_posargs+=("$mservice") |
|
|
|
done |
|
|
|
else |
|
|
|
elif [ "$is_docker_compose_action_multi_service" == "1" ]; then |
|
|
|
action_posargs+=("${services[@]}") |
|
|
|
elif [ "$is_docker_compose_action_multi_service" == "0" ]; then |
|
|
|
action_posargs+=("${services[0]}") ## only the first service is the legit one |
|
|
|
fi |
|
|
|
## Get rid of subordinates |
|
|
|
action_posargs=($(get_master_services "${action_posargs[@]}")) || exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
get_docker_compose "${services_args[@]}" >/dev/null || { ## precalculate variable \$_current_docker_compose |
|
|
|
err "Fails to compile base 'docker-compose.yml'" |
|
|
|
exit 1 |
|
|
|
} |
|
|
|
# if [ "$action" != "status" ]; then |
|
|
|
get_docker_compose "${services_args[@]}" >/dev/null || { ## precalculate variable \$_current_docker_compose |
|
|
|
err "Fails to compile base 'docker-compose.yml'" |
|
|
|
exit 1 |
|
|
|
} |
|
|
|
# fi |
|
|
|
|
|
|
|
|
|
|
|
## |
|
|
|
## Pre-action |
|
|
@ -4967,7 +5371,7 @@ case "$action" in |
|
|
|
full_init=true |
|
|
|
post_hook=true |
|
|
|
;; |
|
|
|
""|down|restart|logs|config|ps) |
|
|
|
""|down|restart|logs|config|ps|status) |
|
|
|
full_init= |
|
|
|
;; |
|
|
|
*) |
|
|
@ -5032,6 +5436,47 @@ fi |
|
|
|
[ "$action" == "build" ] && exit 0 |
|
|
|
|
|
|
|
|
|
|
|
service:state() { |
|
|
|
local service="$1" states state |
|
|
|
project_name=$(get_default_project_name) || return 1 |
|
|
|
states=() |
|
|
|
for state in "$SERVICE_STATE_PATH"/"$project_name"/"$service"/*; do |
|
|
|
[ -e "$state" ] || continue |
|
|
|
state=${state##*/} |
|
|
|
states+=("$state") |
|
|
|
done |
|
|
|
|
|
|
|
if [[ " ${states[*]} " == *" deploying "* ]]; then |
|
|
|
echo "deploying" |
|
|
|
elif [[ " ${states[*]} " == *" up "* ]]; then |
|
|
|
echo "up" |
|
|
|
else |
|
|
|
echo "down" |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if [ "$action" == "status" ]; then |
|
|
|
for service in "${services_args[@]}"; do |
|
|
|
msgs="" |
|
|
|
if has_service_action "$service" "get-version" >/dev/null; then |
|
|
|
version_msg=$(run_service_action "$service" "get-version") || exit 1 |
|
|
|
if [[ "$version_msg" == *$'\n'* ]]; then |
|
|
|
version="${version_msg%%$'\n'*}" |
|
|
|
msgs="${version_msg#*$'\n'}" |
|
|
|
else |
|
|
|
version=${version_msg} |
|
|
|
fi |
|
|
|
else |
|
|
|
version="N/A" |
|
|
|
fi |
|
|
|
state=$(service:state "$service") |
|
|
|
printf "%-20s %8s %10s %s\n" "$service" "$state" "$version" "$msgs" |
|
|
|
done |
|
|
|
exit 0 |
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if [ "$action" == "run" ] && [ "${#services_args}" != 0 ]; then |
|
|
|
charm=$(get_service_charm "${services_args[0]}") || exit 1 |
|
|
|
metadata=$(charm.metadata "$charm") || exit 1 |
|
|
@ -5114,6 +5559,38 @@ if [ "$action" == "run" -a "${#services_args}" != 0 ]; then |
|
|
|
fi |
|
|
|
fi |
|
|
|
|
|
|
|
case "$action" in |
|
|
|
up) |
|
|
|
## Notify that services in 'deploying' states have been deployed |
|
|
|
for service in "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/deploying; do |
|
|
|
[ -e "$service" ] || continue |
|
|
|
state=${service##*/} |
|
|
|
service=${service%/$state} |
|
|
|
service=${service##*/} |
|
|
|
|
|
|
|
mv "$SERVICE_STATE_PATH/$PROJECT_NAME"/"${service}"/{deploying,up} |
|
|
|
done |
|
|
|
## Notify that services in 'orphaning' states have been removed |
|
|
|
for service in "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/orphaning; do |
|
|
|
[ -e "$service" ] || continue |
|
|
|
state=${service##*/} |
|
|
|
service=${service%/$state} |
|
|
|
service=${service##*/} |
|
|
|
|
|
|
|
rm "$SERVICE_STATE_PATH/$PROJECT_NAME"/"${service}"/orphaning |
|
|
|
done |
|
|
|
;; |
|
|
|
down) |
|
|
|
PROJECT_NAME=$(get_default_project_name) || return 1 |
|
|
|
if [ -d "$SERVICE_STATE_PATH/$PROJECT_NAME" ]; then |
|
|
|
if ! dir_is_empty "$SERVICE_STATE_PATH/$PROJECT_NAME"; then |
|
|
|
rm -f "$SERVICE_STATE_PATH/$PROJECT_NAME"/*/* |
|
|
|
fi |
|
|
|
rmdir "$SERVICE_STATE_PATH/$PROJECT_NAME"/{*,} |
|
|
|
fi |
|
|
|
;; |
|
|
|
esac |
|
|
|
|
|
|
|
clean_unused_docker_compose || exit 1 |
|
|
|
|
|
|
|
exit "$errlvl" |