/* * JBoss, Home of Professional Open Source. * Copyright 2016, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.patching; import static org.jboss.as.patching.IoUtils.mkdir; import static org.jboss.as.patching.IoUtils.newFile; import static org.jboss.as.test.patching.PatchingTestUtil.AS_DISTRIBUTION; import static org.jboss.as.test.patching.PatchingTestUtil.AS_VERSION; import static org.jboss.as.test.patching.PatchingTestUtil.FILE_SEPARATOR; import static org.jboss.as.test.patching.PatchingTestUtil.MODULES_PATH; import static org.jboss.as.test.patching.PatchingTestUtil.PRODUCT; import static org.jboss.as.test.patching.PatchingTestUtil.createPatchXMLFile; import static org.jboss.as.test.patching.PatchingTestUtil.createZippedPatchFile; import static org.jboss.as.test.patching.PatchingTestUtil.randomString; import java.io.File; import java.nio.charset.StandardCharsets; import org.jboss.as.patching.Constants; import org.jboss.as.patching.HashUtils; import org.jboss.as.patching.metadata.ContentModification; import org.jboss.as.patching.metadata.Patch; import org.jboss.as.patching.metadata.PatchBuilder; import org.jboss.as.patching.metadata.PatchMerger; import org.jboss.as.test.patching.util.module.Module; import org.jboss.as.version.ProductConfig; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.core.testrunner.ServerControl; import org.wildfly.core.testrunner.WildflyTestRunner; /** * @author Martin Simka, updated for WildFly 10 by Jan Martiska */ @RunWith(WildflyTestRunner.class) @ServerControl(manual = true) public class MergedPatchesTestCase extends AbstractPatchingTestCase { /** * creates 4 CPs * creates bundle containing 4 CPs * applies bundle * rolls back back to base version * * @throws Exception */ @Test public void testApplyMergedPatch() throws Exception { File patchTmpDir = mkdir(tempDir, randomString("patchdir")); final String cpPatchID = randomString("cp1"); final String cpPatchID2 = randomString("cp2"); final String cpPatchID3 = randomString("cp3"); final String cpPatchID4 = randomString("cp4"); final String eapWithCP = "EAP with cp patch"; final String eapWithCP2 = "EAP with cp patch 2"; final String eapWithCP3 = "EAP with cp patch 3"; final String eapWithCP4 = "EAP with cp patch 4"; File cpZip = createCP1(cpPatchID, AS_VERSION, eapWithCP, patchTmpDir); File cpZip2 = createCP2(cpPatchID2, eapWithCP, cpPatchID, eapWithCP2, patchTmpDir); File cpZip3 = createCP3(cpPatchID3, eapWithCP2, cpPatchID2, eapWithCP3, patchTmpDir); File cpZip4 = createCP2(cpPatchID4, eapWithCP3, cpPatchID3, eapWithCP4, patchTmpDir); final File mergedCp1Cp2 = PatchMerger.merge(cpZip, cpZip2, new File("merged-cp1-cp2.zip")); mergedCp1Cp2.deleteOnExit(); final File mergedCp1Cp2Cp3 = PatchMerger .merge(mergedCp1Cp2, cpZip3, new File("merged-cp1-cp2-cp3.zip")); mergedCp1Cp2Cp3.deleteOnExit(); final File mergedCp1Cp2Cp3Cp4 = PatchMerger .merge(mergedCp1Cp2Cp3, cpZip4, new File("merged-cp1-cp2-cp3-cp4.zip")); mergedCp1Cp2Cp3Cp4.deleteOnExit(); // apply bundle controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(mergedCp1Cp2Cp3Cp4.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify installed cp version controller.start(); Assert.assertTrue("The patch " + cpPatchID4 + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(cpPatchID4)); Assert.assertTrue("Module " + cp1AddedModuleName + " should exist", CliUtilsForPatching.doesModuleExist(cp1AddedModuleName)); // rollback to base Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackCumulativePatch(true)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify base controller.start(); Assert.assertTrue("The patch " + Constants.BASE + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(Constants.BASE)); Assert.assertFalse("Module " + cp1AddedModuleName + " should not exist", CliUtilsForPatching.doesModuleExist(cp1AddedModuleName)); controller.stop(); } /** * creates 4 CPs * creates bundle containing 4 CPs (merging them together) * applies CP1 * applies CP2 * applies one-off * applies bundle * rolls back to CP2, verifies the one-off is still there * rolls back to CP1 * rolls back to base * * @throws Exception */ @Test public void testApplyBundleSkipTwoCPs() throws Exception { File patchTmpDir = mkdir(tempDir, randomString("patchdir")); final String cpPatchID = randomString("cp1"); final String cpPatchID2 = randomString("cp2"); final String cpPatchID3 = randomString("cp3"); final String cpPatchID4 = randomString("cp4"); final String oneOffId = randomString("oneoff"); final String eapWithCP = "EAP with cp patch"; final String eapWithCP2 = "EAP with cp patch 2"; final String eapWithCP3 = "EAP with cp patch 3"; final String eapWithCP4 = "EAP with cp patch 4"; File cpZip = createCP1(cpPatchID, AS_VERSION, eapWithCP, patchTmpDir); File cpZip2 = createCP2(cpPatchID2, eapWithCP, cpPatchID, eapWithCP2, patchTmpDir); File cpZip3 = createCP3(cpPatchID3, eapWithCP2, cpPatchID2, eapWithCP3, patchTmpDir); File cpZip4 = createCP2(cpPatchID4, eapWithCP3, cpPatchID3, eapWithCP4, patchTmpDir); File oneOffZip = createOneOffPatchAddingMiscFile(oneOffId, eapWithCP2); final File mergedCp1Cp2 = PatchMerger.merge(cpZip, cpZip2, new File("merged-cp1-cp2.zip")); mergedCp1Cp2.deleteOnExit(); final File mergedCp1Cp2Cp3 = PatchMerger .merge(mergedCp1Cp2, cpZip3, new File("merged-cp1-cp2-cp3.zip")); mergedCp1Cp2Cp3.deleteOnExit(); final File mergedCp1Cp2Cp3Cp4 = PatchMerger .merge(mergedCp1Cp2Cp3, cpZip4, new File("merged-cp1-cp2-cp3-cp4.zip")); mergedCp1Cp2Cp3Cp4.deleteOnExit(); // apply cp1 controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(cpZip.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp1 and apply cp2 controller.start(); Assert.assertTrue("The patch " + cpPatchID + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(cpPatchID)); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(cpZip2.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp2 and apply oneoff controller.start(); Assert.assertTrue("The patch " + cpPatchID2 + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(cpPatchID2)); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(oneOffZip.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp2 + oneoff and apply bundle to cp4 controller.start(); Assert.assertTrue("The patch " + cpPatchID2 + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(cpPatchID2)); Assert.assertTrue("The patch " + oneOffId + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(oneOffId)); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(mergedCp1Cp2Cp3Cp4.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp4 and rollback cp4 back to cp2 controller.start(); Assert.assertTrue("The patch " + cpPatchID4 + " should be listed as installed", CliUtilsForPatching.getCumulativePatchId().equalsIgnoreCase(cpPatchID4)); Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackCumulativePatch(true)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp2 + oneoff and roll back the oneoff controller.start(); Assert.assertEquals("The patch " + cpPatchID2 + " should be listed as installed", cpPatchID2, CliUtilsForPatching.getCumulativePatchId()); Assert.assertTrue("The patch " + oneOffId + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(oneOffId)); Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(oneOffId)); controller.stop(); // verify oneoff is not installed, rollback to cp1 controller.start(); Assert.assertFalse("The patch " + oneOffId + " should not be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(oneOffId)); Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackCumulativePatch(true)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // verify cp1 and roll back to base controller.start(); Assert.assertEquals("The patch " + cpPatchID + " should be listed as installed", cpPatchID, CliUtilsForPatching.getCumulativePatchId()); Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackCumulativePatch(true)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); } /* -------------- THINGS RELATED TO CP1 ------------- */ private String cp1AddedModuleName; private String cp1LayerPatchID; private ResourceItem cp1ResourceItem1; private ResourceItem cp1ResourceItem2; private String cp1Slot; private File createCP1(String patchID, String asVersion, final String targetAsVersion, File targetDir) throws Exception { cp1LayerPatchID = "layer" + patchID; File cpPatchDir = mkdir(tempDir, patchID); cp1AddedModuleName = "org.wildfly.test." + randomString("cp1"); cp1ResourceItem1 = new ResourceItem("testFile1", "content1".getBytes(StandardCharsets.UTF_8)); cp1ResourceItem2 = new ResourceItem("testFile2", "content2".getBytes(StandardCharsets.UTF_8)); Module newModule = new Module.Builder(cp1AddedModuleName) .miscFile(cp1ResourceItem1) .miscFile(cp1ResourceItem2) .build(); // Create the version module final String versionModuleName = ProductInfo.getVersionModule(); cp1Slot = ProductInfo.getVersionModuleSlot(); final String originalVersionModulePath = MODULES_PATH + FILE_SEPARATOR + versionModuleName .replace(".", FILE_SEPARATOR) + FILE_SEPARATOR + cp1Slot; final Module modifiedModule = PatchingTestUtil.createVersionModule(targetAsVersion); ContentModification moduleAdded = ContentModificationUtils .addModule(cpPatchDir, cp1LayerPatchID, newModule); ContentModification versionModuleModified = ContentModificationUtils .modifyModule(cpPatchDir, cp1LayerPatchID, HashUtils.hashFile(new File(originalVersionModulePath)), modifiedModule); Patch cpPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A cp patch.") .upgradeIdentity(PRODUCT, asVersion, targetAsVersion) .getParent() .upgradeElement(cp1LayerPatchID, "base", false) .addContentModification(moduleAdded) .addContentModification(versionModuleModified) .getParent() .build(); createPatchXMLFile(cpPatchDir, cpPatch); return createZippedPatchFile(cpPatchDir, patchID, targetDir); } private File createCP2(String patchID, String asVersion, final String currentPatch, final String targetAsVersion, File targetDir) throws Exception { String layerPatchID = "layer" + patchID; File cpPatchDir = mkdir(tempDir, patchID); final String moduleName = "org.wildfly.test." + randomString("cp2"); // Create the version module final String versionModuleName = ProductInfo.getVersionModule(); final Module modifiedModule = PatchingTestUtil.createVersionModule(targetAsVersion); // Calculate the target hash of the currently active module final String currentLayerPatchID = "layer" + currentPatch; File originalVersionModulePath = new File(tempDir, currentPatch); originalVersionModulePath = new File(originalVersionModulePath, currentLayerPatchID); originalVersionModulePath = new File(originalVersionModulePath, Constants.MODULES); originalVersionModulePath = newFile(originalVersionModulePath, versionModuleName.split("\\.")); originalVersionModulePath = new File(originalVersionModulePath, ProductInfo.getVersionModuleSlot()); byte[] patchedAsVersionHash = HashUtils.hashFile(originalVersionModulePath); assert patchedAsVersionHash != null; final ResourceItem resourceItem1 = new ResourceItem("testFile1", "content1".getBytes(StandardCharsets.UTF_8)); final ResourceItem resourceItem2 = new ResourceItem("testFile2", "content2".getBytes(StandardCharsets.UTF_8)); Module newModule = new Module.Builder(moduleName) .miscFile(resourceItem1) .miscFile(resourceItem2) .build(); ContentModification moduleAdded = ContentModificationUtils .addModule(cpPatchDir, layerPatchID, newModule); ContentModification versionModuleModified = ContentModificationUtils .modifyModule(cpPatchDir, layerPatchID, patchedAsVersionHash, modifiedModule); ProductConfig productConfig = new ProductConfig(PRODUCT, asVersion, "main"); Patch cpPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A cp patch.") .upgradeIdentity(productConfig.getProductName(), productConfig.getProductVersion(), targetAsVersion) .getParent() .upgradeElement(layerPatchID, "base", false) .addContentModification(versionModuleModified) .addContentModification(moduleAdded) .getParent() .build(); createPatchXMLFile(cpPatchDir, cpPatch); return createZippedPatchFile(cpPatchDir, patchID, targetDir); } private File createCP3(String patchID, String asVersion, final String currentPatch, final String targetAsVersion, File targetDir) throws Exception { String layerPatchID = "layer" + patchID; File cpPatchDir = mkdir(tempDir, patchID); // Also see if we can update jboss-modules final File installation = new File(AS_DISTRIBUTION); final File patchDir = new File(cpPatchDir, patchID); final ContentModification jbossModulesModification = PatchingTestUtil .updateModulesJar(installation, patchDir); // Create the version module final String versionModuleName = ProductInfo.getVersionModule(); final Module modifiedModule = PatchingTestUtil.createVersionModule(targetAsVersion); // Calculate the target hash of the currently active module final String currentLayerPatchID = "layer" + currentPatch; File originalVersionModulePath = new File(tempDir, currentPatch); originalVersionModulePath = new File(originalVersionModulePath, currentLayerPatchID); originalVersionModulePath = new File(originalVersionModulePath, Constants.MODULES); originalVersionModulePath = newFile(originalVersionModulePath, versionModuleName.split("\\.")); originalVersionModulePath = new File(originalVersionModulePath, ProductInfo.getVersionModuleSlot()); byte[] patchedAsVersionHash = HashUtils.hashFile(originalVersionModulePath); assert patchedAsVersionHash != null; ContentModification versionModuleModified = ContentModificationUtils .modifyModule(cpPatchDir, layerPatchID, patchedAsVersionHash, modifiedModule); Patch cpPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A cp patch.") .upgradeIdentity(PRODUCT, asVersion, targetAsVersion) .getParent() .upgradeElement(layerPatchID, "base", false) .addContentModification(versionModuleModified) .getParent() .addContentModification(jbossModulesModification) .build(); createPatchXMLFile(cpPatchDir, cpPatch); return createZippedPatchFile(cpPatchDir, patchID, targetDir); } private File createOneOffPatchAddingMiscFile(String patchID, String asVersion) throws Exception { File oneOffPatchDir = mkdir(tempDir, patchID); ContentModification miscFileAdded = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, "test content", "awesomeDirectory", "awesomeFile"); ProductConfig productConfig = new ProductConfig(PRODUCT, asVersion, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); return createZippedPatchFile(oneOffPatchDir, patchID); } }