From 809ad7aa9d63ca7d21375874660fe56385938bfc Mon Sep 17 00:00:00 2001
From: Matthias Stecher <matthiasstecher@gmx.de>
Date: Sun, 25 Oct 2020 14:55:11 +0100
Subject: [PATCH] __nextcloud: migrate if the database type change

This adds a migration progess if the database type changes automaticly.
---
 type/__nextcloud/man.rst             | 16 +++++-
 type/__nextcloud/map-conf-changes.sh | 81 +++++++++++++++++++++++++---
 2 files changed, 89 insertions(+), 8 deletions(-)

diff --git a/type/__nextcloud/man.rst b/type/__nextcloud/man.rst
index 2bff614..2a6ec9d 100644
--- a/type/__nextcloud/man.rst
+++ b/type/__nextcloud/man.rst
@@ -113,13 +113,22 @@ database-type
     **This parameter defaults to the SQLite database backend, as it is the
     simplest one to setup and do not require extra parameters.**
 
+    If this parameter change, the type will migrate to the new database type.
+    It will not work for SQLite because the upstream migration script does not
+    support it. **Be aware that migrations take there time, plan at minimum
+    40 seconds of migration for a stock installation.**
+
 database-host
     The database host to connect to. Possible are hostnames, ip addresses or
     UNIX sockets. UNIX sockets must set in the format of
     ``localhost:/path/to/socket``. If an non-standard port is used, set it
-    after the hostname or ip address seperated by an colon (``:``).
+    after the hostname or ip address seperated by an colon (``:``). If this
+    value is not set, nextcloud defaults to the value ``localhost``.
 
-    If this value is not set, nextcloud defaults to the value ``localhost``.
+    This type will not migrate data if the type does not change. You must do
+    this manually by setting the maintainer mode (to avoid data changes) and
+    then cloning the database to the new destination. After that, run cdist to
+    apply the config changes. It should automaticly remove the maintainer mode.
 
 database-name
     The name of the database to connect to. Required if MariaDB or PostgreSQL
@@ -173,6 +182,9 @@ nextcloud configuration is broken and must be resolved manually: Move the data
 directory to the correct location or change the configuration to point to the
 old destination and retry.
 
+It aborts if it should migrate to a SQLite database. This will be done before
+the upstream migration script is executed, as it would throw the same error.
+
 
 EXAMPLES
 --------
diff --git a/type/__nextcloud/map-conf-changes.sh b/type/__nextcloud/map-conf-changes.sh
index caea3bd..3c98c3b 100755
--- a/type/__nextcloud/map-conf-changes.sh
+++ b/type/__nextcloud/map-conf-changes.sh
@@ -7,6 +7,21 @@
 # explorer.
 
 
+# Print the value of the given configuration.
+#
+# Arguments:
+#  1: the nextcloud configuration name
+#
+# Returns with a unsuccessful return code if no parameter found.
+getparam() {
+    awk -v FS=" = " -v name="$1" '
+             function ntostring(n) { ret=""; for(i=n; i<=NF; i++) ret=ret $i (i<NF ? OFS : ""); return ret }
+             $1 == name { print ntostring(2); success = 1 }
+             END { if(!success) exit 4 }
+             ' "$__object/explorer/config"
+    return $?
+}
+
 # Test if the value exists as given.
 #
 # Arguments:
@@ -194,6 +209,44 @@ conf_array() {
     fi
 }
 
+# Migrate the database to a new database type
+#
+# Arguments:
+#  1: the database type to convert to
+migrate_db() {
+    # from argument
+    database_type="$1"
+
+    # hostname, database, username and password
+    database_host="$(cat "$__object/parameter/database-host" 2>/dev/null || printf "localhost")"
+    database_name="$(cat "$__object/parameter/database-name")"
+    database_user="$(cat "$__object/parameter/database-user")"
+    database_pass="$(cat "$__object/parameter/database-password")"
+
+    # Extract the port from the host
+    # this is required for pgsql, but mysql can do it itself, too
+    if printf "%s" "$database_host" | grep -q ":[[:digit:]]\+$"; then
+        # extract the last part, which is the port number
+        database_port="${database_host##*:}"
+    else
+        # set default port because the tool can not do this for pgsql
+        case "$database_type" in
+            mysql)
+                database_port=3306
+                ;;
+            pgsql)
+                database_port=5432
+                ;;
+        esac
+    fi
+
+    # print out the correct command
+    printf "php occ db:convert-type --no-interaction --no-ansi --clear-schema --all-apps \
+        '%s' '%s' --password '%s' '%s' --port '%u' '%s'\n" \
+        "$database_type" "$database_user" "$database_pass" "$database_host" "$database_port" "$database_name"
+}
+
+
 # Set the install variable if nextcloud was not installed before this type.
 if ! testparam installed 1; then
     install="yes"
@@ -210,19 +263,35 @@ conf_array host trusted_domains
 # Already set via the installer
 # set default values from the nextcloud installer to do not override them
 if [ -z "$install" ]; then
-    # database
+    # Database to check if the type changed
+    # use the current type if no old type found to match instead of migrate
     database_type="$(cat "$__object/parameter/database-type")"
+    old_db_type="$(getparam dbtype || printf "%s" "$database_type")"
+
     case "$database_type" in
         sqlite3)
+            if [ "$old_db_type" != "sqlite3" ]; then
+                echo "Migrating to a SQLite database is not supported by upstream!" >&2
+                echo "Do it manually or reinstall nextcloud .." >&2
+                exit 1
+            fi
             conf_string database-type dbtype
             ;;
 
         mysql|pgsql)
-            conf_string database-type dbtype
-            conf_string database-host dbhost installdef "localhost"
-            conf_string database-name dbname required
-            conf_string database-user dbuser required
-            conf_string database-password dbpassword required
+            if [ "$old_db_type" != "$database_type" ]; then
+                # the migration will change all database parameters itself
+                migrate_db "$database_type"
+            else
+                # no change of dbtype cause it will cause a migration
+                conf_string database-host dbhost installdef "localhost"
+                conf_string database-name dbname required
+                conf_string database-user dbuser required
+                conf_string database-password dbpassword required
+            fi
+
+            # It may not be a good idea to change this parameter, but do what
+            # the user want to do.
             conf_string database-prefix dbtableprefix
             ;;