/* * JBoss, Home of Professional Open Source * Copyright 2015, Red Hat, Inc., and individual contributors as indicated * by the @authors tag. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.core.test.standalone.mgmt.api; import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.jboss.as.test.integration.management.util.MgmtOperationException; import org.jboss.as.test.integration.management.util.ModelUtil; import org.jboss.dmr.ModelNode; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.core.test.standalone.base.ContainerResourceMgmtTestBase; import org.wildfly.core.testrunner.WildflyTestRunner; /** * Tests both automated and manual configuration model persistence snapshot generation. * * @author Dominik Pospisil <dpospisi@redhat.com> */ @RunWith(WildflyTestRunner.class) public class ModelPersistenceTestCase extends ContainerResourceMgmtTestBase { private class CfgFileDescription { public CfgFileDescription(int version, File file, long hash) { this.version = version; this.file = file; this.hash = hash; } public int version; public File file; public long hash; } private static final String SERVER_CONFIG_DIR = System.getProperty("jboss.home") + "/standalone/configuration"; private static final String HISTORY_DIR = "standalone_xml_history"; private static final String CURRENT_DIR = "current"; private static Path configDir; private static Path currentCfgDir; private static Path lastCfgFile; @Before public void before() throws IOException, MgmtOperationException { if (configDir == null) { configDir = Paths.get(SERVER_CONFIG_DIR); assertTrue("Server config dir " + SERVER_CONFIG_DIR + " does not exists.", Files.exists(configDir)); assertTrue(Files.isDirectory(configDir)); currentCfgDir = configDir.resolve(HISTORY_DIR).resolve(CURRENT_DIR); // get server configuration name ModelNode op = createOpNode("core-service=server-environment", "read-attribute"); op.get("name").set("config-file"); ModelNode result = executeOperation(op); String configFile = result.asString(); String configFileName = Paths.get(configFile).getFileName().toString(); assertTrue(configFileName.endsWith(".xml")); configFileName = configFileName.substring(0, configFileName.length() - 4); lastCfgFile = configDir.resolve(HISTORY_DIR).resolve(configFileName + ".last.xml"); } } @Test public void testSimpleOperation() throws Exception { CfgFileDescription lastBackupDesc = getLatestBackup(currentCfgDir); long lastFileHash = Files.exists(lastCfgFile) ? FileUtils.checksumCRC32(lastCfgFile.toFile()) : -1; ModelNode op; CfgFileDescription newBackupDesc; try { // execute operation so the model gets updated op = createOpNode("system-property=test", "add"); op.get("value").set("test"); executeOperation(op); // check that the automated snapshat has been generated newBackupDesc = getLatestBackup(currentCfgDir); assertNotNull("Model snapshot not found.", newBackupDesc); // check that the version is incremented by one assertTrue(lastBackupDesc.version == newBackupDesc.version - 1); // check that the last cfg file has changed assertTrue(lastFileHash != FileUtils.checksumCRC32(lastCfgFile.toFile())); } finally { // remove testing attribute op = createOpNode("system-property=test", "remove"); executeOperation(op); } // check that the snapshot has been updated again lastBackupDesc = newBackupDesc; newBackupDesc = getLatestBackup(currentCfgDir); assertNotNull("Model snapshot not found.", newBackupDesc); // check that the version is incremented by one assertEquals("Version was not properly incremented", lastBackupDesc.version, newBackupDesc.version - 1); } @Test public void testCompositeOperation() throws Exception { CfgFileDescription lastBackupDesc = null; CfgFileDescription newBackupDesc = null; try { lastBackupDesc = getLatestBackup(currentCfgDir); // execute composite operation ModelNode[] steps = new ModelNode[2]; steps[0] = createOpNode("system-property=test", "add"); steps[0].get("value").set("test"); steps[1] = createOpNode("system-property=test", "write-attribute"); steps[1].get("name").set("value"); steps[1].get("value").set("test2"); executeOperation(ModelUtil.createCompositeNode(steps)); // check that the automated snapshat has been generated newBackupDesc = getLatestBackup(currentCfgDir); // check that the version is incremented by one assertTrue(lastBackupDesc.version == newBackupDesc.version - 1); } finally { // remove testing attribute ModelNode op = createOpNode("system-property=test", "remove"); executeOperation(op); } // check that the snapshot has been updated again lastBackupDesc = newBackupDesc; newBackupDesc = getLatestBackup(currentCfgDir); assertNotNull("Model snapshot not found.", newBackupDesc); // check that the version is incremented by one assertEquals("Version was not properly incremented", lastBackupDesc.version, newBackupDesc.version - 1); } @Test public void testCompositeOperationRollback() throws Exception { CfgFileDescription lastBackupDesc = getLatestBackup(currentCfgDir); // execute operation so the model gets updated ModelNode op = createOpNode("system-property=test", "add"); op.get("value").set("test"); executeAndRollbackOperation(op); // check that the model has not been updated CfgFileDescription newBackupDesc = getLatestBackup(currentCfgDir); assertNotNull("Model snapshot not found.", newBackupDesc); assertNotNull("Last backup snapshot not found.", lastBackupDesc); // check that the config did not change assertEquals("Version should be same", lastBackupDesc.version, newBackupDesc.version); assertEquals("hash should match", lastBackupDesc.hash, newBackupDesc.hash); } @Test public void testTakeAndDeleteSnapshot() throws Exception { // take snapshot ModelNode op = createOpNode(null, "take-snapshot"); ModelNode result = executeOperation(op); // check that the snapshot file exists String snapshotFileName = result.asString(); File snapshotFile = new File(snapshotFileName); assertTrue(snapshotFile.exists()); // compare with current cfg long snapshotHash = FileUtils.checksumCRC32(snapshotFile); long lastHash = FileUtils.checksumCRC32(lastCfgFile.toFile()); assertEquals(snapshotHash,lastHash); // delete snapshot op = createOpNode(null, "delete-snapshot"); op.get("name").set(snapshotFile.getName()); executeOperation(op); // check that the file is deleted assertFalse("Snapshot file still exists.", snapshotFile.exists()); } @Test public void testListSnapshots() throws Exception { // take snapshot ModelNode op = createOpNode(null, "take-snapshot"); ModelNode result = executeOperation(op); // check that the snapshot file exists String snapshotFileName = result.asString(); File snapshotFile = new File(snapshotFileName); assertTrue(snapshotFile.exists()); // get the snapshot listing op = createOpNode(null, "list-snapshots"); result = executeOperation(op); File snapshotDir = new File(result.get("directory").asString()); assertTrue(snapshotDir.isDirectory()); List<String> snapshotNames = ModelUtil.modelNodeAsStingList(result.get("names")); assertTrue(snapshotNames.contains(snapshotFile.getName())); } private CfgFileDescription getLatestBackup(Path dir) throws IOException { final int[] lastVersion = {0}; final File[] lastFile = {null}; if (Files.isDirectory(dir)) { try(Stream<Path> files = Files.list(dir)) { files.filter(file -> { String fileName = file.getFileName().toString(); String[] nameParts = fileName.split("\\."); return !(!nameParts[0].contains("standalone") && !nameParts[2].equals("xml")); }).forEach(path -> { String fileName = path.getFileName().toString(); String[] nameParts = fileName.split("\\."); if (!nameParts[0].contains("standalone")) { return; } if (!nameParts[2].equals("xml")) { return; } int version = Integer.valueOf(nameParts[1].substring(1)); if (version > lastVersion[0]) { lastVersion[0] = version; lastFile[0] = path.toFile(); } }); } } return new CfgFileDescription(lastVersion[0], lastFile[0], (lastFile[0] != null) ? FileUtils.checksumCRC32(lastFile[0]) : 0); } }