8 Commits

  1. 4
      Dockerfile
  2. 515
      bin/compose-core

4
Dockerfile

@ -15,11 +15,11 @@ ENV KAL_SHLIB_ARRAY_VERSION="0.2.0" \
KAL_SHLIB_CACHE_VERSION="0.0.1" \
KAL_SHLIB_CHARM_VERSION="0.5.3" \
KAL_SHLIB_CMDLINE_VERSION="0.0.9" \
KAL_SHLIB_COMMON_VERSION="0.5.1" \
KAL_SHLIB_COMMON_VERSION="0.5.2" \
KAL_SHLIB_CONFIG_VERSION="0.0.2" \
KAL_SHLIB_CORE_VERSION="0.7.0" \
KAL_SHLIB_FIREWALL_VERSION="0.2.0" \
KAL_SHLIB_DOCKER_VERSION="0.0.9" \
KAL_SHLIB_DOCKER_VERSION="0.0.10" \
KAL_SHLIB_OTHER_VERSION="0.2.2" \
KAL_SHLIB_PRETTY_VERSION="0.4.3"

515
bin/compose-core

@ -2734,6 +2734,81 @@ service:state() {
}
export -f service:state
charm:upstream-version() {
local charm="$1" version cache_file="$state_tmpdir/$FUNCNAME.cache.$1" path
if [ -e "$cache_file" ]; then
{
read-0 errlvl
cat
} <"$cache_file"
return $errlvl
fi
(
if ! mkdir "$cache_file.lock" 2>/dev/null; then
while true; do
sleep 0.1
[ -d "${cache_file}.lock" ] || break
done
if [ -e "$cache_file" ]; then
{
read-0 errlvl
if [ "$errlvl" == 0 ]; then
cat
else
cat >&2
fi
} <"$cache_file"
return $errlvl
fi
return 1
fi
trap_add EXIT,ERR "rmdir \"${cache_file}\".lock"
if ! path=$(charm.has_direct_action "$charm" "upstream-versions"); then
touch "$cache_file"
return 0
fi
rm -f "${cache_file}.wip"
touch "${cache_file}.wip"
(
version=$("$path" -l 1)
errlvl=$?
if [ "$errlvl" != 0 ]; then
err "Action ${WHITE}upstream-versions${NORMAL} failed for ${DARKPINK}$charm${NORMAL}."
return $errlvl
fi
if path=$(charm.has_direct_action "$charm" "upstream-version-normalize"); then
version=$("$path" "$version")
errlvl=$?
if [ "$errlvl" != 0 ]; then
err "Failed to normalize upstream version for ${DARKPINK}$charm${NORMAL}."
return $errlvl
fi
fi
echo "$version"
) > "${cache_file}.wip" 2>&1
errlvl=$?
p0 "$errlvl" > "${cache_file}"
if [ "$errlvl" != 0 ]; then
cat "${cache_file}.wip" | tee -a "${cache_file}" >&2
rm "${cache_file}.wip"
return $errlvl
fi
cat "${cache_file}.wip" | tee -a "${cache_file}"
rm "${cache_file}.wip"
)
}
export -f charm:upstream-version
service:upstream-version() {
local service="$1" version
charm=$(get_service_charm "$service") || return $?
version=$(charm:upstream-version "$charm") || return $?
e "$version"
}
export -f service:upstream-version
_get_charm_metadata_uses() {
local metadata="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(printf "%s\0" "$@" | md5_compat)"
if [ -e "$cache_file" ]; then
@ -5270,15 +5345,26 @@ Usage: status [options] [SERVICE...]
Options:
-h, --help Print this message and quit
-a, --all Display status of all services
(removes all filter, and will add a
'root' first column by default)
-c, --column Column to display, can provide several
separated by commas, or option can be repeated.
-a, --all Display status of all services (removes all
filter, and will add a 'root' first column by
default)
-c, --column Columns to display, can provide several separated
by commas, or option can be repeated. You can add
a sign prefix to the name of the column to force
the alignment of the column (+: right, -: left),
(default: ${state_columns_default_msg})
-f, --filter Filter services by a key=value pair,
separated by commas or can be repeated.
(default: --filter root=yes)
-r, --raw Raw data output (no colors nor alignment)
-0 Separate field with NUL char. Implies raw data
output.
"
while read-0 arg; do
case "$arg" in
@ -5286,6 +5372,19 @@ Options:
echo "$help"
exit 0
;;
--raw|-r|-0)
state_raw_output="$arg";
## check if any state_columns have alignements specs
for col in "${state_columns[@]}"; do
if [[ "$col" == [-+]* ]]; then
err "Cannot use $arg and provide columns with alignment specs."
exit 1
fi
done
if [[ "$arg" == "-0" ]]; then
state_raw_output_nul=1
fi
;;
--all|-a)
if [ "${#state_services[@]}" -gt 0 ]; then
err "Cannot use --all and provide services at the same time."
@ -5300,10 +5399,19 @@ Options:
--column|-c)
read-0 value
if [[ "$value" == *,* ]]; then
state_columns+=(${value//,/ })
state_columns_candidate=(${value//,/ })
else
state_columns+=("$value")
state_columns_candidate=("$value")
fi
if [[ -n "$state_raw_output" ]]; then
for col in "${state_columns_candidate[@]}"; do
if [[ "$col" == [-+]* ]]; then
err "Cannot use ${state_raw_output} and provide columns with alignment specs."
exit 1
fi
done
fi
state_columns+=("${state_columns_candidate[@]}")
;;
--filter|-f)
if [ "${#state_services[@]}" -gt 0 ]; then
@ -5363,12 +5471,24 @@ aexport remainder_args
## Actual code
##
if [ -n "$DEBUG" ]; then
Elt "compute hashes"
start=$(time_now)
fi
COMPOSE_YML_FILE=$(get_compose_yml_location) || exit 1
COMPOSE_YML_CONTENT=$(get_compose_yml_content) || exit 1
COMPOSE_YML_CONTENT_HASH=$(compose:yml:hash) || exit 1
CHARM_STORE_HASH=$(charm.store_metadata_hash) || exit 1
COMBINED_HASH=$(H "$COMPOSE_YML_CONTENT_HASH" "$CHARM_STORE_HASH") || exit 1
export COMPOSE_YML_FILE COMPOSE_YML_CONTENT COMPOSE_YML_CONTENT_HASH CHARM_STORE_HASH COMBINED_HASH
if [ -n "$DEBUG" ]; then
elapsed="$(time_elapsed $start "$(time_now)")" || exit 1
print_info "$(printf "%.3fs" "$elapsed")"
Feedback
fi
charm.sanity_checks || die "Sanity checks about charm-store failed. Please correct."
##
@ -5637,37 +5757,250 @@ if [ "${PIPESTATUS[0]}" != 0 ]; then
fi
[ "$action" == "build" ] && exit 0
state:fields:resolve-parallel() {
local cols rowsservice jobs state_msg out errlvl col
first_job=1
tick_pid=
concurrent_jobs=0
MAX_CONCURRENT_JOBS=$((3 + $(nproc)))
rows=()
cols=()
while [ "$#" -gt 0 ]; do
case "$1" in
--) shift; rows=("$@"); break;;
*) cols+=("$1") ;;
esac
shift
done
for col in "${cols[@]}"; do
for service in "${rows[@]}"; do
if [ "$concurrent_jobs" -ge "$MAX_CONCURRENT_JOBS" ]; then
wait -n # -p job_id ## not supported in this version of bash
## job list is not accurate, but the number of elt is
((concurrent_jobs--))
fi
(
p0 "$service" "$col" "-1" "" ## started
out=$(
case "${col//_/-}" in
root)
if [[ " ${compose_yml_services[*]} " == *" ${service} "* ]]; then
echo "1"
else
echo "0"
fi
;;
name) e "$service" ;;
charm) get_service_charm "$service" ;;
state) service:state "$service" ;;
type) get_service_type "$service" ;;
upstream-version) service:upstream-version "$service" ;;
*)
if has_service_action "$service" "get-$col" >/dev/null; then
state_msg=$(run_service_action "$service" "get-$col") || exit 1
if [[ "$state_msg" == *$'\n'* ]]; then
e "${state_msg%%$'\n'*}"
else
e "${state_msg}"
fi
fi
;;
esac 2>&1
)
errlvl="$?"
p0 "$service" "$col" "$errlvl" "$out"
) &
jobs=("${jobs[@]}" $!)
((concurrent_jobs++))
if [ -n "$first_job" ]; then
## launch tick
(
while true; do
sleep 0.1
p0 "" "" "" ""
done
) &
tick_pid=$!
first_job=
fi
done
done
wait "${jobs[@]}"
if [ -n "$tick_pid" ]; then
kill "$tick_pid"
fi
}
export -f state:fields:resolve-parallel
if [ "$action" == "status" ]; then
if [ -n "$DEBUG" ]; then
start=$(time_now)
fi
if ! [ -t 1 ]; then
state_raw_output=1
fi
if [[ -n "${state_all_services}" ]] || [[ "${#state_filters[@]}" -gt 0 ]]; then
compose_yml_services=($(compose:yml:root:services)) || exit 1
fi
if [[ -n "${state_all_services}" ]]; then
state_columns=("root" ${state_columns[@]})
fi
state_columns_raw=()
for col in "${state_columns[@]}"; do
if [[ "$col" == "-"* ]]; then
col=${col#-}
if [[ "$col" =~ ^[+-] ]]; then
col=${col:1}
fi
state_columns_raw+=("$col")
state_columns_raw+=("${col//-/_}")
done
state_columns_align=""
for col in "${state_columns[@]}"; do
if [[ "$col" == "-"* ]]; then
state_columns_align+="-"
elif [[ "$col" == "+"* ]]; then
state_columns_align+="+"
else
state_columns_align+="-"
case "${col//_/-}" in
version|upstream-version) state_columns_align+="+";;
*) state_columns_align+="-";;
esac
fi
done
declare -A state_columns_idx=()
declare -A filter_idx=()
filter_cols=()
non_filter_cols=("${state_columns_raw[@]}")
for filter in "${state_filters[@]}"; do
IFS="=" read -r key value <<<"$filter"
if [[ " ${non_filter_cols[*]} " == *" $key "* ]]; then
## remove from non_filter_cols
non_filter_cols=(${non_filter_cols[*]/$key})
fi
state_columns_idx["$col"]="${#filter_cols[@]}"
filter_cols+=("${key}")
done
tot_nb_cols=$(( ${#non_filter_cols[@]} + ${#filter_cols[@]} ))
## make services_idx
declare -A services_idx=()
idx=0
for service in "${services_args[@]}"; do
services_idx["$service"]=$((idx++))
done
## make state_columns_idx
idx=0
for col in "${non_filter_cols[@]}"; do
state_columns_idx["$col"]=$((${#filter_cols[@]} + idx++))
done
values=() ## all values
new_service_args=("${services_args[@]}") ## will remove service not satisfying filters
while read-0 service col E out; do
if [[ " ${new_service_args[*]} " != *" $service "* ]]; then
continue
fi
col_index="${state_columns_idx[$col]}"
service_index="${services_idx[$service]}"
values[service_index * tot_nb_cols + col_index]="$out"
## check if all filter are valuated and satisfied
for filter in "${state_filters[@]}"; do
IFS="=" read -r key value <<<"$filter"
col_index="${state_columns_idx[$key]}"
if [ -z "${values[$((service_index * tot_nb_cols + col_index))]}" ]; then
break
fi
if [ "${values[$((service_index * tot_nb_cols + col_index))]}" != "$value" ]; then
new_service_args=(${new_service_args[*]/"$service"})
break
fi
done
done < <(state:fields:resolve-parallel "${filter_cols[@]}" -- "${services_args[@]}")
services_args=("${new_service_args[@]}")
if [ "${#services_args[@]}" == 0 ]; then
warn "No services found matching the filters." >&2
exit 0
fi
spinner_chars="⠋⠙⠸⠴⠤⠦⠇"
spinner_idx=0
SPINNERGRAY=$'\e[38;5;16;48;5;234m'
SPINNERRUNNING=$'\e[38;5;28;48;5;234m'
first_draw=1
last_draw=
if [ -z "$state_raw_output" ]; then
echo -en "\e[?25l"; stty -echo 2>/dev/null
trap_add EXIT,ERR "echo -en '\e[?25h'; stty echo 2>/dev/null"
fi
errors=()
declare -A errors_hash_idx=()
error_idx=0
values_valued=0
values_total=$(( ${#services_args[@]} * ${#state_columns_raw[@]} ))
values_threshold=$(( values_total / 2 ))
while read-0 service col E out; do
if [ -n "$service" ]; then
col_index="${state_columns_idx[$col]}"
service_index="${services_idx[$service]}"
if [[ "$E" -gt 0 ]]; then
error_hash=$(H "$col" "$E" "$out")
matching_error_idx="${errors_hash_idx[$error_hash]}"
if [[ -z "${matching_error_idx}" ]]; then
errors+=("$error_idx:$service:$col:$E:$out")
out="!Err[$((error_idx))]"
errors_hash_idx["$error_hash"]="$error_idx"
error_idx=$((error_idx + 1))
else
## find the error to add the service
error="${errors[$matching_error_idx]}"
error="${error#*:}"
error_service="${error%%:*}"
error_tail="${error#*:}"
errors[matching_error_idx]="$matching_error_idx:$error_service,$service:$error_tail"
out="!Err[$((matching_error_idx))]"
fi
elif [[ "$E" == -1 ]]; then
values[service_index * tot_nb_cols + col_index]=$'\t'
continue
fi
values[service_index * tot_nb_cols + col_index]="$out"
values_valued=$((values_valued + 1))
if [[ "$values_valued" == "$values_total" ]]; then
last_draw=1
else
continue
fi
fi
[ -n "$state_raw_output" ] && continue
[[ $((values_valued)) -lt $((values_threshold)) ]] && continue
## Draw table
if [ -n "$first_draw" ]; then
first_draw=
full_table=""
else
## move up one line per service
full_table=$'\e'"[${#services_args[@]}A"
fi
spinner_idx=$(( (spinner_idx + 1) % ${#spinner_chars} ))
while read-0-err E "${state_columns_raw[@]}"; do
values=()
line_values=()
for col in "${state_columns_raw[@]}"; do
color=
value="${!col}"
read -r -- value_trim <<<"${!col}"
case "$col" in
case "${value_trim}" in
"N/A") color=gray ;;
"!Err"*) color=darkred ;;
"⠿") color=spinnergray ;;
*)
if [[ "$spinner_chars" == *"$value_trim"* ]]; then
color=spinnerrunning
else
case "${col//_/-}" in
root)
case "$value_trim" in
0) value=" ";;
@ -5692,90 +6025,130 @@ if [ "$action" == "status" ]; then
esac
;;
*)
if [[ "${value_trim}" == "N/A" ]]; then
color=gray
;;
esac
fi
;;
esac
color="${color^^}"
if [ -n "$color" ]; then
values+=("${!color}$value${NORMAL}")
line_values+=("${!color}$value${NORMAL}")
else
values+=("$value")
line_values+=("$value")
fi
done
first=1
for value in "${values[@]}"; do
full_line=""
for value in "${line_values[@]}"; do
if [ -n "$first" ]; then
first=
else
printf " "
full_line+=" "
fi
printf "%s" "$value"
full_line+="$value"
done
printf "\n"
full_table+="$full_line"$'\e[K\n'
done < <(
set -o pipefail
filter_cols=()
for filter in "${state_filters[@]}"; do
IFS="=" read -r key value <<<"$filter"
## if not already in state_columns_raw
[[ " ${state_columns_raw[*]} " == *" $key "* ]] ||
filter_cols+=("$key")
done
for service in "${services_args[@]}"; do
declare -A values=()
for col in "${state_columns_raw[@]}" "${filter_cols[@]}"; do
case "$col" in
root)
if [[ " ${compose_yml_services[*]} " == *" ${service} "* ]]; then
value="1"
for col in "${state_columns_raw[@]}"; do
col_index="${state_columns_idx[$col]}"
service_index="${services_idx[$service]}"
value_idx="$((service_index * tot_nb_cols + col_index))"
if ! [[ -v "values[value_idx]" ]]; then
p0 " ⠿ "
continue
fi
value="${values[value_idx]}"
if [[ "$value" == $'\t' ]]; then
p0 " ${spinner_chars:$spinner_idx:1} "
elif [ -z "$value" ]; then
p0 "N/A"
else
value="0"
p0 "$value"
fi
;;
name) value="$service" ;;
charm)
value=$(get_service_charm "$service") || { echo 1; exit 1; }
;;
state)
value=$(service:state "$service") || { echo 1; exit 1; }
;;
type)
value=$(get_service_type "$service") || { echo 1; exit 1; }
;;
*)
if has_service_action "$service" "get-$col" >/dev/null; then
state_msg=$(run_service_action "$service" "get-$col") || { echo 1; exit 1 ; }
if [[ "$state_msg" == *$'\n'* ]]; then
value="${state_msg%%$'\n'*}"
## XXXvlab: For now, these are not used, but we could
## display them in additional lines (in same "cell")
msgs="${state_msg#*$'\n'}"
done
done | {
if [ -z "$state_raw_output" ]; then
col-0:normalize:size "${state_columns_align}"
else
value=${state_msg}
cat
fi
}
echo 0
)
printf "%s" "$full_table"
if [ "$E" != 0 ]; then
err "Unexpected failure while drawing table"
exit $E
fi
done < <(state:fields:resolve-parallel "${non_filter_cols[@]}" -- "${services_args[@]}")
for error in "${errors[@]}"; do
echo "" >&2
idx=${error%%:*}; error=${error#*:}
service=${error%%:*}; error=${error#*:}
col=${error%%:*}; error=${error#*:}
E=${error%%:*}; error=${error#*:}
service_list_str=""
services=(${service//,/ })
first=1
for service in "${services[@]}"; do
if [ -n "$first" ]; then
first=
else
value="N/A"
service_list_str+=", "
fi
;;
esac
values["$col"]="$value"
service_list_str+="${DARKYELLOW}$service${NORMAL}"
done
for filter in "${state_filters[@]}"; do
IFS="=" read -r key value <<<"$filter"
[[ "${values[$key]}" != "$value" ]] &&
continue 2
echo "${RED}Error${DARKRED}[$idx]:${NORMAL} while computing" \
"${WHITE}$col${NORMAL} for $service_list_str" >&2
echo "$error" | prefix " ${GRAY}|${NORMAL} " >&2
echo " ${GRAY}..${NORMAL} ${WHITE}Exited${NORMAL} with errorlevel ${DARKRED}$E${NORMAL}" >&2
done
if [[ "${#errors[@]}" -gt 0 ]]; then
if [ -n "$DEBUG" ]; then
Elt "table computation ${DARKRED}failed${NORMAL}"
elapsed="$(time_elapsed $start "$(time_now)")" || exit 1
print_info "${elapsed}s"
Feedback
fi
exit 1
fi
if [ -n "$state_raw_output" ]; then
for service in "${services_args[@]}"; do
first=1
for col in "${state_columns_raw[@]}"; do
p0 "${values[$col]}"
col_index="${state_columns_idx[$col]}"
service_index="${services_idx[$service]}"
value_idx="$((service_index * tot_nb_cols + col_index))"
value="${values[$value_idx]}"
if [ -n "$first" ]; then
first=
else
if [ -n "$state_raw_output_nul" ]; then
printf "\0"
else
printf " "
fi
fi
printf "%s" "$value"
done
done | col-0:normalize:size "${state_columns_align}"
echo 0
)
if [ "$E" != 0 ]; then
echo "E: '$E'" >&2
exit 1
if [ -n "$state_raw_output_nul" ]; then
printf "\0"
else
printf "\n"
fi
done
fi
if [ -n "$DEBUG" ]; then
Elt "table computation ${GREEN}successful${NORMAL}"
elapsed="$(time_elapsed $start "$(time_now)")" || exit 1
print_info "${elapsed}s"
Feedback
fi
exit 0
fi

Loading…
Cancel
Save