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);
}
}
}
}