Skip to content

Commit

Permalink
0004334: Provide tool to backup and restore configuration that does not
Browse files Browse the repository at this point in the history
reside in the database
  • Loading branch information
philipmarzullo64 committed Apr 6, 2020
1 parent 00a181d commit 6f6d5d6
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 7 deletions.
Expand Up @@ -108,10 +108,7 @@ public abstract class AbstractCommandLauncher {
private static boolean serverPropertiesInitialized = false;

static {
String symHome = System.getenv("SYM_HOME");
if (symHome == null) {
symHome = ".";
}
String symHome = AppUtils.getSymHome();
if (isBlank(System.getProperty("h2.baseDir.disable")) && isBlank(System.getProperty("h2.baseDir"))) {
System.setProperty("h2.baseDir", symHome + "/db/h2");
}
Expand Down
Expand Up @@ -23,20 +23,25 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
Expand All @@ -48,6 +53,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jumpmind.db.model.Table;
import org.jumpmind.exception.IoException;
import org.jumpmind.properties.TypedProperties;
import org.jumpmind.security.ISecurityService;
import org.jumpmind.security.SecurityConstants;
Expand All @@ -66,6 +72,7 @@
import org.jumpmind.symmetric.util.ModuleManager;
import org.jumpmind.util.AppUtils;
import org.jumpmind.util.JarBuilder;
import org.jumpmind.util.ZipBuilder;

/**
* Perform administration tasks with SymmetricDS.
Expand Down Expand Up @@ -118,8 +125,12 @@ public class SymmetricAdmin extends AbstractCommandLauncher {
private static final String CMD_SEND_SCRIPT = "send-script";

private static final String CMD_SEND_SCHEMA = "send-schema";

private static final String CMD_BACKUP_FILE_CONFIGURATION = "backup-config";

private static final String CMD_RESTORE_FILE_CONFIGURATION = "restore-config";

private static final String[] NO_ENGINE_REQUIRED = { CMD_EXPORT_PROPERTIES, CMD_ENCRYPT_TEXT, CMD_OBFUSCATE_TEXT, CMD_LIST_ENGINES, CMD_MODULE };
private static final String[] NO_ENGINE_REQUIRED = { CMD_EXPORT_PROPERTIES, CMD_ENCRYPT_TEXT, CMD_OBFUSCATE_TEXT, CMD_LIST_ENGINES, CMD_MODULE, CMD_BACKUP_FILE_CONFIGURATION, CMD_RESTORE_FILE_CONFIGURATION };

private static final String OPTION_NODE = "node";

Expand All @@ -136,7 +147,9 @@ public class SymmetricAdmin extends AbstractCommandLauncher {
private static final String OPTION_NODE_GROUP = "node-group";

private static final String OPTION_REVERSE = "reverse";


private static final String OPTION_IN = "in";

private static final int WIDTH = 120;

private static final int PAD = 3;
Expand Down Expand Up @@ -201,6 +214,8 @@ protected void printHelp(CommandLine line, Options options) {
printHelpLine(pw, CMD_SEND_SQL);
printHelpLine(pw, CMD_SEND_SCHEMA);
printHelpLine(pw, CMD_SEND_SCRIPT);
printHelpLine(pw, CMD_BACKUP_FILE_CONFIGURATION);
printHelpLine(pw, CMD_RESTORE_FILE_CONFIGURATION);
printHelpLine(pw, CMD_UNINSTALL);
printHelpLine(pw, CMD_MODULE);
pw.flush();
Expand Down Expand Up @@ -243,7 +258,7 @@ private void printHelpCommand(CommandLine line) {
addOption(options, "w", OPTION_WHERE, true);
}
if (cmd.equals(CMD_SYNC_TRIGGERS)) {
addOption(options, "o", OPTION_OUT, false);
addOption(options, "o", OPTION_OUT, true);
addOption(options, "f", OPTION_FORCE, false);
}
if (cmd.equals(CMD_RELOAD_NODE)) {
Expand All @@ -252,6 +267,12 @@ private void printHelpCommand(CommandLine line) {
if (cmd.equals(CMD_REMOVE_NODE)) {
addOption(options, "n", OPTION_NODE, true);
}
if(cmd.equals(CMD_BACKUP_FILE_CONFIGURATION)) {
addOption(options, "o", OPTION_OUT, true);
}
if(cmd.equals(CMD_RESTORE_FILE_CONFIGURATION)) {
addOption(options, "i", OPTION_IN, true);
}

if (options.getOptions().size() > 0) {
format.printWrapped(writer, WIDTH, "\nOptions:");
Expand Down Expand Up @@ -284,6 +305,7 @@ protected void buildOptions(Options options) {
addOption(options, "f", OPTION_FORCE, false);
addOption(options, "o", OPTION_OUT, true);
addOption(options, "r", OPTION_REVERSE, false);
addOption(options, "i", OPTION_IN, true);
buildCryptoOptions(options);
}

Expand Down Expand Up @@ -359,6 +381,12 @@ protected boolean executeWithOptions(CommandLine line) throws Exception {
} else if (cmd.equals(CMD_MODULE)) {
module(line, args);
return true;
} else if (cmd.equals(CMD_BACKUP_FILE_CONFIGURATION)) {
backup(line, args);
return true;
} else if (cmd.equals(CMD_RESTORE_FILE_CONFIGURATION)) {
restore(line, args);
return true;
} else {
throw new ParseException("ERROR: no subcommand '" + cmd + "' was found.");
}
Expand Down Expand Up @@ -495,6 +523,79 @@ private void reloadNode(CommandLine line, List<String> args) {
String message = dataService.reloadNode(nodeId, reverse, "symadmin");
System.out.println(message);
}

private void backup(CommandLine line, List<String> args) throws IOException {
String filename = line.getOptionValue(OPTION_OUT);
if(filename == null) {
filename = "symmetric-file-configuration-" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip";
}
File jarFile = null;
if(filename != null) {
jarFile = new File(filename);
if (jarFile.getParentFile() != null) {
jarFile.getParentFile().mkdirs();
}
}
List<String> listOfDirs = new ArrayList<String>();
listOfDirs.add(AbstractCommandLauncher.getEnginesDir());
listOfDirs.add(AppUtils.getSymHome() + "/conf");
listOfDirs.add(AppUtils.getSymHome() + "/patches");
listOfDirs.add(AppUtils.getSymHome() + "/security");

String parentDir = new File(DEFAULT_SERVER_PROPERTIES).getParent();
if(parentDir != null) {
if(listOfDirs.indexOf(parentDir) < 0) {
// Need to add DEFAULT_SERVER_PROPERTIES to list of files to back up
// because the file is specified outside of the SymmetricDS installation
listOfDirs.add(DEFAULT_SERVER_PROPERTIES);
}
}

File[] arrayOfFile = new File[listOfDirs.size()];
for(int i = 0; i < listOfDirs.size(); i++) {
arrayOfFile[i] = new File(listOfDirs.get(i));
}

System.out.println("Backing up files to " + filename);
try {
ZipBuilder builder = new ZipBuilder(new File(AppUtils.getSymHome()), jarFile, arrayOfFile);
builder.build();
} catch (Exception e) {
throw new IoException("Failed to backup configuration files into archive", e);
}
}

private void restore(CommandLine line, List<String> args) throws IOException {
String filename = line.getOptionValue(OPTION_IN);
if(filename == null) {
throw new IoException("Input filename must be specified");
}
try(FileInputStream finput = new FileInputStream(filename);ZipInputStream zip = new ZipInputStream(finput)) {
ZipEntry entry = null;
for (entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
if(entry.isDirectory()) {
continue;
}
System.out.println("Restoring " + entry.getName());

File fileToOpen = null;
File f = new File(entry.getName());
if(f.isAbsolute()) {
f.getParentFile().mkdirs();
fileToOpen = f;
} else {
fileToOpen = new File(AppUtils.getSymHome(), entry.getName());
}
try(FileOutputStream foutput = new FileOutputStream(fileToOpen)) {
final byte buffer[] = new byte[4096];
int readCount;
while ((readCount = zip.read(buffer, 0, buffer.length)) > 0) {
foutput.write(buffer, 0, readCount);
}
}
}
}
}

private void syncTrigger(CommandLine line, List<String> args) throws IOException {
boolean genAlways = line.hasOption(OPTION_FORCE);
Expand Down
Expand Up @@ -74,6 +74,8 @@ SymAdmin.Cmd.send-schema=Send schema change to node
SymAdmin.Cmd.send-script=Send script to node
SymAdmin.Cmd.uninstall=Uninstall all SymmetricDS objects from the database
SymAdmin.Cmd.module=Manage modules to add or remove features
SymAdmin.Cmd.backup-config=Backup configuration files
SymAdmin.Cmd.restore-config=Restore configuration files
SymAdmin.Usage.reload-node=<node-id>
SymAdmin.Usage.reload-table=<table> [<table> ...]
SymAdmin.Usage.export-batch=<node-id> <batch number> [<filename>]
Expand All @@ -95,6 +97,8 @@ SymAdmin.Usage.send-sql=<table> <sql>
SymAdmin.Usage.send-schema=[<table>] ...
SymAdmin.Usage.send-script=<filename>
SymAdmin.Usage.uninstall=
SymAdmin.Usage.backup-config=
SymAdmin.Usage.restore-config=
SymAdmin.Usage.module=[install <module> | remove <module> | list-files <module> | list | list-all]
SymAdmin.Help.export-sym-tables=Output the SQL to create the SymmetricDS tables. If a filename is given, the SQL statements are written to it, otherwise standard output is used.
SymAdmin.Help.run-job=Run one of the scheduled jobs immediately.
Expand All @@ -118,13 +122,16 @@ SymAdmin.Help.send-schema=Send a schema update for a table to be executed on a r
SymAdmin.Help.send-script=Send a script to a node to be run there. The script is read from the filename provided as an argument or read from standard input. Only BeanShell scripts are supported.
SymAdmin.Help.uninstall=Uninstall all SymmetricDS objects from the database, including the SYM tables, sequences, functions, stored procedures, and triggers.
SymAdmin.Help.module=\nManage modules to add or remove features.\n\nmodule list List modules that are currently installed\nmodule list-all List all modules available to install\nmodule list-files <module> List files for a module that is installed\nmodule list-deps <module> List dependencies for a module\nmodule install <module> Install a module\nmodule remove <module> Remove a module
SymAdmin.Help.backup-config=Backup configuration files to a zip file for later restoration if necessary.
SymAdmin.Help.restore-config=Restore configuration files from a zip file.
SymAdmin.Option.catalog=Look for tables in catalog.
SymAdmin.Option.schema=Look for tables in schema.
SymAdmin.Option.where=Add where clause to SQL statement that selects data from table.
SymAdmin.Option.node=Send to this node ID.
SymAdmin.Option.node-group=Send to all nodes in this node group ID.
SymAdmin.Option.force=Force triggers to regenerate even if no change is detected.
SymAdmin.Option.out=Write output to file
SymAdmin.Option.in=Read from file
SymAdmin.Option.reverse=Reverse initial load from client to server

DbExport.Option.compatible=Change export to be compatible with given database: db2, db2zos, derby, firebird, greenplum, h2, hsqldb, hsqldb2, informix, interbase, mssql, mysql, oracle, postgres, sybase.
Expand Down

0 comments on commit 6f6d5d6

Please sign in to comment.