Mercurial > hg > release > thermostat-1.0
changeset 1280:e88531d6fdc6
Add build steps in order to allow stand-alone itest runs.
Reviewed-by: omajid
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006317.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/pom.xml Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,224 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2013 Red Hat, Inc. + + This file is part of Thermostat. + + Thermostat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + Thermostat 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 for more details. + + You should have received a copy of the GNU General Public License + along with Thermostat; see the file COPYING. If not see + <http://www.gnu.org/licenses />. + + Linking this code with other modules is making a combined work + based on this code. Thus, the terms and conditions of the GNU + General Public License cover the whole combination. + + As a special exception, the copyright holders of this code give + you permission to link this code with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also + meet, for each linked independent module, the terms and conditions + of the license of that module. An independent module is a module + which is not derived from or based on this code. If you modify + this code, you may extend this exception to your version of the + library, but you are not obligated to do so. If you do not wish + to do so, delete this exception statement from your version. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-integration-tests</artifactId> + <version>0.16.0-SNAPSHOT</version> + </parent> + + <artifactId>thermostat-integration-tests-run</artifactId> + <packaging>jar</packaging> + + <name>Thermostat Integration Tests (Runner)</name> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <plugins> + <!-- jacoco:report insists to have a target/classes dir. since this + module only contains test classes it won't exist after a build. + Hence, create manually in order to unbreak the build. For some + reason skipping both, report AND prepare-agent goals for the + jacoco plugin does not work. --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.6</version> + <executions> + <execution> + <id>make-target-classes-dir</id> + <phase>prepare-package</phase> + <configuration> + <target> + <mkdir dir="${project.build.directory}/classes" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <!-- skip unit test run, tests to be executed during integration-test --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-Djava.security.auth.login.config=${thermostat.home}/etc/thermostat_jaas.conf ${coverageAgent}</argLine> + <skip>true</skip> + </configuration> + <executions> + <execution> + <id>run-integration-tests</id> + <phase>integration-test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <skip>false</skip> + <!-- Don't run the suite class. We only have it for the stand-alone itest thingy --> + <excludes> + <exclude>**/AllStandaloneTests.java</exclude> + </excludes> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.4</version> + <executions> + <execution> + <id>copy-deps</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <excludeTransitive>true</excludeTransitive> + <excludeGroupIds>org.osgi,junit,org.hamcrest</excludeGroupIds> + <outputDirectory>${project.build.directory}/libs</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <!-- produce a jar with the integration tests, which the standalone module can consume --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.apache.maven.plugins + </groupId> + <artifactId> + maven-dependency-plugin + </artifactId> + <versionRange>[2.4,)</versionRange> + <goals> + <goal>copy-dependencies</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore></ignore> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + <dependencies> + + <!-- integration tests --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sourceforge.expectj</groupId> + <artifactId>expectj</artifactId> + <scope>test</scope> + </dependency> + + <!-- thermostat parts --> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-distribution</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-web-war</artifactId> + <version>${project.version}</version> + <type>war</type> + <scope>test</scope> + </dependency> + + <!-- Embedded jetty for testing the WAR (no peace from here on..). --> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-plus</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-webapp</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + + </dependencies> +</project> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/CliTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,257 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + +import expectj.Spawn; + +/** + * Integration tests to exercise the basics of the thermostat command line. + */ +public class CliTest extends IntegrationTest { + + @Test + public void testExpectIsSane() throws Exception { + Spawn shell = spawnThermostat(); + + try { + shell.expect("some-random-text-that-is-not-really-possible"); + fail("should never match"); + } catch (IOException endOfStream) { + assertTrue(endOfStream.getMessage().contains("End of stream reached, no match found")); + } + shell.expectClose(); + } + + @Test + public void testSimpleInvocationPrintsHelp() throws Exception { + Spawn shell = spawnThermostat(); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + + assertMatchesHelpCommandList(stdOut); + + String stdErr = shell.getCurrentStandardErrContents(); + assertEquals(stdErr, ""); + } + + @Test + public void testHelpCommandInvocation() throws Exception { + Spawn shell = spawnThermostat("help"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + String stdErr = shell.getCurrentStandardErrContents(); + + assertMatchesHelpCommandList(stdOut); + assertEquals(stdErr, ""); + } + + @Test + public void testHelpOnHelp() throws Exception { + Spawn shell = spawnThermostat("help", "help"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + String stdErr = shell.getCurrentStandardErrContents(); + + String[] lines = stdOut.split("\n"); + String usage = lines[0]; + assertEquals("usage: thermostat help [command-name]", usage); + + assertEquals(stdErr, ""); + } + + @Test + public void testVersionArgument() throws Exception { + Spawn shell = spawnThermostat("--version"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + String stdErr = shell.getCurrentStandardErrContents(); + + assertTrue(stdOut.matches("Thermostat version \\d+\\.\\d+\\.\\d+\n")); + assertEquals(stdErr, ""); + } + + @Test + public void testShell() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("help\n"); + + shell.expect(SHELL_PROMPT); + + assertMatchesShellHelpCommandList(shell.getCurrentStandardOutContents()); + + shell.send("exit\n"); + + shell.expectClose(); + } + + @Test + public void testShellPrintsVersionOnStartup() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + + String stdOut = shell.getCurrentStandardOutContents(); + assertTrue(stdOut.contains("Thermostat version ")); + } + + @Test + public void versionArgumentInShellIsNotAllowed() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("--version\n"); + + shell.expect(SHELL_PROMPT); + + String stdOut = shell.getCurrentStandardOutContents(); + String stdErr = shell.getCurrentStandardErrContents(); + + assertMatchesShellHelpCommandList(shell.getCurrentStandardOutContents()); + // use the Pattern.DOTALL flag (?s) so that line terminators match with + // ".*". stdOut contains the SHELL_PROMPT too. + assertTrue(stdOut.matches("(?s)^.*\nunknown command '--version'\n.*$")); + assertEquals(stdErr, ""); + + shell.send("exit\n"); + + shell.expectClose(); + } + + @Test + public void testShellHelp() throws Exception { + Spawn shell = spawnThermostat("help", "shell"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + + String[] lines = stdOut.split("\n"); + String usage = lines[0]; + assertTrue(usage.matches("^usage: thermostat shell$")); + String description = lines[1]; + assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$")); + assertTrue(lines[3].matches("thermostat shell")); + } + + @Test + public void testShellHelpArgument() throws Exception { + Spawn shell = spawnThermostat("shell", "--help"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + + String[] lines = stdOut.split("\n"); + String usage = lines[0]; + assertTrue(usage.matches("^usage: thermostat shell$")); + String description = lines[1]; + assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$")); + assertTrue(lines[3].matches("thermostat shell")); + } + + @Test + public void testShellUnrecognizedArgument() throws Exception { + Spawn shell = spawnThermostat("shell", "--foo"); + shell.expectClose(); + String stdOut = shell.getCurrentStandardOutContents(); + String expectedOut = "Could not parse options: Unrecognized option: --foo\n" + + "usage: thermostat shell\n" + + " launches the Thermostat interactive shell\n" + + "\n" + + "thermostat shell\n\n"; + assertEquals(expectedOut, stdOut); + } + + @Test + public void testUnrecognizedEventsInShell() throws Exception { + // test '!' events + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("what!?!\n"); + shell.expect(SHELL_PROMPT); + shell.send("exit\n"); + shell.expectClose(); + + assertTrue(shell.getCurrentStandardErrContents().contains("!?!: event not found")); + assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + } + + @Test + public void testInvalidCommand() throws Exception { + Spawn shell = spawnThermostat("foobar", "baz"); + + // TODO should this be stderr? + shell.expect("unknown command 'foobar'"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + + assertMatchesHelpCommandList(stdOut); + } + + private static void assertMatchesHelpCommandList(String actual) { + assertTrue(actual.contains("list of commands")); + assertTrue(actual.contains("help")); + assertTrue(actual.contains("agent")); + assertTrue(actual.contains("gui")); + assertTrue(actual.contains("ping")); + assertTrue(actual.contains("shell")); + } + + private static void assertMatchesShellHelpCommandList(String actual) { + assertTrue(actual.contains("list of commands")); + assertTrue(actual.contains("help")); + assertTrue(actual.contains("connect")); + assertTrue(actual.contains("disconnect")); + assertTrue(actual.contains("ping")); + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,391 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.redhat.thermostat.common.utils.StreamUtils; + +import expectj.Executor; +import expectj.ExpectJ; +import expectj.Spawn; +import expectj.TimeoutException; + +/** + * Helper methods to support writing an integration test. + * <p> + * This class should be used by all integration tests to start + * thermostat and to obtain paths to various locations. Starting + * thermostat manually will cause issues with wrong paths being + * used. + */ +public class IntegrationTest { + + public static final String ITEST_USER_HOME_PROP = "com.redhat.thermostat.itest.thermostatUserHome"; + public static final String ITEST_THERMOSTAT_HOME_PROP = "com.redhat.thermostat.itest.thermostatHome"; + + public static class SpawnResult { + final Process process; + final Spawn spawn; + + public SpawnResult(Process process, Spawn spawn) { + this.process = process; + this.spawn = spawn; + } + } + + // FIXME Make sure all methods are using a sane environment that's set up correctly + + public static final long TIMEOUT_IN_SECONDS = 30; + + public static final String SHELL_PROMPT = "Thermostat >"; + + private static final String THERMOSTAT_HOME = "THERMOSTAT_HOME"; + private static final String USER_THERMOSTAT_HOME = "USER_THERMOSTAT_HOME"; + + /* This is a mirror of paths from c.r.t.shared.Configuration */ + + private static String getThermostatExecutable() { + return getThermostatHome() + "/bin/thermostat"; + } + + public static String getThermostatHome() { + String propHome = System.getProperty(ITEST_THERMOSTAT_HOME_PROP); + if (propHome == null) { + return "../../distribution/target/image"; + } else { + return propHome; + } + } + + public static String getSystemPluginHome() { + return getThermostatHome() + "/plugins"; + } + + public static String getConfigurationDir() { + return getThermostatHome() + "/etc"; + } + + public static String getUserThermostatHome() { + String userHomeProp = System.getProperty(ITEST_USER_HOME_PROP); + if (userHomeProp == null) { + return "../../distribution/target/user-home"; + } else { + return userHomeProp; + } + } + + public static String getStorageDataDirectory() { + return getUserThermostatHome() + "/data/db"; + } + + public static void clearStorageDataDirectory() throws IOException { + File storageDir = new File(getStorageDataDirectory()); + if (storageDir.exists()) { + if (storageDir.isDirectory()) { + deleteFilesRecursivelyUnder(storageDir); + } else { + throw new IllegalStateException(storageDir + " exists but is not a directory"); + } + } + } + + public static Process runThermostat(String... args) throws IOException { + List<String> completeArgs = new ArrayList<String>(args.length+1); + completeArgs.add(getThermostatExecutable()); + completeArgs.addAll(Arrays.asList(args)); + ProcessBuilder builder = buildThermostatProcess(completeArgs); + + return builder.start(); + } + + public static Spawn spawnThermostat(String... args) throws IOException { + return spawnThermostat(false, args); + } + + public static Spawn startStorage() throws Exception { + clearStorageDataDirectory(); + + Spawn storage = spawnThermostat("storage", "--start"); + try { + storage.expect("pid:"); + } catch (IOException e) { + // this may happen if storage is already running. + e.printStackTrace(); + String stdOutContents = storage.getCurrentStandardOutContents(); + + System.err.flush(); + System.out.flush(); + System.err.println("stdout was: -->" + stdOutContents +"<--"); + System.err.println("stderr was: -->" + storage.getCurrentStandardErrContents() + "<--"); + System.err.flush(); + assertFalse(stdOutContents.contains("Storage is already running with pid")); + throw new Exception("Something funny is going on when trying to start storage!", e); + } + storage.expectClose(); + + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + return storage; + } + + public static Spawn stopStorage() throws Exception { + Spawn storage = spawnThermostat("storage", "--stop"); + storage.expect("server shutdown complete"); + storage.expectClose(); + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + return storage; + } + + public static Spawn spawnThermostat(boolean localeDependent, String... args) throws IOException { + ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); + StringBuilder result = new StringBuilder(getThermostatExecutable()); + if (args != null) { + for (String arg : args) { + result.append(" ").append(arg); + } + } + String toExecute = result.toString(); + Executor exec = null; + if (localeDependent) { + exec = new LocaleExecutor(toExecute); + } else { + exec = new SimpleExecutor(toExecute); + } + return expect.spawn(exec); + } + + public static SpawnResult spawnThermostatAndGetProcess(String... args) throws IOException { + final List<String> completeArgs = new ArrayList<String>(args.length+1); + completeArgs.add(getThermostatExecutable()); + completeArgs.addAll(Arrays.asList(args)); + + final Process[] process = new Process[1]; + + ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); + + Spawn spawn = expect.spawn(new Executor() { + @Override + public Process execute() throws IOException { + ProcessBuilder builder = buildThermostatProcess(completeArgs); + Process service = builder.start(); + process[0] = service; + return service; + } + }); + + return new SpawnResult(process[0], spawn); + } + + private static ProcessBuilder buildThermostatProcess(List<String> args) { + ProcessBuilder builder = new ProcessBuilder(args); + builder.environment().put(THERMOSTAT_HOME, getThermostatHome()); + builder.environment().put(USER_THERMOSTAT_HOME, getUserThermostatHome()); + + return builder; + } + + /** + * Generic method to run a program. + * <p> + * DO NOT USE THIS TO RUN THERMOSTAT ITSELF. It does not set up the + * environment correctly, using incorrect data and possibly overwriting + * important data. + */ + public static Spawn spawn(List<String> args) throws IOException { + ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); + StringBuilder result = new StringBuilder(); + for (String arg : args) { + result.append(arg).append(" "); + } + return expect.spawn(result.substring(0, result.length() - 1)); + } + + /** + * Kill the process and all its children, recursively. Sends SIGTERM. + */ + public static void killRecursively(Process process) throws Exception { + killRecursively(getPid(process)); + } + + private static void killRecursively(int pid) throws Exception { + List<Integer> childPids = findChildPids(pid); + for (Integer childPid : childPids) { + killRecursively(childPid); + } + killProcess(pid); + } + + private static void killProcess(int processId) throws Exception { + System.err.println("Killing process with pid: " + processId); + Runtime.getRuntime().exec("kill " + processId).waitFor(); + } + + private static List<Integer> findChildPids(int processId) throws IOException { + String children = new String(StreamUtils.readAll(Runtime.getRuntime().exec("ps --ppid " + processId + " -o pid=").getInputStream())); + String[] childPids = children.split("\n"); + List<Integer> result = new ArrayList<>(); + for (String childPid : childPids) { + String pidString = childPid.trim(); + if (pidString.length() == 0) { + continue; + } + try { + result.add(Integer.parseInt(pidString)); + } catch (NumberFormatException nfe) { + System.err.println(nfe); + } + } + return result; + } + + private static int getPid(Process process) throws Exception { + final String UNIX_PROCESS_CLASS = "java.lang.UNIXProcess"; + if (!process.getClass().getName().equals(UNIX_PROCESS_CLASS)) { + throw new IllegalArgumentException("can only kill " + UNIX_PROCESS_CLASS + "; input is a " + process.getClass()); + } + + Class<?> processClass = process.getClass(); + Field pidField = processClass.getDeclaredField("pid"); + pidField.setAccessible(true); + return (int) pidField.get(process); + } + + private static void deleteFilesRecursivelyUnder(File path) throws IOException { + if (!path.isDirectory()) { + throw new IOException("Cannot delete files under a non-directory: " + path); + } + File[] filesToDelete = path.listFiles(); + if (filesToDelete == null) { + throw new IOException("Error getting directory listing: " + path); + } + for (File theFile : filesToDelete) { + if (theFile.isDirectory()) { + deleteFilesRecursivelyUnder(theFile); + } + Files.deleteIfExists(theFile.toPath()); + } + } + + /** Confirm that there are no 'command not found'-like messages in the spawn's stdout/stderr */ + public static void assertCommandIsFound(Spawn spawn) { + assertCommandIsFound(spawn.getCurrentStandardOutContents(), spawn.getCurrentStandardErrContents()); + } + + public static void assertCommandIsFound(String stdOutContents, String stdErrContents) { + assertFalse(stdOutContents.contains("unknown command")); + assertFalse(stdErrContents.contains("unknown command")); + } + + /** Confirm that there are no exception stack traces in the spawn's stdout/stderr */ + public static void assertNoExceptions(Spawn spawn) { + assertNoExceptions(spawn.getCurrentStandardOutContents(), spawn.getCurrentStandardErrContents()); + } + + public static void assertNoExceptions(String stdOutContents, String stdErrContents) { + assertFalse(stdOutContents.contains("Exception")); + assertFalse(stdErrContents.contains("Exception")); + } + + public static void assertOutputEndsWith(String stdOutContents, String expectedOutput) { + String endOfOut = stdOutContents.substring(stdOutContents.length() - expectedOutput.length()); + assertEquals(expectedOutput, endOfOut); + } + + public static void handleAuthPrompt(Spawn spawn, String url, String user, String password) throws IOException, TimeoutException { + spawn.expect("Please enter username for storage at " + url + ":"); + spawn.send(user + "\r"); + spawn.expect("Please enter password for storage at " + url + ":"); + spawn.send(password + "\r"); + } + + private static class LocaleExecutor extends EnvironmentExecutor { + + public static final String[] ENV_WITH_LANG_C = { + THERMOSTAT_HOME + "=" + getThermostatHome(), + USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(), + "LANG=C" + }; + + public LocaleExecutor(String process) { + super(process, ENV_WITH_LANG_C); + } + + } + + private static class SimpleExecutor extends EnvironmentExecutor { + + public static final String[] ENV_WITH = { + THERMOSTAT_HOME + "=" + getThermostatHome(), + USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(), + }; + + public SimpleExecutor(String process) { + super(process, ENV_WITH); + } + } + + private static class EnvironmentExecutor implements Executor { + + private final String[] env; + private final String process; + + public EnvironmentExecutor(String process, String[] env) { + this.process = process; + this.env = env; + } + + @Override + public Process execute() throws IOException { + return Runtime.getRuntime().exec(process, env); + } + + @Override + public String toString() { + return process; + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/MongoQueriesTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,543 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +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.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.redhat.thermostat.host.cpu.common.CpuStatDAO; +import com.redhat.thermostat.host.cpu.common.model.CpuStat; +import com.redhat.thermostat.storage.config.StartupConfiguration; +import com.redhat.thermostat.storage.core.Add; +import com.redhat.thermostat.storage.core.BackingStorage; +import com.redhat.thermostat.storage.core.Connection.ConnectionListener; +import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Query.SortDirection; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.mongodb.internal.MongoStorage; +import com.redhat.thermostat.storage.query.Expression; +import com.redhat.thermostat.storage.query.ExpressionFactory; +import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; +import com.redhat.thermostat.vm.classstat.common.model.VmClassStat; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; +import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; + +/* + * This test class starts up a mongod instance and tests if thermostat + * queries with expressions return what they supposed to return. + * + * Tests should make their own connection to storage, probably by making use of + * one of the getAndConnectStorage() method variants. + * + * Because the storage instance is shared among all of the tests, it is + * necessary to take precautions to avoid introducing data dependencies + * between tests. Such precautions could include: using a different + * category (ie mongod collection) than any other existing test; setting + * a unique agent-id for all data written and then deleting the data + * at the end of the test; <insert other clever idea here>. + * + */ +public class MongoQueriesTest extends IntegrationTest { + + private static class CountdownConnectionListener implements ConnectionListener { + + private final ConnectionStatus target; + private final CountDownLatch latch; + + private CountdownConnectionListener(ConnectionStatus target, CountDownLatch latch) { + this.target = target; + this.latch = latch; + } + + @Override + public void changed(ConnectionStatus newStatus) { + assertEquals(target, newStatus); + latch.countDown(); + } + } + + private static final double EQUALS_DELTA = 0.00000000000001; + private static final String VM_ID1 = "vmId1"; + private static final String VM_ID2 = "vmId2"; + private static final String VM_ID3 = "vmId3"; + + private ExpressionFactory factory = new ExpressionFactory(); + + @BeforeClass + public static void setUpOnce() throws Exception { + startStorage(); + + addCpuData(4); + } + + @AfterClass + public static void tearDownOnce() throws Exception { + deleteCpuData(); + + stopStorage(); + } + + /* + * Make a connection to mongo storage (returning the Storage object). Before + * initiating the connection, add the ConnectionListener to Storage. + */ + private static BackingStorage getAndConnectStorage(ConnectionListener listener) { + final String url = "mongodb://127.0.0.1:27518"; + StartupConfiguration config = new StartupConfiguration() { + + @Override + public String getDBConnectionString() { + return url; + } + + }; + BackingStorage storage = new MongoStorage(config); + if (listener != null) { + storage.getConnection().addListener(listener); + } + storage.getConnection().connect(); + return storage; + } + + private static void addCpuData(int numberOfItems) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage storage = getAndConnectStorage(listener); + latch.await(); + storage.getConnection().removeListener(listener); + + storage.registerCategory(CpuStatDAO.cpuStatCategory); + + for (int i = 0; i < numberOfItems; i++) { + CpuStat pojo = new CpuStat("test-agent-id", i, new double[] {i, i*2}); + Add<CpuStat> add = storage.createAdd(CpuStatDAO.cpuStatCategory); + add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); + add.set(CpuStatDAO.cpuLoadKey.getName(), pojo.getPerProcessorUsage()); + add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); + add.apply(); + } + + storage.getConnection().disconnect(); + } + + private static void deleteCpuData() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + Storage storage = getAndConnectStorage(listener); + latch.await(); + storage.getConnection().removeListener(listener); + storage.registerCategory(CpuStatDAO.cpuStatCategory); + + storage.purge("test-agent-id"); + + storage.getConnection().disconnect(); + } + + private void executeAndVerifyQuery(Query<CpuStat> query, List<Long> expectedTimestamps) { + Cursor<CpuStat> cursor = query.execute(); + + for (Long time : expectedTimestamps) { + assertTrue(cursor.hasNext()); + CpuStat pojo = cursor.next(); + assertEquals("test-agent-id", pojo.getAgentId()); + assertEquals(time.longValue(), pojo.getTimeStamp()); + double[] data = pojo.getPerProcessorUsage(); + assertEquals(time, data[0], EQUALS_DELTA); + assertEquals(time*2, data[1], EQUALS_DELTA); + } + assertFalse(cursor.hasNext()); + } + + @Test + public void testMongoAdd() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); + + Add<VmClassStat> add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); + VmClassStat pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(12345); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID1); + addVmClassStat(add, pojo); + + // Add another couple of entries + add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); + pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(67890); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID2); + addVmClassStat(add, pojo); + + add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); + pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(34567); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID3); + addVmClassStat(add, pojo); + + mongoStorage.getConnection().disconnect(); + } + + private void addVmClassStat(Add<VmClassStat> add, VmClassStat pojo) { + add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); + add.set(Key.VM_ID.getName(), pojo.getVmId()); + add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); + add.set(VmClassStatDAO.loadedClassesKey.getName(), pojo.getLoadedClasses()); + add.apply(); + } + + @Test + public void canQueryNoWhere() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l, 3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryEqualTo() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.equalTo(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(2l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryNotEqualTo() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.notEqualTo(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryGreaterThan() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.greaterThan(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryGreaterThanOrEqualTo() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.greaterThanOrEqualTo(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(2l, 3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryLessThan() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.lessThan(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryLessThanOrEqualTo() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.lessThanOrEqualTo(Key.TIMESTAMP, 2l); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryIn() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + List<Long> times = Arrays.asList(0l, 2l); + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.in(Key.TIMESTAMP, new HashSet<>(times), Long.class); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, times); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryNotIn() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.notIn(Key.TIMESTAMP, new HashSet<>(Arrays.asList(0l, 2l)), Long.class); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(1l, 3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryNot() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.not(factory.greaterThan(Key.TIMESTAMP, 2l)); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryAnd() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.and(factory.greaterThan(Key.TIMESTAMP, 0l), + factory.lessThan(Key.TIMESTAMP, 2l)); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(1l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canQueryOr() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); + Expression expr = factory.or(factory.greaterThan(Key.TIMESTAMP, 2l), + factory.lessThan(Key.TIMESTAMP, 1l)); + query.where(expr); + query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); + + executeAndVerifyQuery(query, Arrays.asList(0l, 3l)); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void canLoadSave() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + Storage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + + byte[] data = "Hello World".getBytes(); + mongoStorage.saveFile("test", new ByteArrayInputStream(data)); + // Note: On the server side, the file is saved into mongodb + // via GridFS. The save operation returns before write is + // complete, and there is no callback mechanism to find out + // when the write is complete. So, we try a few times to + // load it before considering it a failure. + InputStream loadStream = null; + int loadAttempts = 0; + while (loadStream == null && loadAttempts < 3) { + Thread.sleep(300); + loadStream = mongoStorage.loadFile("test"); + loadAttempts++; + } + assertNotNull(loadStream); + StringBuilder str = new StringBuilder(); + int i = loadStream.read(); + while (i != -1) { + str.append((char) i); + i = loadStream.read(); + } + assertEquals("Hello World", str.toString()); + + mongoStorage.getConnection().disconnect(); + } + + @Test + public void storagePurge() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); + BackingStorage mongoStorage = getAndConnectStorage(listener); + latch.await(); + mongoStorage.getConnection().removeListener(listener); + UUID uuid = new UUID(42, 24); + + mongoStorage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); + long timeStamp = 5; + double cpuLoad = 0.15; + VmCpuStat pojo = new VmCpuStat(uuid.toString(), timeStamp, VM_ID1, cpuLoad); + Add<VmCpuStat> add = mongoStorage.createAdd(VmCpuStatDAO.vmCpuStatCategory); + add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); + add.set(Key.VM_ID.getName(), pojo.getVmId()); + add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); + add.set(VmCpuStatDAO.vmCpuLoadKey.getName(), pojo.getCpuLoad()); + add.apply(); + + Query<VmCpuStat> query = mongoStorage.createQuery(VmCpuStatDAO.vmCpuStatCategory); + Cursor<VmCpuStat> cursor = query.execute(); + assertTrue(cursor.hasNext()); + pojo = cursor.next(); + assertFalse(cursor.hasNext()); + + assertEquals(timeStamp, pojo.getTimeStamp()); + assertEquals(VM_ID1, pojo.getVmId()); + assertEquals(cpuLoad, pojo.getCpuLoad(), EQUALS_DELTA); + assertEquals(uuid.toString(), pojo.getAgentId()); + + mongoStorage.purge(uuid.toString()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/PluginTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,232 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import expectj.Spawn; + +public class PluginTest extends IntegrationTest { + + private static final String PLUGIN_HOME = getSystemPluginHome(); + + private static NewCommandPlugin fooPlugin = new NewCommandPlugin("foo", "provides foo command", PLUGIN_HOME + File.separator + "new"); + private static NewCommandPlugin userPlugin = new NewCommandPlugin( + "user", + "a plugin that is provided by the user", + getUserThermostatHome() + File.separator + "data" + File.separator + "plugins" + File.separator + "user"); + private static UnknownExtendsPlugin unknownExtension = new UnknownExtendsPlugin(PLUGIN_HOME + File.separator + "unknown"); + + @BeforeClass + public static void setUpOnce() { + fooPlugin.install(); + userPlugin.install(); + unknownExtension.install(); + } + + @AfterClass + public static void tearDownOnce() { + unknownExtension.uninstall(); + userPlugin.uninstall(); + fooPlugin.uninstall(); + } + + @Test + public void testHelpIsOkay() throws Exception { + Spawn shell = spawnThermostat("help"); + shell.expectClose(); + + String stdOut = shell.getCurrentStandardOutContents(); + + assertTrue(stdOut.contains("list of commands")); + assertTrue(stdOut.contains("help")); + assertTrue(stdOut.contains("agent")); + assertTrue(stdOut.contains("gui")); + assertTrue(stdOut.contains("ping")); + assertTrue(stdOut.contains("shell")); + + assertTrue(stdOut.contains(fooPlugin.command)); + assertTrue(stdOut.contains(fooPlugin.description)); + + assertTrue(stdOut.contains(userPlugin.command)); + + assertFalse(stdOut.contains(unknownExtension.command)); + + // TODO assertEquals("", stdErr); + } + + /** + * This plugin provides a new command + */ + private static class NewCommandPlugin { + + private final String pluginHome; + private final String command; + private final String description; + + public NewCommandPlugin(String command, String description, String pluginLocation) { + this.pluginHome = pluginLocation; + + this.command = command; + this.description = description; + } + + private void install() { + File home = new File(pluginHome); + if (!home.isDirectory() && !home.mkdirs()) { + throw new AssertionError("could not create directory: " + pluginHome); + } + + String pluginContents = "" + + "<?xml version=\"1.0\"?>\n" + + "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" + + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + + " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermost-plugin.xsd\">\n" + + " <commands>" + + " <command>" + + " <name>" + command + "</name>" + + " <description>" + description + "</description>" + + " <options>" + + " <option>" + + " <long>aaaaa</long>" + + " <short>a</short>" + + " </option>" + + " </options>" + + " <environments>" + + " <environment>shell</environment>" + + " <environment>cli</environment>" + + " </environments>" + + " <bundles>" + + " <bundle>" + + " <symbolic-name>bar</symbolic-name>" + + " <version>0.1.0</version>" + + " </bundle>" + + " </bundles>" + + " </command>" + + " </commands>" + + "</plugin>"; + + try (FileWriter writer = new FileWriter(pluginHome + File.separator + "thermostat-plugin.xml")) { + writer.write(pluginContents); + } catch (IOException e) { + throw new AssertionError("unable to write plugin configuration", e); + } + + } + + private void uninstall() { + if (!new File(pluginHome).exists()) { + return; + } + if (!new File(pluginHome, "thermostat-plugin.xml").delete()) { + throw new AssertionError("Could not delete plugin file"); + } + if (!new File(pluginHome).delete()) { + throw new AssertionError("Could not delete plugin directory"); + } + } + } + + /** + * This plugin extends an unknown command + */ + private static class UnknownExtendsPlugin { + + private final String pluginHome; + private final String command; + + public UnknownExtendsPlugin(String pluginLocation) { + this.pluginHome = pluginLocation; + + this.command = "unknown-command"; + } + + private void install() { + File home = new File(pluginHome); + if (!home.isDirectory() && !home.mkdir()) { + throw new AssertionError("could not create directory: " + pluginHome); + } + + String pluginContents = "" + + "<?xml version=\"1.0\"?>\n" + + "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" + + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + + " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermost-plugin.xsd\">\n" + + " <extensions>" + + " <extension>" + + " <name>" + command + "</name>" + + " <bundles>" + + " <bundle>" + + " <symbolic-name>bar</symbolic-name>" + + " <version>0.1.0</version>" + + " </bundle>" + + " </bundles>" + + " </extension>" + + " </extensions>" + + "</plugin>"; + + try (FileWriter writer = new FileWriter(pluginHome + File.separator + "thermostat-plugin.xml")) { + writer.write(pluginContents); + } catch (IOException e) { + throw new AssertionError("unable to write plugin configuration", e); + } + + } + + private void uninstall() { + if (!new File(pluginHome).exists()) { + return; + } + if (!new File(pluginHome, "thermostat-plugin.xml").delete()) { + throw new AssertionError("Could not delete plugin file"); + } + if (!new File(pluginHome).delete()) { + throw new AssertionError("Could not delete plugin directory"); + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import java.io.IOException; + +import org.junit.Ignore; +import org.junit.Test; + +import expectj.ExpectJException; +import expectj.Spawn; +import expectj.TimeoutException; + +public class StorageConnectionTest extends IntegrationTest { + + // @BeforeClass // reinstate once we actually need storage running (see ignored tests) + public static void setUpOnce() throws Exception { + startStorage(); + } + + // @AfterClass // reinstate once we actually need storage running + public static void tearDownOnce() throws Exception { + stopStorage(); + } + + @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore + @Test + public void testConnect() throws ExpectJException, TimeoutException, IOException { + Spawn shell = spawnThermostat(true, "shell"); + + shell.expect(SHELL_PROMPT); + shell.send("connect -d mongodb://127.0.0.1:27518\n"); + handleAuthPrompt(shell, "mongodb://127.0.0.1:27518", "", ""); + shell.expect(SHELL_PROMPT); + shell.send("exit\n"); + shell.expectClose(); + + assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + } + + @Test + public void testDisconnectWithoutConnecting() throws ExpectJException, TimeoutException, IOException { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("disconnect\n"); + shell.expect(SHELL_PROMPT); + shell.send("exit\n"); + shell.expectClose(); + + assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + } + + @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore + @Test + public void testConnectAndDisconnectInShell() throws IOException, TimeoutException, ExpectJException { + Spawn shell = spawnThermostat(true, "shell"); + + shell.expect(SHELL_PROMPT); + shell.send("connect -d mongodb://127.0.0.1:27518\n"); + handleAuthPrompt(shell, "mongodb://127.0.0.1:27518", "", ""); + shell.expect(SHELL_PROMPT); + shell.send("disconnect\n"); + shell.send("exit\n"); + shell.expectClose(); + + assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); + } + + // TODO add a test to make sure connect/disconnect is not visible outside the shell +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import org.junit.Test; + +import expectj.Spawn; + +public class StorageTest extends IntegrationTest { + + @Test + public void startAndStopStorage() throws Exception { + Spawn storage; + + storage = startStorage(); + + storage = spawnThermostat("storage", "--status"); + storage.expect("Storage is running"); + storage.expectClose(); + + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + + storage = stopStorage(); + + storage = spawnThermostat("storage", "--status"); + storage.expect("Storage is not running"); + storage.expectClose(); + + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + } + + @Test + public void testServiceStartAndKilling() throws Exception { + + SpawnResult spawnResult = spawnThermostatAndGetProcess("service"); + Spawn service = spawnResult.spawn; + + try { + service.expectErr("agent started"); + } finally { + killRecursively(spawnResult.process); + } + + service.stop(); + service.expectClose(); + + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,160 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import expectj.Spawn; + +/** Integration tests for the various vm commands */ +public class VmCommandsTest extends IntegrationTest { + + @BeforeClass + public static void setUpOnce() throws Exception { + startStorage(); + + // TODO insert actual data into the database and test that + } + + @AfterClass + public static void tearDownOnce() throws Exception { + stopStorage(); + } + + @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore + @Test + public void testListVms() throws Exception { + Spawn vmList = commandAgainstMongo("list-vms"); + handleAuthPrompt(vmList, "mongodb://127.0.0.1:27518", "", ""); + vmList.expectClose(); + assertOutputEndsWith(vmList.getCurrentStandardOutContents(), "HOST_ID HOST VM_ID STATUS VM_NAME\n\n"); + } + + @Test + public void testVmStat() throws Exception { + Spawn vmStat = commandAgainstMongo("vm-stat"); + // TODO include required options to test meaningfully + //handleAuthPrompt(vmStat, "mongodb://127.0.0.1:27518", "", ""); + vmStat.expectClose(); + + System.out.println(vmStat.getCurrentStandardOutContents()); + assertCommandIsFound(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents()); + assertNoExceptions(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents()); + } + + @Test + public void testVmInfo() throws Exception { + Spawn vmInfo = commandAgainstMongo("vm-info"); + // TODO include required options to test meaningfully + // handleAuthPrompt(vmInfo, "mongodb://127.0.0.1:27518", "", ""); + vmInfo.expectClose(); + + assertNoExceptions(vmInfo.getCurrentStandardOutContents(), vmInfo.getCurrentStandardErrContents()); + } + + @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore + @Test + public void testHeapCommands() throws Exception { + String[] commands = new String[] { + "dump-heap", + "list-heap-dumps", + "save-heap-dump-to-file", + "show-heap-histogram", + "find-objects", + "object-info", + "find-root" + }; + + for (String command : commands) { + Spawn heapCommand = commandAgainstMongo(command); + // TODO include required options to test each command meaningfully + if (command.equals("list-heap-dumps")) { + // No missing options, times out waiting for user/pass input without the following: + handleAuthPrompt(heapCommand, "mongodb://127.0.0.1:27518", "", ""); + } + heapCommand.expectClose(); + + assertCommandIsFound( + heapCommand.getCurrentStandardOutContents(), + heapCommand.getCurrentStandardErrContents()); + assertNoExceptions( + heapCommand.getCurrentStandardOutContents(), + heapCommand.getCurrentStandardErrContents()); + } + } + + @Test + public void testNormalCommandAndPluginInShell() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("list-vms\n"); + + shell.expect(SHELL_PROMPT); + + shell.send("dump-heap\n"); + + shell.expect(SHELL_PROMPT); + + shell.send("exit\n"); + shell.expectClose(); + + assertCommandIsFound(shell); + assertNoExceptions(shell); + } + + private static Spawn commandAgainstMongo(String... args) throws IOException { + if (args == null || args.length == 0) { + throw new IllegalArgumentException("args must be an array with something"); + } + List<String> completeArgs = new ArrayList<>(); + completeArgs.addAll(Arrays.asList(args)); + completeArgs.add("-d"); + completeArgs.add("mongodb://127.0.0.1:27518"); + return spawnThermostat(true, completeArgs.toArray(new String[0])); + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,1092 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +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 static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.component.LifeCycle.Listener; +import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.redhat.thermostat.common.ApplicationInfo; +import com.redhat.thermostat.host.cpu.common.CpuStatDAO; +import com.redhat.thermostat.host.cpu.common.model.CpuStat; +import com.redhat.thermostat.storage.config.ConnectionConfiguration; +import com.redhat.thermostat.storage.config.StartupConfiguration; +import com.redhat.thermostat.storage.core.Add; +import com.redhat.thermostat.storage.core.BackingStorage; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.CategoryAdapter; +import com.redhat.thermostat.storage.core.Connection.ConnectionListener; +import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.DescriptorParsingException; +import com.redhat.thermostat.storage.core.IllegalDescriptorException; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.PreparedStatement; +import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.StatementExecutionException; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.core.auth.DescriptorMetadata; +import com.redhat.thermostat.storage.dao.HostInfoDAO; +import com.redhat.thermostat.storage.model.AggregateCount; +import com.redhat.thermostat.storage.model.HostInfo; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.mongodb.internal.MongoStorage; +import com.redhat.thermostat.storage.query.Expression; +import com.redhat.thermostat.storage.query.ExpressionFactory; +import com.redhat.thermostat.test.FreePortFinder; +import com.redhat.thermostat.test.FreePortFinder.TryPort; +import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; +import com.redhat.thermostat.vm.classstat.common.model.VmClassStat; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; +import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; +import com.redhat.thermostat.web.client.internal.WebStorage; +import com.redhat.thermostat.web.server.auth.Roles; + +/** + * This test class starts up a mongod instance and a web storage instance + * (in jetty container) in front of that. Tests should make their own + * connection to the web storage, probably by making use of one of the + * getAndConnectStorage() method variants. + * + * Because the storage instance is shared among all of the tests, it is + * necessary to take precautions to avoid introducing data dependencies + * between tests. Such precautions could include: using a different + * category (ie mongod collection) than any other existing test; setting + * a unique agent-id for all data written and then deleting the data + * at the end of the test; <insert other clever idea here>. + * + * Please don't introduce any more sporadic test failures to this + * integration test!!! + */ +public class WebAppTest extends IntegrationTest { + + /* + * Registry of descriptors this test needs to allow in order to avoid + * illegal statement descriptor exceptions being thrown. See also: + * WebAppTestStatementDescriptorRegistration + */ + public static final Set<String> TRUSTED_DESCRIPTORS; + /* + * Map which maps a string descriptor to DescriptorMetadata. + * See also: WebAppTestStatementDescriptorRegistration + * + */ + public static final Map<String, DescriptorMetadata> METADATA_MAPPING; + // descriptive name -> descriptor mapping + private static final Map<String, String> DESCRIPTOR_MAP; + + private static final String KEY_AUTHORIZED_QUERY = "authorizedQuery"; + private static final String KEY_AUTHORIZED_QUERY_EQUAL_TO = "authorizedQueryEqualTo"; + private static final String KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO = "authorizedQueryNotEqualTo"; + private static final String KEY_AUTHORIZED_QUERY_GREATER_THAN = "authorizedQueryGreaterThan"; + private static final String KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO = "authorizedQueryGreaterThanOrEqualTo"; + private static final String KEY_AUTHORIZED_QUERY_LESS_THAN = "authorizedQueryLessThan"; + private static final String KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO = "authorizedQueryLessThanOrEqualTo"; + private static final String KEY_AUTHORIZED_QUERY_NOT = "authorizedQueryNot"; + private static final String KEY_AUTHORIZED_QUERY_AND = "authorizedQueryAnd"; + private static final String KEY_AUTHORIZED_QUERY_OR = "authorizedQueryOr"; + private static final String KEY_STORAGE_PURGE = "storagePurge"; + private static final String KEY_AUTHORIZED_FILTERED_QUERY = "authorizedFilteredQuerySubset"; + + static { + Map<String, String> descMap = new HashMap<>(); + descMap.put(KEY_AUTHORIZED_FILTERED_QUERY, "QUERY agent-config"); + descMap.put(KEY_AUTHORIZED_QUERY, "QUERY cpu-stats SORT ?s ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' = ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' != ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_GREATER_THAN, "QUERY cpu-stats WHERE 'timeStamp' > ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' >= ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_LESS_THAN, "QUERY cpu-stats WHERE 'timeStamp' < ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' <= ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_NOT, "QUERY cpu-stats WHERE NOT 'timeStamp' > ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_AND, "QUERY cpu-stats WHERE 'timeStamp' > 0 AND 'timeStamp' < ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_AUTHORIZED_QUERY_OR, "QUERY cpu-stats WHERE 'timeStamp' > ?l OR 'timeStamp' < ?l SORT 'timeStamp' ASC"); + descMap.put(KEY_STORAGE_PURGE, "QUERY vm-cpu-stats"); + Set<String> trustedDescriptors = new HashSet<>(); + Map<String, DescriptorMetadata> metadata = new HashMap<>(); + DescriptorMetadata descMetadata = new DescriptorMetadata(); + for (String val: descMap.values()) { + trustedDescriptors.add(val); + metadata.put(val, descMetadata); + } + TRUSTED_DESCRIPTORS = trustedDescriptors; + DESCRIPTOR_MAP = descMap; + METADATA_MAPPING = metadata; + } + + + private static class CountdownConnectionListener implements ConnectionListener { + + private final ConnectionStatus target; + private final CountDownLatch latch; + private final AtomicBoolean indicator; + + private CountdownConnectionListener(ConnectionStatus target, CountDownLatch latch, AtomicBoolean indicator) { + this.target = target; + this.latch = latch; + this.indicator = indicator; + } + + @Override + public void changed(ConnectionStatus newStatus) { + indicator.set(true); + assertEquals(target, newStatus); + latch.countDown(); + } + } + + private static class WebAppContextListener implements Listener { + private Throwable cause; + private boolean failed = false; + private final CountDownLatch contextStartedLatch; + private WebAppContextListener(CountDownLatch latch) { + this.contextStartedLatch = latch; + } + + @Override + public void lifeCycleStarting(LifeCycle event) { + // nothing + } + + @Override + public void lifeCycleStarted(LifeCycle event) { + contextStartedLatch.countDown(); + } + + @Override + public void lifeCycleFailure(LifeCycle event, Throwable cause) { + this.failed = true; + this.cause = cause; + contextStartedLatch.countDown(); + } + + @Override + public void lifeCycleStopping(LifeCycle event) { + // nothing + } + + @Override + public void lifeCycleStopped(LifeCycle event) { + // nothing + } + } + + private static final String TEST_USER = "testuser"; + private static final String TEST_PASSWORD = "testpassword"; + private static final String PREP_USER = "prepuser"; + private static final String PREP_PASSWORD = "preppassword"; + private static final double EQUALS_DELTA = 0.00000000000001; + private static final String THERMOSTAT_USERS_FILE = getConfigurationDir() + "/thermostat-users.properties"; + private static final String THERMOSTAT_ROLES_FILE = getConfigurationDir() + "/thermostat-roles.properties"; + private static final String VM_ID1 = "vmId1"; + private static final String VM_ID2 = "vmId2"; + private static final String VM_ID3 = "vmId3"; + + private static Server server; + private static int port; + private static Path backupUsers; + private static Path backupRoles; + + @BeforeClass + public static void setUpOnce() throws Exception { + startStorage(); + + backupUsers = Files.createTempFile("itest-backup-thermostat-users", ""); + backupRoles = Files.createTempFile("itest-backup-thermostat-roles", ""); + backupRoles.toFile().deleteOnExit(); + backupUsers.toFile().deleteOnExit(); + Files.copy(new File(THERMOSTAT_USERS_FILE).toPath(), backupUsers, StandardCopyOption.REPLACE_EXISTING); + Files.copy(new File(THERMOSTAT_ROLES_FILE).toPath(), backupRoles, StandardCopyOption.REPLACE_EXISTING); + + + // start the server, deploy the war + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port); + } + }); + + addCpuData(4); + } + + @AfterClass + public static void tearDownOnce() throws Exception { + deleteCpuData(); + + server.stop(); + server.join(); + + stopStorage(); + + Files.copy(backupUsers, new File(THERMOSTAT_USERS_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(backupRoles, new File(THERMOSTAT_ROLES_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + /* + * Queries tests use write operations to put things into storage. For them + * we don't want to go through the hassles of using prepared writes. Instead + * use mongo-storage directly (which is a BackingStorage). + */ + private static BackingStorage getAndConnectBackingStorage() { + String url = "mongodb://127.0.0.1:27518"; + StartupConfiguration config = new ConnectionConfiguration(url, "", ""); + BackingStorage storage = new MongoStorage(config); + storage.getConnection().connect(); + return storage; + } + + /* + * Using the given username and password, set up a user for JAAS in the web app, + * with the given roles, and make a storage connection to the web app (returning + * the Storage object). + */ + private static Storage getAndConnectStorage(String username, String password, + String[] roleNames) throws IOException { + return getAndConnectStorage(username, password, roleNames, null); + } + + /* + * Using the given username and password, set up a user for JAAS in the web app, + * with the given roles, and make a connection to the web app (returning the + * Storage object). Before initiating the connection, add the ConnectionListener + * to Storage. + */ + private static Storage getAndConnectStorage(String username, String password, + String[] roleNames, + ConnectionListener listener) throws IOException { + setupJAASForUser(roleNames, username, password); + String url = "http://localhost:" + port + "/thermostat/storage"; + StartupConfiguration config = new ConnectionConfiguration(url, username, password); + Storage storage = new WebStorage(config); + if (listener != null) { + storage.getConnection().addListener(listener); + } + storage.getConnection().connect(); + return storage; + } + + private static void setupJAASForUser(String[] roleNames, String user, + String password) throws IOException { + Properties userProps = new Properties(); + userProps.put(user, password); + Properties roleProps = new Properties(); + StringBuffer roles = new StringBuffer(); + for (int i = 0; i < roleNames.length - 1; i++) { + roles.append(roleNames[i] + ", "); + } + roles.append(roleNames[roleNames.length - 1]); + roleProps.put(user, roles.toString()); + writeThermostatUsersRolesFile(userProps, roleProps); + } + + private static void writeThermostatUsersRolesFile(Properties usersContent, Properties rolesContent) throws IOException { + File thermostatUsers = new File(THERMOSTAT_USERS_FILE); + File thermostatRoles = new File(THERMOSTAT_ROLES_FILE); + try (FileOutputStream usersStream = new FileOutputStream(thermostatUsers)) { + usersContent.store(usersStream, "integration-test users"); + } + try (FileOutputStream rolesStream = new FileOutputStream(thermostatRoles)) { + rolesContent.store(rolesStream, "integration-test roles"); + } + } + + private static void startServer(int port) throws Exception { + final CountDownLatch contextStartedLatch = new CountDownLatch(1); + server = new Server(port); + ApplicationInfo appInfo = new ApplicationInfo(); + String version = appInfo.getMavenVersion(); + String warfile = "target/libs/thermostat-web-war-" + version + ".war"; + WebAppContext ctx = new WebAppContext(warfile, "/thermostat"); + + // We need to set this to true in order for WebStorageEndPoint to pick + // up the descriptor registrations from WebAppTestStatementDescriptorRegistration + // which would result in + // "java.util.ServiceConfigurationError: com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration: Provider com.redhat.thermostat.itest.WebAppTestStatementDescriptorRegistration not a subtype" + // errors. + ctx.setParentLoaderPriority(true); + + WebAppContextListener listener = new WebAppContextListener(contextStartedLatch); + ctx.addLifeCycleListener(listener); + /* The web archive has a jetty-web.xml config file which sets up the + * JAAS config. If done in code, this would look like this: + * + * JAASLoginService loginS = new JAASLoginService(); + * loginS.setLoginModuleName("ThermostatJAASLogin"); + * loginS.setName("Thermostat Realm"); + * loginS.setRoleClassNames(new String[] { + * WrappedRolePrincipal.class.getName(), + * RolePrincipal.class.getName(), + * UserPrincipal.class.getName() + * }); + * ctx.getSecurityHandler().setLoginService(loginS); + * + */ + server.setHandler(ctx); + server.start(); + // wait for context to start + contextStartedLatch.await(); + if (listener.failed) { + throw new IllegalStateException(listener.cause); + } + } + + private static void addCpuData(int numberOfItems) throws IOException { + BackingStorage storage = getAndConnectBackingStorage(); + storage.registerCategory(CpuStatDAO.cpuStatCategory); + + for (int i = 0; i < numberOfItems; i++) { + CpuStat pojo = new CpuStat("test-agent-id", i, new double[] {i, i*2}); + Add<CpuStat> add = storage.createAdd(CpuStatDAO.cpuStatCategory); + add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); + add.set(CpuStatDAO.cpuLoadKey.getName(), pojo.getPerProcessorUsage()); + add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); + add.apply(); + } + + storage.getConnection().disconnect(); + } + + private static void addHostInfoData(int numberOfItems) throws IOException { + BackingStorage storage = getAndConnectBackingStorage(); + storage.registerCategory(HostInfoDAO.hostInfoCategory); + + for (int i = 0; i < numberOfItems; i++) { + HostInfo hostInfo = new HostInfo("test-host-agent-id", "foo " + i, "linux " + i, "kernel", "t8", i, i * 1000); + Add<HostInfo> add = storage.createAdd(HostInfoDAO.hostInfoCategory); + add.set(Key.AGENT_ID.getName(), hostInfo.getAgentId()); + add.set(HostInfoDAO.hostNameKey.getName(), hostInfo.getHostname()); + add.set(HostInfoDAO.cpuCountKey.getName(), hostInfo.getCpuCount()); + add.set(HostInfoDAO.cpuModelKey.getName(), hostInfo.getCpuModel()); + add.set(HostInfoDAO.hostMemoryTotalKey.getName(), hostInfo.getTotalMemory()); + add.set(HostInfoDAO.osKernelKey.getName(), hostInfo.getOsKernel()); + add.set(HostInfoDAO.osNameKey.getName(), hostInfo.getOsName()); + add.apply(); + } + + storage.getConnection().disconnect(); + } + + private static void addAgentConfigData(List<AgentInformation> items) throws IOException { + BackingStorage storage = getAndConnectBackingStorage(); + storage.registerCategory(AgentInfoDAO.CATEGORY); + + for (AgentInformation info: items) { + Add<AgentInformation> add = storage.createAdd(AgentInfoDAO.CATEGORY); + add.set(Key.AGENT_ID.getName(), info.getAgentId()); + add.set(AgentInfoDAO.ALIVE_KEY.getName(), info.isAlive()); + add.set(AgentInfoDAO.CONFIG_LISTEN_ADDRESS.getName(), info.getConfigListenAddress()); + add.set(AgentInfoDAO.START_TIME_KEY.getName(), info.getStartTime()); + add.set(AgentInfoDAO.STOP_TIME_KEY.getName(), info.getStopTime()); + add.apply(); + } + + storage.getConnection().disconnect(); + } + + private static void deleteCpuData() throws IOException { + doDeleteData(CpuStatDAO.cpuStatCategory, "test-agent-id"); + } + + private static void deleteHostInfoData() throws IOException { + doDeleteData(HostInfoDAO.hostInfoCategory, "test-host-agent-id"); + } + + private static void doDeleteData(Category<?> category, String agentId) throws IOException { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.ACCESS_REALM, + Roles.LOGIN, + Roles.PURGE + }; + Storage storage = getAndConnectStorage(PREP_USER, PREP_PASSWORD, roleNames); + storage.registerCategory(category); + storage.purge(agentId); + storage.getConnection().disconnect(); + } + + private static void deleteAgentConfigData(List<AgentInformation> items) throws IOException { + BackingStorage storage = getAndConnectBackingStorage(); + storage.registerCategory(AgentInfoDAO.CATEGORY); + ExpressionFactory factory = new ExpressionFactory(); + Remove<AgentInformation> remove = storage.createRemove(AgentInfoDAO.CATEGORY); + Set<String> agentIds = new HashSet<>(); + for (AgentInformation info: items) { + agentIds.add(info.getAgentId()); + } + Expression expression = factory.in(Key.AGENT_ID, agentIds, String.class); + remove.where(expression); + remove.apply(); + + storage.getConnection().disconnect(); + } + + private void executeAndVerifyQuery(PreparedStatement<CpuStat> query, List<Long> expectedTimestamps) throws StatementExecutionException { + Cursor<CpuStat> cursor = query.executeQuery(); + + for (Long time : expectedTimestamps) { + assertTrue(cursor.hasNext()); + CpuStat pojo = cursor.next(); + assertEquals("test-agent-id", pojo.getAgentId()); + assertEquals(time.longValue(), pojo.getTimeStamp()); + double[] data = pojo.getPerProcessorUsage(); + assertEquals(time, data[0], EQUALS_DELTA); + assertEquals(time*2, data[1], EQUALS_DELTA); + } + assertFalse(cursor.hasNext()); + } + + @Test + public void authorizedPreparedAdd() throws Exception { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.WRITE, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + }; + + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); + + // This is the same descriptor as VmClassStatDAOImpl uses. It also + // gets registered automatically for that reason, no need to do it + // manually for this test. + String strDesc = "ADD vm-class-stats SET 'agentId' = ?s , " + + "'vmId' = ?s , " + + "'timeStamp' = ?l , " + + "'loadedClasses' = ?l"; + StatementDescriptor<VmClassStat> desc = new StatementDescriptor<>(VmClassStatDAO.vmClassStatsCategory, strDesc); + VmClassStat pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(12345); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID1); + PreparedStatement<VmClassStat> add; + add = webStorage.prepareStatement(desc); + addPreparedVmClassStat(pojo, add); + + // Add another couple of entries + pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(67890); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID2); + addPreparedVmClassStat(pojo, add); + + pojo = new VmClassStat(); + pojo.setAgentId("fluff"); + pojo.setLoadedClasses(34567); + pojo.setTimeStamp(42); + pojo.setVmId(VM_ID3); + addPreparedVmClassStat(pojo, add); + + webStorage.getConnection().disconnect(); + } + + private void addPreparedVmClassStat(VmClassStat pojo, + PreparedStatement<VmClassStat> add) + throws StatementExecutionException { + add.setString(0, pojo.getAgentId()); + add.setString(1, pojo.getVmId()); + add.setLong(2, pojo.getTimeStamp()); + add.setLong(3, pojo.getLoadedClasses()); + add.execute(); + } + + /* + * Tests whether a query only returns results which a user is allowed to see. + * + * In particular, multiple agent-config records available in the DB, but + * only a subset are allowed to be seen by the user. + */ + @Test + public void authorizedFilteredQuerySubset() throws Exception { + // add agent records into the DB + List<AgentInformation> items = Collections.emptyList(); + try { + String agentIdGrantPrefix = "thermostat-agents-grant-read-agentId-"; + String agent1Id = "agent1"; + String agent2Id = "agent2"; + items = getAgentInformationItemsIncluding(new String[] { agent1Id, agent2Id }); + // assert pre-condition. records in db need to be more than expected + // result set size. + assertTrue(items.size() > 2); + addAgentConfigData(items); + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + // Grant read access only for "agent1" and "agent2" agend IDs + agentIdGrantPrefix + agent1Id, + agentIdGrantPrefix + agent2Id + }; + + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(AgentInfoDAO.CATEGORY); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_FILTERED_QUERY); + StatementDescriptor<AgentInformation> queryDesc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); + PreparedStatement<AgentInformation> query = webStorage.prepareStatement(queryDesc); + Cursor<AgentInformation> cursor = query.executeQuery(); + assertTrue(cursor.hasNext()); + List<AgentInformation> actual = new ArrayList<>(); + while (cursor.hasNext()) { + AgentInformation info = cursor.next(); + actual.add(info); + } + assertEquals(2, actual.size()); + assertFalse("Returned agentIds should be different!", actual.get(0).getAgentId().equals(actual.get(1).getAgentId())); + for (AgentInformation info: actual) { + assertTrue(info.getAgentId().equals(agent1Id) || info.getAgentId().equals(agent2Id)); + } + } finally { + deleteAgentConfigData(items); + } + } + + + private List<AgentInformation> getAgentInformationItemsIncluding( + String[] includeItems) { + List<AgentInformation> infos = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + String agentId = UUID.randomUUID().toString() + "--" + i; + if (i < includeItems.length) { + agentId = includeItems[i]; + } + AgentInformation info = new AgentInformation(); + info.setAgentId(agentId); + info.setAlive((i % 2) == 0); + info.setConfigListenAddress("127.0.0." + i + ":88888"); + info.setStartTime(i * 300); + info.setStopTime((i + 1) * 400); + infos.add(info); + } + return infos; + } + + /* + * Tests whether no query results are returned for a user which lacks *any* + * granting roles for reads. + */ + @Test + public void authorizedFilteredQueryNone() throws Exception { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, // this is just the stop-gap role. + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + // lacking read grant roles + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + + query.setString(0, "timeStamp"); + // Note: with read-all granted, this returns 4 records. See authorized + // query test. + // For this test, however, it should come back empty. + + Cursor<CpuStat> cursor = query.executeQuery(); + assertFalse(cursor.hasNext()); + try { + cursor.next(); + fail("cursor should have thrown exception!"); + } catch (NoSuchElementException e) { + // pass + } + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQuery() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + + query.setString(0, "timeStamp"); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l, 3l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedAggregateCount() throws Exception { + try { + int count = 2; + // registers host info category + addHostInfoData(count); + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL // don't want to test filtered results + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + Category<AggregateCount> adapted = new CategoryAdapter<HostInfo, AggregateCount>(HostInfoDAO.hostInfoCategory).getAdapted(AggregateCount.class); + // register non-adapted + adapted category in that order. Adapted + // category needs to be registered, since it gets it's own mapped id + webStorage.registerCategory(HostInfoDAO.hostInfoCategory); + webStorage.registerCategory(adapted); + + // storage-core registers this descriptor. no need to do it in this + // test. + String strDesc = "QUERY-COUNT host-info"; + StatementDescriptor<AggregateCount> queryDesc = new StatementDescriptor<>(adapted, strDesc); + PreparedStatement<AggregateCount> query = webStorage.prepareStatement(queryDesc); + + Cursor<AggregateCount> cursor = query.executeQuery(); + assertTrue(cursor.hasNext()); + AggregateCount c = cursor.next(); + assertFalse(cursor.hasNext()); + assertEquals(count, c.getCount()); + + webStorage.getConnection().disconnect(); + } finally { + deleteHostInfoData(); + } + } + + @Test + public void authorizedQueryEqualTo() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_EQUAL_TO); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(2l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryNotEqualTo() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 3l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryGreaterThan() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_GREATER_THAN); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(3l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryGreaterThanOrEqualTo() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(2l, 3l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryLessThan() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_LESS_THAN); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryLessThanOrEqualTo() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryNot() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_NOT); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryAnd() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_AND); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2l); + + executeAndVerifyQuery(query, Arrays.asList(1l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedQueryOr() throws Exception { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT, + Roles.GRANT_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_OR); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); + query.setLong(0, 2); + query.setLong(1, 1); + + executeAndVerifyQuery(query, Arrays.asList(0l, 3l)); + + webStorage.getConnection().disconnect(); + } + + @Test + public void refuseUnknownQueryDescriptor() throws IOException { + + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM, + Roles.PREPARE_STATEMENT + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(CpuStatDAO.cpuStatCategory); + + String strDesc = "QUERY cpu-stats WHERE 'fooBarTest' = ?s"; + assertFalse("wanted this descriptor to be untrusted!", TRUSTED_DESCRIPTORS.contains(strDesc)); + StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); + + try { + webStorage.prepareStatement(queryDesc); + } catch (IllegalDescriptorException e) { + // pass + String expectedMsg = "Unknown query descriptor which endpoint of com.redhat.thermostat.web.client.internal.WebStorage refused to accept!"; + assertEquals(expectedMsg, e.getMessage()); + } catch (DescriptorParsingException e) { + // should have been able to parse the descriptor + fail(e.getMessage()); + } + + webStorage.getConnection().disconnect(); + } + + @Test + public void authorizedLoadSave() throws Exception { + String[] roleNames = new String[] { + Roles.LOAD_FILE, + Roles.SAVE_FILE, + Roles.ACCESS_REALM, + Roles.LOGIN, + Roles.GRANT_FILES_WRITE_ALL, + Roles.GRANT_FILES_READ_ALL + }; + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + + byte[] data = "Hello World".getBytes(); + webStorage.saveFile("test", new ByteArrayInputStream(data)); + // Note: On the server side, the file is saved into mongodb + // via GridFS. The save operation returns before write is + // complete, and there is no callback mechanism to find out + // when the write is complete. So, we try a few times to + // load it before considering it a failure. + InputStream loadStream = null; + int loadAttempts = 0; + while (loadStream == null && loadAttempts < 3) { + Thread.sleep(300); + loadStream = webStorage.loadFile("test"); + loadAttempts++; + } + assertNotNull(loadStream); + StringBuilder str = new StringBuilder(); + int i = loadStream.read(); + while (i != -1) { + str.append((char) i); + i = loadStream.read(); + } + assertEquals("Hello World", str.toString()); + + webStorage.getConnection().disconnect(); + } + + @Test + public void unauthorizedLogin() throws Exception { + String[] roleNames = new String[] { + Roles.ACCESS_REALM + }; + + CountDownLatch statusLatch = new CountDownLatch(1); + AtomicBoolean listenerTriggered = new AtomicBoolean(false); + ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.FAILED_TO_CONNECT, statusLatch, listenerTriggered); + @SuppressWarnings("unused") + Storage storage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames, listener); + statusLatch.await(); + assertTrue(listenerTriggered.get()); + } + + @Test + public void storagePurge() throws Exception { + // Add some data to purge (uses backing storage) + UUID uuid = new UUID(42, 24); + long timeStamp = 5; + double cpuLoad = 0.15; + VmCpuStat pojo = new VmCpuStat(uuid.toString(), timeStamp, VM_ID1, cpuLoad); + addVmCpuStat(pojo); + + String[] roleNames = new String[] { + Roles.ACCESS_REALM, + Roles.LOGIN, + Roles.PURGE, + Roles.PREPARE_STATEMENT, + Roles.READ, + Roles.GRANT_READ_ALL, + Roles.REGISTER_CATEGORY + }; + + Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); + webStorage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); + + String strDesc = DESCRIPTOR_MAP.get(KEY_STORAGE_PURGE); + StatementDescriptor<VmCpuStat> queryDesc = new StatementDescriptor<>(VmCpuStatDAO.vmCpuStatCategory, strDesc); + PreparedStatement<VmCpuStat> query = webStorage.prepareStatement(queryDesc); + Cursor<VmCpuStat> cursor = query.executeQuery(); + assertTrue(cursor.hasNext()); + pojo = cursor.next(); + assertFalse(cursor.hasNext()); + + assertEquals(timeStamp, pojo.getTimeStamp()); + assertEquals(VM_ID1, pojo.getVmId()); + assertEquals(cpuLoad, pojo.getCpuLoad(), EQUALS_DELTA); + assertEquals(uuid.toString(), pojo.getAgentId()); + + webStorage.purge(uuid.toString()); + } + + private void addVmCpuStat(VmCpuStat pojo) { + BackingStorage storage = getAndConnectBackingStorage(); + storage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); + Add<VmCpuStat> add = storage.createAdd(VmCpuStatDAO.vmCpuStatCategory); + add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); + add.set(Key.VM_ID.getName(), pojo.getVmId()); + add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); + add.set(VmCpuStatDAO.vmCpuLoadKey.getName(), pojo.getCpuLoad()); + add.apply(); + storage.getConnection().disconnect(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/WebAppTestStatementDescriptorRegistration.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest; + +import java.util.Set; + +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.auth.DescriptorMetadata; +import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration; + +public class WebAppTestStatementDescriptorRegistration implements + StatementDescriptorRegistration { + + @Override + public Set<String> getStatementDescriptors() { + return WebAppTest.TRUSTED_DESCRIPTORS; + } + + @Override + public DescriptorMetadata getDescriptorMetadata(String descriptor, + PreparedParameter[] params) { + return WebAppTest.METADATA_MAPPING.get(descriptor); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/standalone/AllStandaloneTests.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,100 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest.standalone; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +import com.redhat.thermostat.itest.CliTest; +import com.redhat.thermostat.itest.PluginTest; +import com.redhat.thermostat.itest.StorageConnectionTest; +import com.redhat.thermostat.itest.StorageTest; +import com.redhat.thermostat.itest.VmCommandsTest; + + +/** + * <p> + * Suite class in order to facilitate running integration tests from CLI. In + * particular this has been added so as to be able to quickly test downstream + * packaged thermostat. + * </p> + * <p> + * After building thermostat from the same sources than packaged thermostat + * locally via "mvn clean package", this produces the + * thermostat-integration-tests-standalone-<VERSION>.jar in folder + * integration-tests/standalone/target. + * </p> + * <p> + * Concrete steps are: + * + * <pre> + * $ wget http://icedtea.wildebeest.org/download/thermostat/thermostat-<VERSION>.tar.gz + * $ tar -xzf thermostat-<VERSION>.tar.gz + * $ cd thermostat-<VERSION> + * $ mvn clean package + * $ java -Dcom.redhat.thermostat.itest.thermostatHome=/path/to/thermostat/install \ + * -Dcom.redhat.thermostat.itest.thermostatUserHome=$(echo ~/.thermostat) \ + * -cp $(ls integration-tests/standalone/target/thermostat-integration-tests-standalone-*.jar) + * com.redhat.thermostat.itest.standalone.ItestRunner + * </pre> + * + * This should produce a human readable test report in + * $(pwd)/thermostat-itest-reports/summary.txt + * </p> + * <p> + * If you add more test classes, please add those classes to the set of + * SuiteClasses. + * </p> + * <p> + * Note that it's only useful to add expectj based tests to + * the suite. MongoQueriesTest + WebAppTest don't seem to be suitable for + * downstream packaged thermostat. They would require to be adjusted and/or + * would need more jars to be on the class path. In particular packaged + * storage-core, storage-mongodb etc. jars. + * </p> + */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + CliTest.class, + StorageConnectionTest.class, + StorageTest.class, + VmCommandsTest.class, + PluginTest.class, +}) +public class AllStandaloneTests { + // nothing +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/standalone/ItestRunner.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,171 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest.standalone; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; + +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Custom JUnitCore runner which produces human readable reports. + * + * @see StandaloneReportsListener + * @see AllStandaloneTests + */ +public class ItestRunner { + + private static class Runner { + + private static final String PASS_TOKEN = "PASSED."; + private static final String FAIL_TOKEN = "FAILED."; + private static final File REPORTS_FOLDER = new File("thermostat-itest-reports"); + static final File MAIN_REPORT_FILE = new File(REPORTS_FOLDER, "summary.txt"); + private PrintStream printStream; + + private Runner() { + printStream = prepareReportsFolder(); + String msg = "Running Thermostat standalone integration tests ..."; + printStream.println(msg + "\n"); + } + + private PrintStream prepareReportsFolder() { + if (REPORTS_FOLDER.exists()) { + deleteReportsFolder(); + } + REPORTS_FOLDER.mkdir(); + PrintStream ps; + try { + ps = new PrintStream(MAIN_REPORT_FILE); + } catch (FileNotFoundException e) { + // can't continue + throw new RuntimeException(e); + } + return ps; + } + + private void deleteReportsFolder() { + try { + Files.walkFileTree(REPORTS_FOLDER.toPath(), new FileVisitor<Path>() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, + IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, + IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + }); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void startTest(Class<?> clazz) { + printStream.print(" Running " + clazz.getName() + " "); + } + + private void testFinished(Result result) { + String testToken = PASS_TOKEN; + if (!result.wasSuccessful()) { + testToken = FAIL_TOKEN; + } + printStream.println(testToken); + } + + private void finished() { + printStream.println("\n"); + printStream.println("Done."); + printStream.close(); + } + + } + + public static void main(String args[]) { + if (args.length != 0) { + usage(); + } + Runner myRunner = new Runner(); + System.out.print("Running Thermostat standalone integration tests "); + SuiteClasses cls = AllStandaloneTests.class.getAnnotation(Suite.SuiteClasses.class); + boolean allPass = true; + for (Class<?> c: cls.value()) { + myRunner.startTest(c); + JUnitCore core = new JUnitCore(); + core.addListener(new StandaloneReportsListener(new File(Runner.REPORTS_FOLDER, c.getName() + ".txt"))); + Result result = core.run(c); + allPass = allPass && result.wasSuccessful(); + String testToken = result.wasSuccessful() ? "." : "F"; + System.out.print(testToken); + myRunner.testFinished(result); + } + System.out.println(" Done.\n\nSee " + Runner.MAIN_REPORT_FILE.getAbsolutePath() + " for details."); + myRunner.finished(); + } + + private static void usage() { + System.err.println("Usage: " + ItestRunner.class.getName()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/standalone/StandaloneReportsListener.java Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.itest.standalone; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** + * Stack trace printer for failed tests. + * + */ +public class StandaloneReportsListener extends RunListener { + + private final File currentTestFailureFile; + + public StandaloneReportsListener(File testFile) { + currentTestFailureFile = testFile; + } + + @Override + public void testFailure(Failure failure) { + writeStackTraceToErrorFile(failure); + } + + private void writeStackTraceToErrorFile(Failure failure) { + Throwable expn = failure.getException(); + // make sure we append to any existing file so as to not overwrite + // earlier failed tests. + try (FileWriter fw = new FileWriter(currentTestFailureFile, true); + PrintWriter pw = new PrintWriter(fw)) { + expn.printStackTrace(pw); + } catch (IOException e) { + e.printStackTrace(System.err); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/itest-run/src/test/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,1 @@ +com.redhat.thermostat.itest.WebAppTestStatementDescriptorRegistration \ No newline at end of file
--- a/integration-tests/pom.xml Wed Oct 16 13:19:53 2013 -0400 +++ b/integration-tests/pom.xml Fri Apr 05 18:57:56 2013 +0200 @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright 2013 Red Hat, Inc. + Copyright 2012, 2013 Red Hat, Inc. This file is part of Thermostat. @@ -46,163 +46,14 @@ </parent> <artifactId>thermostat-integration-tests</artifactId> - <packaging>jar</packaging> - - <name>Thermostat Integration Tests</name> + <packaging>pom</packaging> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> + <name>Thermostat Integration Tests (parent)</name> - <build> - <plugins> - <!-- jacoco:report insists to have a target/classes dir. since this - module only contains test classes it won't exist after a build. - Hence, create manually in order to unbreak the build. For some - reason skipping both, report AND prepare-agent goals for the - jacoco plugin does not work. --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <version>1.6</version> - <executions> - <execution> - <id>make-target-classes-dir</id> - <phase>prepare-package</phase> - <configuration> - <target> - <mkdir dir="${project.build.directory}/classes" /> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - <!-- skip unit test run, tests to be executed during integration-test --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <argLine>-Djava.security.auth.login.config=${thermostat.home}/etc/thermostat_jaas.conf ${coverageAgent}</argLine> - <skip>true</skip> - </configuration> - <executions> - <execution> - <id>run-integration-tests</id> - <phase>integration-test</phase> - <goals> - <goal>test</goal> - </goals> - <configuration> - <skip>false</skip> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-dependency-plugin</artifactId> - <version>2.4</version> - <executions> - <execution> - <id>copy-deps</id> - <phase>package</phase> - <goals> - <goal>copy-dependencies</goal> - </goals> - <configuration> - <excludeTransitive>true</excludeTransitive> - <excludeGroupIds>org.osgi,junit,org.hamcrest</excludeGroupIds> - <outputDirectory>${project.build.directory}/libs</outputDirectory> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - <pluginManagement> - <plugins> - <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> - <plugin> - <groupId>org.eclipse.m2e</groupId> - <artifactId>lifecycle-mapping</artifactId> - <version>1.0.0</version> - <configuration> - <lifecycleMappingMetadata> - <pluginExecutions> - <pluginExecution> - <pluginExecutionFilter> - <groupId> - org.apache.maven.plugins - </groupId> - <artifactId> - maven-dependency-plugin - </artifactId> - <versionRange>[2.4,)</versionRange> - <goals> - <goal>copy-dependencies</goal> - </goals> - </pluginExecutionFilter> - <action> - <ignore></ignore> - </action> - </pluginExecution> - </pluginExecutions> - </lifecycleMappingMetadata> - </configuration> - </plugin> - </plugins> - </pluginManagement> - </build> - <dependencies> + <modules> + <module>itest-run</module> + <module>standalone</module> + </modules> - <!-- integration tests --> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>net.sourceforge.expectj</groupId> - <artifactId>expectj</artifactId> - <scope>test</scope> - </dependency> - - <!-- thermostat parts --> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-distribution</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-web-war</artifactId> - <version>${project.version}</version> - <type>war</type> - <scope>test</scope> - </dependency> - - <!-- Embedded jetty for testing the WAR (no peace from here on..). --> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - <version>${jetty.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-plus</artifactId> - <version>${jetty.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - <version>${jetty.version}</version> - <scope>test</scope> - </dependency> - - </dependencies> </project>
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,257 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; - -import org.junit.Test; - -import expectj.Spawn; - -/** - * Integration tests to exercise the basics of the thermostat command line. - */ -public class CliTest extends IntegrationTest { - - @Test - public void testExpectIsSane() throws Exception { - Spawn shell = spawnThermostat(); - - try { - shell.expect("some-random-text-that-is-not-really-possible"); - fail("should never match"); - } catch (IOException endOfStream) { - assertTrue(endOfStream.getMessage().contains("End of stream reached, no match found")); - } - shell.expectClose(); - } - - @Test - public void testSimpleInvocationPrintsHelp() throws Exception { - Spawn shell = spawnThermostat(); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - - assertMatchesHelpCommandList(stdOut); - - String stdErr = shell.getCurrentStandardErrContents(); - assertEquals(stdErr, ""); - } - - @Test - public void testHelpCommandInvocation() throws Exception { - Spawn shell = spawnThermostat("help"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - String stdErr = shell.getCurrentStandardErrContents(); - - assertMatchesHelpCommandList(stdOut); - assertEquals(stdErr, ""); - } - - @Test - public void testHelpOnHelp() throws Exception { - Spawn shell = spawnThermostat("help", "help"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - String stdErr = shell.getCurrentStandardErrContents(); - - String[] lines = stdOut.split("\n"); - String usage = lines[0]; - assertEquals("usage: thermostat help [command-name]", usage); - - assertEquals(stdErr, ""); - } - - @Test - public void testVersionArgument() throws Exception { - Spawn shell = spawnThermostat("--version"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - String stdErr = shell.getCurrentStandardErrContents(); - - assertTrue(stdOut.matches("Thermostat version \\d+\\.\\d+\\.\\d+\n")); - assertEquals(stdErr, ""); - } - - @Test - public void testShell() throws Exception { - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - shell.send("help\n"); - - shell.expect(SHELL_PROMPT); - - assertMatchesShellHelpCommandList(shell.getCurrentStandardOutContents()); - - shell.send("exit\n"); - - shell.expectClose(); - } - - @Test - public void testShellPrintsVersionOnStartup() throws Exception { - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - - String stdOut = shell.getCurrentStandardOutContents(); - assertTrue(stdOut.contains("Thermostat version ")); - } - - @Test - public void versionArgumentInShellIsNotAllowed() throws Exception { - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - shell.send("--version\n"); - - shell.expect(SHELL_PROMPT); - - String stdOut = shell.getCurrentStandardOutContents(); - String stdErr = shell.getCurrentStandardErrContents(); - - assertMatchesShellHelpCommandList(shell.getCurrentStandardOutContents()); - // use the Pattern.DOTALL flag (?s) so that line terminators match with - // ".*". stdOut contains the SHELL_PROMPT too. - assertTrue(stdOut.matches("(?s)^.*\nunknown command '--version'\n.*$")); - assertEquals(stdErr, ""); - - shell.send("exit\n"); - - shell.expectClose(); - } - - @Test - public void testShellHelp() throws Exception { - Spawn shell = spawnThermostat("help", "shell"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - - String[] lines = stdOut.split("\n"); - String usage = lines[0]; - assertTrue(usage.matches("^usage: thermostat shell$")); - String description = lines[1]; - assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$")); - assertTrue(lines[3].matches("thermostat shell")); - } - - @Test - public void testShellHelpArgument() throws Exception { - Spawn shell = spawnThermostat("shell", "--help"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - - String[] lines = stdOut.split("\n"); - String usage = lines[0]; - assertTrue(usage.matches("^usage: thermostat shell$")); - String description = lines[1]; - assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$")); - assertTrue(lines[3].matches("thermostat shell")); - } - - @Test - public void testShellUnrecognizedArgument() throws Exception { - Spawn shell = spawnThermostat("shell", "--foo"); - shell.expectClose(); - String stdOut = shell.getCurrentStandardOutContents(); - String expectedOut = "Could not parse options: Unrecognized option: --foo\n" - + "usage: thermostat shell\n" - + " launches the Thermostat interactive shell\n" - + "\n" - + "thermostat shell\n\n"; - assertEquals(expectedOut, stdOut); - } - - @Test - public void testUnrecognizedEventsInShell() throws Exception { - // test '!' events - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - shell.send("what!?!\n"); - shell.expect(SHELL_PROMPT); - shell.send("exit\n"); - shell.expectClose(); - - assertTrue(shell.getCurrentStandardErrContents().contains("!?!: event not found")); - assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - } - - @Test - public void testInvalidCommand() throws Exception { - Spawn shell = spawnThermostat("foobar", "baz"); - - // TODO should this be stderr? - shell.expect("unknown command 'foobar'"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - - assertMatchesHelpCommandList(stdOut); - } - - private static void assertMatchesHelpCommandList(String actual) { - assertTrue(actual.contains("list of commands")); - assertTrue(actual.contains("help")); - assertTrue(actual.contains("agent")); - assertTrue(actual.contains("gui")); - assertTrue(actual.contains("ping")); - assertTrue(actual.contains("shell")); - } - - private static void assertMatchesShellHelpCommandList(String actual) { - assertTrue(actual.contains("list of commands")); - assertTrue(actual.contains("help")); - assertTrue(actual.contains("connect")); - assertTrue(actual.contains("disconnect")); - assertTrue(actual.contains("ping")); - } - -} -
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,378 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.redhat.thermostat.common.utils.StreamUtils; - -import expectj.Executor; -import expectj.ExpectJ; -import expectj.Spawn; -import expectj.TimeoutException; - -/** - * Helper methods to support writing an integration test. - * <p> - * This class should be used by all integration tests to start - * thermostat and to obtain paths to various locations. Starting - * thermostat manually will cause issues with wrong paths being - * used. - */ -public class IntegrationTest { - - public static class SpawnResult { - final Process process; - final Spawn spawn; - - public SpawnResult(Process process, Spawn spawn) { - this.process = process; - this.spawn = spawn; - } - } - - // FIXME Make sure all methods are using a sane environment that's set up correctly - - public static final long TIMEOUT_IN_SECONDS = 30; - - public static final String SHELL_PROMPT = "Thermostat >"; - - private static final String THERMOSTAT_HOME = "THERMOSTAT_HOME"; - private static final String USER_THERMOSTAT_HOME = "USER_THERMOSTAT_HOME"; - - /* This is a mirror of paths from c.r.t.shared.Configuration */ - - private static String getThermostatExecutable() { - return getThermostatHome() + "/bin/thermostat"; - } - - public static String getThermostatHome() { - return "../distribution/target/image"; - } - - public static String getSystemPluginHome() { - return getThermostatHome() + "/plugins"; - } - - public static String getConfigurationDir() { - return getThermostatHome() + "/etc"; - } - - public static String getUserThermostatHome() { - return "../distribution/target/user-home"; - } - - public static String getStorageDataDirectory() { - return getUserThermostatHome() + "/data/db"; - } - - public static void clearStorageDataDirectory() throws IOException { - File storageDir = new File(getStorageDataDirectory()); - if (storageDir.exists()) { - if (storageDir.isDirectory()) { - deleteFilesRecursivelyUnder(storageDir); - } else { - throw new IllegalStateException(storageDir + " exists but is not a directory"); - } - } - } - - public static Process runThermostat(String... args) throws IOException { - List<String> completeArgs = new ArrayList<String>(args.length+1); - completeArgs.add(getThermostatExecutable()); - completeArgs.addAll(Arrays.asList(args)); - ProcessBuilder builder = buildThermostatProcess(completeArgs); - - return builder.start(); - } - - public static Spawn spawnThermostat(String... args) throws IOException { - return spawnThermostat(false, args); - } - - public static Spawn startStorage() throws Exception { - clearStorageDataDirectory(); - - Spawn storage = spawnThermostat("storage", "--start"); - try { - storage.expect("pid:"); - } catch (IOException e) { - // this may happen if storage is already running. - e.printStackTrace(); - String stdOutContents = storage.getCurrentStandardOutContents(); - - System.err.flush(); - System.out.flush(); - System.err.println("stdout was: -->" + stdOutContents +"<--"); - System.err.println("stderr was: -->" + storage.getCurrentStandardErrContents() + "<--"); - System.err.flush(); - assertFalse(stdOutContents.contains("Storage is already running with pid")); - throw new Exception("Something funny is going on when trying to start storage!", e); - } - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - return storage; - } - - public static Spawn stopStorage() throws Exception { - Spawn storage = spawnThermostat("storage", "--stop"); - storage.expect("server shutdown complete"); - storage.expectClose(); - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - return storage; - } - - public static Spawn spawnThermostat(boolean localeDependent, String... args) throws IOException { - ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); - StringBuilder result = new StringBuilder(getThermostatExecutable()); - if (args != null) { - for (String arg : args) { - result.append(" ").append(arg); - } - } - String toExecute = result.toString(); - Executor exec = null; - if (localeDependent) { - exec = new LocaleExecutor(toExecute); - } else { - exec = new SimpleExecutor(toExecute); - } - return expect.spawn(exec); - } - - public static SpawnResult spawnThermostatAndGetProcess(String... args) throws IOException { - final List<String> completeArgs = new ArrayList<String>(args.length+1); - completeArgs.add(getThermostatExecutable()); - completeArgs.addAll(Arrays.asList(args)); - - final Process[] process = new Process[1]; - - ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); - - Spawn spawn = expect.spawn(new Executor() { - @Override - public Process execute() throws IOException { - ProcessBuilder builder = buildThermostatProcess(completeArgs); - Process service = builder.start(); - process[0] = service; - return service; - } - }); - - return new SpawnResult(process[0], spawn); - } - - private static ProcessBuilder buildThermostatProcess(List<String> args) { - ProcessBuilder builder = new ProcessBuilder(args); - builder.environment().put(THERMOSTAT_HOME, getThermostatHome()); - builder.environment().put(USER_THERMOSTAT_HOME, getUserThermostatHome()); - - return builder; - } - - /** - * Generic method to run a program. - * <p> - * DO NOT USE THIS TO RUN THERMOSTAT ITSELF. It does not set up the - * environment correctly, using incorrect data and possibly overwriting - * important data. - */ - public static Spawn spawn(List<String> args) throws IOException { - ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); - StringBuilder result = new StringBuilder(); - for (String arg : args) { - result.append(arg).append(" "); - } - return expect.spawn(result.substring(0, result.length() - 1)); - } - - /** - * Kill the process and all its children, recursively. Sends SIGTERM. - */ - public static void killRecursively(Process process) throws Exception { - killRecursively(getPid(process)); - } - - private static void killRecursively(int pid) throws Exception { - List<Integer> childPids = findChildPids(pid); - for (Integer childPid : childPids) { - killRecursively(childPid); - } - killProcess(pid); - } - - private static void killProcess(int processId) throws Exception { - System.err.println("Killing process with pid: " + processId); - Runtime.getRuntime().exec("kill " + processId).waitFor(); - } - - private static List<Integer> findChildPids(int processId) throws IOException { - String children = new String(StreamUtils.readAll(Runtime.getRuntime().exec("ps --ppid " + processId + " -o pid=").getInputStream())); - String[] childPids = children.split("\n"); - List<Integer> result = new ArrayList<>(); - for (String childPid : childPids) { - String pidString = childPid.trim(); - if (pidString.length() == 0) { - continue; - } - try { - result.add(Integer.parseInt(pidString)); - } catch (NumberFormatException nfe) { - System.err.println(nfe); - } - } - return result; - } - - private static int getPid(Process process) throws Exception { - final String UNIX_PROCESS_CLASS = "java.lang.UNIXProcess"; - if (!process.getClass().getName().equals(UNIX_PROCESS_CLASS)) { - throw new IllegalArgumentException("can only kill " + UNIX_PROCESS_CLASS + "; input is a " + process.getClass()); - } - - Class<?> processClass = process.getClass(); - Field pidField = processClass.getDeclaredField("pid"); - pidField.setAccessible(true); - return (int) pidField.get(process); - } - - private static void deleteFilesRecursivelyUnder(File path) throws IOException { - if (!path.isDirectory()) { - throw new IOException("Cannot delete files under a non-directory: " + path); - } - File[] filesToDelete = path.listFiles(); - if (filesToDelete == null) { - throw new IOException("Error getting directory listing: " + path); - } - for (File theFile : filesToDelete) { - if (theFile.isDirectory()) { - deleteFilesRecursivelyUnder(theFile); - } - Files.deleteIfExists(theFile.toPath()); - } - } - - /** Confirm that there are no 'command not found'-like messages in the spawn's stdout/stderr */ - public static void assertCommandIsFound(Spawn spawn) { - assertCommandIsFound(spawn.getCurrentStandardOutContents(), spawn.getCurrentStandardErrContents()); - } - - public static void assertCommandIsFound(String stdOutContents, String stdErrContents) { - assertFalse(stdOutContents.contains("unknown command")); - assertFalse(stdErrContents.contains("unknown command")); - } - - /** Confirm that there are no exception stack traces in the spawn's stdout/stderr */ - public static void assertNoExceptions(Spawn spawn) { - assertNoExceptions(spawn.getCurrentStandardOutContents(), spawn.getCurrentStandardErrContents()); - } - - public static void assertNoExceptions(String stdOutContents, String stdErrContents) { - assertFalse(stdOutContents.contains("Exception")); - assertFalse(stdErrContents.contains("Exception")); - } - - public static void assertOutputEndsWith(String stdOutContents, String expectedOutput) { - String endOfOut = stdOutContents.substring(stdOutContents.length() - expectedOutput.length()); - assertEquals(expectedOutput, endOfOut); - } - - public static void handleAuthPrompt(Spawn spawn, String url, String user, String password) throws IOException, TimeoutException { - spawn.expect("Please enter username for storage at " + url + ":"); - spawn.send(user + "\r"); - spawn.expect("Please enter password for storage at " + url + ":"); - spawn.send(password + "\r"); - } - - private static class LocaleExecutor extends EnvironmentExecutor { - - public static final String[] ENV_WITH_LANG_C = { - THERMOSTAT_HOME + "=" + getThermostatHome(), - USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(), - "LANG=C" - }; - - public LocaleExecutor(String process) { - super(process, ENV_WITH_LANG_C); - } - - } - - private static class SimpleExecutor extends EnvironmentExecutor { - - public static final String[] ENV_WITH = { - THERMOSTAT_HOME + "=" + getThermostatHome(), - USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(), - }; - - public SimpleExecutor(String process) { - super(process, ENV_WITH); - } - } - - private static class EnvironmentExecutor implements Executor { - - private final String[] env; - private final String process; - - public EnvironmentExecutor(String process, String[] env) { - this.process = process; - this.env = env; - } - - @Override - public Process execute() throws IOException { - return Runtime.getRuntime().exec(process, env); - } - - @Override - public String toString() { - return process; - } - } -} -
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/MongoQueriesTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,543 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -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.ByteArrayInputStream; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.redhat.thermostat.host.cpu.common.CpuStatDAO; -import com.redhat.thermostat.host.cpu.common.model.CpuStat; -import com.redhat.thermostat.storage.config.StartupConfiguration; -import com.redhat.thermostat.storage.core.Add; -import com.redhat.thermostat.storage.core.BackingStorage; -import com.redhat.thermostat.storage.core.Connection.ConnectionListener; -import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Query.SortDirection; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.mongodb.internal.MongoStorage; -import com.redhat.thermostat.storage.query.Expression; -import com.redhat.thermostat.storage.query.ExpressionFactory; -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; -import com.redhat.thermostat.vm.classstat.common.model.VmClassStat; -import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; - -/* - * This test class starts up a mongod instance and tests if thermostat - * queries with expressions return what they supposed to return. - * - * Tests should make their own connection to storage, probably by making use of - * one of the getAndConnectStorage() method variants. - * - * Because the storage instance is shared among all of the tests, it is - * necessary to take precautions to avoid introducing data dependencies - * between tests. Such precautions could include: using a different - * category (ie mongod collection) than any other existing test; setting - * a unique agent-id for all data written and then deleting the data - * at the end of the test; <insert other clever idea here>. - * - */ -public class MongoQueriesTest extends IntegrationTest { - - private static class CountdownConnectionListener implements ConnectionListener { - - private final ConnectionStatus target; - private final CountDownLatch latch; - - private CountdownConnectionListener(ConnectionStatus target, CountDownLatch latch) { - this.target = target; - this.latch = latch; - } - - @Override - public void changed(ConnectionStatus newStatus) { - assertEquals(target, newStatus); - latch.countDown(); - } - } - - private static final double EQUALS_DELTA = 0.00000000000001; - private static final String VM_ID1 = "vmId1"; - private static final String VM_ID2 = "vmId2"; - private static final String VM_ID3 = "vmId3"; - - private ExpressionFactory factory = new ExpressionFactory(); - - @BeforeClass - public static void setUpOnce() throws Exception { - startStorage(); - - addCpuData(4); - } - - @AfterClass - public static void tearDownOnce() throws Exception { - deleteCpuData(); - - stopStorage(); - } - - /* - * Make a connection to mongo storage (returning the Storage object). Before - * initiating the connection, add the ConnectionListener to Storage. - */ - private static BackingStorage getAndConnectStorage(ConnectionListener listener) { - final String url = "mongodb://127.0.0.1:27518"; - StartupConfiguration config = new StartupConfiguration() { - - @Override - public String getDBConnectionString() { - return url; - } - - }; - BackingStorage storage = new MongoStorage(config); - if (listener != null) { - storage.getConnection().addListener(listener); - } - storage.getConnection().connect(); - return storage; - } - - private static void addCpuData(int numberOfItems) throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage storage = getAndConnectStorage(listener); - latch.await(); - storage.getConnection().removeListener(listener); - - storage.registerCategory(CpuStatDAO.cpuStatCategory); - - for (int i = 0; i < numberOfItems; i++) { - CpuStat pojo = new CpuStat("test-agent-id", i, new double[] {i, i*2}); - Add<CpuStat> add = storage.createAdd(CpuStatDAO.cpuStatCategory); - add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); - add.set(CpuStatDAO.cpuLoadKey.getName(), pojo.getPerProcessorUsage()); - add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); - add.apply(); - } - - storage.getConnection().disconnect(); - } - - private static void deleteCpuData() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - Storage storage = getAndConnectStorage(listener); - latch.await(); - storage.getConnection().removeListener(listener); - storage.registerCategory(CpuStatDAO.cpuStatCategory); - - storage.purge("test-agent-id"); - - storage.getConnection().disconnect(); - } - - private void executeAndVerifyQuery(Query<CpuStat> query, List<Long> expectedTimestamps) { - Cursor<CpuStat> cursor = query.execute(); - - for (Long time : expectedTimestamps) { - assertTrue(cursor.hasNext()); - CpuStat pojo = cursor.next(); - assertEquals("test-agent-id", pojo.getAgentId()); - assertEquals(time.longValue(), pojo.getTimeStamp()); - double[] data = pojo.getPerProcessorUsage(); - assertEquals(time, data[0], EQUALS_DELTA); - assertEquals(time*2, data[1], EQUALS_DELTA); - } - assertFalse(cursor.hasNext()); - } - - @Test - public void testMongoAdd() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); - - Add<VmClassStat> add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); - VmClassStat pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(12345); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID1); - addVmClassStat(add, pojo); - - // Add another couple of entries - add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); - pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(67890); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID2); - addVmClassStat(add, pojo); - - add = mongoStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); - pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(34567); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID3); - addVmClassStat(add, pojo); - - mongoStorage.getConnection().disconnect(); - } - - private void addVmClassStat(Add<VmClassStat> add, VmClassStat pojo) { - add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); - add.set(Key.VM_ID.getName(), pojo.getVmId()); - add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); - add.set(VmClassStatDAO.loadedClassesKey.getName(), pojo.getLoadedClasses()); - add.apply(); - } - - @Test - public void canQueryNoWhere() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l, 3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryEqualTo() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.equalTo(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(2l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryNotEqualTo() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.notEqualTo(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryGreaterThan() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.greaterThan(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryGreaterThanOrEqualTo() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.greaterThanOrEqualTo(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(2l, 3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryLessThan() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.lessThan(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryLessThanOrEqualTo() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.lessThanOrEqualTo(Key.TIMESTAMP, 2l); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryIn() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - List<Long> times = Arrays.asList(0l, 2l); - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.in(Key.TIMESTAMP, new HashSet<>(times), Long.class); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, times); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryNotIn() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.notIn(Key.TIMESTAMP, new HashSet<>(Arrays.asList(0l, 2l)), Long.class); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(1l, 3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryNot() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.not(factory.greaterThan(Key.TIMESTAMP, 2l)); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryAnd() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.and(factory.greaterThan(Key.TIMESTAMP, 0l), - factory.lessThan(Key.TIMESTAMP, 2l)); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(1l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canQueryOr() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - mongoStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - Query<CpuStat> query = mongoStorage.createQuery(CpuStatDAO.cpuStatCategory); - Expression expr = factory.or(factory.greaterThan(Key.TIMESTAMP, 2l), - factory.lessThan(Key.TIMESTAMP, 1l)); - query.where(expr); - query.sort(Key.TIMESTAMP, SortDirection.ASCENDING); - - executeAndVerifyQuery(query, Arrays.asList(0l, 3l)); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void canLoadSave() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - Storage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - - byte[] data = "Hello World".getBytes(); - mongoStorage.saveFile("test", new ByteArrayInputStream(data)); - // Note: On the server side, the file is saved into mongodb - // via GridFS. The save operation returns before write is - // complete, and there is no callback mechanism to find out - // when the write is complete. So, we try a few times to - // load it before considering it a failure. - InputStream loadStream = null; - int loadAttempts = 0; - while (loadStream == null && loadAttempts < 3) { - Thread.sleep(300); - loadStream = mongoStorage.loadFile("test"); - loadAttempts++; - } - assertNotNull(loadStream); - StringBuilder str = new StringBuilder(); - int i = loadStream.read(); - while (i != -1) { - str.append((char) i); - i = loadStream.read(); - } - assertEquals("Hello World", str.toString()); - - mongoStorage.getConnection().disconnect(); - } - - @Test - public void storagePurge() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.CONNECTED, latch); - BackingStorage mongoStorage = getAndConnectStorage(listener); - latch.await(); - mongoStorage.getConnection().removeListener(listener); - UUID uuid = new UUID(42, 24); - - mongoStorage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); - long timeStamp = 5; - double cpuLoad = 0.15; - VmCpuStat pojo = new VmCpuStat(uuid.toString(), timeStamp, VM_ID1, cpuLoad); - Add<VmCpuStat> add = mongoStorage.createAdd(VmCpuStatDAO.vmCpuStatCategory); - add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); - add.set(Key.VM_ID.getName(), pojo.getVmId()); - add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); - add.set(VmCpuStatDAO.vmCpuLoadKey.getName(), pojo.getCpuLoad()); - add.apply(); - - Query<VmCpuStat> query = mongoStorage.createQuery(VmCpuStatDAO.vmCpuStatCategory); - Cursor<VmCpuStat> cursor = query.execute(); - assertTrue(cursor.hasNext()); - pojo = cursor.next(); - assertFalse(cursor.hasNext()); - - assertEquals(timeStamp, pojo.getTimeStamp()); - assertEquals(VM_ID1, pojo.getVmId()); - assertEquals(cpuLoad, pojo.getCpuLoad(), EQUALS_DELTA); - assertEquals(uuid.toString(), pojo.getAgentId()); - - mongoStorage.purge(uuid.toString()); - } -}
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import expectj.Spawn; - -public class PluginTest extends IntegrationTest { - - private static final String PLUGIN_HOME = getSystemPluginHome(); - - private static NewCommandPlugin fooPlugin = new NewCommandPlugin("foo", "provides foo command", PLUGIN_HOME + File.separator + "new"); - private static NewCommandPlugin userPlugin = new NewCommandPlugin( - "user", - "a plugin that is provided by the user", - getUserThermostatHome() + File.separator + "data" + File.separator + "plugins" + File.separator + "user"); - private static UnknownExtendsPlugin unknownExtension = new UnknownExtendsPlugin(PLUGIN_HOME + File.separator + "unknown"); - - @BeforeClass - public static void setUpOnce() { - fooPlugin.install(); - userPlugin.install(); - unknownExtension.install(); - } - - @AfterClass - public static void tearDownOnce() { - unknownExtension.uninstall(); - userPlugin.uninstall(); - fooPlugin.uninstall(); - } - - @Test - public void testHelpIsOkay() throws Exception { - Spawn shell = spawnThermostat("help"); - shell.expectClose(); - - String stdOut = shell.getCurrentStandardOutContents(); - - assertTrue(stdOut.contains("list of commands")); - assertTrue(stdOut.contains("help")); - assertTrue(stdOut.contains("agent")); - assertTrue(stdOut.contains("gui")); - assertTrue(stdOut.contains("ping")); - assertTrue(stdOut.contains("shell")); - - assertTrue(stdOut.contains(fooPlugin.command)); - assertTrue(stdOut.contains(fooPlugin.description)); - - assertTrue(stdOut.contains(userPlugin.command)); - - assertFalse(stdOut.contains(unknownExtension.command)); - - // TODO assertEquals("", stdErr); - } - - /** - * This plugin provides a new command - */ - private static class NewCommandPlugin { - - private final String pluginHome; - private final String command; - private final String description; - - public NewCommandPlugin(String command, String description, String pluginLocation) { - this.pluginHome = pluginLocation; - - this.command = command; - this.description = description; - } - - private void install() { - File home = new File(pluginHome); - if (!home.isDirectory() && !home.mkdirs()) { - throw new AssertionError("could not create directory: " + pluginHome); - } - - String pluginContents = "" + - "<?xml version=\"1.0\"?>\n" + - "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" + - " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + - " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermost-plugin.xsd\">\n" + - " <commands>" + - " <command>" + - " <name>" + command + "</name>" + - " <description>" + description + "</description>" + - " <options>" + - " <option>" + - " <long>aaaaa</long>" + - " <short>a</short>" + - " </option>" + - " </options>" + - " <environments>" + - " <environment>shell</environment>" + - " <environment>cli</environment>" + - " </environments>" + - " <bundles>" + - " <bundle>" + - " <symbolic-name>bar</symbolic-name>" + - " <version>0.1.0</version>" + - " </bundle>" + - " </bundles>" + - " </command>" + - " </commands>" + - "</plugin>"; - - try (FileWriter writer = new FileWriter(pluginHome + File.separator + "thermostat-plugin.xml")) { - writer.write(pluginContents); - } catch (IOException e) { - throw new AssertionError("unable to write plugin configuration", e); - } - - } - - private void uninstall() { - if (!new File(pluginHome).exists()) { - return; - } - if (!new File(pluginHome, "thermostat-plugin.xml").delete()) { - throw new AssertionError("Could not delete plugin file"); - } - if (!new File(pluginHome).delete()) { - throw new AssertionError("Could not delete plugin directory"); - } - } - } - - /** - * This plugin extends an unknown command - */ - private static class UnknownExtendsPlugin { - - private final String pluginHome; - private final String command; - - public UnknownExtendsPlugin(String pluginLocation) { - this.pluginHome = pluginLocation; - - this.command = "unknown-command"; - } - - private void install() { - File home = new File(pluginHome); - if (!home.isDirectory() && !home.mkdir()) { - throw new AssertionError("could not create directory: " + pluginHome); - } - - String pluginContents = "" + - "<?xml version=\"1.0\"?>\n" + - "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" + - " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + - " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermost-plugin.xsd\">\n" + - " <extensions>" + - " <extension>" + - " <name>" + command + "</name>" + - " <bundles>" + - " <bundle>" + - " <symbolic-name>bar</symbolic-name>" + - " <version>0.1.0</version>" + - " </bundle>" + - " </bundles>" + - " </extension>" + - " </extensions>" + - "</plugin>"; - - try (FileWriter writer = new FileWriter(pluginHome + File.separator + "thermostat-plugin.xml")) { - writer.write(pluginContents); - } catch (IOException e) { - throw new AssertionError("unable to write plugin configuration", e); - } - - } - - private void uninstall() { - if (!new File(pluginHome).exists()) { - return; - } - if (!new File(pluginHome, "thermostat-plugin.xml").delete()) { - throw new AssertionError("Could not delete plugin file"); - } - if (!new File(pluginHome).delete()) { - throw new AssertionError("Could not delete plugin directory"); - } - } - } - -}
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import java.io.IOException; - -import org.junit.Ignore; -import org.junit.Test; - -import expectj.ExpectJException; -import expectj.Spawn; -import expectj.TimeoutException; - -public class StorageConnectionTest extends IntegrationTest { - - // @BeforeClass // reinstate once we actually need storage running (see ignored tests) - public static void setUpOnce() throws Exception { - startStorage(); - } - - // @AfterClass // reinstate once we actually need storage running - public static void tearDownOnce() throws Exception { - stopStorage(); - } - - @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore - @Test - public void testConnect() throws ExpectJException, TimeoutException, IOException { - Spawn shell = spawnThermostat(true, "shell"); - - shell.expect(SHELL_PROMPT); - shell.send("connect -d mongodb://127.0.0.1:27518\n"); - handleAuthPrompt(shell, "mongodb://127.0.0.1:27518", "", ""); - shell.expect(SHELL_PROMPT); - shell.send("exit\n"); - shell.expectClose(); - - assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - } - - @Test - public void testDisconnectWithoutConnecting() throws ExpectJException, TimeoutException, IOException { - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - shell.send("disconnect\n"); - shell.expect(SHELL_PROMPT); - shell.send("exit\n"); - shell.expectClose(); - - assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - } - - @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore - @Test - public void testConnectAndDisconnectInShell() throws IOException, TimeoutException, ExpectJException { - Spawn shell = spawnThermostat(true, "shell"); - - shell.expect(SHELL_PROMPT); - shell.send("connect -d mongodb://127.0.0.1:27518\n"); - handleAuthPrompt(shell, "mongodb://127.0.0.1:27518", "", ""); - shell.expect(SHELL_PROMPT); - shell.send("disconnect\n"); - shell.send("exit\n"); - shell.expectClose(); - - assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents()); - } - - // TODO add a test to make sure connect/disconnect is not visible outside the shell -} -
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import org.junit.Test; - -import expectj.Spawn; - -public class StorageTest extends IntegrationTest { - - @Test - public void startAndStopStorage() throws Exception { - Spawn storage; - - storage = startStorage(); - - storage = spawnThermostat("storage", "--status"); - storage.expect("Storage is running"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - - storage = stopStorage(); - - storage = spawnThermostat("storage", "--status"); - storage.expect("Storage is not running"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - } - - @Test - public void testServiceStartAndKilling() throws Exception { - - SpawnResult spawnResult = spawnThermostatAndGetProcess("service"); - Spawn service = spawnResult.spawn; - - try { - service.expectErr("agent started"); - } finally { - killRecursively(spawnResult.process); - } - - service.stop(); - service.expectClose(); - - } - -} -
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import expectj.Spawn; - -/** Integration tests for the various vm commands */ -public class VmCommandsTest extends IntegrationTest { - - @BeforeClass - public static void setUpOnce() throws Exception { - startStorage(); - - // TODO insert actual data into the database and test that - } - - @AfterClass - public static void tearDownOnce() throws Exception { - stopStorage(); - } - - @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore - @Test - public void testListVms() throws Exception { - Spawn vmList = commandAgainstMongo("list-vms"); - handleAuthPrompt(vmList, "mongodb://127.0.0.1:27518", "", ""); - vmList.expectClose(); - assertOutputEndsWith(vmList.getCurrentStandardOutContents(), "HOST_ID HOST VM_ID STATUS VM_NAME\n\n"); - } - - @Test - public void testVmStat() throws Exception { - Spawn vmStat = commandAgainstMongo("vm-stat"); - // TODO include required options to test meaningfully - //handleAuthPrompt(vmStat, "mongodb://127.0.0.1:27518", "", ""); - vmStat.expectClose(); - - System.out.println(vmStat.getCurrentStandardOutContents()); - assertCommandIsFound(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents()); - assertNoExceptions(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents()); - } - - @Test - public void testVmInfo() throws Exception { - Spawn vmInfo = commandAgainstMongo("vm-info"); - // TODO include required options to test meaningfully - // handleAuthPrompt(vmInfo, "mongodb://127.0.0.1:27518", "", ""); - vmInfo.expectClose(); - - assertNoExceptions(vmInfo.getCurrentStandardOutContents(), vmInfo.getCurrentStandardErrContents()); - } - - @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore - @Test - public void testHeapCommands() throws Exception { - String[] commands = new String[] { - "dump-heap", - "list-heap-dumps", - "save-heap-dump-to-file", - "show-heap-histogram", - "find-objects", - "object-info", - "find-root" - }; - - for (String command : commands) { - Spawn heapCommand = commandAgainstMongo(command); - // TODO include required options to test each command meaningfully - if (command.equals("list-heap-dumps")) { - // No missing options, times out waiting for user/pass input without the following: - handleAuthPrompt(heapCommand, "mongodb://127.0.0.1:27518", "", ""); - } - heapCommand.expectClose(); - - assertCommandIsFound( - heapCommand.getCurrentStandardOutContents(), - heapCommand.getCurrentStandardErrContents()); - assertNoExceptions( - heapCommand.getCurrentStandardOutContents(), - heapCommand.getCurrentStandardErrContents()); - } - } - - @Test - public void testNormalCommandAndPluginInShell() throws Exception { - Spawn shell = spawnThermostat("shell"); - - shell.expect(SHELL_PROMPT); - shell.send("list-vms\n"); - - shell.expect(SHELL_PROMPT); - - shell.send("dump-heap\n"); - - shell.expect(SHELL_PROMPT); - - shell.send("exit\n"); - shell.expectClose(); - - assertCommandIsFound(shell); - assertNoExceptions(shell); - } - - private static Spawn commandAgainstMongo(String... args) throws IOException { - if (args == null || args.length == 0) { - throw new IllegalArgumentException("args must be an array with something"); - } - List<String> completeArgs = new ArrayList<>(); - completeArgs.addAll(Arrays.asList(args)); - completeArgs.add("-d"); - completeArgs.add("mongodb://127.0.0.1:27518"); - return spawnThermostat(true, completeArgs.toArray(new String[0])); - } - -} -
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1092 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -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 static org.junit.Assert.fail; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Properties; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.component.LifeCycle.Listener; -import org.eclipse.jetty.webapp.WebAppContext; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.redhat.thermostat.common.ApplicationInfo; -import com.redhat.thermostat.host.cpu.common.CpuStatDAO; -import com.redhat.thermostat.host.cpu.common.model.CpuStat; -import com.redhat.thermostat.storage.config.ConnectionConfiguration; -import com.redhat.thermostat.storage.config.StartupConfiguration; -import com.redhat.thermostat.storage.core.Add; -import com.redhat.thermostat.storage.core.BackingStorage; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.CategoryAdapter; -import com.redhat.thermostat.storage.core.Connection.ConnectionListener; -import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.DescriptorParsingException; -import com.redhat.thermostat.storage.core.IllegalDescriptorException; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.PreparedStatement; -import com.redhat.thermostat.storage.core.Remove; -import com.redhat.thermostat.storage.core.StatementDescriptor; -import com.redhat.thermostat.storage.core.StatementExecutionException; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.core.auth.DescriptorMetadata; -import com.redhat.thermostat.storage.dao.HostInfoDAO; -import com.redhat.thermostat.storage.model.AggregateCount; -import com.redhat.thermostat.storage.model.HostInfo; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.mongodb.internal.MongoStorage; -import com.redhat.thermostat.storage.query.Expression; -import com.redhat.thermostat.storage.query.ExpressionFactory; -import com.redhat.thermostat.test.FreePortFinder; -import com.redhat.thermostat.test.FreePortFinder.TryPort; -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; -import com.redhat.thermostat.vm.classstat.common.model.VmClassStat; -import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; -import com.redhat.thermostat.web.client.internal.WebStorage; -import com.redhat.thermostat.web.server.auth.Roles; - -/** - * This test class starts up a mongod instance and a web storage instance - * (in jetty container) in front of that. Tests should make their own - * connection to the web storage, probably by making use of one of the - * getAndConnectStorage() method variants. - * - * Because the storage instance is shared among all of the tests, it is - * necessary to take precautions to avoid introducing data dependencies - * between tests. Such precautions could include: using a different - * category (ie mongod collection) than any other existing test; setting - * a unique agent-id for all data written and then deleting the data - * at the end of the test; <insert other clever idea here>. - * - * Please don't introduce any more sporadic test failures to this - * integration test!!! - */ -public class WebAppTest extends IntegrationTest { - - /* - * Registry of descriptors this test needs to allow in order to avoid - * illegal statement descriptor exceptions being thrown. See also: - * WebAppTestStatementDescriptorRegistration - */ - public static final Set<String> TRUSTED_DESCRIPTORS; - /* - * Map which maps a string descriptor to DescriptorMetadata. - * See also: WebAppTestStatementDescriptorRegistration - * - */ - public static final Map<String, DescriptorMetadata> METADATA_MAPPING; - // descriptive name -> descriptor mapping - private static final Map<String, String> DESCRIPTOR_MAP; - - private static final String KEY_AUTHORIZED_QUERY = "authorizedQuery"; - private static final String KEY_AUTHORIZED_QUERY_EQUAL_TO = "authorizedQueryEqualTo"; - private static final String KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO = "authorizedQueryNotEqualTo"; - private static final String KEY_AUTHORIZED_QUERY_GREATER_THAN = "authorizedQueryGreaterThan"; - private static final String KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO = "authorizedQueryGreaterThanOrEqualTo"; - private static final String KEY_AUTHORIZED_QUERY_LESS_THAN = "authorizedQueryLessThan"; - private static final String KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO = "authorizedQueryLessThanOrEqualTo"; - private static final String KEY_AUTHORIZED_QUERY_NOT = "authorizedQueryNot"; - private static final String KEY_AUTHORIZED_QUERY_AND = "authorizedQueryAnd"; - private static final String KEY_AUTHORIZED_QUERY_OR = "authorizedQueryOr"; - private static final String KEY_STORAGE_PURGE = "storagePurge"; - private static final String KEY_AUTHORIZED_FILTERED_QUERY = "authorizedFilteredQuerySubset"; - - static { - Map<String, String> descMap = new HashMap<>(); - descMap.put(KEY_AUTHORIZED_FILTERED_QUERY, "QUERY agent-config"); - descMap.put(KEY_AUTHORIZED_QUERY, "QUERY cpu-stats SORT ?s ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' = ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' != ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_GREATER_THAN, "QUERY cpu-stats WHERE 'timeStamp' > ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' >= ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_LESS_THAN, "QUERY cpu-stats WHERE 'timeStamp' < ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO, "QUERY cpu-stats WHERE 'timeStamp' <= ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_NOT, "QUERY cpu-stats WHERE NOT 'timeStamp' > ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_AND, "QUERY cpu-stats WHERE 'timeStamp' > 0 AND 'timeStamp' < ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_AUTHORIZED_QUERY_OR, "QUERY cpu-stats WHERE 'timeStamp' > ?l OR 'timeStamp' < ?l SORT 'timeStamp' ASC"); - descMap.put(KEY_STORAGE_PURGE, "QUERY vm-cpu-stats"); - Set<String> trustedDescriptors = new HashSet<>(); - Map<String, DescriptorMetadata> metadata = new HashMap<>(); - DescriptorMetadata descMetadata = new DescriptorMetadata(); - for (String val: descMap.values()) { - trustedDescriptors.add(val); - metadata.put(val, descMetadata); - } - TRUSTED_DESCRIPTORS = trustedDescriptors; - DESCRIPTOR_MAP = descMap; - METADATA_MAPPING = metadata; - } - - - private static class CountdownConnectionListener implements ConnectionListener { - - private final ConnectionStatus target; - private final CountDownLatch latch; - private final AtomicBoolean indicator; - - private CountdownConnectionListener(ConnectionStatus target, CountDownLatch latch, AtomicBoolean indicator) { - this.target = target; - this.latch = latch; - this.indicator = indicator; - } - - @Override - public void changed(ConnectionStatus newStatus) { - indicator.set(true); - assertEquals(target, newStatus); - latch.countDown(); - } - } - - private static class WebAppContextListener implements Listener { - private Throwable cause; - private boolean failed = false; - private final CountDownLatch contextStartedLatch; - private WebAppContextListener(CountDownLatch latch) { - this.contextStartedLatch = latch; - } - - @Override - public void lifeCycleStarting(LifeCycle event) { - // nothing - } - - @Override - public void lifeCycleStarted(LifeCycle event) { - contextStartedLatch.countDown(); - } - - @Override - public void lifeCycleFailure(LifeCycle event, Throwable cause) { - this.failed = true; - this.cause = cause; - contextStartedLatch.countDown(); - } - - @Override - public void lifeCycleStopping(LifeCycle event) { - // nothing - } - - @Override - public void lifeCycleStopped(LifeCycle event) { - // nothing - } - } - - private static final String TEST_USER = "testuser"; - private static final String TEST_PASSWORD = "testpassword"; - private static final String PREP_USER = "prepuser"; - private static final String PREP_PASSWORD = "preppassword"; - private static final double EQUALS_DELTA = 0.00000000000001; - private static final String THERMOSTAT_USERS_FILE = getConfigurationDir() + "/thermostat-users.properties"; - private static final String THERMOSTAT_ROLES_FILE = getConfigurationDir() + "/thermostat-roles.properties"; - private static final String VM_ID1 = "vmId1"; - private static final String VM_ID2 = "vmId2"; - private static final String VM_ID3 = "vmId3"; - - private static Server server; - private static int port; - private static Path backupUsers; - private static Path backupRoles; - - @BeforeClass - public static void setUpOnce() throws Exception { - startStorage(); - - backupUsers = Files.createTempFile("itest-backup-thermostat-users", ""); - backupRoles = Files.createTempFile("itest-backup-thermostat-roles", ""); - backupRoles.toFile().deleteOnExit(); - backupUsers.toFile().deleteOnExit(); - Files.copy(new File(THERMOSTAT_USERS_FILE).toPath(), backupUsers, StandardCopyOption.REPLACE_EXISTING); - Files.copy(new File(THERMOSTAT_ROLES_FILE).toPath(), backupRoles, StandardCopyOption.REPLACE_EXISTING); - - - // start the server, deploy the war - port = FreePortFinder.findFreePort(new TryPort() { - - @Override - public void tryPort(int port) throws Exception { - startServer(port); - } - }); - - addCpuData(4); - } - - @AfterClass - public static void tearDownOnce() throws Exception { - deleteCpuData(); - - server.stop(); - server.join(); - - stopStorage(); - - Files.copy(backupUsers, new File(THERMOSTAT_USERS_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); - Files.copy(backupRoles, new File(THERMOSTAT_ROLES_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); - } - - /* - * Queries tests use write operations to put things into storage. For them - * we don't want to go through the hassles of using prepared writes. Instead - * use mongo-storage directly (which is a BackingStorage). - */ - private static BackingStorage getAndConnectBackingStorage() { - String url = "mongodb://127.0.0.1:27518"; - StartupConfiguration config = new ConnectionConfiguration(url, "", ""); - BackingStorage storage = new MongoStorage(config); - storage.getConnection().connect(); - return storage; - } - - /* - * Using the given username and password, set up a user for JAAS in the web app, - * with the given roles, and make a storage connection to the web app (returning - * the Storage object). - */ - private static Storage getAndConnectStorage(String username, String password, - String[] roleNames) throws IOException { - return getAndConnectStorage(username, password, roleNames, null); - } - - /* - * Using the given username and password, set up a user for JAAS in the web app, - * with the given roles, and make a connection to the web app (returning the - * Storage object). Before initiating the connection, add the ConnectionListener - * to Storage. - */ - private static Storage getAndConnectStorage(String username, String password, - String[] roleNames, - ConnectionListener listener) throws IOException { - setupJAASForUser(roleNames, username, password); - String url = "http://localhost:" + port + "/thermostat/storage"; - StartupConfiguration config = new ConnectionConfiguration(url, username, password); - Storage storage = new WebStorage(config); - if (listener != null) { - storage.getConnection().addListener(listener); - } - storage.getConnection().connect(); - return storage; - } - - private static void setupJAASForUser(String[] roleNames, String user, - String password) throws IOException { - Properties userProps = new Properties(); - userProps.put(user, password); - Properties roleProps = new Properties(); - StringBuffer roles = new StringBuffer(); - for (int i = 0; i < roleNames.length - 1; i++) { - roles.append(roleNames[i] + ", "); - } - roles.append(roleNames[roleNames.length - 1]); - roleProps.put(user, roles.toString()); - writeThermostatUsersRolesFile(userProps, roleProps); - } - - private static void writeThermostatUsersRolesFile(Properties usersContent, Properties rolesContent) throws IOException { - File thermostatUsers = new File(THERMOSTAT_USERS_FILE); - File thermostatRoles = new File(THERMOSTAT_ROLES_FILE); - try (FileOutputStream usersStream = new FileOutputStream(thermostatUsers)) { - usersContent.store(usersStream, "integration-test users"); - } - try (FileOutputStream rolesStream = new FileOutputStream(thermostatRoles)) { - rolesContent.store(rolesStream, "integration-test roles"); - } - } - - private static void startServer(int port) throws Exception { - final CountDownLatch contextStartedLatch = new CountDownLatch(1); - server = new Server(port); - ApplicationInfo appInfo = new ApplicationInfo(); - String version = appInfo.getMavenVersion(); - String warfile = "target/libs/thermostat-web-war-" + version + ".war"; - WebAppContext ctx = new WebAppContext(warfile, "/thermostat"); - - // We need to set this to true in order for WebStorageEndPoint to pick - // up the descriptor registrations from WebAppTestStatementDescriptorRegistration - // which would result in - // "java.util.ServiceConfigurationError: com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration: Provider com.redhat.thermostat.itest.WebAppTestStatementDescriptorRegistration not a subtype" - // errors. - ctx.setParentLoaderPriority(true); - - WebAppContextListener listener = new WebAppContextListener(contextStartedLatch); - ctx.addLifeCycleListener(listener); - /* The web archive has a jetty-web.xml config file which sets up the - * JAAS config. If done in code, this would look like this: - * - * JAASLoginService loginS = new JAASLoginService(); - * loginS.setLoginModuleName("ThermostatJAASLogin"); - * loginS.setName("Thermostat Realm"); - * loginS.setRoleClassNames(new String[] { - * WrappedRolePrincipal.class.getName(), - * RolePrincipal.class.getName(), - * UserPrincipal.class.getName() - * }); - * ctx.getSecurityHandler().setLoginService(loginS); - * - */ - server.setHandler(ctx); - server.start(); - // wait for context to start - contextStartedLatch.await(); - if (listener.failed) { - throw new IllegalStateException(listener.cause); - } - } - - private static void addCpuData(int numberOfItems) throws IOException { - BackingStorage storage = getAndConnectBackingStorage(); - storage.registerCategory(CpuStatDAO.cpuStatCategory); - - for (int i = 0; i < numberOfItems; i++) { - CpuStat pojo = new CpuStat("test-agent-id", i, new double[] {i, i*2}); - Add<CpuStat> add = storage.createAdd(CpuStatDAO.cpuStatCategory); - add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); - add.set(CpuStatDAO.cpuLoadKey.getName(), pojo.getPerProcessorUsage()); - add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); - add.apply(); - } - - storage.getConnection().disconnect(); - } - - private static void addHostInfoData(int numberOfItems) throws IOException { - BackingStorage storage = getAndConnectBackingStorage(); - storage.registerCategory(HostInfoDAO.hostInfoCategory); - - for (int i = 0; i < numberOfItems; i++) { - HostInfo hostInfo = new HostInfo("test-host-agent-id", "foo " + i, "linux " + i, "kernel", "t8", i, i * 1000); - Add<HostInfo> add = storage.createAdd(HostInfoDAO.hostInfoCategory); - add.set(Key.AGENT_ID.getName(), hostInfo.getAgentId()); - add.set(HostInfoDAO.hostNameKey.getName(), hostInfo.getHostname()); - add.set(HostInfoDAO.cpuCountKey.getName(), hostInfo.getCpuCount()); - add.set(HostInfoDAO.cpuModelKey.getName(), hostInfo.getCpuModel()); - add.set(HostInfoDAO.hostMemoryTotalKey.getName(), hostInfo.getTotalMemory()); - add.set(HostInfoDAO.osKernelKey.getName(), hostInfo.getOsKernel()); - add.set(HostInfoDAO.osNameKey.getName(), hostInfo.getOsName()); - add.apply(); - } - - storage.getConnection().disconnect(); - } - - private static void addAgentConfigData(List<AgentInformation> items) throws IOException { - BackingStorage storage = getAndConnectBackingStorage(); - storage.registerCategory(AgentInfoDAO.CATEGORY); - - for (AgentInformation info: items) { - Add<AgentInformation> add = storage.createAdd(AgentInfoDAO.CATEGORY); - add.set(Key.AGENT_ID.getName(), info.getAgentId()); - add.set(AgentInfoDAO.ALIVE_KEY.getName(), info.isAlive()); - add.set(AgentInfoDAO.CONFIG_LISTEN_ADDRESS.getName(), info.getConfigListenAddress()); - add.set(AgentInfoDAO.START_TIME_KEY.getName(), info.getStartTime()); - add.set(AgentInfoDAO.STOP_TIME_KEY.getName(), info.getStopTime()); - add.apply(); - } - - storage.getConnection().disconnect(); - } - - private static void deleteCpuData() throws IOException { - doDeleteData(CpuStatDAO.cpuStatCategory, "test-agent-id"); - } - - private static void deleteHostInfoData() throws IOException { - doDeleteData(HostInfoDAO.hostInfoCategory, "test-host-agent-id"); - } - - private static void doDeleteData(Category<?> category, String agentId) throws IOException { - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.ACCESS_REALM, - Roles.LOGIN, - Roles.PURGE - }; - Storage storage = getAndConnectStorage(PREP_USER, PREP_PASSWORD, roleNames); - storage.registerCategory(category); - storage.purge(agentId); - storage.getConnection().disconnect(); - } - - private static void deleteAgentConfigData(List<AgentInformation> items) throws IOException { - BackingStorage storage = getAndConnectBackingStorage(); - storage.registerCategory(AgentInfoDAO.CATEGORY); - ExpressionFactory factory = new ExpressionFactory(); - Remove<AgentInformation> remove = storage.createRemove(AgentInfoDAO.CATEGORY); - Set<String> agentIds = new HashSet<>(); - for (AgentInformation info: items) { - agentIds.add(info.getAgentId()); - } - Expression expression = factory.in(Key.AGENT_ID, agentIds, String.class); - remove.where(expression); - remove.apply(); - - storage.getConnection().disconnect(); - } - - private void executeAndVerifyQuery(PreparedStatement<CpuStat> query, List<Long> expectedTimestamps) throws StatementExecutionException { - Cursor<CpuStat> cursor = query.executeQuery(); - - for (Long time : expectedTimestamps) { - assertTrue(cursor.hasNext()); - CpuStat pojo = cursor.next(); - assertEquals("test-agent-id", pojo.getAgentId()); - assertEquals(time.longValue(), pojo.getTimeStamp()); - double[] data = pojo.getPerProcessorUsage(); - assertEquals(time, data[0], EQUALS_DELTA); - assertEquals(time*2, data[1], EQUALS_DELTA); - } - assertFalse(cursor.hasNext()); - } - - @Test - public void authorizedPreparedAdd() throws Exception { - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.WRITE, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - }; - - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); - - // This is the same descriptor as VmClassStatDAOImpl uses. It also - // gets registered automatically for that reason, no need to do it - // manually for this test. - String strDesc = "ADD vm-class-stats SET 'agentId' = ?s , " + - "'vmId' = ?s , " + - "'timeStamp' = ?l , " + - "'loadedClasses' = ?l"; - StatementDescriptor<VmClassStat> desc = new StatementDescriptor<>(VmClassStatDAO.vmClassStatsCategory, strDesc); - VmClassStat pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(12345); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID1); - PreparedStatement<VmClassStat> add; - add = webStorage.prepareStatement(desc); - addPreparedVmClassStat(pojo, add); - - // Add another couple of entries - pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(67890); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID2); - addPreparedVmClassStat(pojo, add); - - pojo = new VmClassStat(); - pojo.setAgentId("fluff"); - pojo.setLoadedClasses(34567); - pojo.setTimeStamp(42); - pojo.setVmId(VM_ID3); - addPreparedVmClassStat(pojo, add); - - webStorage.getConnection().disconnect(); - } - - private void addPreparedVmClassStat(VmClassStat pojo, - PreparedStatement<VmClassStat> add) - throws StatementExecutionException { - add.setString(0, pojo.getAgentId()); - add.setString(1, pojo.getVmId()); - add.setLong(2, pojo.getTimeStamp()); - add.setLong(3, pojo.getLoadedClasses()); - add.execute(); - } - - /* - * Tests whether a query only returns results which a user is allowed to see. - * - * In particular, multiple agent-config records available in the DB, but - * only a subset are allowed to be seen by the user. - */ - @Test - public void authorizedFilteredQuerySubset() throws Exception { - // add agent records into the DB - List<AgentInformation> items = Collections.emptyList(); - try { - String agentIdGrantPrefix = "thermostat-agents-grant-read-agentId-"; - String agent1Id = "agent1"; - String agent2Id = "agent2"; - items = getAgentInformationItemsIncluding(new String[] { agent1Id, agent2Id }); - // assert pre-condition. records in db need to be more than expected - // result set size. - assertTrue(items.size() > 2); - addAgentConfigData(items); - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - // Grant read access only for "agent1" and "agent2" agend IDs - agentIdGrantPrefix + agent1Id, - agentIdGrantPrefix + agent2Id - }; - - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(AgentInfoDAO.CATEGORY); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_FILTERED_QUERY); - StatementDescriptor<AgentInformation> queryDesc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); - PreparedStatement<AgentInformation> query = webStorage.prepareStatement(queryDesc); - Cursor<AgentInformation> cursor = query.executeQuery(); - assertTrue(cursor.hasNext()); - List<AgentInformation> actual = new ArrayList<>(); - while (cursor.hasNext()) { - AgentInformation info = cursor.next(); - actual.add(info); - } - assertEquals(2, actual.size()); - assertFalse("Returned agentIds should be different!", actual.get(0).getAgentId().equals(actual.get(1).getAgentId())); - for (AgentInformation info: actual) { - assertTrue(info.getAgentId().equals(agent1Id) || info.getAgentId().equals(agent2Id)); - } - } finally { - deleteAgentConfigData(items); - } - } - - - private List<AgentInformation> getAgentInformationItemsIncluding( - String[] includeItems) { - List<AgentInformation> infos = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - String agentId = UUID.randomUUID().toString() + "--" + i; - if (i < includeItems.length) { - agentId = includeItems[i]; - } - AgentInformation info = new AgentInformation(); - info.setAgentId(agentId); - info.setAlive((i % 2) == 0); - info.setConfigListenAddress("127.0.0." + i + ":88888"); - info.setStartTime(i * 300); - info.setStopTime((i + 1) * 400); - infos.add(info); - } - return infos; - } - - /* - * Tests whether no query results are returned for a user which lacks *any* - * granting roles for reads. - */ - @Test - public void authorizedFilteredQueryNone() throws Exception { - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, // this is just the stop-gap role. - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - // lacking read grant roles - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - - query.setString(0, "timeStamp"); - // Note: with read-all granted, this returns 4 records. See authorized - // query test. - // For this test, however, it should come back empty. - - Cursor<CpuStat> cursor = query.executeQuery(); - assertFalse(cursor.hasNext()); - try { - cursor.next(); - fail("cursor should have thrown exception!"); - } catch (NoSuchElementException e) { - // pass - } - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQuery() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - - query.setString(0, "timeStamp"); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l, 3l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedAggregateCount() throws Exception { - try { - int count = 2; - // registers host info category - addHostInfoData(count); - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL // don't want to test filtered results - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - Category<AggregateCount> adapted = new CategoryAdapter<HostInfo, AggregateCount>(HostInfoDAO.hostInfoCategory).getAdapted(AggregateCount.class); - // register non-adapted + adapted category in that order. Adapted - // category needs to be registered, since it gets it's own mapped id - webStorage.registerCategory(HostInfoDAO.hostInfoCategory); - webStorage.registerCategory(adapted); - - // storage-core registers this descriptor. no need to do it in this - // test. - String strDesc = "QUERY-COUNT host-info"; - StatementDescriptor<AggregateCount> queryDesc = new StatementDescriptor<>(adapted, strDesc); - PreparedStatement<AggregateCount> query = webStorage.prepareStatement(queryDesc); - - Cursor<AggregateCount> cursor = query.executeQuery(); - assertTrue(cursor.hasNext()); - AggregateCount c = cursor.next(); - assertFalse(cursor.hasNext()); - assertEquals(count, c.getCount()); - - webStorage.getConnection().disconnect(); - } finally { - deleteHostInfoData(); - } - } - - @Test - public void authorizedQueryEqualTo() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_EQUAL_TO); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(2l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryNotEqualTo() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_NOT_EQUAL_TO); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 3l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryGreaterThan() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_GREATER_THAN); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(3l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryGreaterThanOrEqualTo() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_GREATER_THAN_OR_EQUAL_TO); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(2l, 3l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryLessThan() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_LESS_THAN); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryLessThanOrEqualTo() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_LESS_THAN_OR_EQUAL_TO); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryNot() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_NOT); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(0l, 1l, 2l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryAnd() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_AND); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2l); - - executeAndVerifyQuery(query, Arrays.asList(1l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedQueryOr() throws Exception { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT, - Roles.GRANT_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_AUTHORIZED_QUERY_OR); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - PreparedStatement<CpuStat> query = webStorage.prepareStatement(queryDesc); - query.setLong(0, 2); - query.setLong(1, 1); - - executeAndVerifyQuery(query, Arrays.asList(0l, 3l)); - - webStorage.getConnection().disconnect(); - } - - @Test - public void refuseUnknownQueryDescriptor() throws IOException { - - String[] roleNames = new String[] { - Roles.REGISTER_CATEGORY, - Roles.READ, - Roles.LOGIN, - Roles.ACCESS_REALM, - Roles.PREPARE_STATEMENT - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(CpuStatDAO.cpuStatCategory); - - String strDesc = "QUERY cpu-stats WHERE 'fooBarTest' = ?s"; - assertFalse("wanted this descriptor to be untrusted!", TRUSTED_DESCRIPTORS.contains(strDesc)); - StatementDescriptor<CpuStat> queryDesc = new StatementDescriptor<>(CpuStatDAO.cpuStatCategory, strDesc); - - try { - webStorage.prepareStatement(queryDesc); - } catch (IllegalDescriptorException e) { - // pass - String expectedMsg = "Unknown query descriptor which endpoint of com.redhat.thermostat.web.client.internal.WebStorage refused to accept!"; - assertEquals(expectedMsg, e.getMessage()); - } catch (DescriptorParsingException e) { - // should have been able to parse the descriptor - fail(e.getMessage()); - } - - webStorage.getConnection().disconnect(); - } - - @Test - public void authorizedLoadSave() throws Exception { - String[] roleNames = new String[] { - Roles.LOAD_FILE, - Roles.SAVE_FILE, - Roles.ACCESS_REALM, - Roles.LOGIN, - Roles.GRANT_FILES_WRITE_ALL, - Roles.GRANT_FILES_READ_ALL - }; - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - - byte[] data = "Hello World".getBytes(); - webStorage.saveFile("test", new ByteArrayInputStream(data)); - // Note: On the server side, the file is saved into mongodb - // via GridFS. The save operation returns before write is - // complete, and there is no callback mechanism to find out - // when the write is complete. So, we try a few times to - // load it before considering it a failure. - InputStream loadStream = null; - int loadAttempts = 0; - while (loadStream == null && loadAttempts < 3) { - Thread.sleep(300); - loadStream = webStorage.loadFile("test"); - loadAttempts++; - } - assertNotNull(loadStream); - StringBuilder str = new StringBuilder(); - int i = loadStream.read(); - while (i != -1) { - str.append((char) i); - i = loadStream.read(); - } - assertEquals("Hello World", str.toString()); - - webStorage.getConnection().disconnect(); - } - - @Test - public void unauthorizedLogin() throws Exception { - String[] roleNames = new String[] { - Roles.ACCESS_REALM - }; - - CountDownLatch statusLatch = new CountDownLatch(1); - AtomicBoolean listenerTriggered = new AtomicBoolean(false); - ConnectionListener listener = new CountdownConnectionListener(ConnectionStatus.FAILED_TO_CONNECT, statusLatch, listenerTriggered); - @SuppressWarnings("unused") - Storage storage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames, listener); - statusLatch.await(); - assertTrue(listenerTriggered.get()); - } - - @Test - public void storagePurge() throws Exception { - // Add some data to purge (uses backing storage) - UUID uuid = new UUID(42, 24); - long timeStamp = 5; - double cpuLoad = 0.15; - VmCpuStat pojo = new VmCpuStat(uuid.toString(), timeStamp, VM_ID1, cpuLoad); - addVmCpuStat(pojo); - - String[] roleNames = new String[] { - Roles.ACCESS_REALM, - Roles.LOGIN, - Roles.PURGE, - Roles.PREPARE_STATEMENT, - Roles.READ, - Roles.GRANT_READ_ALL, - Roles.REGISTER_CATEGORY - }; - - Storage webStorage = getAndConnectStorage(TEST_USER, TEST_PASSWORD, roleNames); - webStorage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); - - String strDesc = DESCRIPTOR_MAP.get(KEY_STORAGE_PURGE); - StatementDescriptor<VmCpuStat> queryDesc = new StatementDescriptor<>(VmCpuStatDAO.vmCpuStatCategory, strDesc); - PreparedStatement<VmCpuStat> query = webStorage.prepareStatement(queryDesc); - Cursor<VmCpuStat> cursor = query.executeQuery(); - assertTrue(cursor.hasNext()); - pojo = cursor.next(); - assertFalse(cursor.hasNext()); - - assertEquals(timeStamp, pojo.getTimeStamp()); - assertEquals(VM_ID1, pojo.getVmId()); - assertEquals(cpuLoad, pojo.getCpuLoad(), EQUALS_DELTA); - assertEquals(uuid.toString(), pojo.getAgentId()); - - webStorage.purge(uuid.toString()); - } - - private void addVmCpuStat(VmCpuStat pojo) { - BackingStorage storage = getAndConnectBackingStorage(); - storage.registerCategory(VmCpuStatDAO.vmCpuStatCategory); - Add<VmCpuStat> add = storage.createAdd(VmCpuStatDAO.vmCpuStatCategory); - add.set(Key.AGENT_ID.getName(), pojo.getAgentId()); - add.set(Key.VM_ID.getName(), pojo.getVmId()); - add.set(Key.TIMESTAMP.getName(), pojo.getTimeStamp()); - add.set(VmCpuStatDAO.vmCpuLoadKey.getName(), pojo.getCpuLoad()); - add.apply(); - storage.getConnection().disconnect(); - } -}
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTestStatementDescriptorRegistration.java Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.itest; - -import java.util.Set; - -import com.redhat.thermostat.storage.core.PreparedParameter; -import com.redhat.thermostat.storage.core.auth.DescriptorMetadata; -import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration; - -public class WebAppTestStatementDescriptorRegistration implements - StatementDescriptorRegistration { - - @Override - public Set<String> getStatementDescriptors() { - return WebAppTest.TRUSTED_DESCRIPTORS; - } - - @Override - public DescriptorMetadata getDescriptorMetadata(String descriptor, - PreparedParameter[] params) { - return WebAppTest.METADATA_MAPPING.get(descriptor); - } - -}
--- a/integration-tests/src/test/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration Wed Oct 16 13:19:53 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.redhat.thermostat.itest.WebAppTestStatementDescriptorRegistration \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration-tests/standalone/pom.xml Fri Apr 05 18:57:56 2013 +0200 @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2013 Red Hat, Inc. + + This file is part of Thermostat. + + Thermostat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + Thermostat 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 for more details. + + You should have received a copy of the GNU General Public License + along with Thermostat; see the file COPYING. If not see + <http://www.gnu.org/licenses />. + + Linking this code with other modules is making a combined work + based on this code. Thus, the terms and conditions of the GNU + General Public License cover the whole combination. + + As a special exception, the copyright holders of this code give + you permission to link this code with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also + meet, for each linked independent module, the terms and conditions + of the license of that module. An independent module is a module + which is not derived from or based on this code. If you modify + this code, you may extend this exception to your version of the + library, but you are not obligated to do so. If you do not wish + to do so, delete this exception statement from your version. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-integration-tests</artifactId> + <version>0.16.0-SNAPSHOT</version> + </parent> + + <artifactId>thermostat-integration-tests-standalone</artifactId> + <packaging>jar</packaging> + + <name>Thermostat Integration Tests (Standalone Package)</name> + + <build> + <plugins> + <!-- jacoco:report insists to have a target/classes dir. since this + module only contains test classes it won't exist after a build. + Hence, create manually in order to unbreak the build. For some + reason skipping both, report AND prepare-agent goals for the + jacoco plugin does not work. --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.6</version> + <executions> + <execution> + <id>make-target-classes-dir</id> + <phase>prepare-package</phase> + <configuration> + <target> + <mkdir dir="${project.build.directory}/classes" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <!-- skip unit tests. The itest-run module runs them if required --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <!-- As a downstream packaging aid bundle a jar of stand-alone + integration tests --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + <dependencies> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + <dependency> + <groupId>net.sourceforge.expectj</groupId> + <artifactId>expectj</artifactId> + </dependency> + + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-integration-tests-run</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + </dependency> + + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-core</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> +</project>
--- a/pom.xml Wed Oct 16 13:19:53 2013 -0400 +++ b/pom.xml Fri Apr 05 18:57:56 2013 +0200 @@ -209,6 +209,11 @@ <artifactId>maven-assembly-plugin</artifactId> <version>2.3</version> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.3</version> + </plugin> <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> <plugin> <groupId>org.eclipse.m2e</groupId>