Compare commits

...

4 Commits

Author SHA1 Message Date
Valentin Lab 3cb15b9350 new: [matomo] add charm 6 months ago
Valentin Lab b8bf3ed2f5 fix: [rsync-backup-target] make target accept ``rsync`` client ``3.2.0`` 6 months ago
Boris Gallet 09fa2678a4 new: [smtp-stub] new charm 6 months ago
Valentin Lab 74c434c64f new: [postgres] add ``upgrade`` action 6 months ago
  1. 24
      matomo/hooks/init
  2. 26
      matomo/hooks/mysql_database-relation-joined
  3. 117
      matomo/hooks/post_deploy
  4. 77
      matomo/hooks/publish_dir-relation-joined
  5. 95
      matomo/lib/common
  6. 45
      matomo/metadata.yml
  7. 540
      postgres/actions/upgrade
  8. 4
      rsync-backup-target/build/src/usr/local/sbin/ssh-cmd-validate
  9. 18
      smtp-stub/hooks/smtp_server-relation-joined
  10. 11
      smtp-stub/metadata.yml

24
matomo/hooks/init

@ -0,0 +1,24 @@
#!/bin/bash
## Init is run on host
## For now it is run every time the script is launched, but
## it should be launched only once after build.
## Accessible variables are:
## - SERVICE_NAME Name of current service
## - DOCKER_BASE_IMAGE Base image from which this service might be built if any
## - SERVICE_DATASTORE Location on host of the DATASTORE of this service
## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service
. lib/common
set -e
## If no admin-password set, fail
options-get admin-password
matomo:init
matomo:config

26
matomo/hooks/mysql_database-relation-joined

@ -0,0 +1,26 @@
#!/bin/bash
. lib/common
## XXXvlab: should get location of code
#CONFIG=$(echo $COMPOSE_CONFIG | shyaml get-value $SERVICE_NAME.relations.publish-dir...)
# CONFIG="$SERVICE_DATASTORE/opt/apps/piwigo/local/config/database.inc.php"
set -e
PASSWORD="$(relation-get password)"
USER="$(relation-get user)"
DBNAME="$(relation-get dbname)"
crudini --set "$MATOMO_CONFIG_FILE" database host "\"$TARGET_SERVICE_NAME\""
crudini --set "$MATOMO_CONFIG_FILE" database username "\"$USER\""
crudini --set "$MATOMO_CONFIG_FILE" database password "\"$PASSWORD\""
crudini --set "$MATOMO_CONFIG_FILE" database dbname "\"$DBNAME\""
crudini --set "$MATOMO_CONFIG_FILE" database tables_prefix "\"\""
crudini --set "$MATOMO_CONFIG_FILE" database charset "\"utf8mb4\""
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."

117
matomo/hooks/post_deploy

