From f0ee0993ee907e19d468a47d674b0d4db5568e50 Mon Sep 17 00:00:00 2001 From: felmer <felmer> Date: Tue, 20 Jan 2015 12:51:16 +0000 Subject: [PATCH] SSDM-1388 handle backup of remote databases. Improve error messaging. SVN: 33287 --- .../installer/bin/backup-databases.sh | 30 +++-- .../installer/bin/chmodx-all-scripts.sh | 1 + .../installer/bin/common-functions.sh | 11 +- .../installer/bin/database-existence-check.sh | 4 +- .../installer/bin/finish-installation.sh | 1 + .../izpack/AbstractScriptExecutor.java | 61 ++++++++-- .../installer/izpack/ExecuteBackupAction.java | 6 +- .../izpack/ExecuteSetupScriptsAction.java | 22 ++-- .../izpack/SetDatabasesToBackupAction.java | 105 +++++++++++++----- 9 files changed, 173 insertions(+), 68 deletions(-) diff --git a/installation/resource/installer/bin/backup-databases.sh b/installation/resource/installer/bin/backup-databases.sh index 7ca81934eb7..f36b6b1ac68 100755 --- a/installation/resource/installer/bin/backup-databases.sh +++ b/installation/resource/installer/bin/backup-databases.sh @@ -4,13 +4,14 @@ # # Usage: backup-databases.sh $BACKUP_DIR # +set -o errexit # # Sets the variable DB_LIST to contain a list of databases # to be backed up. # Each line of the function's result has the form : # -# database=XXXX;username=XXXX;password=XXXX +# database=XXXX;username=XXXX;password=XXXX;host=XXXX # function listDatabases() { @@ -25,13 +26,19 @@ function listDatabases() { # # $1 - a semi-color delimited string of properties (e.g. "key1=value1;key2=value2") # $2 - the name of the property (e.g. "key1") -# +# $3 - default property value # function getProperty() { local properties=$1 local propName=$2 - echo $properties | tr ";" "\n" | grep "$propName=" | sed "s/$propName=//" + local defaultValue=$3 + local value=`echo $properties | tr ";" "\n" | grep "$propName=" | sed "s/$propName=//"` + if [ "$value" == "" ]; then + echo $defaultValue + else + echo $value + fi } function checkForBackup() @@ -61,12 +68,16 @@ function backupDatabase() { return fi - username=$(getProperty $DB_PROPS "username") - if [ $(databaseExist $database $username) == "TRUE" ]; then + echo "Database description: $DB_PROPS" + local hostAndPort=$(getProperty $DB_PROPS "host" "localhost") + local host=${hostAndPort%:*} + local port=`if [ "${hostAndPort#*:}" == "$host" ]; then echo 5432; else echo ${hostAndPort#*:}; fi` + local username=$(getProperty $DB_PROPS "username") + if [ $(databaseExist $host $port $database $username) == "TRUE" ]; then local dumpDir=$BACKUP_DIR/$database - echo "Backing up database $database to $dumpDir..." - exe_pg_dump -U $username -Fd $database $PG_DUMP_OPTION -f $dumpDir + echo "Backing up database $database@$host:$port (for user $username) to $dumpDir..." + exe_pg_dump -U $username -h $host -p $port -Fd $database $PG_DUMP_OPTION -f $dumpDir if [ "$?" -ne 0 ]; then echo "Failed to backup database '$database' !" @@ -77,7 +88,7 @@ function backupDatabase() { BASE=`dirname "$0"` if [ ${BASE#/} == ${BASE} ]; then - BASE="`pwd`/${BASE}" + BASE="`pwd`/${BASE}" fi source $BASE/common-functions.sh @@ -94,7 +105,7 @@ AS_SERVER=$SERVERS/openBIS-server/ DSS_SERVER=$SERVERS/datastore_server listDatabases $AS_SERVER/jetty/etc/service.properties $DSS_SERVER/etc/service.properties - +echo "Databases: $DB_LIST" PG_DUMP_OPTION="" if [[ "`exe_pg_dump --version|awk '{print $3}'`" > "9.2.x" ]]; then if [ -f /proc/cpuinfo ]; then @@ -107,6 +118,7 @@ if [[ "`exe_pg_dump --version|awk '{print $3}'`" > "9.2.x" ]]; then echo Database dumping will use $NUMBER_OF_PROCESSORS processors. PG_DUMP_OPTION=--jobs=$NUMBER_OF_PROCESSORS fi +echo "dump options: $PG_DUMP_OPtION" for DB in $DB_LIST; do backupDatabase $DB $PG_DUMP_OPTION done diff --git a/installation/resource/installer/bin/chmodx-all-scripts.sh b/installation/resource/installer/bin/chmodx-all-scripts.sh index 800f634d58f..72026d62eb2 100644 --- a/installation/resource/installer/bin/chmodx-all-scripts.sh +++ b/installation/resource/installer/bin/chmodx-all-scripts.sh @@ -2,6 +2,7 @@ # # Sets the executable flag for all *.sh files within the openBIS installation folder # +set -o errexit BASE=`dirname "$0"` if [ ${BASE#/} == ${BASE} ]; then diff --git a/installation/resource/installer/bin/common-functions.sh b/installation/resource/installer/bin/common-functions.sh index 6529ea683f6..7a92084b49d 100644 --- a/installation/resource/installer/bin/common-functions.sh +++ b/installation/resource/installer/bin/common-functions.sh @@ -45,20 +45,21 @@ contains() # # This function should be used as follows: # -# if [ $(databaseExist "openbis_prod" $owner) == "TRUE" ]; then doBackup; fi +# if [ $(databaseExist localhost 5432 "openbis_prod" $owner) == "TRUE" ]; then doBackup; fi # databaseExist() { - local database=$1 - local owner=$2 - if [ `exe_psql -U $owner -l | eval "awk '/$database /'" | wc -l` -gt 0 ]; then + local host=$1 + local port=$2 + local database=$3 + local owner=$4 + if [ `exe_psql -U $owner -h $host -p $port -l | eval "awk '/$database /'" | wc -l` -gt 0 ]; then echo "TRUE" else echo "FALSE" fi } - # # Run psql command using POSTGRES_BIN path # diff --git a/installation/resource/installer/bin/database-existence-check.sh b/installation/resource/installer/bin/database-existence-check.sh index f0a4e8b1c67..d791dd32f73 100644 --- a/installation/resource/installer/bin/database-existence-check.sh +++ b/installation/resource/installer/bin/database-existence-check.sh @@ -11,5 +11,7 @@ source $BASE/common-functions.sh DATABASE="$1" OWNER="$2" +HOST=$3 +PORT=$4 -databaseExist "$DATABASE" "$OWNER" \ No newline at end of file +databaseExist $HOST $PORT "$DATABASE" "$OWNER" \ No newline at end of file diff --git a/installation/resource/installer/bin/finish-installation.sh b/installation/resource/installer/bin/finish-installation.sh index 987f6d9841b..7c5197b67dc 100644 --- a/installation/resource/installer/bin/finish-installation.sh +++ b/installation/resource/installer/bin/finish-installation.sh @@ -3,6 +3,7 @@ # 1) Mark all scripts as executable # 2) Clean up all temporary folders after installation # +set -o errexit BASE=`dirname "$0"` if [ ${BASE#/} == ${BASE} ]; then diff --git a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/AbstractScriptExecutor.java b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/AbstractScriptExecutor.java index 9569913a32e..ec8b22067e5 100644 --- a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/AbstractScriptExecutor.java +++ b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/AbstractScriptExecutor.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.openbis.installer.izpack; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -23,15 +24,32 @@ import java.io.OutputStream; import java.util.Map; import com.izforge.izpack.api.data.AutomatedInstallData; +import com.izforge.izpack.api.handler.AbstractUIHandler; +import com.izforge.izpack.data.PanelAction; /** * Abstract class that can execute admin scripts as part of the installation. * * @author Kaloyan Enimanev */ -public abstract class AbstractScriptExecutor +public abstract class AbstractScriptExecutor implements PanelAction { protected static final String INSTALL_BIN_PATH_VARNAME = "INSTALL_BIN_PATH"; + + @Override + public final synchronized void executeAction(AutomatedInstallData data, AbstractUIHandler handler) + { + try + { + executeAction(data); + } catch (Exception ex) + { + ex.printStackTrace(); + handler.emitErrorAndBlockNext("Error", ex.toString()); + } + } + + protected abstract void executeAction(AutomatedInstallData data); protected String getAdminScript(AutomatedInstallData data, String scriptFileName) { @@ -60,19 +78,28 @@ public abstract class AbstractScriptExecutor } try { + ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); + ByteArrayOutputStream errOutput = new ByteArrayOutputStream(); Process process = pb.start(); - pipe(process.getErrorStream(), err); - pipe(process.getInputStream(), out); + pipe(process.getErrorStream(), new DelegatingOutputStream(errOutput, err)); + pipe(process.getInputStream(), new DelegatingOutputStream(stdOutput, out)); int returnValue = process.waitFor(); if (returnValue != 0) { - System.err.println("Executing of command " + pb.command() - + " has failed. Aborting ..."); - System.exit(returnValue); + System.err.println("Executing of command " + pb.command() + " has failed. Aborting ..."); + throw new RuntimeException("Executing of command " + pb.command() + " has failed:\n" + + "Exit value: " + returnValue + "\n" + + "-------- Std out ---------\n" + stdOutput + "\n" + + "-------- Err out ---------\n" + errOutput); } } catch (Exception e) { - System.out.println("Error executing " + command[0] + ": " + e.getMessage()); + e.printStackTrace(); + if (e instanceof RuntimeException) + { + throw (RuntimeException) e; + } + throw new RuntimeException("Error executing " + command[0] + ": " + e.getMessage(), e); } } @@ -97,5 +124,25 @@ public abstract class AbstractScriptExecutor } }).start(); } + + private static final class DelegatingOutputStream extends OutputStream + { + private OutputStream[] outputStreams; + + DelegatingOutputStream(OutputStream...outputStreams) + { + this.outputStreams = outputStreams; + } + + @Override + public void write(int b) throws IOException + { + for (OutputStream outputStream : outputStreams) + { + outputStream.write(b); + } + } + + } } diff --git a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteBackupAction.java b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteBackupAction.java index 5e972acc21d..1e99aa77741 100644 --- a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteBackupAction.java +++ b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteBackupAction.java @@ -21,15 +21,13 @@ import java.util.Map; import com.izforge.izpack.api.data.AutomatedInstallData; import com.izforge.izpack.api.data.PanelActionConfiguration; -import com.izforge.izpack.api.handler.AbstractUIHandler; -import com.izforge.izpack.data.PanelAction; /** * Executes a script that creates a backup of the existing installation. * * @author Kaloyan Enimanev */ -public class ExecuteBackupAction extends AbstractScriptExecutor implements PanelAction +public class ExecuteBackupAction extends AbstractScriptExecutor { /** * a script that creates a installation backup. @@ -37,7 +35,7 @@ public class ExecuteBackupAction extends AbstractScriptExecutor implements Panel private static final String CREATE_BACKUP_SCRIPT = "backup-installation.sh"; @Override - public synchronized void executeAction(AutomatedInstallData data, AbstractUIHandler arg1) + public synchronized void executeAction(AutomatedInstallData data) { String script = getAdminScript(data, CREATE_BACKUP_SCRIPT); String backupFolder = data.getVariable(GlobalInstallationContext.BACKUP_FOLDER_VARNAME); diff --git a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteSetupScriptsAction.java b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteSetupScriptsAction.java index fa550a0332e..13b79c7c96d 100644 --- a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteSetupScriptsAction.java +++ b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/ExecuteSetupScriptsAction.java @@ -28,8 +28,6 @@ import org.apache.commons.io.FileUtils; import com.izforge.izpack.api.data.AutomatedInstallData; import com.izforge.izpack.api.data.PanelActionConfiguration; -import com.izforge.izpack.api.handler.AbstractUIHandler; -import com.izforge.izpack.data.PanelAction; import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; @@ -40,7 +38,7 @@ import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; * @author Kaloyan Enimanev * @author Franz-Josef Elmer */ -public class ExecuteSetupScriptsAction extends AbstractScriptExecutor implements PanelAction +public class ExecuteSetupScriptsAction extends AbstractScriptExecutor { static final String DATA_SOURCES_KEY = "data-sources"; static final String PATHINFO_DB_DATA_SOURCE = "path-info-db"; @@ -66,7 +64,7 @@ public class ExecuteSetupScriptsAction extends AbstractScriptExecutor implements private static final String RESTORE_CONFIG_FROM_BACKUP_SCRIPT = "restore-config-from-backup.sh"; @Override - public synchronized void executeAction(AutomatedInstallData data, AbstractUIHandler handler) + public synchronized void executeAction(AutomatedInstallData data) { if (GlobalInstallationContext.isFirstTimeInstallation) { @@ -82,17 +80,11 @@ public class ExecuteSetupScriptsAction extends AbstractScriptExecutor implements String certificatePassword = data.getVariable(GlobalInstallationContext.KEY_PASSWORD_VARNAME); File installDir = GlobalInstallationContext.installDir; - try - { - String pathinfoDBEnabled = - data.getVariable(GlobalInstallationContext.PATHINFO_DB_ENABLED); - enablePathinfoDB("false".equalsIgnoreCase(pathinfoDBEnabled) == false, installDir); - installKeyStore(keyStoreFileName, installDir); - injectPasswords(keyStorePassword, certificatePassword, installDir); - } catch (Exception ex) - { - handler.emitErrorAndBlockNext("Fatal Error", ex.toString()); - } + String pathinfoDBEnabled = + data.getVariable(GlobalInstallationContext.PATHINFO_DB_ENABLED); + enablePathinfoDB("false".equalsIgnoreCase(pathinfoDBEnabled) == false, installDir); + installKeyStore(keyStoreFileName, installDir); + injectPasswords(keyStorePassword, certificatePassword, installDir); } void installKeyStore(String keyStoreFileName, File installDir) diff --git a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/SetDatabasesToBackupAction.java b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/SetDatabasesToBackupAction.java index 4d35fb8b637..e0b640aa894 100644 --- a/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/SetDatabasesToBackupAction.java +++ b/installation/source/java/ch/systemsx/cisd/openbis/installer/izpack/SetDatabasesToBackupAction.java @@ -27,7 +27,6 @@ import org.apache.commons.io.output.ByteArrayOutputStream; import com.izforge.izpack.api.data.AutomatedInstallData; import com.izforge.izpack.api.data.PanelActionConfiguration; -import com.izforge.izpack.api.handler.AbstractUIHandler; import com.izforge.izpack.data.PanelAction; import ch.systemsx.cisd.common.shared.basic.string.CommaSeparatedListBuilder; @@ -49,18 +48,16 @@ public class SetDatabasesToBackupAction extends AbstractScriptExecutor implement } @Override - public void executeAction(AutomatedInstallData data, AbstractUIHandler handler) + public void executeAction(AutomatedInstallData data) { try { String descriptions = extractDescriptions().trim(); String databases = extractDatabases(data, descriptions); data.setVariable(DATABASES_TO_BACKUP_VARNAME, databases); - return; } catch (Exception ex) { - ex.printStackTrace(); - handler.emitError("Exception", ex.toString()); + throw new RuntimeException("Databse description extraction failed.", ex); } } @@ -71,23 +68,17 @@ public class SetDatabasesToBackupAction extends AbstractScriptExecutor implement { for (String description : descriptions.split("\n")) { - String[] splitted = description.split(";")[0].split("="); - if (splitted.length < 2) + DatabaseDescription databaseDescription = new DatabaseDescription(description); + if (databaseExists(data, databaseDescription)) { - throw new IllegalArgumentException("Invalid database description: " - + description); - } - String database = splitted[1].trim(); - if (databaseExists(data, database)) - { - builder.append(database); + builder.append(databaseDescription.getDatabase()); } } } return builder.toString(); } - - private boolean databaseExists(AutomatedInstallData data, String database) + + private boolean databaseExists(AutomatedInstallData data, DatabaseDescription databaseDescription) { File scriptFile = getAdminScriptFile(data, "database-existence-check.sh"); if (scriptFile.exists() == false) @@ -95,18 +86,13 @@ public class SetDatabasesToBackupAction extends AbstractScriptExecutor implement return true; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - String owner = - Utils.tryToGetServicePropertyOfAS(GlobalInstallationContext.installDir, - "database.owner"); - String password = - Utils.tryToGetServicePropertyOfAS(GlobalInstallationContext.installDir, - "database.owner-password"); - Map<String, String> env = new HashMap<String, String>(); - env.put("PGPASSWORD", password); - - executeAdminScript(env, outputStream, outputStream, scriptFile.getAbsolutePath(), database, owner); + env.put("PGPASSWORD", databaseDescription.getPassword()); + String database = databaseDescription.getDatabase(); + String owner = databaseDescription.getUsername(); + String host = databaseDescription.getHost(); + String port = databaseDescription.getPort(); + executeAdminScript(env, outputStream, outputStream, scriptFile.getAbsolutePath(), database, owner, host, port); return outputStream.toString().trim().equals("FALSE") == false; } @@ -134,4 +120,69 @@ public class SetDatabasesToBackupAction extends AbstractScriptExecutor implement return new Object[] { paths.toArray(new String[0]) }; } + + private static final class DatabaseDescription + { + private final String description; + private final String database; + private final String username; + private final String password; + private final String host; + + DatabaseDescription(String description) + { + this.description = description; + String[] parts = description.split(";"); + if (parts.length < 3) + { + throw new IllegalArgumentException("Only " + parts.length + + " parts separated by ';' in database description: " + description); + } + database = getValue(parts[0]); + username = getValue(parts[1]); + password = getValue(parts[2]); + host = parts.length == 4 ? getValue(parts[3]) : "localhost"; + } + + private String getValue(String part) + { + String[] splitted = part.split("="); + return splitted.length == 1 ? "" : splitted[1].trim(); + } + + public String getDatabase() + { + return database; + } + + public String getUsername() + { + return username; + } + + public String getPassword() + { + return password; + } + + public String getHost() + { + int indexOfColon = host.indexOf(':'); + return indexOfColon < 0 ? host : host.substring(0, indexOfColon); + } + + public String getPort() + { + int indexOfColon = host.indexOf(':'); + return indexOfColon < 0 ? "5432" : host.substring(indexOfColon + 1); + } + + @Override + public String toString() + { + return description; + } + + } + } -- GitLab