Browse Source

new: [vps] make {odoo,cyclos} actions are compatible with more general environments

Signed-off-by: Valentin Lab <valentin.lab@kalysto.org>
rc1
Valentin Lab 2 years ago
parent
commit
69a6416ae0
  1. 309
      bin/vps

309
bin/vps

@ -495,13 +495,110 @@ mailcow:get_default_backup_host_ident() {
echo "$dest $ident"
}
compose:service:containers() {
local project="$1" service="$2"
docker ps \
--filter label="com.docker.compose.project=$project" \
--filter label="compose.master-service=$service" \
--format="{{.ID}}"
}
export -f compose:service:containers
compose:service:container_one() {
local project="$1" service="$2" container_id
{
read-0a container_id || {
err "service ${DARKYELLOW}$service${NORMAL} has no running container."
return 1
}
if read-0a _; then
err "service ${DARKYELLOW}$service${NORMAL} has more than one running container."
return 1
fi
} < <(compose:service:containers "$project" "$service")
echo "$container_id"
}
export -f compose:service:container_one
compose:service:container_first() {
local project="$1" service="$2" container_id
{
read-0a container_id || {
err "service ${DARKYELLOW}$service${NORMAL} has no running container."
return 1
}
if read-0a _; then
warn "service ${DARKYELLOW}$service${NORMAL} has more than one running container."
fi
} < <(compose:service:containers "$project" "$service")
echo "$container_id"
}
export -f compose:service:container_first
compose:charm:containers() {
local project="$1" charm="$2"
docker ps \
--filter label="com.docker.compose.project=$project" \
--filter label="compose.charm=$charm" \
--format="{{.ID}}"
}
export -f compose:charm:containers
compose:charm:container_one() {
local project="$1" charm="$2" container_id
{
read-0a container_id || {
err "charm ${DARKPINK}$charm${NORMAL} has no running container in project '$project'."
return 1
}
if read-0a _; then
err "charm ${DARKPINK}$charm${NORMAL} has more than one running container."
return 1
fi
} < <(compose:charm:containers "$project" "$charm")
echo "$container_id"
}
export -f compose:charm:container_one
compose:charm:container_first() {
local project="$1" charm="$2" container_id
{
read-0a container_id || {
warn "charm ${DARKYELLOW}$charm${NORMAL} has no running container in project '$project'."
}
if read-0a _; then
warn "charm ${DARKYELLOW}$charm${NORMAL} has more than one running container."
fi
} < <(compose:charm:containers "$project" "$charm")
echo "$container_id"
}
export -f compose:charm:container_first
compose:get_url() {
local service="$1"
(
set -o pipefail
cat "/var/lib/compose/relations/myc/${service}-frontend/web-proxy/data" |
shyaml get-value url
) || {
local project_name="$1" service="$2" data_file network ip
data_file="/var/lib/compose/relations/${project_name}/${service}-frontend/web-proxy/data"
if [ -e "$data_file" ]; then
(
set -o pipefail
cat "$data_file" | shyaml get-value url
)
else
## Assume there are no frontend relation here, the url is direct IP
container_id=$(compose:service:container_one "${project_name}" "${service}") || return 1
network_ip=$(docker:container:network_ip_one "${container_id}") || return 1
IFS=":" read -r network ip <<<"$network_ip"
echo "http://$ip"
fi || {
err "Failed querying ${service} to frontend relation to get url."
return 1
}
@ -509,19 +606,90 @@ compose:get_url() {
export -f compose:get_url
compose:container:service() {
local container="$1" service
if ! service=$(docker:container:label "$container" "compose.service"); then
err "Failed to get service name from container ${container}."
return 1
fi
if [ -z "$service" ]; then
err "No service found for container ${container}."
return 1
fi
echo "$service"
}
export -f compose:container:service
compose:psql() {
local dbname="$1"
docker exec -i myc_postgres_1 psql -U postgres "$dbname"
local project_name="$1" dbname="$2"
container_id=$(compose:charm:container_one "$project_name" "postgres") || return 1
docker exec -i "${container_id}" psql -U postgres "$dbname"
}
export -f compose:psql
compose:pgm() {
local project_name="$1" container_network_ip container_ip container_network
shift
container_id=$(compose:charm:container_one "$project_name" "postgres") || return 1
service_name=$(compose:container:service "$container_id") || return 1
image_id=$(docker:container:image "$container_id") || return 1
container_network_ip=$(docker:container:network_ip_one "$container_id") || return 1
IFS=":" read -r container_network container_ip <<<"$container_network_ip"
pgpass="/srv/datastore/data/${service_name}/var/lib/postgresql/data/pgpass"
local final_pgm_docker_run_opts+=(
-u 0 -e prefix_pg_local_command=" "
--network "${container_network}"
-e PGHOST="$container_ip"
-e PGUSER=postgres
-v "$pgpass:/root/.pgpass"
"${pgm_docker_run_opts[@]}"
)
cmd=(docker run --rm \
"${final_pgm_docker_run_opts[@]}" \
"${image_id}" pgm "$@"
)
echo "${cmd[@]}"
"${cmd[@]}"
}
export -f compose:pgm
postgres:dump() {
local project_name="$1" src="$2" dst="$3"
(
settmpdir PGM_TMP_LOCATION
pgm_docker_run_opts=('-v' "${PGM_TMP_LOCATION}:/tmp/dump")
compose:pgm "$project_name" cp -f "$src" "/tmp/dump/dump.gz" &&
mv "$PGM_TMP_LOCATION/dump.gz" "$dst"
) || return 1
}
export -f postgres:dump
postgres:restore() {
local project_name="$1" src="$2" dst="$3"
full_src_path=$(readlink -e "$src") || exit 1
(
pgm_docker_run_opts=('-v' "${full_src_path}:/tmp/dump.gz")
compose:pgm "$project_name" cp -f "/tmp/dump.gz" "$dst"
) || return 1
}
export -f postgres:restore
cyclos:set_root_url() {
local dbname="$1" cyclos_service="$2" url
local project_name="$1" dbname="$2" url="$3"
url=$(compose:get_url "${cyclos_service}") || return 1
echo "UPDATE configurations SET root_url = '$url';" |
compose:psql "$dbname" || {
compose:psql "$project_name" "$dbname" || {
err "Failed to set cyclos url value in '$dbname' database."
return 1
}
@ -529,9 +697,30 @@ cyclos:set_root_url() {
export -f cyclos:set_root_url
compose:project_name() {
if [ -z "$PROJECT_NAME" ]; then
PROJECT_NAME=$(compose --get-project-name) || {
err "Couldn't get project name."
return 1
}
if [ -z "$PROJECT_NAME" -o "$PROJECT_NAME" == "orphan" ]; then
err "Couldn't get project name, probably because 'compose.yml' wasn't found."
echo " Please ensure to either configure a global 'compose.yml' or run this command" >&2
echo " in a compose project (with 'compose.yml' on the top level directory)." >&2
return 1
fi
export PROJECT_NAME
fi
echo "$PROJECT_NAME"
}
export -f compose:project_name
compose:get_cron_docker_cmd() {
local cron_line cmd_line docker_cmd
if ! cron_line=$(docker exec myc_cron_1 cat /etc/cron.d/rsync-backup | grep "\* \* \*"); then
project_name=$(compose:project_name) || return 1
if ! cron_line=$(docker exec "${project_name}"_cron_1 cat /etc/cron.d/rsync-backup | grep "\* \* \*"); then
err "Can't find cron_line in cron container."
echo " Have you forgotten to run 'compose up' ?" >&2
return 1
@ -559,9 +748,10 @@ compose:get_cron_docker_cmd() {
compose:recover-target() {
local backup_host="$1" ident="$2" src="$3" dst="$4" service_name="${5:-rsync-backup}"
local backup_host="$1" ident="$2" src="$3" dst="$4" service_name="${5:-rsync-backup}" project_name
project_name=$(compose:project_name) || return 1
docker_image="myc_${service_name}"
docker_image="${project_name}_${service_name}"
if ! docker_has_image "$docker_image"; then
compose build "${service_name}" || {
err "Couldn't find nor build image for service '$service_name'."
@ -820,13 +1010,14 @@ set_errlvl() { return "${1:-1}"; }
cmdline.spec:backup:cmd:compose:run() {
local cron_line args
project_name=$(compose:project_name) || return 1
docker_cmd=$(compose:get_cron_docker_cmd) || return 1
echo "${WHITE}Launching:${NORMAL} docker exec -i myc_cron_1 $docker_cmd"
echo "${WHITE}Launching:${NORMAL} docker exec -i "${project_name}_cron_1" $docker_cmd"
{
{
eval "docker exec -i myc_cron_1 $docker_cmd" | sed -r "s/^/ ${GRAY}|${NORMAL} /g"
eval "docker exec -i \"${project_name}_cron_1\" $docker_cmd" | sed -r "s/^/ ${GRAY}|${NORMAL} /g"
set_errlvl "${PIPESTATUS[0]}"
} 3>&1 1>&2 2>&3 | sed -r "s/^/ $DARKRED\!$NORMAL /g"
set_errlvl "${PIPESTATUS[0]}"
@ -888,22 +1079,23 @@ cmdline.spec:odoo:cmd:restart:run() {
local out odoo_service
odoo_service="${opt_service:-odoo}"
project_name=$(compose:project_name) || return 1
if ! out=$(docker restart "myc_${odoo_service}_1" 2>&1); then
if ! out=$(docker restart "${project_name}_${odoo_service}_1" 2>&1); then
if [[ "$out" == *"no matching entries in passwd file" ]]; then
warn "Catched docker bug. Restarting once more."
if ! out=$(docker restart "myc_${odoo_service}_1"); then
err "Can't restart container myc_${odoo_service}_1 (restarted twice)."
if ! out=$(docker restart "${project_name}_${odoo_service}_1"); then
err "Can't restart container ${project_name}_${odoo_service}_1 (restarted twice)."
echo " output:" >&2
echo "$out" | prefix " ${GRAY}|${NORMAL} " >&2
exit 1
fi
else
err "Couldn't restart container myc_${odoo_service}_1 (and no restart bug detected)."
err "Couldn't restart container ${project_name}_${odoo_service}_1 (and no restart bug detected)."
exit 1
fi
fi
info "Container myc_${odoo_service}_1 was ${DARKGREEN}successfully${NORMAL} restarted."
info "Container ${project_name}_${odoo_service}_1 was ${DARKGREEN}successfully${NORMAL} restarted."
}
@ -921,7 +1113,8 @@ cmdline.spec:odoo:cmd:restore:run() {
odoo_service="${opt_service:-odoo}"
if [[ "$ZIP_DUMP_LOCATION" == "http://"* ]]; then
if [[ "$ZIP_DUMP_LOCATION" == "http://"* ]] ||
[[ "$ZIP_DUMP_LOCATION" == "https://"* ]]; then
settmpdir ZIP_TMP_LOCATION
tmp_location="$ZIP_TMP_LOCATION/dump.zip"
curl -k -s -L "$ZIP_DUMP_LOCATION" > "$tmp_location" || {
@ -1015,11 +1208,11 @@ cmdline.spec:odoo:cmd:set-cyclos-url:run() {
dbname=${opt_database:-odoo}
cyclos_service="${opt_service:-cyclos}"
URL=$(compose:get_url "${cyclos_service}") || exit 1
project_name=$(compose:project_name) || exit 1
URL=$(compose:get_url "${project_name}" "${cyclos_service}") || exit 1
echo "UPDATE res_company SET cyclos_server_url = '$URL/api' WHERE id=1;" |
compose:psql "$dbname" || {
compose:psql "$project_name" "$dbname" || {
err "Failed to set cyclos url value in '$dbname' database."
exit 1
}
@ -1043,13 +1236,18 @@ cmdline.spec:cyclos:cmd:dump:run() {
cyclos_service="${opt_service:-cyclos}"
cyclos_database="${opt_database:-cyclos}"
project_name=$(compose:project_name) || exit 1
container_id=$(compose:service:container_one "$project_name" "${cyclos_service}") || exit 1
Wrap -d "stop ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker stop "$container_id" || exit 1
Wrap -d "stop container 'myc_${cyclos_service}_1'" -- \
docker stop "myc_${cyclos_service}_1" || exit 1
Wrap -d "Dump postgres database '${cyclos_database}'." -- \
pgm cp "$cyclos_database" "$DUMP_GZFILE" || exit 1
Wrap -d "start container 'myc_${cyclos_service}_1'." -- \
docker start "myc_${cyclos_service}_1" || exit 1
postgres:dump "${project_name}" "$cyclos_database" "$DUMP_GZFILE" || exit 1
Wrap -d "start ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker start "${container_id}" || exit 1
}
@ -1066,13 +1264,23 @@ cmdline.spec:cyclos:cmd:restore:run() {
cyclos_service="${opt_service:-cyclos}"
cyclos_database="${opt_database:-cyclos}"
project_name=$(compose:project_name) || exit 1
url=$(compose:get_url "${project_name}" "${cyclos_service}") || return 1
container_id=$(compose:service:container_one "$project_name" "${cyclos_service}") || exit 1
if [[ "$GZ_DUMP_LOCATION" == "http://"* ]]; then
if [[ "$GZ_DUMP_LOCATION" == "http://"* ]] ||
[[ "$GZ_DUMP_LOCATION" == "https://"* ]]; then
settmpdir GZ_TMP_LOCATION
tmp_location="$GZ_TMP_LOCATION/dump.gz"
Wrap -d "get '$GZ_DUMP_LOCATION'" <<EOF
curl -k -s -L "$GZ_DUMP_LOCATION" > "$tmp_location" || exit 1
Wrap -d "get '$GZ_DUMP_LOCATION'" <<EOF || exit 1
## Note that curll version before 7.76.0 do not have
curl -k -s -L "$GZ_DUMP_LOCATION" --fail \\
> "$tmp_location" || {
echo "Error fetching ressource. Is url correct ?" >&2
exit 1
}
if [[ "\$(dd if="$tmp_location" count=2 bs=1 2>/dev/null |
hexdump -v -e "/1 \"%02x\"")" != "1f8b" ]]; then
@ -1089,27 +1297,27 @@ EOF
exit 1
}
Wrap -d "stop container 'myc_${cyclos_service}_1'" -- \
docker stop "myc_${cyclos_service}_1" || exit 1
Wrap -d "stop ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker stop "$container_id" || exit 1
## XXXvlab: making the assumption that the postgres username should
## be the same as the cyclos service selected (which is the default,
## but not always the case).
Wrap -d "restore postgres database '${cyclos_database}'." -- \
pgm cp -f "$GZ_DUMP_LOCATION" "${cyclos_service}@${cyclos_database}" || exit 1
postgres:restore "$project_name" "$GZ_DUMP_LOCATION" "${cyclos_service}@${cyclos_database}" || exit 1
## ensure that the database is not locked
## XXXvlab: 70 is uid of user postgres, this avoids the docker bug
## but introduce hardwritten value
Wrap -d "check and remove database lock if any" <<EOF
echo "delete from database_lock;" | compose:psql "${cyclos_database}" || exit 1
Wrap -d "check and remove database lock if any" <<EOF || exit 1
echo "delete from database_lock;" | compose:psql "${project_name}" "${cyclos_database}"
EOF
Wrap -d "set root url" -- \
cyclos:set_root_url "$cyclos_database" "$cyclos_service" || exit 1
Wrap -d "set root url to '$url'" -- \
cyclos:set_root_url "${project_name}" "${cyclos_database}" "${url}" || exit 1
Wrap -d "start container 'myc_${cyclos_service}_1'." -- \
docker start "myc_${cyclos_service}_1" || exit 1
Wrap -d "start ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker start "${container_id}" || exit 1
}
@ -1124,19 +1332,20 @@ cmdline.spec:cyclos:cmd:set-root-url:run() {
cyclos_database=${opt_database:-cyclos}
cyclos_service="${opt_service:-cyclos}"
project_name=$(compose:project_name) || exit 1
url=$(compose:get_url "${project_name}" "${cyclos_service}") || exit 1
container_id=$(compose:service:container_one "${project_name}" "${cyclos_service}") || exit 1
Wrap -d "stop container 'myc_${cyclos_service}_1'" -- \
docker stop "myc_${cyclos_service}_1" || exit 1
Wrap -d "stop ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker stop "$container_id" || exit 1
Wrap -d "set root url" -- \
cyclos:set_root_url "$cyclos_database" "$cyclos_service" || exit 1
Wrap -d "set root url to '$url'" -- \
cyclos:set_root_url "${project_name}" "${cyclos_database}" "${url}" || exit 1
Wrap -d "start container 'myc_${cyclos_service}_1'." -- \
docker start "myc_${cyclos_service}_1" || exit 1
Wrap -d "start ${DARKYELLOW}${cyclos_service}${NORMAL}'s container" -- \
docker start "${container_id}" || exit 1
}
cmdline::parse "$@"
Loading…
Cancel
Save