@ -0,0 +1,117 @@
#!/bin/bash
. lib/common
set -e
## If no admin-password set, then don't try to pre-initialize database
admin_password=$(options-get admin-password 2>/dev/null) || exit 0
admin_email=$(options-get admin-email 2>/dev/null ) || true
CONTROL="$SERVICE_DATASTORE/.control"
## Was it already properly propagated to database ?
control=$(H "${admin_password}" "${admin_email}")
if [ -e "$CONTROL" ]; then
if [ "$control" == "$(cat "$CONTROL")" ]; then
exit 0
else
err "Changing admin password in compose file not yet supported"
exit 1
fi
fi
if ! [ -d "$MATOMO_CODE/vendor" ]; then
# XXXvlab: can't get real config here
if ! read-0 publish_dir_ts _ _ < <(get_service_relation "$SERVICE_NAME" "publish-dir"); then
err "Couldn't find relation ${DARKCYAN}publish-dir${NORMAL}."
exit 1
fi
publish_dir_relation_dir=$(get_relation_data_dir "$SERVICE_NAME" "$publish_dir_ts" "publish-dir") || {
err "Failed to find relation file"
exit 1
}
publish_dir_relation_config=$(cat "$publish_dir_relation_dir/data") || exit 2
domain=$(e "$publish_dir_relation_config" | shyaml get-value domain) || {
err "Couldn't get domain information in ${DARKCYAN}publish-dir${NORMAL} relation's data."
exit 1
}
url=$(e "$publish_dir_relation_config" | shyaml get-value url) || {
err "Couldn't get url information in ${DARKCYAN}publish-dir${NORMAL} relation's data."
exit 1
}
##
## Get domain in option of relation "publish-dir"
##
container_id=$(
for container_id in $(get_running_containers_for_service "$MASTER_BASE_SERVICE_NAME"); do
e "$container_id"
break
done
)
docker exec -i "$container_id" bash <<EOF
type -p "composer" || {
cd /tmp
curl -sS https://getcomposer.org/installer | php || {
echo "Error occured while attempting to install compose." >&2
exit 1
}
mv -v /tmp/composer.phar /usr/local/bin/composer || exit 1
}
cd /var/www/$domain &&
composer install
EOF
fi
##
## Required wizard
##
if [ "$(crudini --get "$MATOMO_CONFIG_FILE" General installation_in_progress)" == 1 ]; then
curl "$url/index.php?action=tablesCreation" >/dev/null || {
err "Table creation failed."
exit 1
}
orig_uid_gid=$(stat --format=%u:%g "$MATOMO_CONFIG_FILE")
uid_gid=$(stat --format=%u:%g "$SERVICE_DATASTORE/var/tmp/matomo/index.php")
chown "$uid_gid" "$MATOMO_CONFIG_FILE"
curl "$url/index.php?action=setupSuperUser" \
--data-urlencode login="admin" \
--data-urlencode password="$admin_password" \
--data-urlencode password_bis="$admin_password" \
--data-urlencode email="${admin_email:-admin@localhost.localnet}" \
--data-urlencode subscribe_newsletter_piwikorg="0" \
--data-urlencode subscribe_newsletter_piwikpro="0" || {
err "Setting admin account failed."
exit 1
}
chown "$orig_uid_gid" "$MATOMO_CONFIG_FILE"
crudini --set "$MATOMO_CONFIG_FILE" General installation_in_progress 0
#curl "$url/index.php?action=firstWebsiteSetup" \
# --data-urlencode siteName="$SITE_NAME" \
# --data-urlencode url="$url" \
# --data-urlencode timezone="$(cat /etc/timezone)" \
# --data-urlencode ecommerce="0" || {
# err "First Web Site Setup failed."
# exit 1
#}
fi
exit 0

77
matomo/hooks/publish_dir-relation-joined

@ -0,0 +1,77 @@
#!/bin/bash
. lib/common
set -e
#domain=$(relation-get domain) || exit 1
domain="matomo"
url=$(relation-get url) || exit 1
# location=$CONFIGSTORE/$BASE_SERVICE_NAME/var/www/$domain
upload_dir="${SERVICE_DATASTORE}/var/www/$domain/uploads"
if [ -d "$upload_dir" ]; then
uid_gid=$(stat --format=%u:%g "$upload_dir")
else
err "Upload dir '${upload_dir}' was not created. Can't continue."
exit 1
fi
dirs=(
/var/cache/matomo
/var/lib/matomo{,/assets}
/var/log/matomo
/var/tmp/matomo{,/tcpdf,/templates_c}
)
to_create=()
for dir in "${dirs[@]}"; do
fdir="${SERVICE_DATASTORE}${dir}"
if ! [ -d "$fdir" ]; then
to_create+=("$fdir")
fi
done
if [ "${#to_create[@]}" -gt 0 ]; then
mkdir -p "${to_create[@]}" &&
chown -v "$uid_gid" "${to_create[@]}" &&
chmod -v g+rwx "${to_create[@]}"
fi
config-add "
services:
$MASTER_TARGET_SERVICE_NAME:
volumes:
- $SERVICE_DATASTORE/var/tmp/matomo:/var/www/$domain/tmp:rw
- $SERVICE_DATASTORE/var/cache/matomo:/var/www/$domain/tmp/cache:rw
- $SERVICE_DATASTORE/var/lib/matomo/assets:/var/www/$domain/tmp/assets:rw
- $SERVICE_DATASTORE/var/log/matomo:/var/www/$domain/tmp/logs:rw
- $SERVICE_DATASTORE/var/tmp/matomo/tcpdf:/var/www/$domain/tmp/tcpdf:rw
- $SERVICE_DATASTORE/var/tmp/matomo/templates_c:/var/www/$domain/tmp/templates_c:rw
"
#
#cat <<EOF >> "${MATOMO_CODE}"/.env
#
#BASE_PROTOCOL=${url%%://*}
#BASE_URL=$domain
#BASE_PATH=/index.php
#
#EOF
#
#
#cat <<EOF > "${MATOMO_CODE}"/web/.htaccess
#
#Options -MultiViews
#RewriteEngine On
#RewriteRule ^js/.* - [L]
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^(.*)$ index.php/\$1 [QSA,L]
#
#EOF
#

