/* * 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.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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 static final Log LOG = LogFactory.getLog(DeployerCanonicalPathTest.class); 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, DestinationComplianceMode.full); HashMap<File, 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, tmpDirSrc, destDir, rawFiles, realizeRawFiles, zipFiles, null, templateEngine, ignoreRegex, 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 rawFileRelativeDestAbsolute = FileUtil.normalizePath( new File(tmpDirDest, rawFileRelativeDest.getPath())).getAbsolutePath(); String rawFileRelativeDestAbsolute2 = FileUtil.normalizePath( new File(tmpDirDest, rawFileRelativeDest2.getPath())).getAbsolutePath(); String rawFileAbsoluteDestAbsolute = FileUtil.normalizePath(rawFileAbsoluteDest).getAbsolutePath(); assert map.size() == 3 : map; assert map.containsKey("rawA.txt") : map; assert new File(rawFileRelativeDestAbsolute).exists(); assert new File(rawFileRelativeDestAbsolute2).exists(); assert MessageDigestGenerator.getDigestString(new File(rawFileRelativeDestAbsolute)).equals( map.get("rawA.txt")); // rawFileRelativeDestAbsolute2 should be treated just like an absolute, external file assert MessageDigestGenerator.getDigestString(new File(rawFileRelativeDestAbsolute2)).equals( map.get(rawFileRelativeDestAbsolute2)); assert !MessageDigestGenerator.getDigestString(testRawFileA).equals(map.get("rawA.txt")) : "should have different hash, we realize this one!"; assert map.containsKey(rawFileAbsoluteDestAbsolute) : map; assert new File(rawFileAbsoluteDestAbsolute).exists(); assert MessageDigestGenerator.getDigestString(new File(rawFileAbsoluteDestAbsolute)).equals( map.get(rawFileAbsoluteDestAbsolute)); assert !MessageDigestGenerator.getDigestString(testRawFileB).equals(map.get(rawFileAbsoluteDestAbsolute)) : "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(rawFileRelativeDestAbsolute2)) : diff; assert diff.getAddedFiles().contains(diff.convertPath(rawFileAbsoluteDestAbsolute)) : diff; assert diff.getRealizedFiles().size() == 3 : diff; assert diff.getRealizedFiles().keySet().contains(diff.convertPath("rawA.txt")) : diff; assert diff.getRealizedFiles().keySet().contains(diff.convertPath(rawFileRelativeDestAbsolute2)) : diff; assert diff.getRealizedFiles().keySet().contains(diff.convertPath(rawFileAbsoluteDestAbsolute)) : 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, DestinationComplianceMode.full); HashMap<File, 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, tmpDirSrc, destDir, rawFiles, null, zipFiles, null, templateEngine, ignoreRegex, 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, DestinationComplianceMode.full); dd = new DeploymentData(deploymentProps, tmpDirSrc, destDir, rawFiles, null, zipFiles, null, templateEngine, ignoreRegex, 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 rawFileRelativeDestAbsolute = FileUtil.normalizePath( new File(tmpDirDest, rawFileRelativeDest.getPath())).getAbsolutePath(); String rawFileRelativeDestAbsolute2 = FileUtil.normalizePath( new File(tmpDirDest, rawFileRelativeDest2.getPath())).getAbsolutePath(); String rawFileAbsoluteDestAbsolute = FileUtil.normalizePath(rawFileAbsoluteDest).getAbsolutePath(); assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileRelativeDestAbsolute)))) .equals("src.modifiedR"); assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileRelativeDestAbsolute2)))) .equals("src.modifiedR2"); assert new String(StreamUtil.slurp(new FileInputStream(new File(rawFileAbsoluteDestAbsolute)))) .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/" + rawFileRelativeDestAbsolute2); } else { StringBuilder str = new StringBuilder(rawFileRelativeDestAbsolute2); 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/" + rawFileAbsoluteDestAbsolute); } else { StringBuilder str = new StringBuilder(rawFileAbsoluteDestAbsolute); 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(rawFileRelativeDestAbsolute2)) : diff; assert diff.getChangedFiles().contains(diff.convertPath(rawFileAbsoluteDestAbsolute)) : 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(rawFileRelativeDestAbsolute2)) : diff; assert diff.getBackedUpFiles().keySet().contains(diff.convertPath(rawFileAbsoluteDestAbsolute)) : diff; } finally { FileUtil.purge(tmpDirDest, true); FileUtil.purge(tmpDirSrc, true); rawFileAbsoluteDest.getCanonicalFile().delete(); } } public void testInitialDeploymentGlossesOverSymlinksInParents() throws Exception { //java7 API, but we're in tests, and require java7 to build anyway Path root = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".symlink-root", null).toPath(); Path symlinkTarget = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".symlink-target", null) .toPath(); File src = FileUtil.createTempDirectory("DeployerCanonicalPathTest", ".src", null); Path parent = root.resolve("parent"); parent.toFile().mkdirs(); try { File destination = null; try { destination = Files.createSymbolicLink(parent.resolve("destination"), symlinkTarget).toFile(); } catch (UnsupportedOperationException e) { LOG.info("Skipping testInitialDeploymentGlossesOverSymlinksInParents. The current filesystem doesn't support symlinks"); return; } // put some source files in our tmpDirSrc location File testRawFileA = new File(src, "updater-testA.txt"); File testRawFileA2 = new File(src, "updater-testA2.txt"); File testRawFileB = new File(src, "updater-testB.txt"); File testRawFileADest = new File(destination, "../realDest/rawA.txt"); File testRawFileA2Dest = new File(destination, "../realDest/rawA2.txt"); File testRawFileBDest = new File(destination, "../../realDest/rawB.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, DestinationComplianceMode.full); HashMap<File, File> zipFiles = null; Map<File, File> rawFiles = new HashMap<File, File>(3); rawFiles.put(testRawFileA, testRawFileADest); rawFiles.put(testRawFileA2, testRawFileA2Dest); rawFiles.put(testRawFileB, testRawFileBDest); DeploymentData dd = new DeploymentData(deploymentProps, src, destination, rawFiles, null, zipFiles, null, templateEngine, null, 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; assert parent.resolve("realDest/rawA.txt").toFile().exists() : "rawA.txt not deployed correctly"; assert parent.resolve("realDest/rawA2.txt").toFile().exists() : "rawA2.txt not deployed correctly"; assert root.resolve("realDest/rawB.txt").toFile().exists() : "rawB.txt not deployed correctly"; //the symlink target, being the destination of the deployment should have the .rhqdeployments directory //specified. No other files should exist there though. assert symlinkTarget.resolve(".rhqdeployments").toFile().exists() : "Could not find .rhqdeployments on the expected location"; assert symlinkTarget.toFile().listFiles().length == 1 : "The target of the symlink should have no other files than .rhqdeployments"; } finally { FileUtil.purge(root.toFile(), true); FileUtil.purge(symlinkTarget.toFile(), true); FileUtil.purge(src, true); } } }