modules/core/util/src/main/java/org/rhq/core/util/updater/DeploymentData.java | 92 +++ modules/core/util/src/test/java/org/rhq/core/util/updater/DeployerCanonicalPathTest.java | 275 ++++++++++ modules/plugins/ant-bundle/src/test/java/org/rhq/plugins/ant/AntBundlePluginComponentTest.java | 173 +++++- modules/plugins/ant-bundle/src/test/resources/test-bundle-dotdot.xml | 19 4 files changed, 547 insertions(+), 12 deletions(-)
New commits: commit 8ae20bd510aa660e94f70cad95fc680fd0dedd47 Author: John Mazzitelli mazz@redhat.com Date: Fri Apr 26 14:41:02 2013 -0400
BZ 917085 - allow for relative and absolute raw file paths to include ".."
diff --git a/modules/core/util/src/main/java/org/rhq/core/util/updater/DeploymentData.java b/modules/core/util/src/main/java/org/rhq/core/util/updater/DeploymentData.java index e81fbaa..c55c1bc 100644 --- a/modules/core/util/src/main/java/org/rhq/core/util/updater/DeploymentData.java +++ b/modules/core/util/src/main/java/org/rhq/core/util/updater/DeploymentData.java @@ -30,6 +30,7 @@ import java.util.Set; import java.util.regex.Pattern;
import org.rhq.core.template.TemplateEngine; +import org.rhq.core.util.file.FileUtil;
/** * Data that describes a particular deployment. In effect, this provides the @@ -114,7 +115,7 @@ public class DeploymentData { this.deploymentProps = deploymentProps; this.zipFiles = zipFiles; this.rawFiles = rawFiles; - this.destinationDir = destinationDir; + this.destinationDir = getCanonicalFile(destinationDir); this.sourceDir = sourceDir; this.ignoreRegex = ignoreRegex; this.manageRootDir = manageRootDir; @@ -131,6 +132,69 @@ public class DeploymentData { this.templateEngine = templateEngine; }
+ // We need to "normalize" all raw file paths that have ".." in them to ensure everything works properly. + // Any raw file pathname (the values in this.rawFiles) that needs to be normalized will be converted to + // a canonical path. Note that any pathname that is relative but have ".." paths that end up taking the file + // above the destination directory needs to be normalized and will end up being an absolute path + // (so all log messages will indicate the full absolute path and if the file + // needs to be backed up it will be backed up as if it was an external file that was specified with an absolute path). + // If the relative path has ".." but does not take the file above the destination directory will simply have its ".." + // normalized out but will still be a relative path (relative to destination directory) (we can't make it absolute + // otherwise Deployer's update will run into errors while backing up and scanning for deleted files). + // See BZ 917085. + for (Map.Entry<File, File> entry : this.rawFiles.entrySet()) { + File rawFile = entry.getValue(); + String rawFilePath = rawFile.getPath(); + + boolean doubledot = rawFilePath.replace('\', '/').matches(".*((/\.\.)|(\.\./)).*"); // finds "/.." or "../" in the string + + if (doubledot) { + File fileToCanonicalize; + + if (rawFile.isAbsolute()) { + fileToCanonicalize = rawFile; + } else { + boolean isWindows = (File.separatorChar == '\'); + if (isWindows) { + // of course, Windows has to make it enormously difficult to do this right... + + // determine if the windows rawFile relative path specified a drive (e.g. C:foobar.txt) + StringBuilder rawFilePathBuilder = new StringBuilder(rawFilePath); + String rawFileDriveLetter = FileUtil.stripDriveLetter(rawFilePathBuilder); // rawFilePathBuilder now has drive letter stripped + + // determine what, if any, drive letter is specified in the destination directory + StringBuilder destDirAbsPathBuilder = new StringBuilder(this.destinationDir.getAbsolutePath()); + String destDirDriveLetter = FileUtil.stripDriveLetter(destDirAbsPathBuilder); + + // figure out what the absolute, normalized path is for the raw file + if ((destDirDriveLetter == null || rawFileDriveLetter == null) + || rawFileDriveLetter.equals(destDirDriveLetter)) { + fileToCanonicalize = new File(this.destinationDir, rawFilePathBuilder.toString()); + } else { + throw new IllegalArgumentException("Cannot normalize relative path [" + rawFilePath + + "]; its drive letter is different than the destination directory [" + + this.destinationDir.getAbsolutePath() + "]"); + } + } else { + fileToCanonicalize = new File(this.destinationDir, rawFilePath); + } + } + + fileToCanonicalize = getCanonicalFile(fileToCanonicalize); + + if (isPathUnderBaseDir(this.destinationDir, fileToCanonicalize)) { + // we can keep rawFile path relative, but we need to normalize out the ".." paths + String baseDir = this.destinationDir.getAbsolutePath(); + String absRawFilePath = fileToCanonicalize.getAbsolutePath(); + String canonicalRelativePath = absRawFilePath.substring(baseDir.length() + 1); // should always return a valid path; if not, let it throw exception (which likely means there is a bug here) + entry.setValue(new File(canonicalRelativePath)); + } else { + // raw file path has ".." such that the file is really above destination dir - use an absolute, canonical path + entry.setValue(fileToCanonicalize); + } + } + } + return; }
@@ -177,4 +241,30 @@ public class DeploymentData { public Map<File, Boolean> getZipsExploded() { return zipsExploded; } + + private File getCanonicalFile(File file) { + try { + file = file.getCanonicalFile(); + } catch (Exception e) { + // ignore this - this really should never happen, but if it does, + // we want to continue and hope using the non-normalized file is ok; + file = file.getAbsoluteFile(); + } + return file; + } + + private boolean isPathUnderBaseDir(File base, File path) { + // this method assumes base and path are absolute and canonical + if (base == null) { + return false; + } + + while (path != null) { + if (base.equals(path)) { + return true; + } + path = path.getParentFile(); + } + return false; + } } diff --git a/modules/core/util/src/test/java/org/rhq/core/util/updater/DeployerCanonicalPathTest.java b/modules/core/util/src/test/java/org/rhq/core/util/updater/DeployerCanonicalPathTest.java new file mode 100644 index 0000000..7a62bf0 --- /dev/null +++ b/modules/core/util/src/test/java/org/rhq/core/util/updater/DeployerCanonicalPathTest.java @@ -0,0 +1,275 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program 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 General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.rhq.core.util.updater; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import org.rhq.core.template.TemplateEngine; +import org.rhq.core.util.MessageDigestGenerator; +import org.rhq.core.util.file.FileUtil; +import org.rhq.core.util.stream.StreamUtil; + +/** + * Tests deploying raw files to deployment locations specified with ".." in the path. + * This will require the deployer code to transform the paths to canonical paths. + * + * @author John Mazzitelli + */ +@Test +public class DeployerCanonicalPathTest { + private TemplateEngine templateEngine; + + @BeforeClass + public void beforeClass() { + Map<String, String> tokens = new HashMap<String, String>(); + tokens.put("rhq.system.hostname", "localhost"); + tokens.put("rhq.system.sysprop.java.version", System.getProperty("java.version")); + + templateEngine = new TemplateEngine(tokens); + } + + @BeforeMethod + public void beforeMethod() { + System.out.println("\n\n=============== START OF NEW TEST ===============\n"); + } + + // any raw file path (absolute or relative) that contains ".." will be converted to an absolute, canonical path - this is what we test + public void testInitialDeployRawFilesWithCanonicalPaths() throws Exception { + File tmpDirDest = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".dest", null); + File tmpDirSrc = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".src", null); + File rawFileRelativeDest = new File("dir-does-not-existA/../rawA.txt"); // relative to "tmpDirDest" that we just created above + File rawFileRelativeDest2 = new File("dir-does-not-existA/../../rawA.txt"); // relative to "tmpDirDest" but it takes us above it + File rawFileAbsoluteDest = new File(System.getProperty("java.io.tmpdir"), "dir-does-not-existB/../rawB.txt"); + + try { + // put some source files in our tmpDirSrc location + File testRawFileA = new File(tmpDirSrc, "updater-testA.txt"); + File testRawFileA2 = new File(tmpDirSrc, "updater-testA2.txt"); + File testRawFileB = new File(tmpDirSrc, "updater-testB.txt"); + FileUtil.copyFile(new File("target/test-classes/updater-testA.txt"), testRawFileA); + FileUtil.copyFile(new File("target/test-classes/updater-testA.txt"), testRawFileA2); + FileUtil.copyFile(new File("target/test-classes/updater-testB.txt"), testRawFileB); + + DeploymentProperties deploymentProps = new DeploymentProperties(0, "testbundle", "1.0.test", null); + Set<File> zipFiles = null; + Map<File, File> rawFiles = new HashMap<File, File>(3); + rawFiles.put(testRawFileA, rawFileRelativeDest); // we will realize this one ... + rawFiles.put(testRawFileA2, rawFileRelativeDest2); // and this one ... + rawFiles.put(testRawFileB, rawFileAbsoluteDest); // and we will realize this one, too + File destDir = tmpDirDest; + Pattern ignoreRegex = null; + Set<File> realizeRawFiles = new HashSet<File>(3); + realizeRawFiles.add(testRawFileA); + realizeRawFiles.add(testRawFileA2); + realizeRawFiles.add(testRawFileB); + DeploymentData dd = new DeploymentData(deploymentProps, zipFiles, rawFiles, tmpDirSrc, destDir, null, + realizeRawFiles, templateEngine, ignoreRegex, true, null); + Deployer deployer = new Deployer(dd); + DeployDifferences diff = new DeployDifferences(); + FileHashcodeMap map = deployer.deploy(diff); + System.out.println("map-->\n" + map); + System.out.println("diff->\n" + diff); + + String rawFileRelativeDestCanonical = new File(tmpDirDest, rawFileRelativeDest.getPath()).getCanonicalPath(); + String rawFileRelativeDestCanonical2 = new File(tmpDirDest, rawFileRelativeDest2.getPath()) + .getCanonicalPath(); + String rawFileAbsoluteDestCanonical = rawFileAbsoluteDest.getCanonicalPath(); + + assert map.size() == 3 : map; + + assert map.containsKey("rawA.txt") : map; + assert new File(rawFileRelativeDestCanonical).exists(); + assert new File(rawFileRelativeDestCanonical2).exists(); + assert MessageDigestGenerator.getDigestString(new File(rawFileRelativeDestCanonical)).equals( + map.get("rawA.txt")); + + // rawFileRelativeDestCanonical2 should be treated just like an absolute, external file + assert MessageDigestGenerator.getDigestString(new File(rawFileRelativeDestCanonical2)).equals( + map.get(rawFileRelativeDestCanonical2)); + assert !MessageDigestGenerator.getDigestString(testRawFileA).equals(map.get("rawA.txt")) : "should have different hash, we realize this one!"; + + assert map.containsKey(rawFileAbsoluteDestCanonical) : map; + assert new File(rawFileAbsoluteDestCanonical).exists(); + assert MessageDigestGenerator.getDigestString(new File(rawFileAbsoluteDestCanonical)).equals( + map.get(rawFileAbsoluteDestCanonical)); + assert !MessageDigestGenerator.getDigestString(testRawFileB).equals(map.get(rawFileAbsoluteDestCanonical)) : "should have different hash, we realized this one"; + + assert diff.getAddedFiles().size() == 3 : diff; + assert diff.getAddedFiles().contains(diff.convertPath("rawA.txt")) : diff; + assert diff.getAddedFiles().contains(diff.convertPath(rawFileRelativeDestCanonical2)) : diff; + assert diff.getAddedFiles().contains(diff.convertPath(rawFileAbsoluteDestCanonical)) : diff; + assert diff.getRealizedFiles().size() == 3 : diff; + assert diff.getRealizedFiles().keySet().contains(diff.convertPath("rawA.txt")) : diff; + assert diff.getRealizedFiles().keySet().contains(diff.convertPath(rawFileRelativeDestCanonical2)) : diff; + assert diff.getRealizedFiles().keySet().contains(diff.convertPath(rawFileAbsoluteDestCanonical)) : diff; + } finally { + FileUtil.purge(tmpDirDest, true); + FileUtil.purge(tmpDirSrc, true); + rawFileAbsoluteDest.getCanonicalFile().delete(); + } + } + + public void testUpdateDeployRawFileWithRelativePath() throws Exception { + File tmpDirDest = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".dest", null); + File tmpDirSrc = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".src", null); + File rawFileRelativeDest = new File("dir-does-not-existA/../rawA.txt"); // relative to "tmpDirDest" that we just created above + File rawFileRelativeDest2 = new File("dir-does-not-existA/../../rawA.txt"); // relative to "tmpDirDest" but it takes us above it + File rawFileAbsoluteDest = new File(System.getProperty("java.io.tmpdir"), "dir-does-not-existB/../rawB.txt"); + + try { + // put some source files in our tmpDirSrc location + File testRawFileA = new File(tmpDirSrc, "updater-testA.txt"); + File testRawFileA2 = new File(tmpDirSrc, "updater-testA2.txt"); + File testRawFileB = new File(tmpDirSrc, "updater-testB.txt"); + FileUtil.copyFile(new File("target/test-classes/updater-testA.txt"), testRawFileA); + FileUtil.copyFile(new File("target/test-classes/updater-testA.txt"), testRawFileA2); + FileUtil.copyFile(new File("target/test-classes/updater-testB.txt"), testRawFileB); + + DeploymentProperties deploymentProps = new DeploymentProperties(0, "testbundle", "1.0.test", null); + Set<File> zipFiles = null; + Map<File, File> rawFiles = new HashMap<File, File>(3); + rawFiles.put(testRawFileA, rawFileRelativeDest); + rawFiles.put(testRawFileA2, rawFileRelativeDest2); + rawFiles.put(testRawFileB, rawFileAbsoluteDest); + File destDir = tmpDirDest; + Pattern ignoreRegex = null; + DeploymentData dd = new DeploymentData(deploymentProps, zipFiles, rawFiles, tmpDirSrc, destDir, null, null, + templateEngine, ignoreRegex, true, null); + Deployer deployer = new Deployer(dd); + DeployDifferences diff = new DeployDifferences(); + FileHashcodeMap map = deployer.deploy(diff); + System.out.println("map-->\n" + map); + System.out.println("diff->\n" + diff); + assert map.size() == 3 : map; + + // make sure the first raw file is in the dest dir + String f = rawFileRelativeDest.getPath(); + File destFile = new File(tmpDirDest, f).getCanonicalFile(); // notice f is assumed relative to tmpDirDest, must convert to canonical path + assert destFile.exists() : destFile; + FileUtil.writeFile(new ByteArrayInputStream("modifiedR".getBytes()), destFile); // change the file so we back it up during update + + // make sure the second raw file, though specified originally as a relative file, is in the external location + f = rawFileRelativeDest2.getPath(); + destFile = new File(tmpDirDest, f).getCanonicalFile(); // must convert to canonical path + assert destFile.exists() : destFile; + FileUtil.writeFile(new ByteArrayInputStream("modifiedR2".getBytes()), destFile); // change the file so we back it up during update + + // make sure the third raw file is in the external location + destFile = rawFileAbsoluteDest.getCanonicalFile(); // must convert to canonical path + assert destFile.exists() : destFile; + FileUtil.writeFile(new ByteArrayInputStream("modifiedA".getBytes()), destFile); // change the file so we back it up during update + + // UPDATE + // alter the src files so we backup our changed files + FileUtil.writeFile(new ByteArrayInputStream("src.modifiedR".getBytes()), testRawFileA); + FileUtil.writeFile(new ByteArrayInputStream("src.modifiedR2".getBytes()), testRawFileA2); + FileUtil.writeFile(new ByteArrayInputStream("src.modifiedA".getBytes()), testRawFileB); + + deploymentProps = new DeploymentProperties(1, "testbundle", "2.0.test", null); + dd = new DeploymentData(deploymentProps, zipFiles, rawFiles, tmpDirSrc, destDir, null, null, + templateEngine, ignoreRegex, true, null); + deployer = new Deployer(dd); + diff = new DeployDifferences(); + map = deployer.deploy(diff); + System.out.println("map-->\n" + map); + System.out.println("diff->\n" + diff); + + String rawFileRelativeDestCanonical = new File(tmpDirDest, rawFileRelativeDest.getPath()) + .getCanonicalPath(); + String rawFileRelativeDestCanonical2 = new File(tmpDirDest, rawFileRelativeDest2.getPath()) + .getCanonicalPath(); + String rawFileAbsoluteDestCanonical = rawFileAbsoluteDest.getCanonicalPath(); + + assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileRelativeDestCanonical)))) + .equals("src.modifiedR"); + assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileRelativeDestCanonical2)))) + .equals("src.modifiedR2"); + assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileAbsoluteDestCanonical)))) + .equals("src.modifiedA"); + + boolean isWindows = File.separatorChar == '\'; + final File metadir = new File(tmpDirDest, ".rhqdeployments"); + File backupRel = new File(metadir, "1/backup/rawA.txt"); + File backupRel2; + // test the second raw file, the one that was specified originally as a relative file but took us out of the dest dir + if (!isWindows) { + backupRel2 = new File(metadir, "1/ext-backup/" + rawFileRelativeDestCanonical2); + } else { + StringBuilder str = new StringBuilder(rawFileRelativeDestCanonical2); + String driveLetter = FileUtil.stripDriveLetter(str); + if (driveLetter != null) { + driveLetter = "_" + driveLetter + '/'; + } else { + driveLetter = ""; + } + backupRel2 = new File(metadir, "1/ext-backup/" + driveLetter + str.toString()); + } + // test the third raw file, the one that was specified originally as an absolute, external file + File backupAbs; + if (!isWindows) { + backupAbs = new File(metadir, "1/ext-backup/" + rawFileAbsoluteDestCanonical); + } else { + StringBuilder str = new StringBuilder(rawFileAbsoluteDestCanonical); + String driveLetter = FileUtil.stripDriveLetter(str); + if (driveLetter != null) { + driveLetter = "_" + driveLetter + '/'; + } else { + driveLetter = ""; + } + backupAbs = new File(metadir, "1/ext-backup/" + driveLetter + str.toString()); + } + + // the backup files should exist + assert backupRel.exists() : backupRel; + assert backupRel2.exists() : backupRel2; + assert backupAbs.exists() : backupAbs; + + assert map.size() == 3 : map; + assert diff.getChangedFiles().size() == 3 : diff; + assert diff.getChangedFiles().contains(diff.convertPath("rawA.txt")) : diff; + assert diff.getChangedFiles().contains(diff.convertPath(rawFileRelativeDestCanonical2)) : diff; + assert diff.getChangedFiles().contains(diff.convertPath(rawFileAbsoluteDestCanonical)) : diff; + assert diff.getDeletedFiles().isEmpty() : diff; + assert diff.getBackedUpFiles().size() == 3 : diff; + assert diff.getBackedUpFiles().keySet().contains(diff.convertPath("rawA.txt")) : diff; + assert diff.getBackedUpFiles().keySet().contains(diff.convertPath(rawFileRelativeDestCanonical2)) : diff; + assert diff.getBackedUpFiles().keySet().contains(diff.convertPath(rawFileAbsoluteDestCanonical)) : diff; + } finally { + FileUtil.purge(tmpDirDest, true); + FileUtil.purge(tmpDirSrc, true); + rawFileAbsoluteDest.getCanonicalFile().delete(); + } + } +} \ No newline at end of file diff --git a/modules/plugins/ant-bundle/src/test/java/org/rhq/plugins/ant/AntBundlePluginComponentTest.java b/modules/plugins/ant-bundle/src/test/java/org/rhq/plugins/ant/AntBundlePluginComponentTest.java index daf5463..f0605a2 100644 --- a/modules/plugins/ant-bundle/src/test/java/org/rhq/plugins/ant/AntBundlePluginComponentTest.java +++ b/modules/plugins/ant-bundle/src/test/java/org/rhq/plugins/ant/AntBundlePluginComponentTest.java @@ -26,10 +26,12 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -70,20 +72,25 @@ import org.rhq.core.util.updater.DeploymentsMetadata;
@Test public class AntBundlePluginComponentTest { + private static final boolean ENABLE_TESTS = true; + private static final String DEST_BASE_DIR_NAME = "Root File System"; // just mimics the real platform plugin types' name
private AntBundlePluginComponent plugin; + private File testFilesBaseDir; // under here will go all our test files: tmpDir, bundleFilesDir and destDir private File tmpDir; private File bundleFilesDir; private File destDir;
@BeforeClass public void initDirs() throws Exception { - this.tmpDir = new File("target/antbundletest/tmp"); + this.testFilesBaseDir = new File("target/antbundletest"); + + this.tmpDir = new File(this.testFilesBaseDir, "tmp"); FileUtil.purge(this.tmpDir, true); - this.bundleFilesDir = new File("target/antbundletest/bundlefiles"); + this.bundleFilesDir = new File(this.testFilesBaseDir, "bundlefiles"); FileUtil.purge(this.bundleFilesDir, true); - this.destDir = new File("target/antbundletest/destination"); + this.destDir = new File(this.testFilesBaseDir, "destination"); FileUtil.purge(this.destDir, true); }
@@ -117,7 +124,7 @@ public class AntBundlePluginComponentTest { FileUtil.purge(this.destDir, true); }
- @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleRevert() throws Exception { // install then upgrade a bundle first testAntBundleUpgrade(); @@ -227,17 +234,17 @@ public class AntBundlePluginComponentTest { assert previousProps.getBundleVersion().equals("3.0"); // testAntBundleUpgrade deployed version 3.0 }
- @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleUpgrade() throws Exception { upgrade(false); }
- @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleCleanUpgrade() throws Exception { upgrade(true); }
- @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleInitialInstall() throws Exception { doAntBundleInitialInstall(true); } @@ -348,7 +355,7 @@ public class AntBundlePluginComponentTest { /** * Test deployment of an RHQ bundle recipe. */ - @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundle() throws Exception { ResourceType resourceType = new ResourceType("testSimpleBundle", "plugin", ResourceCategory.SERVER, null); BundleType bundleType = new BundleType("testSimpleBundle", resourceType); @@ -412,9 +419,149 @@ public class AntBundlePluginComponentTest { }
/** + * Test raw files whose destination locations have ".." in their paths. + */ + @Test(enabled = ENABLE_TESTS) + public void testRawFilesWithDotDotPaths() throws Exception { + // our test bundle's relative raw file paths that resolve above dest dir will resolve to here + final File externalDir = new File(this.testFilesBaseDir, "ext"); + + ResourceType resourceType = new ResourceType("testSimpleBundle", "plugin", ResourceCategory.SERVER, null); + BundleType bundleType = new BundleType("testSimpleBundle", resourceType); + Repo repo = new Repo("testSimpleBundle"); + PackageType packageType = new PackageType("testSimpleBundle", resourceType); + Bundle bundle = new Bundle("testSimpleBundle", bundleType, repo, packageType); + BundleVersion bundleVersion = new BundleVersion("testSimpleBundle", "1.0", bundle, + getRecipeFromFile("test-bundle-dotdot.xml")); + BundleDestination destination = new BundleDestination(bundle, "testSimpleBundle", new ResourceGroup( + "testSimpleBundle"), DEST_BASE_DIR_NAME, this.destDir.getAbsolutePath()); + Configuration config = new Configuration(); + + BundleDeployment deployment = new BundleDeployment(); + deployment.setId(0); + deployment.setName("test bundle deployment name"); + deployment.setBundleVersion(bundleVersion); + deployment.setConfiguration(config); + deployment.setDestination(destination); + + // create test files (see bundle recipe for why these files are created) + final String TEST1 = "test1.txt"; + File file1 = new File(this.bundleFilesDir, TEST1); + writeFile(TEST1, file1); + + final String TEST2 = "test2.txt"; + File file2 = new File(this.bundleFilesDir, TEST2); + writeFile(TEST2, file2); + + final String TEST3 = "test3.txt"; + File file3 = new File(this.bundleFilesDir, TEST3); + writeFile(TEST3, file3); + + final String TEST4 = "test4.txt"; + File file4 = new File(this.bundleFilesDir, TEST4); + writeFile(TEST4, file4); + + // ----- initial deployment ----- + BundleDeployRequest request = new BundleDeployRequest(); + request.setBundleFilesLocation(this.bundleFilesDir); + request.setResourceDeployment(createNewBundleDeployment(deployment)); + request.setBundleManagerProvider(new MockBundleManagerProvider()); + request.setAbsoluteDestinationDirectory(this.destDir); + BundleDeployResult results = plugin.deployBundle(request); + assertResultsSuccess(results); + + // test that the files were put where we expected them to be + File file1Dest = new File(this.destDir, "subdir/" + TEST1); + File file2Dest = new File(this.destDir, TEST2); + File file3Dest = new File(externalDir, TEST3); + File file4Dest = new File(externalDir, TEST4); + assert TEST1.equals(readFile(file1Dest)); // inside dest dir + assert TEST2.equals(readFile(file2Dest)); // inside dest dir + assert TEST3.equals(readFile(file3Dest)); // outside dest dir + assert TEST4.equals(readFile(file4Dest)); // outside dest dir + + // ----- prepare to update the bundle ---- + cleanPluginDirs(); // clean everything but the dest dir - we want to keep the metadata (this should not purge ext/ dir) + prepareBeforeTestMethod(); // prepare for our new test + // our src files will have different content for this bundle deployment compared to the initial deployment + writeFile(TEST1 + "update", file1); + writeFile(TEST2 + "update", file2); + writeFile(TEST3 + "update", file3); + writeFile(TEST4 + "update", file4); + // change our initial deployment files, RHQ should see the changes and back these files up + writeFile(TEST1 + "modified", file1Dest); + writeFile(TEST2 + "modified", file2Dest); + writeFile(TEST3 + "modified", file3Dest); + writeFile(TEST4 + "modified", file4Dest); + + // ----- update deployment ----- + deployment.setId(1); + request = new BundleDeployRequest(); + request.setBundleFilesLocation(this.bundleFilesDir); + request.setResourceDeployment(createNewBundleDeployment(deployment)); + request.setBundleManagerProvider(new MockBundleManagerProvider()); + request.setAbsoluteDestinationDirectory(this.destDir); + results = plugin.deployBundle(request); + assertResultsSuccess(results); + + // test that all files were updated + assert (TEST1 + "update").equals(readFile(file1Dest)); // inside dest dir + assert (TEST2 + "update").equals(readFile(file2Dest)); // inside dest dir + assert (TEST3 + "update").equals(readFile(file3Dest)); // outside dest dir + assert (TEST4 + "update").equals(readFile(file4Dest)); // outside dest dir + + // test that our changed files that were under dest dir were properly backed up + DeploymentsMetadata metadata = new DeploymentsMetadata(this.destDir); + File backupDir = metadata.getDeploymentBackupDirectory(deployment.getId()); + File file1Backup = new File(backupDir, "subdir/" + TEST1); + File file2Backup = new File(backupDir, TEST2); + assert file1Backup.isFile() : "should have been backed up: " + file1Backup; + assert file2Backup.isFile() : "should have been backed up: " + file2Backup; + assert (TEST1 + "modified").equals(readFile(file1Backup)) : "bad backup file: " + file1Backup; + assert (TEST2 + "modified").equals(readFile(file2Backup)) : "bad backup file: " + file2Backup; + + // test that our changed files that were above dest dir were properly backed up + Map<String, File> winDirs = metadata.getDeploymentExternalBackupDirectoriesForWindows(deployment.getId()); + if (winDirs == null) { + // we are running on non-windows platform + backupDir = metadata.getDeploymentExternalBackupDirectory(deployment.getId()); + } else { + // we are on windows, our test only uses a single drive root, so we can grab the only item in the map + assert winDirs.size() == 1 : "should only have 1 ext backup dir on windows: " + winDirs; + backupDir = winDirs.values().iterator().next().getAbsoluteFile(); + } + File file3Backup = new File(backupDir, file3Dest.getAbsolutePath()); + File file4Backup = new File(backupDir, file4Dest.getAbsolutePath()); + assert file3Backup.isFile() : "should have been backed up: " + file3Backup; + assert file4Backup.isFile() : "should have been backed up: " + file4Backup; + assert (TEST3 + "modified").equals(readFile(file3Backup)) : "bad backup file: " + file3Backup; + assert (TEST4 + "modified").equals(readFile(file4Backup)) : "bad backup file: " + file4Backup; + + // ----- revert to last deployment, restoring backed up files + deployment.setId(2); + request = new BundleDeployRequest(); + request.setBundleFilesLocation(this.bundleFilesDir); + request.setResourceDeployment(createNewBundleDeployment(deployment)); + request.setBundleManagerProvider(new MockBundleManagerProvider()); + request.setAbsoluteDestinationDirectory(this.destDir); + request.setRevert(true); + results = plugin.deployBundle(request); + assertResultsSuccess(results); + + // make sure our files were reverted, giving us back the files that were backed up + assert readFile(file1Backup).equals(readFile(file1Dest)); // inside dest dir + assert readFile(file2Backup).equals(readFile(file2Dest)); // inside dest dir + assert readFile(file3Backup).equals(readFile(file3Dest)); // outside dest dir + assert readFile(file4Backup).equals(readFile(file4Dest)); // outside dest dir + + // ----- clean up our test ----- + FileUtil.purge(externalDir, true); + } + + /** * Test realizing of replacement tokens of resource tags. */ - @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testTags() throws Exception { ResourceType resourceType = new ResourceType("testSimpleBundle", "plugin", ResourceCategory.SERVER, null); BundleType bundleType = new BundleType("testSimpleBundle", resourceType); @@ -484,7 +631,7 @@ public class AntBundlePluginComponentTest { /** * Test deployment of an RHQ bundle recipe where the deploy directory is not to be fully managed. */ - @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleNoManageRootDir() throws Exception { ResourceType resourceType = new ResourceType("testNoManageRootDirBundle", "plugin", ResourceCategory.SERVER, null); @@ -618,7 +765,7 @@ public class AntBundlePluginComponentTest { * Test deployment of an RHQ bundle recipe where the deploy directory is to be fully managed. * This is the typical use-case and the default behavior. */ - @Test(enabled = true) + @Test(enabled = ENABLE_TESTS) public void testAntBundleManageRootDir() throws Exception { ResourceType resourceType = new ResourceType("testManageRootDirBundle", "plugin", ResourceCategory.SERVER, null); BundleType bundleType = new BundleType("testManageRootDirBundle", resourceType); @@ -878,6 +1025,10 @@ public class AntBundlePluginComponentTest { return new String(StreamUtil.slurp(new FileInputStream(file))); }
+ private void writeFile(final String content, final File file) throws IOException { + FileUtil.writeFile(new ByteArrayInputStream(content.getBytes()), file); + } + private void loadProperties(Properties realizedProps, FileInputStream fileInputStream) throws Exception { try { realizedProps.load(fileInputStream); diff --git a/modules/plugins/ant-bundle/src/test/resources/test-bundle-dotdot.xml b/modules/plugins/ant-bundle/src/test/resources/test-bundle-dotdot.xml new file mode 100644 index 0000000..f96b241 --- /dev/null +++ b/modules/plugins/ant-bundle/src/test/resources/test-bundle-dotdot.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> + +<project name="bundle-dotdot" default="main" xmlns:rhq="antlib:org.rhq.bundle"> + + <rhq:bundle name="bundle-dotdot" version="1.0" + description="testing raw files with .. in their destination paths"> + + <rhq:deployment-unit name="bundle"> + <rhq:file name="test1.txt" destinationFile="nothere/../phantom/../subdir/test1.txt" /> <!-- still under dest dir --> + <rhq:file name="test2.txt" destinationDir="nothere/../" /> <!-- still under dest dir --> + <rhq:file name="test3.txt" destinationFile="../ext/test3.txt" /> <!-- external location outside dest dir--> + <rhq:file name="test4.txt" destinationDir="../ext" /> <!-- external location outside dest dir--> + </rhq:deployment-unit> + + </rhq:bundle> + + <target name="main"/> + +</project> \ No newline at end of file
rhq-commits@lists.fedorahosted.org