95
matomo/lib/common

@ -0,0 +1,95 @@
# -*- mode: shell-script -*-
MATOMO_DIR="/opt/apps/matomo"
MATOMO_CODE="$SERVICE_CONFIGSTORE$MATOMO_DIR"
MATOMO_RELEASE=4.16.0
MATOMO_URL=https://github.com/matomo-org/matomo/archive/"${MATOMO_RELEASE}".tar.gz
#MATOMO_URL=https://docker.0k.io/downloads/matomo-"${MATOMO_RELEASE}".tar.gz
MATOMO_CONFIG_FILE="${MATOMO_CODE}"/config/config.ini.php
matomo:init() {
current_version=""
if [ -d "${MATOMO_CODE}" ]; then
current_version=$(cat "${MATOMO_CODE}"/.version) || {
err "Couldn't find ${MATOMO_CODE}/.version file."
echo " Your config dir is in a broken state." >&2
return 1
}
else
mkdir -p "${MATOMO_CODE}" &&
cd "${MATOMO_CODE}" &&
git init . &&
git config user.email "root@localhost" &&
git config user.name "Root" || {
err "Couldn't create directory ${MATOMO_CODE}, or init it with git."
return 1
}
fi
if [ "$current_version" != "$MATOMO_RELEASE" ]; then
cd "${MATOMO_CODE}" || return 1
[ -d "$MATOMO_CODE"/.git ] || {
err "Can't find the '.git' directory in ${MATOMO_CODE}."
return 1
}
rm -rf "$MATOMO_CODE"/* "$MATOMO_CODE"/{.version,.inited-*} || return 1
curl -L "$MATOMO_URL" | tar xzv || {
#if [ -f "$MATOMO_URL" ]; then
# git checkout HEAD
#else
# rmdir "$MATOMO_URL"
#fi
err "Couldn't download $MATOMO_URL."
return 1
}
mv matomo-*/* matomo-*/{.bowerrc,.lfsconfig} . && rmdir matomo-*
echo "$MATOMO_RELEASE" > .version
git add -A . &&
git commit -m "Release $MATOMO_RELEASE"
fi
}
matomo:config() {
[ -f "$MATOMO_CONFIG_FILE" ] || {
cat <<EOF > "$MATOMO_CONFIG_FILE"
; <?php exit; ?> DO NOT REMOVE THIS LINE
; file automatically generated or modified by Matomo; you can manually override the default values in global.ini.php by redefining them in this file.
EOF
}
crudini --get "$MATOMO_CONFIG_FILE" General salt >dev/null || {
salt=$(dd if=/dev/urandom bs=1 count=16 2>/dev/null | hexdump -v -e '/1 "%02x"')
crudini --set "$MATOMO_CONFIG_FILE" General salt \"$salt\"
crudini --set "$MATOMO_CONFIG_FILE" General installation_in_progress 1
}
}
matomo:curl() {
local url="$1"
curl "$url"
}
# matomo:wizard() {
# PAGES=(
# "systemCheck"
# "databaseSetup"
# "tablesCreation"
# "setupSuperUser"
# "setupSuperUser"
# "firstWebsiteSetup"
# "firstWebsiteSetup"
# "trackingCode"
# "finished"
# )
# matomo:curl
# }

45
matomo/metadata.yml

