/* * JBoss, Home of Professional Open Source. * Copyright 2013, 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.patching.cli; import static org.jboss.as.patching.Constants.BASE; import static org.jboss.as.patching.Constants.LAYERS; import static org.jboss.as.patching.Constants.SYSTEM; import static org.jboss.as.patching.IoUtils.mkdir; import static org.jboss.as.patching.IoUtils.newFile; import static org.jboss.as.patching.runner.TestUtils.createInstalledImage; import static org.jboss.as.patching.runner.TestUtils.createModule0; import static org.jboss.as.patching.runner.TestUtils.createPatchXMLFile; import static org.jboss.as.patching.runner.TestUtils.createZippedPatchFile; import static org.jboss.as.patching.runner.TestUtils.dump; import static org.jboss.as.patching.runner.TestUtils.randomString; import static org.jboss.as.patching.runner.TestUtils.touch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.File; import org.jboss.as.cli.CommandContext; import org.jboss.as.cli.CommandContextFactory; import org.jboss.as.cli.CommandLineException; import org.jboss.as.patching.logging.PatchLogger; 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.runner.AbstractTaskTestCase; import org.jboss.as.patching.runner.ContentModificationUtils; import org.junit.Test; /** * @author Alexey Loubyansky * */ public class ContentConflictsUnitTestCase extends AbstractTaskTestCase { /** * Tests content conflicts reporting in the CLI during patch application. * * The test creates 2 misc files, 2 modules and 2 bundles and a patch * which updates all of them. Before the patch is applied, one file, * one module and one bundle are modified on the disk. The patch is * expected to fail and the failure description should contain the * info about the conflicting content items. * * @throws Exception */ @Test public void testApply() throws Exception { final File binDir = createInstalledImage(env, "consoleSlot", productConfig.getProductName(), productConfig.getProductVersion()); // build a one-off patch for the base installation // with 1 updated file String patchID = randomString(); File patchDir = mkdir(tempDir, patchID); // create a module for the conflict File baseModuleDir = newFile(env.getInstalledImage().getModulesDir(), SYSTEM, LAYERS, BASE); String moduleConflictName = "module-conflict"; File moduleConflictDir = createModule0(baseModuleDir, moduleConflictName); // create the patch with the updated module ContentModification moduleConflictModified = ContentModificationUtils.modifyModule(patchDir, patchID, moduleConflictDir, "new resource in the module"); // create a module to be updated w/o a conflict String moduleNoConflictName = "module-no-conflict"; File moduleNoConflictDir = createModule0(baseModuleDir, moduleNoConflictName); // create the patch with the updated module ContentModification moduleNoConflictModified = ContentModificationUtils.modifyModule(patchDir, patchID, moduleNoConflictDir, "new resource in the module"); // create a file for the conflict String fileConflictName = "file-conflict.txt"; File conflictFile = touch(binDir, fileConflictName); dump(conflictFile, "original script to run standalone AS7"); // patch the file ContentModification fileConflictModified = ContentModificationUtils.modifyMisc(patchDir, patchID, "updated script", conflictFile, "bin", fileConflictName); // create a file for the conflict String fileNoConflictName = "file-no-conflict.txt"; File noConflictFile = touch(binDir, fileNoConflictName); dump(noConflictFile, "original script to run standalone AS7"); // patch the file ContentModification fileNoConflictModified = ContentModificationUtils.modifyMisc(patchDir, patchID, "updated script", noConflictFile, "bin", fileNoConflictName); //TestUtils.tree(env.getInstalledImage().getJbossHome()); Patch patch = PatchBuilder.create() .setPatchId(patchID) .setDescription(randomString()) .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(fileConflictModified) .addContentModification(fileNoConflictModified) .oneOffPatchElement(patchID, "base", false) .addContentModification(moduleConflictModified) .addContentModification(moduleNoConflictModified) .getParent() .build(); // create the patch createPatchXMLFile(patchDir, patch, false); File zippedPatch = createZippedPatchFile(patchDir, patch.getPatchId()); // create a conflict for the file dump(conflictFile, "conflicting change"); // create a conflict for the module createModule0(baseModuleDir, moduleConflictName, "oops"); // apply the patch using the cli CommandContext ctx = CommandContextFactory.getInstance().newCommandContext(); try { ctx.handle("patch apply " + zippedPatch.getAbsolutePath() + " --distribution=" + env.getInstalledImage().getJbossHome()); fail("Conflicts expected."); } catch(CommandLineException e) { //e.printStackTrace(); final int relativeIndex = env.getInstalledImage().getJbossHome().getAbsolutePath().length() + 1; assertConflicts(e, moduleConflictName + ":main", conflictFile.getAbsolutePath().substring(relativeIndex)); } finally { ctx.terminateSession(); } } /** * Tests content conflicts reporting in the CLI during patch rollback. * * The test creates 2 misc files, 2 modules and 2 bundles and a patch * which updates all of them. The patch is applied. Then one file, * one module and one bundle are modified on the disk. The patch is * rolled back then which is expected to fail and the failure description * should contain the info about the conflicting content items. * * @throws Exception */ @Test public void testRollback() throws Exception { final File binDir = createInstalledImage(env, "consoleSlot", productConfig.getProductName(), productConfig.getProductVersion()); // build a one-off patch for the base installation // with 1 updated file String patchID = randomString(); String patchElementId = randomString(); File patchDir = mkdir(tempDir, patchID); // create a module for the conflict File baseModuleDir = newFile(env.getInstalledImage().getModulesDir(), SYSTEM, LAYERS, BASE); String moduleConflictName = "module-conflict"; File moduleConflictDir = createModule0(baseModuleDir, moduleConflictName); // create the patch with the updated module ContentModification moduleConflictModified = ContentModificationUtils.modifyModule(patchDir, patchElementId, moduleConflictDir, "new resource in the module"); // create a module to be updated w/o a conflict String moduleNoConflictName = "module-no-conflict"; File moduleNoConflictDir = createModule0(baseModuleDir, moduleNoConflictName); // create the patch with the updated module ContentModification moduleNoConflictModified = ContentModificationUtils.modifyModule(patchDir, patchElementId, moduleNoConflictDir, "new resource in the module"); // create a file for the conflict String fileConflictName = "file-conflict.txt"; File conflictFile = touch(binDir, fileConflictName); dump(conflictFile, "original script to run standalone AS7"); // patch the file ContentModification fileConflictModified = ContentModificationUtils.modifyMisc(patchDir, patchID, "updated script", conflictFile, "bin", fileConflictName); // create a file for the conflict String fileNoConflictName = "file-no-conflict.txt"; File noConflictFile = touch(binDir, fileNoConflictName); dump(noConflictFile, "original script to run standalone AS7"); // patch the file ContentModification fileNoConflictModified = ContentModificationUtils.modifyMisc(patchDir, patchID, "updated script", noConflictFile, "bin", fileNoConflictName); //TestUtils.tree(env.getInstalledImage().getJbossHome()); Patch patch = PatchBuilder.create() .setPatchId(patchID) .setDescription(randomString()) .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(fileConflictModified) .addContentModification(fileNoConflictModified) .oneOffPatchElement(patchElementId, "base", false) .addContentModification(moduleConflictModified) .addContentModification(moduleNoConflictModified) .getParent() .build(); // create the patch createPatchXMLFile(patchDir, patch, false); File zippedPatch = createZippedPatchFile(patchDir, patch.getPatchId()); // apply the patch using the cli CommandContext ctx = CommandContextFactory.getInstance().newCommandContext(); try { ctx.handle("patch apply " + zippedPatch.getAbsolutePath() + " --distribution=" + env.getInstalledImage().getJbossHome()); } catch(CommandLineException e) { ctx.terminateSession(); fail("Failed to apply the patch: " + e); } // create a conflict for the file dump(conflictFile, "conflicting change"); // create a conflict for the module createModule0(baseModuleDir, moduleConflictName, "oops"); try { ctx.handle("patch rollback --patch-id=" + patchID + " --distribution=" + env.getInstalledImage().getJbossHome() + " --reset-configuration=false"); fail("Conflicts expected"); } catch(CommandLineException e) { final int relativeIndex = env.getInstalledImage().getJbossHome().getAbsolutePath().length() + 1; // TODO modules and bundles are not checked at the moment assertConflicts(e, moduleConflictName + ":main", conflictFile.getAbsolutePath().substring(relativeIndex)); //assertConflicts(e, conflictFile.getAbsolutePath().substring(relativeIndex)); } finally { ctx.terminateSession(); } } protected void assertConflicts(CommandLineException e, String... conflicts) { final StringBuilder buf = new StringBuilder(); buf.append(PatchLogger.ROOT_LOGGER.detectedConflicts()).append(": "); int i = 0; while(i < conflicts.length) { buf.append(conflicts[i++].replace('\\', '/')); // fix paths on windows if(i < conflicts.length) { buf.append(", "); } } assertEquals(e.getMessage().split(System.getProperty("line.separator"))[0], buf.toString()); } }