package org.rhq.storage.installer; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.util.Properties; import com.google.common.collect.ImmutableMap; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.PosixParser; import org.apache.commons.exec.Executor; import org.apache.commons.io.FileUtils; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.rhq.cassandra.CassandraClusterManager; import org.rhq.cassandra.Deployer; import org.rhq.cassandra.DeploymentException; import org.rhq.cassandra.util.ConfigEditor; import org.rhq.core.util.file.FileUtil; import org.rhq.core.util.stream.StreamUtil; /** * @author John Sanda */ public class StorageInstallerTest { private File basedir; private File serverDir; private File storageDir; private StorageInstaller installer; @BeforeMethod public void initDirs(Method test) throws Exception { File dir = new File(getClass().getResource(".").toURI()); basedir = new File(dir, getClass().getSimpleName() + "/" + test.getName()); FileUtil.purge(basedir, true); basedir.mkdirs(); serverDir = new File(basedir, "rhq-server"); System.setProperty("rhq.server.basedir", serverDir.getAbsolutePath()); File serverPropsFile = new File(serverDir, "rhq-server.properties"); FileUtils.touch(serverPropsFile); System.setProperty("rhq.server.properties-file", serverPropsFile.getAbsolutePath()); storageDir = new File(serverDir, "rhq-storage"); installer = new SafeStorageInstaller(); } @AfterMethod(alwaysRun = true) public void shutdownStorageNode() throws Exception { if (FileUtils.getFile(storageDir, "bin", "cassandra.pid").exists()) { CassandraClusterManager ccm = new CassandraClusterManager(); ccm.killNode(storageDir); Thread.sleep(1000); } } @Test public void performDefaultInstall() throws Exception { CommandLineParser parser = new PosixParser(); CommandLine cmdLine = parser.parse(installer.getOptions(), new String[] {}); int status = installer.run(cmdLine); String address = InetAddress.getLocalHost().getHostName(); assertEquals(status, 0, "Expected to get back a status code of 0 for a successful default install"); assertNodeIsRunning(); assertRhqServerPropsUpdated(address); File binDir = new File(storageDir, "bin"); assertTrue(binDir.exists(), "Expected to find bin directory at " + binDir); File confDir = new File(storageDir, "conf"); assertTrue(confDir.exists(), "Expected to find conf directory at " + confDir); File libDir = new File(storageDir, "lib"); assertTrue(libDir.exists(), "Expected to find lib directory at " + libDir); File baseDataDir = new File(basedir, "rhq-data"); File commitLogDir = new File(baseDataDir, "commit_log"); assertTrue(commitLogDir.exists(), "Expected to find commit_log directory at " + commitLogDir); File dataDir = new File(baseDataDir, "data"); assertTrue(dataDir.exists(), "Expected to find data directory at " + dataDir); File savedCachesDir = new File(baseDataDir, "saved_caches"); assertTrue(savedCachesDir.exists(), "Expected to find saved_caches directory at " + savedCachesDir); File log4jFile = new File(confDir, "log4j-server.properties"); assertTrue(log4jFile.exists(), log4jFile + " does not exist"); Properties log4jProps = new Properties(); log4jProps.load(new FileInputStream(log4jFile)); assertEquals(log4jProps.getProperty("log4j.appender.R.File"), StorageInstaller.STORAGE_LOG_FILE_PATH, "The log file is wrong"); File yamlFile = new File(confDir, "cassandra.yaml"); ConfigEditor yamlEditor = new ConfigEditor(yamlFile); yamlEditor.load(); assertEquals(yamlEditor.getInternodeAuthenticator(), "org.rhq.cassandra.auth.RhqInternodeAuthenticator", "Failed to set the internode_authenticator property in " + yamlFile); assertEquals(yamlEditor.getAuthenticator(), "org.apache.cassandra.auth.PasswordAuthenticator", "The authenticator property is wrong"); assertEquals(yamlEditor.getListenAddress(), address, "The listen_address property is wrong"); assertEquals(yamlEditor.getNativeTransportPort(), (Integer) 9142, "The native_transport_port property is wrong"); assertEquals(yamlEditor.getRpcAddress(), address, "The rpc_address property is wrong"); assertEquals(yamlEditor.getStoragePort(), (Integer) 7100, "The storage_port property is wrong"); File cassandraJvmPropsFile = new File(confDir, "cassandra-jvm.properties"); Properties properties = new Properties(); properties.load(new FileInputStream(cassandraJvmPropsFile)); assertEquals(properties.getProperty("jmx_port"), "7299", "The jmx_port property is wrong"); assertEquals(properties.getProperty("heap_min"), "-Xms512M", "The heap_min property is wrong"); assertEquals(properties.getProperty("heap_max"), "-Xmx512M", "The heap_max property is wrong"); assertEquals(properties.getProperty("heap_new"), "-Xmn128M", "The heap_new property is wrong"); assertEquals(properties.getProperty("thread_stack_size"), "-Xss256k", "The thread_stack_size property is wrong"); } @Test(dependsOnMethods = "performDefaultInstall") public void upgradeFromDefaultInstall() throws Exception { CommandLineParser parser = new PosixParser(); File defaultInstallDir = new File(basedir.getParentFile(), "performDefaultInstall"); File upgradeFromServerDir = new File(defaultInstallDir, "rhq-server"); // add --no-version-stamp because the test has no support for interacting with a DB String[] args = { "--upgrade", upgradeFromServerDir.getAbsolutePath(), "--no-version-stamp" }; CommandLine cmdLine = parser.parse(installer.getOptions(), args); int status = installer.run(cmdLine); assertEquals(status, 0, "Expected to get back a status code of 0 for a successful default upgrade"); String address = InetAddress.getLocalHost().getHostName(); assertNodeIsRunning(); assertRhqServerPropsUpdated(address); File binDir = new File(storageDir, "bin"); assertTrue(binDir.exists(), "Expected to find bin directory at " + binDir); File confDir = new File(storageDir, "conf"); assertTrue(confDir.exists(), "Expected to find conf directory at " + confDir); File libDir = new File(storageDir, "lib"); assertTrue(libDir.exists(), "Expected to find lib directory at " + libDir); File baseDataDir = new File(defaultInstallDir, "rhq-data"); File commitLogDir = new File(baseDataDir, "commit_log"); assertTrue(commitLogDir.exists(), "Expected to find commit_log directory at " + commitLogDir); File dataDir = new File(baseDataDir, "data"); assertTrue(dataDir.exists(), "Expected to find data directory at " + dataDir); File savedCachesDir = new File(baseDataDir, "saved_caches"); assertTrue(savedCachesDir.exists(), "Expected to find saved_caches directory at " + savedCachesDir); File log4jFile = new File(confDir, "log4j-server.properties"); assertTrue(log4jFile.exists(), log4jFile + " does not exist"); File logsDir = new File(serverDir, "logs"); File logFile = new File(logsDir, "rhq-storage.log"); Properties log4jProps = new Properties(); log4jProps.load(new FileInputStream(log4jFile)); assertEquals(log4jProps.getProperty("log4j.appender.R.File"), StorageInstaller.STORAGE_LOG_FILE_PATH, "The log file is wrong"); File yamlFile = new File(confDir, "cassandra.yaml"); ConfigEditor yamlEditor = new ConfigEditor(yamlFile); yamlEditor.load(); assertEquals(yamlEditor.getInternodeAuthenticator(), "org.rhq.cassandra.auth.RhqInternodeAuthenticator", "Failed to set the internode_authenticator property in " + yamlFile); assertEquals(yamlEditor.getAuthenticator(), "org.apache.cassandra.auth.PasswordAuthenticator", "The authenticator property is wrong"); assertEquals(yamlEditor.getListenAddress(), address, "The listen_address property is wrong"); assertEquals(yamlEditor.getNativeTransportPort(), (Integer) 9142, "The native_transport_port property is wrong"); assertEquals(yamlEditor.getRpcAddress(), address, "The rpc_address property is wrong"); assertEquals(yamlEditor.getStoragePort(), (Integer) 7100, "The storage_port property is wrong"); File cassandraJvmPropsFile = new File(confDir, "cassandra-jvm.properties"); Properties properties = new Properties(); properties.load(new FileInputStream(cassandraJvmPropsFile)); assertEquals(properties.getProperty("jmx_port"), "7299", "The jmx_port property is wrong"); assertEquals(properties.getProperty("heap_min"), "-Xms512M", "The heap_min property is wrong"); assertEquals(properties.getProperty("heap_max"), "-Xmx512M", "The heap_max property is wrong"); assertEquals(properties.getProperty("heap_new"), "-Xmn128M", "The heap_new property is wrong"); assertEquals(properties.getProperty("thread_stack_size"), "-Xss256k", "The thread_stack_size property is wrong"); } @Test public void performValidInstallWithOutputToStderr() throws Exception { installer = new SafeStorageInstaller() { @Override protected void exec(Executor executor, org.apache.commons.exec.CommandLine cmdLine) throws IOException { executor.execute(cmdLine, ImmutableMap.of("JAVA_TOOL_OPTIONS", "-Dfile.encoding=UTF8")); } }; System.setProperty("-Dfile.encoding", "UTF8"); CommandLineParser parser = new PosixParser(); CommandLine cmdLine = parser.parse(installer.getOptions(), new String[] {}); int status = installer.run(cmdLine); assertEquals(status, 0, "A zero status code should be returned even when the storage node writes to stderr."); } @Test public void installWithJMXPortConflict() throws Exception { ServerSocket serverSocket = null; try { String address = InetAddress.getLocalHost().getHostAddress(); serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(address, 7799)); CommandLineParser parser = new PosixParser(); CommandLine cmdLine = parser.parse(installer.getOptions(), new String[] { "--rhq.storage.jmx-port", "7799" }); int status = installer.run(cmdLine); assertEquals(status, StorageInstaller.STATUS_JMX_PORT_CONFLICT, "The status code is wrong"); } finally { if (serverSocket != null) { serverSocket.close(); } } } @Test public void installWithCQLPortConflict() throws Exception { ServerSocket serverSocket = null; try { String address = InetAddress.getLocalHost().getHostAddress(); serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(address, 9342)); CommandLineParser parser = new PosixParser(); CommandLine cmdLine = parser.parse(installer.getOptions(), new String[] { "--rhq.storage.cql-port", "9342" }); int status = installer.run(cmdLine); assertEquals(status, StorageInstaller.STATUS_CQL_PORT_CONFLICT, "The status code is wrong"); } finally { if (serverSocket != null) { serverSocket.close(); } } } @Test public void performValidInstall() throws Exception { CommandLineParser parser = new PosixParser(); String[] args = { "--dir", storageDir.getAbsolutePath(), "--rhq.storage.commitlog", new File(storageDir, "commit_log").getAbsolutePath(), "--rhq.storage.data", new File(storageDir, "data").getAbsolutePath(), "--rhq.storage.saved-caches", new File(storageDir, "saved_caches").getAbsolutePath(), "--rhq.storage.heap-size", "256M", "--rhq.storage.heap-new-size", "64M", "--rhq.storage.hostname", "127.0.0.1" }; CommandLine cmdLine = parser.parse(installer.getOptions(), args); int status = installer.run(cmdLine); assertEquals(status, 0, "Expected to get back a status code of 0 for a successful install"); assertNodeIsRunning(); assertRhqServerPropsUpdated("127.0.0.1"); File binDir = new File(storageDir, "bin"); assertTrue(binDir.exists(), "Expected to find bin directory at " + binDir); File confDir = new File(storageDir, "conf"); assertTrue(confDir.exists(), "Expected to find conf directory at " + confDir); File libDir = new File(storageDir, "lib"); assertTrue(libDir.exists(), "Expected to find lib directory at " + libDir); File commitLogDir = new File(storageDir, "commit_log"); assertTrue(commitLogDir.exists(), "Expected to find commit_log directory at " + commitLogDir); File dataDir = new File(storageDir, "data"); assertTrue(dataDir.exists(), "Expected to find data directory at " + dataDir); File savedCachesDir = new File(storageDir, "saved_caches"); assertTrue(savedCachesDir.exists(), "Expected to find saved_caches directory at " + savedCachesDir); } @Test public void upgradeFromRHQ48Install() throws Exception { File rhq48ServerDir = new File(basedir, "rhq48-server"); File rhq48StorageDir = new File(rhq48ServerDir, "rhq-storage"); File rhq48StorageConfDir = new File(rhq48StorageDir, "conf"); File oldCassandraYamlFile = new File(rhq48StorageConfDir, "cassandra.yaml"); File oldCassandraEnvFile = new File(rhq48StorageConfDir, "cassandra-env.sh"); File oldLog4JFile = new File(rhq48StorageConfDir, "log4j-server.properties"); rhq48StorageConfDir.mkdirs(); StreamUtil.copy(getClass().getResourceAsStream("/rhq48/storage/conf/cassandra.yaml"), new FileOutputStream( oldCassandraYamlFile), true); StreamUtil.copy(getClass().getResourceAsStream("/rhq48/storage/conf/cassandra-env.sh"), new FileOutputStream( oldCassandraEnvFile)); StreamUtil.copy(getClass().getResourceAsStream("/rhq48/storage/conf/log4j-server.properties"), new FileOutputStream(oldLog4JFile)); CommandLineParser parser = new PosixParser(); // add --no-version-stamp because the test has no support for interacting with a DB String[] args = { "--upgrade", rhq48ServerDir.getAbsolutePath(), "--dir", storageDir.getAbsolutePath(), "--no-version-stamp" }; CommandLine cmdLine = parser.parse(installer.getOptions(), args); int status = installer.run(cmdLine); assertEquals(status, 0, "Expected to get back a status code of 0 for a successful upgrade"); assertNodeIsRunning(); File binDir = new File(storageDir, "bin"); assertTrue(binDir.exists(), "Expected to find bin directory at " + binDir); File libDir = new File(storageDir, "lib"); assertTrue(libDir.exists(), "Expected to find lib directory at " + libDir); File confDir = new File(storageDir, "conf"); assertTrue(confDir.exists(), "Expected to find conf directory at " + confDir); File newCassandraYamlFile = new File(confDir, "cassandra.yaml"); assertTrue(newCassandraYamlFile.exists(), newCassandraYamlFile + " does not exist"); File newLog4JFile = new File(confDir, "log4j-server.properties"); assertTrue(newLog4JFile.exists(), newLog4JFile + " does not exist"); File logsDir = new File(serverDir, "logs"); File logFile = new File(logsDir, "rhq-storage.log"); Properties log4jProps = new Properties(); log4jProps.load(new FileInputStream(newLog4JFile)); assertEquals(log4jProps.getProperty("log4j.appender.R.File"), StorageInstaller.STORAGE_LOG_FILE_PATH, "The log file is wrong"); assertFalse(new File(confDir, "cassandra-env.sh").exists(), "cassandra-env.sh should not be used after RHQ 4.8.0"); File cassandraJvmPropsFile = new File(confDir, "cassandra-jvm.properties"); Properties properties = new Properties(); properties.load(new FileInputStream(cassandraJvmPropsFile)); // If this check fails, make sure that the expected value matches the value in // src/test/resources/rhq48/storage/conf/cassandra-env.sh assertEquals(properties.getProperty("jmx_port"), "7399", "Failed to update the JMX port in " + cassandraJvmPropsFile); File yamlFile = new File(confDir, "cassandra.yaml"); ConfigEditor newYamlEditor = new ConfigEditor(yamlFile); newYamlEditor.load(); ConfigEditor oldYamlEditor = new ConfigEditor(oldCassandraYamlFile); oldYamlEditor.load(); assertEquals(newYamlEditor.getInternodeAuthenticator(), "org.rhq.cassandra.auth.RhqInternodeAuthenticator", "Failed to set the internode_authenticator property in " + yamlFile); assertEquals(newYamlEditor.getAuthenticator(), oldYamlEditor.getAuthenticator(), "The authenticator property " + "is wrong"); assertEquals(newYamlEditor.getCommitLogDirectory(), oldYamlEditor.getCommitLogDirectory(), "The commit_log property is wrong"); assertEquals(newYamlEditor.getDataFileDirectories(), oldYamlEditor.getDataFileDirectories(), "The data_files property is wrong"); assertEquals(newYamlEditor.getListenAddress(), oldYamlEditor.getListenAddress(), "The listen_address property is wrong"); assertEquals(newYamlEditor.getNativeTransportPort(), oldYamlEditor.getNativeTransportPort(), "The native_transport_port property is wrong"); assertEquals(newYamlEditor.getRpcAddress(), oldYamlEditor.getRpcAddress(), "The rpc_address property is wrong"); assertEquals(newYamlEditor.getSavedCachesDirectory(), oldYamlEditor.getSavedCachesDirectory(), "The saved_caches_directory property is wrong"); assertEquals(newYamlEditor.getStoragePort(), oldYamlEditor.getStoragePort(), "The storage_port property is wrong"); } private void assertNodeIsRunning() { try { installer.verifyNodeIsUp(7299, 3, 1000); } catch (Exception e) { fail("Failed to verify that node is up", e); } } private void assertRhqServerPropsUpdated() { assertRhqServerPropsUpdated("localhost"); } private void assertRhqServerPropsUpdated(String address) { File serverPropsFile = new File(serverDir, "rhq-server.properties"); Properties properties = new Properties(); try { properties.load(new FileInputStream(serverPropsFile)); } catch (IOException e) { fail("Failed to verify that " + serverPropsFile + " was updated", e); } assertEquals(properties.getProperty("rhq.storage.nodes"), address); assertEquals(properties.getProperty("rhq.storage.cql-port"), "9142"); } private static class SafeStorageInstaller extends StorageInstaller { @Override protected Deployer getDeployer() { return new SafeDeployer(); } } private static class SafeDeployer extends Deployer { @Override public void applyChangesToWindowsServiceWrapper(File deployDir) throws DeploymentException { // Not every test env will have this file set up. If it doesn't exist just skip this. File wrapperDir = new File(deployDir, "../bin/wrapper"); File wrapperEnvFile = new File(wrapperDir, "rhq-storage-wrapper.env"); if (wrapperEnvFile.isFile()) { super.applyChangesToWindowsServiceWrapper(deployDir); } } } }