|
|
@ -49,6 +49,18 @@ quick_cat_file() { quick_cat_stdin < "$1"; } |
|
|
|
quick_cat_stdin() { local IFS=''; while read -r line; do echo "$line"; done ; } |
|
|
|
export -f quick_cat_file quick_cat_stdin md5_compat |
|
|
|
|
|
|
|
clean_cache() { |
|
|
|
local i=0 |
|
|
|
for f in $(ls -t "$CACHEDIR/"*.cache.* 2>/dev/null | tail -n +500); do |
|
|
|
((i++)) |
|
|
|
rm -f "$f" |
|
|
|
done |
|
|
|
if (( i > 0 )); then |
|
|
|
debug "${WHITE}Cleaned cache:${NORMAL} Removed $((i)) elements (current cache size is $(du -sh "$CACHEDIR" | cut -f 1))" |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
trap_add "EXIT" clean_cache |
|
|
|
|
|
|
|
usage="$exname CHARM"' |
|
|
@ -330,6 +342,219 @@ get_file() { |
|
|
|
export -f get_file |
|
|
|
|
|
|
|
|
|
|
|
## |
|
|
|
## Common database lib |
|
|
|
## |
|
|
|
|
|
|
|
_clean_docker() { |
|
|
|
local _DB_NAME="$1" container_id="$2" |
|
|
|
( |
|
|
|
set +e |
|
|
|
debug "Removing container $_DB_NAME" |
|
|
|
docker stop "$container_id" |
|
|
|
docker rm "$_DB_NAME" |
|
|
|
rm -vf "/tmp/${_DB_NAME}.state" |
|
|
|
) |
|
|
|
} |
|
|
|
export -f _clean_docker |
|
|
|
|
|
|
|
|
|
|
|
are_files_locked_in_dir() { |
|
|
|
local dir="$1" device hdev ldev |
|
|
|
device=$(stat -c %d "$dir") || { |
|
|
|
err "Can't stat %d." |
|
|
|
return 1 |
|
|
|
} |
|
|
|
device=$(printf "%04x" $device) |
|
|
|
hdev=${device:0:2} |
|
|
|
ldev=${device:2:2} |
|
|
|
inodes=$(find "$dir" -printf ':%i:\n') |
|
|
|
|
|
|
|
found= |
|
|
|
while read -r inode; do |
|
|
|
debug "try inode:$inode" |
|
|
|
if [[ "$inodes" == *":$inode:"* ]]; then |
|
|
|
found=1 |
|
|
|
break |
|
|
|
fi |
|
|
|
done < <(cat /proc/locks | grep " $hdev:$ldev:" | sed -r "s/^.*$hdev:$ldev:([0-9]+).*$/\1/g") |
|
|
|
|
|
|
|
[ "$found" ] |
|
|
|
} |
|
|
|
export -f are_files_locked_in_dir |
|
|
|
|
|
|
|
export _PID="$$" |
|
|
|
ensure_db_docker_running () { |
|
|
|
|
|
|
|
_DB_NAME="db_${DB_NAME}_${_PID}" |
|
|
|
if [ -e "/tmp/${_DB_NAME}.state" ]; then |
|
|
|
export DOCKER_IP="$(cat "/tmp/${_DB_NAME}.state")" |
|
|
|
debug "Re-using previous docker/connection '$DOCKER_IP'." |
|
|
|
_set_db_params "$DOCKER_IP" |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
if [ -e "/tmp/${_DB_NAME}.working" ]; then |
|
|
|
## avoid recursive calls. |
|
|
|
if [ -z "$DOCKER_IP" ]; then |
|
|
|
err "Currently figuring up DOCKER_IP, please set it yourself before this call if needed." |
|
|
|
return 1 |
|
|
|
else |
|
|
|
debug "Ignoring recursive call." |
|
|
|
fi |
|
|
|
return 0 |
|
|
|
fi |
|
|
|
|
|
|
|
touch "/tmp/${_DB_NAME}.working" |
|
|
|
|
|
|
|
docker rm "$_DB_NAME" 2>/dev/null || true |
|
|
|
|
|
|
|
host_db_working_dir="$DATASTORE/${SERVICE_NAME}$DB_DATADIR" |
|
|
|
|
|
|
|
if is_db_locked; then |
|
|
|
info "Some process is using '$host_db_working_dir'. Trying to find a docker that would do this..." |
|
|
|
found= |
|
|
|
for docker_id in $(docker ps -q); do |
|
|
|
has_volume_mounted=$( |
|
|
|
docker inspect \ |
|
|
|
--format "{{range .Mounts}}{{if eq .Destination \"$DB_DATADIR\"}}{{.Source}}{{end}}{{end}}" \ |
|
|
|
"$docker_id") |
|
|
|
if [ "$has_volume_mounted" == "$host_db_working_dir" ]; then |
|
|
|
found="$docker_id" |
|
|
|
break |
|
|
|
fi |
|
|
|
done |
|
|
|
if [ -z "$found" ]; then |
|
|
|
err "Please shutdown any other docker using this directory." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
export container_id="$found" |
|
|
|
info "Found docker $docker_id is already running." |
|
|
|
else |
|
|
|
verb "Database is not locked." |
|
|
|
if ! docker_has_image "$DOCKER_BASE_IMAGE"; then |
|
|
|
docker pull "$DOCKER_BASE_IMAGE" |
|
|
|
fi |
|
|
|
|
|
|
|
docker_opts= |
|
|
|
if [ -f "$DB_PASSFILE" ]; then |
|
|
|
verb "Found and using '$DB_PASSFILE'." |
|
|
|
docker_opts="$db_docker_opts -v $SERVER_ROOT_PREFIX$DB_PASSFILE:$DB_PASSFILE" |
|
|
|
fi |
|
|
|
debug docker run -d \ |
|
|
|
--name "$_DB_NAME" \ |
|
|
|
$docker_opts \ |
|
|
|
-v "$host_db_working_dir:$DB_DATADIR" \ |
|
|
|
"$DOCKER_BASE_IMAGE" |
|
|
|
if ! container_id=$( |
|
|
|
docker run -d \ |
|
|
|
--name "$_DB_NAME" \ |
|
|
|
$docker_opts \ |
|
|
|
-v "$host_db_working_dir:$DB_DATADIR" \ |
|
|
|
"$DOCKER_BASE_IMAGE" |
|
|
|
); then |
|
|
|
err "'docker run' failed !" |
|
|
|
_clean_docker "$_DB_NAME" "$container_id" |
|
|
|
rm "/tmp/${_DB_NAME}.working" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
trap_add EXIT,ERR "_clean_docker '$_DB_NAME' '$container_id'" |
|
|
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if DOCKER_IP=$(wait_for_docker_ip "$container_id"); then |
|
|
|
echo "$DOCKER_IP" > "/tmp/${_DB_NAME}.state" |
|
|
|
debug "written /tmp/${_DB_NAME}.state" |
|
|
|
rm "/tmp/${_DB_NAME}.working" |
|
|
|
_set_db_params "$DOCKER_IP" |
|
|
|
return 0 |
|
|
|
else |
|
|
|
err "Db not found. Docker logs follows:" |
|
|
|
docker logs --tail=5 "$container_id" 2>&1 | prefix " | " >&2 |
|
|
|
rm "/tmp/${_DB_NAME}.working" |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
} |
|
|
|
export -f ensure_db_docker_running |
|
|
|
|
|
|
|
## Require to set $db_docker_opts if needed, and $DB_PASSFILE |
|
|
|
## |
|
|
|
_dcmd() { |
|
|
|
local docker_opts command="$1" |
|
|
|
shift |
|
|
|
|
|
|
|
debug "Db> $command $@" |
|
|
|
|
|
|
|
if [ -f "$DB_PASSFILE" ]; then |
|
|
|
verb "Found and using '$DB_PASSFILE'." |
|
|
|
db_docker_opts="$db_docker_opts -v $SERVER_ROOT_PREFIX$DB_PASSFILE:$DB_PASSFILE" |
|
|
|
fi |
|
|
|
|
|
|
|
debug docker run -i --rm \ |
|
|
|
$db_docker_opts \ |
|
|
|
--entrypoint "$command" "$DOCKER_BASE_IMAGE" $db_cmd_opts "$@" |
|
|
|
docker run -i --rm \ |
|
|
|
$db_docker_opts \ |
|
|
|
--entrypoint "$command" "$DOCKER_BASE_IMAGE" $db_cmd_opts "$@" |
|
|
|
} |
|
|
|
export -f _dcmd |
|
|
|
|
|
|
|
|
|
|
|
## Executes code through db |
|
|
|
dcmd () { |
|
|
|
|
|
|
|
[ "$DB_NAME" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_NAME." |
|
|
|
[ "$DB_DATADIR" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_DATADIR." |
|
|
|
[ "$DB_PASSFILE" ] || print_syntax_error "$FUNCNAME: You must provide \$DB_PASSFILE." |
|
|
|
[ "$_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'." |
|
|
|
|
|
|
|
ensure_db_docker_running </dev/null || return 1 |
|
|
|
|
|
|
|
_dcmd "$@" |
|
|
|
} |
|
|
|
export -f dcmd |
|
|
|
|
|
|
|
## Warning: requires a ``ddb`` matching current database to be checked |
|
|
|
wait_for_docker_ip() { |
|
|
|
local name=$1 timeout=5 timeout_count=0 DOCKER_IP= DB_OK= |
|
|
|
while [ -z "$DOCKER_IP" ]; do |
|
|
|
sleep 1 |
|
|
|
DOCKER_IP=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' "$name" 2>/dev/null) |
|
|
|
verb "[1/2] Waiting for docker $name... ($[timeout_count + 1]/$timeout)" |
|
|
|
((timeout_count++)) || true |
|
|
|
if [ "$timeout_count" == "$timeout" ]; then |
|
|
|
err "${RED}timeout error${NORMAL}(${timeout}s):"\ |
|
|
|
"Could not find '$name' docker" \ |
|
|
|
"container's IP." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
done |
|
|
|
verb "[1/2] Found docker $name IP: $DOCKER_IP" |
|
|
|
timeout_count=0 |
|
|
|
DB_OK= |
|
|
|
_set_db_params "$DOCKER_IP" |
|
|
|
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):"\ |
|
|
|
"Could not connect to db on $DOCKER_IP." \ |
|
|
|
"container's IP." |
|
|
|
return 1 |
|
|
|
fi |
|
|
|
done |
|
|
|
verb "[2/2] Db is ready !" |
|
|
|
echo "$DOCKER_IP" |
|
|
|
return 0 |
|
|
|
} |
|
|
|
export -f wait_for_docker_ip |
|
|
|
|
|
|
|
## |
|
|
|
## Arrays |
|
|
|
## |
|
|
|