@ -0,0 +1,45 @@
subordinate: true
requires:
web-publishing-directory:
interface: publish-dir
scope: container
default-options:
admin-password: admin
uses:
publish-dir:
#constraint: required | recommended | optional
#auto: pair | summon | none ## default: pair
scope: container
constraint: required
auto: summon
solves:
container: "main running server"
default-options:
location: !var-expand "$CONFIGSTORE/$BASE_SERVICE_NAME/opt/apps/matomo"
data-dirs: ## write permission for web-app
- uploads
mysql-database:
#constraint: required | recommended | optional
#auto: pair | summon | none ## default: pair
constraint: required
auto: summon
solves:
database: "main storage"
backup:
constraint: recommended
auto: pair
solves:
backup: "Automatic regular backup"
default-options:
## First pattern matching wins, no pattern matching includes.
## include-patterns are checked first, then exclude-patterns
## Patterns rules:
## - ending / for directory
## - '*' authorized
## - must start with a '/', will start from $SERVICE_DATASTORE
exclude-patterns:
- "/var/cache/"
- "/var/tmp/"

540
postgres/actions/upgrade

@ -0,0 +1,540 @@
#!/bin/bash
## compose: no-hooks
if [ -z "$SERVICE_DATASTORE" ]; then
echo "This script is meant to be run through 'compose' to work properly." >&2
exit 1
fi
version=0.1
usage="$exname [-h|--help] [--force|-f] [TARGET_VERSION]"
help="
USAGE:
$usage
DESCRIPTION:
Migrate the current $SERVICE_NAME service to given target version. Don't
forget to change your \`\`compose.yml\`\` accordingly afterwards.
EXAMPLES:
$exname 21.0.0
"
no_hint=
force=
target=
while [ "$1" ]; do
case "$1" in
"--help"|"-h")
print_help >&2
exit 0
;;
"--force"|"-f")
force=yes
;;
"--color"|"-c")
ansi_color yes
;;
"--no-hint")
no_hint=yes
;;
--*|-*)
err "Unexpected optional argument '$1'"
print_usage >&2
exit 1
;;
*)
[ -z "$target" ] && { target=$1 ; shift ; continue ; }
err "Unexpected positional argument '$1'"
print_usage >&2
exit 1
;;
esac
shift
done
postgres:image:version() {
local image="$1" image_version
## XXXvlab: why not always use the pragmatic second method ?
if [[ "${image}" =~ ^.*:[0-9]+.[0-9]+.[0-9]+$ ]]; then
debug "Infering postgres's version from image's name $image"
image_version="${image#*:}"
image_version="${image_version%-myc}"
else
debug "Looking for postgres's version in image $image"
if ! out=$(docker run --rm -i --entrypoint postgres "$image" --version); then
err "Couldn't infer image '$image' postgres's version."
exit 1
fi
## Expected `$out` content of the form of: 'postgres (PostgreSQL) 10.14'
out=${out%%$'\n'*}
out=${out%%$'\r'*}
image_version=${out#"postgres (PostgreSQL) "}.0
## check if this is a version
if ! [[ "$image_version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
err "Couldn't infer image '$image' postgres's version: invalid version."
exit 1
fi
fi
echo "$image_version"
}
postgres:container:version() {
local container="$1"
if ! out=$(docker exec -i "$container" postgres --version); then
err "Couldn't infer container's '$container' postgres's version."
exit 1
fi
out=${out%%$'\n'*}
out=${out%%$'\r'*}
image_version=${out#"postgres (PostgreSQL) "}.0
## check if this is a version
if ! [[ "$image_version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
err "Couldn't infer image '$image' postgres's version: invalid version '$image_version'."
exit 1
fi
echo "$image_version"
}
postgres:container:psql () {
local container="$1"
docker exec -i "$container" psql 2>&1
}
current_image_version=$(postgres:image:version "${DOCKER_BASE_IMAGE}") || exit 1
if ! [[ "$current_image_version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
err "Current postgres version ${WHITE}$current_image_version${NORMAL} is unsupported yet."
exit 1
fi
starting_image_version=$current_image_version
## This next line has to be on stdout, it is used by `vps` to get information
info "Current postgres version: ${WHITE}$current_image_version${NORMAL}" 2>&1
last_available_versions=(
$(DEBUG= docker:tags:fetch docker.0k.io/postgres 30 '[0-9]+\.[0-9]+\.[0-9]+-myc$' |
sed -r 's/-myc$//g' |
sort -rV)
)
debug "Last available versions: ${WHITE}${last_available_versions[*]}${NORMAL}"
if [ -z "$target" ]; then
info "Latest version available: ${WHITE}${last_available_versions[0]}${NORMAL}"
fi
## XXXvlab: put this in kal-shlib-common
version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
last_upgradable_versions=()
for v in "${last_available_versions[@]}"; do
if version_gt "$v" "$current_image_version"; then
last_upgradable_versions+=("$v")
fi
done
if [ "${#last_upgradable_versions[@]}" == 0 ]; then
if [ -n "$target" ] && [ "$target" != "$current_image_version" ]; then
warn "Provided target version ${WHITE}$target${NORMAL} is likely incorrect."
echo " It is either non-existent and/or inferior to current version." >&2
fi
## This next line has to be on stdout, it is used by `vps` to get information
info "${DARKYELLOW}postgres${NORMAL} is already ${GREEN}up-to-date${NORMAL}." 2>&1
exit 0
fi
if [ -z "$target" ]; then
info "Target latest version: ${WHITE}${last_upgradable_versions[0]}${NORMAL}"
target=${last_upgradable_versions[0]}
else
if [[ " ${last_available_versions[*]} " != *" $target "* ]]; then
err "Invalid version $target selected, please specify one of:"
for v in "${last_upgradable_versions[@]}"; do
echo " - $v"
done >&2
exit 1
fi
last_upgradable_versions_filtered=()
info "Target version ${WHITE}$target${NORMAL}"
for v in "${last_upgradable_versions[@]}"; do
if [ "$target" == "$v" ] || version_gt "$target" "$v"; then
last_upgradable_versions_filtered+=("$v")
fi
done
last_upgradable_versions=("${last_upgradable_versions_filtered[@]}")
fi
debug "Last upgradable versions: ${WHITE}${last_upgradable_versions[*]}${NORMAL}"
upgrade_path=($(printf "%s\n" "${last_upgradable_versions[@]}" | sort -V | tail -n 1))
containers="$(get_running_containers_for_service "$SERVICE_NAME")"
debug "Running containers for service $SERVICE_NAME: $containers"
debug "Upgrade path: ${WHITE}${upgrade_path[@]}${NORMAL}"
container_stopped=()
if [ -n "$containers" ]; then
#err "Running container(s) for $DARKYELLOW$SERVICE_NAME$NORMAL are still running:"
for container in $containers; do
debug "Stopping container $container"
docker stop "$container" >/dev/null || {
err "Failed to stop container '$container'."
exit 1
}
## We need to delete it as otherwise, on final ``compose --debug up``
## it won't be recreated, and it will not be the correct version.
docker rm "$container" > /dev/null || {
err "Couldn't delete container '$container'."
}
container_stopped+=("$container")
done
fi
## XXXvlab: taking first container is probably not a good idea
container="$(echo "$containers" | head -n 1)"
settmpdir MIGRATION_TMPDIR
debug "Migration temporary directory: $MIGRATION_TMPDIR"
set -o pipefail
(
. "$CHARM_PATH/lib/common"
cp "$LOCAL_DB_PASSFILE" "$MIGRATION_TMPDIR/pgpass"
)
current_image="docker.0k.io/postgres:${current_image_version}-myc"
if ! docker_has_image "$current_image"; then
out=$(docker pull "$current_image" 2>&1) || {
err "Couldn't pull image $current_image:"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} " >&2
exit 1
}
fi
migration_backup_dirs=()
## In postgres, it seems it is not useful to have a path. So path is
## only one step here.
failed=
while [[ "${#upgrade_path[@]}" != 0 ]]; do
image_version="${upgrade_path[0]}"
if version_gt "$image_version" 12; then
docker_version=$(docker info --format '{{.ServerVersion}}')
if ! version_gt "$docker_version" 20.10.0; then
err "Sorry, ${DARKYELLOW}$SERVICE_NAME${NORMAL} ${WHITE}$image_version${NORMAL}" \
"require docker version >= 20.10 (current: $docker_version)"
break
fi
fi
upgrade_path=("${upgrade_path[@]:1}")
## Prevent build of image instead of pulling it
while true; do
info "Upgrading step ${WHITE}$current_image_version${NORMAL} => ${WHITE}$image_version${NORMAL}"
rm -f "$MIGRATION_TMPDIR/dump-${current_image_version}.log" &&
touch "$MIGRATION_TMPDIR/dump-${current_image_version}.log"
rm -f "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log" &&
touch "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log"
uuid=$(openssl rand -hex 16)
## Generate dump with pg_dumpall of current version:
(
{
cmd=(
compose --no-hooks
--add-compose-content="$SERVICE_NAME:
docker-compose:
image: docker.0k.io/postgres:${current_image_version}-myc
hostname: $SERVICE_NAME
"
run --rm --label "migration-uuid=$uuid" "$SERVICE_NAME"
)
echo COMMAND: "${cmd[@]}"
"${cmd[@]}" 2>&1
echo "ERRORLEVEL: $?"
} | tee "$MIGRATION_TMPDIR/dump-${current_image_version}.log" >/dev/null
) &
pid="$!"
debug "Dumping container of $SERVICE_NAME in ${WHITE}$current_image_version${NORMAL}" \
"launched with PID ${YELLOW}$pid${NORMAL}"
expected_stop=""
unexpected_stop=""
errlvl=""
while read -r line; do
case "$line" in
*" database system is ready to accept connections"*)
migration_container=$(docker ps --filter "label=migration-uuid=$uuid" --format '{{.ID}}')
if [ -z "$migration_container" ]; then
err "Couldn't find migration container for $current_image_version"
echo " despite catched line stating postgres is listening." >&2
break 3
fi
## dump all
debug "Start dumping postgres database of ${WHITE}$current_image_version${NORMAL}"
if ! out=$(
{
docker exec -u 70 "${migration_container}" pg_dumpall |
gzip > "$MIGRATION_TMPDIR/dump.sql.gz"
} 2>&1 ); then
err "Failed to dump postgres database."
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
unexpected_stop="1"
break
fi
out=$(docker stop "$migration_container" 2>&1) || {
err "Failed to stop the dump container $migration_container for $current_image_version:"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
break 3
}
expected_stop="1"
debug "Interrupting server process after dump (${YELLOW}$pid${NORMAL})" >&2
kill "$pid"
errlvl="0"
break
;;
*"getaddrinfo(\"$SERVICE_NAME\") failed: Name or service not known"*)
err "Postgres issue with resolving '$SERVICE_NAME'"
failed=1
break 3
;;
"ERRORLEVEL: "*)
unexpected_stop="1"
errlvl="${line#ERRORLEVEL: }"
err "Unexpected stop of postgres dump container with errorlevel $errlvl"
break
;;
esac
done < <(tail -f "$MIGRATION_TMPDIR/dump-${current_image_version}.log")
[ "$unexpected_stop" == "1" ] && {
migration_container=$(docker ps --filter "label=migration-uuid=$uuid" --format '{{.ID}}')
if [ -n "$migration_container" ]; then
err "Stopping dumping docker container '$migration_container' after failure"
docker stop "$migration_container" >/dev/null 2>&1 || true
fi
failed=1
break
}
[ "$expected_stop" == "1" ] && {
out=$(docker rmi "$current_image" 2>&1) || {
err "Failed to remove image $current_image"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
break 2
}
}
next_image="docker.0k.io/postgres:${image_version}-myc"
if ! docker_has_image "$next_image"; then
out=$(docker pull "$next_image" 2>&1) || {
err "Couldn't pull image $next_image:"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} " >&2
unexpected_stop=1
break 2
}
fi
rm -f "$MIGRATION_TMPDIR/dump-${current_image_version}.log"
uuid=$(openssl rand -hex 16)
mv "$SERVICE_DATASTORE"{,".migration-${current_image_version}-${uuid}"} 2>&1 || {
err "Couldn't move datastore to backup location for $SERVICE_NAME"
unexpected_stop=1
break 2
}
migration_backup_dirs+=("$SERVICE_DATASTORE.migration-${current_image_version}-${uuid}")
(
{
cmd=(
compose --debug
--add-compose-content="$SERVICE_NAME:
docker-compose:
image: docker.0k.io/postgres:${image_version}-myc
hostname: $SERVICE_NAME
"
run --rm --label "migration-uuid=$uuid" "$SERVICE_NAME"
)
echo COMMAND: "${cmd[@]}"
"${cmd[@]}" 2>&1
echo "ERRORLEVEL: $?"
} | tee "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log" >/dev/null
) &
pid="$!"
debug "Migration launched with PID ${YELLOW}$pid${NORMAL}"
expected_stop=""
unexpected_stop=""
errlvl=""
while read -r line; do
case "$line" in
*" database system is ready to accept connections"*)
migration_container=$(docker ps --filter "label=migration-uuid=$uuid" --format '{{.ID}}')
## dump all
debug "Start restoring in postgres database ${WHITE}$current_image_version${NORMAL}"
if ! out=$(
{
gunzip -c "$MIGRATION_TMPDIR/dump.sql.gz" |
docker exec -i -u 70 "${migration_container}" psql
} 2>&1 ); then
err "Failed to restore postgres database."
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
unexpected_stop="1"
break
fi
current_image="$next_image"
current_image_version="$image_version"
out=$(docker stop "$migration_container" 2>&1) || {
err "Failed to stop the migration container $migration_container for $image_version:"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
break 3
}
expected_stop="1"
debug "Interrupting server process after migration (${YELLOW}$pid${NORMAL})" >&2
kill "$pid"
errlvl="0"
break
;;
*"getaddrinfo(\"$SERVICE_NAME\") failed: Name or service not known"*)
err "Postgres issue with resolving '$SERVICE_NAME'"
failed=1
break 3
;;
*"Replication has not yet been configured"*)
warn "Postgres was not setup right away, could lead to issues"
;;
"ERRORLEVEL: "*)
unexpected_stop="1"
errlvl="${line#ERRORLEVEL: }"
err "Unexpected stop of postgres migration container with errorlevel $errlvl"
failed=1
break
;;
esac
done < <(tail -f "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log")
[ "$unexpected_stop" == "1" ] && {
migration_container=$(docker ps --filter "label=migration-uuid=$uuid" --format '{{.ID}}')
if [ -n "$migration_container" ]; then
err "Stopping dumping docker container '$migration_container' after failure"
docker stop "$migration_container" >/dev/null 2>&1 || true
fi
failed=1
break
}
[ "$expected_stop" == "1" ] && {
continue 2
}
##
## Damage control, there are some things we can solve
##
if grep "UPGRADE PROBLEM: Found an invalid featureCompatibilityVersion document" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1; then
if [ -z "$feature_comp" ]; then
feature_comp=1
info "Detected featureCompatibilityVersion issue, forcing upgrade from base version"
echo " This will have for effect to reload the current version and set the featureCompatibilityVersion" >&2
upgrade_path=("$current_image_version" "$image_version" "${upgrade_path[@]}")
break
else
err "Already tried to mitigate featureCompatibilityVersion without success..."
break 2
fi
fi
err "Upgrade to ${WHITE}$image_version${NORMAL} ${DARKRED}failed${NORMAL}"
failed=1
break 2
done
done
if [ -z "$unexpected_stop" ] && [ -z "$expected_stop" ]; then
migration_container=$(docker ps --filter "label=migration-uuid=$uuid" --format '{{.ID}}')
if [ -n "$migration_container" ]; then
err "Stopping migration docker container '$migration_container' after failure"
docker stop "$migration_container"
fi
fi
if [ -n "$failed" ]; then
if [ -e "$MIGRATION_TMPDIR/dump-${current_image_version}.log" ]; then
echo "${WHITE}Failing dump logs:${NORMAL}" >&2
cat "$MIGRATION_TMPDIR/dump-${current_image_version}.log" | prefix " ${GRAY}|${NORMAL} " >&2
fi
if [ -e "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log" ]; then
echo "${WHITE}Failing migration logs:${NORMAL}" >&2
cat "$MIGRATION_TMPDIR/migration-${current_image_version}_${image_version}.log" | prefix " ${GRAY}|${NORMAL} " >&2
fi
fi
if [ -n "$unexpected_stop" ]; then
if [ -e "$SERVICE_DATASTORE.migration-${current_image_version}-${uuid}" ]; then
if [ -e "$SERVICE_DATASTORE" ]; then
mv -v "$SERVICE_DATASTORE"{,".migration-${image_version}-${uuid}-failed"} || {
err "Couldn't move back datastore to backup location for $SERVICE_NAME"
}
fi
mv -v "$SERVICE_DATASTORE"{".migration-${current_image_version}-${uuid}",} || {
err "Couldn't move back datastore to backup location for $SERVICE_NAME"
}
fi
err "Migration failed unexpectedly."
exit 1
fi
if [ "$starting_image_version" == "$current_image_version" ]; then
warn "Database not migrated. Current version is still: ${WHITE}$current_image_version${NORMAL}" >&2
exit 1
fi
(
. "$CHARM_PATH/lib/common"
cp "$MIGRATION_TMPDIR/pgpass" "$LOCAL_DB_PASSFILE"
) || {
err "Couldn't restore postgres password file."
exit 1
}
for migration_backup_dir in "${migration_backup_dirs[@]}"; do
out=$(rm -rf "$migration_backup_dir") || {
warn "Couldn't remove backup directory $migration_backup_dir"
printf "%s\n" "$out" | prefix " ${GRAY}|${NORMAL} "
}
done
## This next line has to be on stdout, it is used by `vps` to get information
info "${GREEN}Successfully${NORMAL} upgraded from ${WHITE}$starting_image_version${NORMAL} to ${WHITE}$current_image_version${NORMAL}" 2>&1
if [ -z "$no_hint" ]; then
cat <<EOF >&2
Don't forget to force the version in your \`\`compose.yml\`\`. For instance:
${DARKYELLOW}$SERVICE_NAME${NORMAL}:
${DARKGRAY}# ...${NORMAL}
${WHITE}docker-compose${NORMAL}:
${WHITE}image${NORMAL}: docker.0k.io/postgres:${current_image_version}-myc
${DARKGRAY}# ...${NORMAL}
You could do this automatically with:
yq eval -i ".$SERVICE_NAME.docker-compose.image = \"docker.0k.io/postgres:${current_image_version}-myc\" // \"\"" compose.yml
And don't forget to run \`compose up\` afterwards.
EOF
fi
exit 0

4
rsync-backup-target/build/src/usr/local/sbin/ssh-cmd-validate

@ -45,7 +45,7 @@ if [[ "$SSH_ORIGINAL_COMMAND" =~ [\&\(\{\;\<\>\`\$\}] ]]; then
reject
fi
if [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server -"[vnloHgDtpArRzCeiLsfx\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"$ ]]; then
if [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server -"[vnloHgDtpArRzCeiLsfxIu\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"$ ]]; then
log "ACCEPTED BACKUP COMMAND: $SSH_ORIGINAL_COMMAND"
## Interpret \ to allow passing spaces (want to avoid possible issue with \n)
@ -56,7 +56,7 @@ if [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server -"[vnloHgDtpArRzCeiLsfx\.]+("
"--log-file=/var/log/rsync/target_$1_rsync.log" \
"--log-file-format=%i %o %f %l %b" \
"${ssh_args[@]:3}"
elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server --sender -"[vnloHgDtpArRzCeiLsfx\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"(|/.*)$ ]]; then
elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server --sender -"[vnloHgDtpArRzCeiLsfxIu\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"(|/.*)$ ]]; then
## Interpret \ to allow passing spaces (want to avoid possible issue with \n)
#read -a ssh_args <<< "${SSH_ORIGINAL_COMMAND}"

18
smtp-stub/hooks/smtp_server-relation-joined

@ -0,0 +1,18 @@
#!/bin/bash
OPTS=(
host
port
connection-security
auth-method
)
for var in "${OPTS[@]}"; do
value=$(options-get "$var") && relation-set "$var" "$value" || exit 1
case "$var:$value" in
auth-method:password)
login=$(options-get "login") && relation-set login "$login" || exit 1
password=$(options-get "password") && relation-set password "$password" || exit 1
;;
esac
done

11
smtp-stub/metadata.yml

@ -0,0 +1,11 @@
type: stub
provides:
smtp-server:
default-options:
port: 25
connection-security: none # "none", "starttls", "ssl/tls"
auth-method: plain # "none", "password"
user:
password:
Loading…
Cancel
Save