#!/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 nextcloud 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 ;; "--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 . lib/common nextcloud:code:version() { cat "$SERVICE_DATASTORE/var/www/html/version.php" | grep 'VersionString =' | cut -f 3 -d ' ' | cut -f 2 -d \' } current_image_version="${DOCKER_BASE_IMAGE#*:}" current_image_version="${current_image_version%-myc}" if ! [[ "$current_image_version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then err "Current nextcloud version '$current_image_version' is unsupported yet." exit 1 fi if ! [ -e "$SERVICE_DATASTORE/var/www/html/version.php" ]; then err "No code seem to have been deployed yet in datastore." \ "This is not supported yet." exit 1 fi current_code_version=$(nextcloud:code:version) if [ "$current_code_version" != "$current_image_version" ]; then err "Current code version ${WHITE}$current_code_version${NORMAL}" \ "mismatch with current image version" \ "${WHITE}$current_image_version${NORMAL}" exit 1 fi current_config_version=$(nextcloud:config:version) info "Current config version: ${WHITE}$current_config_version${NORMAL}" if [ "$current_config_version" != "$current_code_version" ]; then warn "Current config version ${WHITE}$current_config_version${NORMAL}" \ "mismatch with current code version" \ "${WHITE}$current_code_version${NORMAL}" echo " Will use the config version as reference for upgrade." >&2 fi last_available_versions=( $(DEBUG= docker:tags:fetch docker.0k.io/nextcloud 30 '[0-9]+\.[0-9+]\.[0-9]+-myc$' | sed -r 's/-myc$//g' | sort -rV) ) ## 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_config_version"; then last_upgradable_versions+=("$v") fi done docker_version=$(docker info --format '{{.ServerVersion}}') last_compatible_versions=() for v in "${last_upgradable_versions[@]}"; do if version_gt "$docker_version" 20.10.0; then last_compatible_versions+=("$v") continue fi if version_gt "26.99.99" "$v"; then last_compatible_versions+=("$v") fi done if [ "${#last_upgradable_versions[@]}" == 0 ]; then info "${DARKYELLOW}nextcloud${NORMAL} is already ${GREEN}up-to-date${NORMAL}." exit 0 fi if [ -z "$target" ]; then info "Target latest version: ${WHITE}${last_available_versions[0]}${NORMAL}" target=${last_upgradable_versions[0]} else if [[ " ${last_available_versions[*]} " != *" $target "* ]]; then err "Invalid version ${WHITE}$target${NORMAL} selected, please specify one of:" for v in "${last_upgradable_versions[@]}"; do echo " - $v" done >&2 exit 1 fi info "Target version ${WHITE}$target${NORMAL}" fi if [[ " ${last_compatible_versions[*]} " != *" $target "* ]]; then err "Sorry, ${DARKYELLOW}$SERVICE_NAME${NORMAL} ${WHITE}$target${NORMAL}" \ "require docker version >= ${WHITE}20.10${NORMAL} (current: $docker_version)" echo " Nextcloud versions <= ${WHITE}27${NORMAL} are still supported." >&2 echo " You can specify one of:" >&2 for v in "${last_compatible_versions[@]}"; do echo " - $v" >&2 done exit 1 fi upgrade_path=($(echo "${last_upgradable_versions[*]}" | tr ' ' '\n' | uniq -w 3 | sort -V)) containers="$(get_running_containers_for_service "$SERVICE_NAME")" container_stopped=() if [ -n "$containers" ]; then #err "Running container(s) for $DARKYELLOW$SERVICE_NAME$NORMAL are still running:" for container in $containers; do docker stop "$container" >/dev/null || { err "Failed to stop container '$container'." exit 1 } 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 set -o pipefail for image_version in "${upgrade_path[@]}"; do while true; do patched=() current_code_version=$(nextcloud:code:version) current_config_version=$(nextcloud:config:version) if [ "$current_config_version" == "$image_version" ]; then err "Unexpected step where config version ${WHITE}$current_config_version${NORMAL} is same than image version" exit 1 fi if ! version_gt "$image_version" "$current_config_version"; then err "Unexpected step where config version ${WHITE}$current_config_version${NORMAL} is greater than image version ${WHITE}$image_version${NORMAL}" exit 1 fi if (( ${image_version%%.*} - ${current_config_version%%.*} > 1 )); then err "Unexpected step where config version ${WHITE}$current_config_version${NORMAL} is more than one major version less than image version ${WHITE}$image_version${NORMAL}" exit 1 fi docker rmi "docker.0k.io/nextcloud:${current_config_version}-myc" >/dev/null 2>&1 || true if [ "$current_code_version" == "$image_version" ]; then ## Code already setup, we need to call ``occ upgrade`` by our selves info "Upgrading ${WHITE}$current_config_version${NORMAL} => ${WHITE}$image_version${NORMAL} (db)" compose --no-hooks \ --add-compose-content="$SERVICE_NAME: docker-compose: image: docker.0k.io/nextcloud:${image_version}-myc" \ run --rm \ -u www-data --entrypoint /var/www/html/occ "$SERVICE_NAME" \ upgrade 2>&1 | tee "$MIGRATION_TMPDIR/migration.log" errlvl="$?" else ## Code will be upgraded info "Upgrading ${WHITE}$current_config_version${NORMAL} => ${WHITE}$image_version${NORMAL} (code, db)" compose --no-hooks \ --add-compose-content="$SERVICE_NAME: docker-compose: image: docker.0k.io/nextcloud:${image_version}-myc" \ run --rm \ -v "$CHARM_PATH"/src/fake-apache:/usr/bin/apache \ --entrypoint /entrypoint.sh "$SERVICE_NAME" apache 2>&1 | tee "$MIGRATION_TMPDIR/migration.log" errlvl="$?" fi [ "$errlvl" == 0 ] && continue 2 ## ## Damage control, there are some things we can solve ## if grep "^Update failed" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1; then ## XXXvlab: this comes from and should move to onlyoffice charm in ## some way. if grep "Update app onlyoffice" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1 && ( grep "SQLSTATE" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1 || grep "^Database error when running migration latest for app onlyoffice" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1 ); then if [ -e "$DATASTORE"/"$SERVICE_NAME/var/www/html/custom_apps/onlyoffice/lib/Migration/Version070400Date20220607111111.php" ]; then patch="$CHARM_PATH/../onlyoffice/src/patch/00-onlyoffice-nextcloud.patch" if ! [[ " ${patched[*]} " == *" $patch "* ]]; then if ( cd "$DATASTORE"/"$SERVICE_NAME/var/www/html/custom_apps/onlyoffice/"; patch -Np1 --dry-run < "$patch" ); then info "Found OnlyOffice issue, correcting it, and retrying." ( cd "$DATASTORE"/"$SERVICE_NAME/var/www/html/custom_apps/onlyoffice/"; patch -Np1 < "$patch" ) || exit 1 patched+=("$patch") continue fi fi fi fi fi err "Upgrade to ${WHITE}$image_version${NORMAL} ${DARKRED}failed${NORMAL}. Aborting." exit 1 done done if grep "^Nextcloud is in maintenance mode" "$MIGRATION_TMPDIR/migration.log" >/dev/null 2>&1; then info "Forcing maintenance mode off" compose --no-hooks \ --add-compose-content="$SERVICE_NAME: docker-compose: image: docker.0k.io/nextcloud:${target}-myc" \ run --rm \ -u www-data --entrypoint /var/www/html/occ "$SERVICE_NAME" \ maintenance:mode --off fi info "Successfully upgraded from ${WHITE}$current_config_version${NORMAL} to ${WHITE}$target${NORMAL}" if [ -z "$no_hint" ]; then cat <&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/nextcloud:${target}-myc ${DARKGRAY}# ...${NORMAL} EOF fi