Skip to content

Commit

Permalink
[14] Configuration backup/restore in online mode
Browse files Browse the repository at this point in the history
  • Loading branch information
mchoma committed Nov 14, 2016
1 parent f931641 commit af7d435
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.wildfly.extras.creaper.commands.foundation.online;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import org.jboss.logging.Logger;
import org.wildfly.extras.creaper.core.CommandFailedException;
import org.wildfly.extras.creaper.core.online.CliException;
import org.wildfly.extras.creaper.core.online.ModelNodeResult;
import org.wildfly.extras.creaper.core.online.OnlineCommand;
import org.wildfly.extras.creaper.core.online.OnlineCommandContext;
import org.wildfly.extras.creaper.core.online.operations.admin.ReloadToSnapshot;

import com.google.common.base.Strings;

/**
* Provides a pair of online commands to backup and then restore application
* server configuration while the server is running. Backup consists of taking a
* snapshot, restore then means reloading the server from the snapshot.
*
* <p>
* The {@code backup} command must be applied before {@code restore},
* {@code backup} can be applied once, and then {@code restore} can be applied
* many times. If any one of these rules is violated, an exception is thrown.
* For special circumstances, when the backup that was already acquired is no
* longer needed and is not going to be restored, a {@code destroy} command is
* provided. If there was no backup acquired, the destroy command does nothing.
* </p>
*/
public final class SnapshotBackup {
private static final Logger log = Logger.getLogger(SnapshotBackup.class);

private String snapshotName; // null <=> backup wasn't acquired, can't restore

private final OnlineCommand backupPart = new OnlineCommand() {
@Override
public void apply(OnlineCommandContext occ) throws CommandFailedException, IOException, CliException {
if (SnapshotBackup.this.snapshotName != null) {
throw new CommandFailedException("Snapshot was already taken: "
+ SnapshotBackup.this.snapshotName);
}
String snapshotAbsolutePath = "";
ModelNodeResult modelNodeResult = occ.client.execute(":take-snapshot");
if (modelNodeResult.isSuccess() && modelNodeResult.hasDefinedValue()) {
snapshotAbsolutePath = modelNodeResult.stringValue();
}
// returns absolute path, but we need just the name of snapshot
String snapshotName = getSnapshotName(snapshotAbsolutePath);
if (Strings.isNullOrEmpty(snapshotAbsolutePath)) {
throw new CommandFailedException("Unable to take snapshot");
}
SnapshotBackup.this.snapshotName = snapshotName;
}

/**
* Extract file name of absolute path to snapshot
*
* <p>
* Also handles situation when server and client are running on different platforms.
* </p>
*
* @param snapshotAbsolutePath Absolute path to snapshot
* @return name of snapshot
*/
private String getSnapshotName(String snapshotAbsolutePath) {
// Windows file names can't contain "/", so if there's a "/" in the full path, it's not Windows.
String fileSeparator = "/";
if (!snapshotAbsolutePath.contains("/")) {
// absolute path is from windows environment
fileSeparator = "\\";
}
return snapshotAbsolutePath.substring(snapshotAbsolutePath.lastIndexOf(fileSeparator) + 1);
}

@Override
public String toString() {
return "SnapshotBackup.backup";
}
};

private final OnlineCommand restorePart = new OnlineCommand() {
@Override
public void apply(OnlineCommandContext occ) throws CommandFailedException, IOException, InterruptedException,
TimeoutException {
if (SnapshotBackup.this.snapshotName == null) {
throw new CommandFailedException("There's no snapshot backup to restore");
}
ReloadToSnapshot reloadToSnapshot = new ReloadToSnapshot(occ.client, SnapshotBackup.this.snapshotName);
reloadToSnapshot.perform();
}

@Override
public String toString() {
return "SnapshotBackup.restore";
}
};

private final OnlineCommand destroyPart = new OnlineCommand() {
@Override
public void apply(OnlineCommandContext occ) throws CliException, IOException {
if (SnapshotBackup.this.snapshotName == null) {
return;
}
occ.client.executeCli(":delete-snapshot(name=" + SnapshotBackup.this.snapshotName + ")");
SnapshotBackup.this.snapshotName = null;
}

@Override
public String toString() {
return "SnapshotBackup.destroy";
}
};

public OnlineCommand backup() {
return backupPart;
}

public OnlineCommand restore() {
return restorePart;
}

public OnlineCommand destroy() {
return destroyPart;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.wildfly.extras.creaper.test;

/** Category marker for tests that require WildFly 11 and can't run with prior versions of server. */
public interface WildFly11Tests {
}
65 changes: 64 additions & 1 deletion testsuite/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@
<version.wildfly10>10.0.0.Final</version.wildfly10>
<version.wildfly10.core>2.0.10.Final</version.wildfly10.core>
<version.wildfly10.arquillian>1.1.0.Alpha1</version.wildfly10.arquillian>
<!-- doesn't exist yet; update when there's a public release of WildFly 11 -->
<version.wildfly11>11.0.0.Alpha1</version.wildfly11>
<version.wildfly11.core>3.0.0.Alpha10</version.wildfly11.core>
<version.wildfly11.arquillian>2.0.0.Final</version.wildfly11.arquillian>

<managementPort.as7>9999</managementPort.as7>
<managementPort.wildfly>9990</managementPort.wildfly>

<ignoredCategory.as7>org.wildfly.extras.creaper.test.WildFlyTests</ignoredCategory.as7>
<ignoredCategory.wildfly>org.wildfly.extras.creaper.test.AS7Tests</ignoredCategory.wildfly>
<ignoredCategory.wildfly>org.wildfly.extras.creaper.test.AS7Tests, org.wildfly.extras.creaper.test.WildFly11Tests</ignoredCategory.wildfly>
<ignoredCategory.wildfly11>org.wildfly.extras.creaper.test.AS7Tests</ignoredCategory.wildfly11>

<specialJvmArgs.as7 />
<specialJvmArgs.wildfly>-Dcreaper.wildfly</specialJvmArgs.wildfly>
Expand Down Expand Up @@ -66,6 +71,13 @@
<wildfly10.arquillianContainer.artifactId>wildfly-arquillian-container-managed</wildfly10.arquillianContainer.artifactId>
<wildfly10.arquillianContainer.version>${version.wildfly10.arquillian}</wildfly10.arquillianContainer.version>

<wildfly11.applicationServer.groupId>org.wildfly</wildfly11.applicationServer.groupId>
<wildfly11.applicationServer.artifactId>wildfly-dist</wildfly11.applicationServer.artifactId>
<wildfly11.applicationServer.version>${version.wildfly11}</wildfly11.applicationServer.version>
<wildfly11.arquillianContainer.groupId>org.wildfly.arquillian</wildfly11.arquillianContainer.groupId>
<wildfly11.arquillianContainer.artifactId>wildfly-arquillian-container-managed</wildfly11.arquillianContainer.artifactId>
<wildfly11.arquillianContainer.version>${version.wildfly11.arquillian}</wildfly11.arquillianContainer.version>

<!-- defined in profiles "as7", "wildfly8", "wildfly9" and "wildfly10" below -->
<applicationServer.groupId>MUST-BE-DEFINED</applicationServer.groupId>
<applicationServer.artifactId>MUST-BE-DEFINED</applicationServer.artifactId>
Expand Down Expand Up @@ -434,5 +446,56 @@
</dependency>
</dependencies>
</profile>
<profile>
<!-- Wildfly 11 doesn't exist yet; update when there's a public release of WildFly 11 -->
<id>wildfly11</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>

<properties>
<version.java>1.8</version.java>

<applicationServer.groupId>${wildfly11.applicationServer.groupId}</applicationServer.groupId>
<applicationServer.artifactId>${wildfly11.applicationServer.artifactId}</applicationServer.artifactId>
<applicationServer.version>${wildfly11.applicationServer.version}</applicationServer.version>
<applicationServer.managementPort>${managementPort.wildfly}</applicationServer.managementPort>

<arquillianContainer.groupId>${wildfly11.arquillianContainer.groupId}</arquillianContainer.groupId>
<arquillianContainer.artifactId>${wildfly11.arquillianContainer.artifactId}</arquillianContainer.artifactId>
<arquillianContainer.version>${wildfly11.arquillianContainer.version}</arquillianContainer.version>

<tests.ignoredCategory>${ignoredCategory.wildfly11}</tests.ignoredCategory>
<tests.specialJvmArgs>${specialJvmArgs.wildfly}</tests.specialJvmArgs>
</properties>

<dependencies>
<dependency>
<groupId>org.wildfly.extras.creaper</groupId>
<artifactId>creaper-core</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.extras.creaper</groupId>
<artifactId>creaper-commands</artifactId>
</dependency>

<dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-controller-client</artifactId>
<version>${version.wildfly11.core}</version>
</dependency>
<dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-cli</artifactId>
<version>${version.wildfly11.core}</version>
</dependency>
<dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-patching</artifactId>
<version>${version.wildfly11.core}</version>
</dependency>
</dependencies>
</profile>

</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.wildfly.extras.creaper.commands.foundation.online;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.IOException;

import org.jboss.arquillian.junit.Arquillian;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.wildfly.extras.creaper.commands.logging.AddConsoleLogHandler;
import org.wildfly.extras.creaper.core.CommandFailedException;
import org.wildfly.extras.creaper.core.ManagementClient;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
import org.wildfly.extras.creaper.core.online.OnlineOptions;
import org.wildfly.extras.creaper.core.online.operations.Address;
import org.wildfly.extras.creaper.core.online.operations.OperationException;
import org.wildfly.extras.creaper.core.online.operations.Operations;
import org.wildfly.extras.creaper.test.WildFly11Tests;
import org.wildfly.extras.creaper.test.WildFlyTests;
import org.xml.sax.SAXException;

@Category({WildFlyTests.class, WildFly11Tests.class})
@RunWith(Arquillian.class)
public class SnapshotBackupTest {

private static final String TEST_RESOURCE_NAME = "TEST-CONSOLE-HANDLER";
private static final Address TEST_RESOURCE_ADDRESS = Address.subsystem("logging")
.and("console-handler", TEST_RESOURCE_NAME);

private static OnlineManagementClient client;
private static Operations ops;

@Before
public void connect() throws IOException {
client = ManagementClient.online(OnlineOptions
.standalone()
.localDefault()
.build());
ops = new Operations(client);
}

@After
public void cleanup() throws IOException {
if (client != null) {
client.close();
}
}

@Test(expected = CommandFailedException.class)
public void restoreBeforeBackup() throws CommandFailedException, IOException {
SnapshotBackup snapshotBackup = new SnapshotBackup();

client.apply(snapshotBackup.restore()); // fail
}

@Test(expected = CommandFailedException.class)
public void backupTwice() throws CommandFailedException, IOException, SAXException {
SnapshotBackup snapshotBackup = new SnapshotBackup();

client.apply(snapshotBackup.backup());
client.apply(snapshotBackup.backup()); // fail

}

@Test
public void restoreTwice() throws CommandFailedException, IOException, SAXException, InterruptedException {
SnapshotBackup snapshotBackup = new SnapshotBackup();

client.apply(snapshotBackup.backup());

client.apply(snapshotBackup.restore());
client.apply(snapshotBackup.restore());
}

@Test
public void backupRestore() throws CommandFailedException, IOException, OperationException, InterruptedException {
SnapshotBackup snapshotBackup = new SnapshotBackup();

client.apply(snapshotBackup.backup());

assertFalse("Resource should not exists", ops.exists(TEST_RESOURCE_ADDRESS));
client.apply(new AddConsoleLogHandler.Builder(TEST_RESOURCE_NAME).build());
assertTrue("Resource should exists", ops.exists(TEST_RESOURCE_ADDRESS));

client.apply(snapshotBackup.restore());
assertFalse("Resource should not exists", ops.exists(TEST_RESOURCE_ADDRESS));
}

}

0 comments on commit af7d435

Please sign in to comment.