blob: 7eccfed0ef2ec580208dd136e96a808c2dd8e8aa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.accumulo.minicluster;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
import org.apache.accumulo.core.cli.Help;
import org.apache.accumulo.core.util.Pair;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.Parameter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* A runner for starting up a {@link MiniAccumuloCluster} from the command line using an optional
* configuration properties file. An example property file looks like the following:
*
* <pre>
* rootPassword=secret
* instanceName=testInstance
* numTServers=1
* zooKeeperPort=3191
* jdwpEnabled=true
* zooKeeperMemory=128M
* tserverMemory=256M
* masterMemory=128M
* defaultMemory=256M
* shutdownPort=4446
* site.instance.secret=HUSH
* </pre>
*
* All items in the properties file above are optional and a default value will be provided in their
* absence. Any site configuration properties (typically found in the accumulo.properties file)
* should be prefixed with "site." in the properties file.
*
* @since 1.6.0
*/
public class MiniAccumuloRunner {
private static final Logger log = LoggerFactory.getLogger(MiniAccumuloRunner.class);
private static final String ROOT_PASSWORD_PROP = "rootPassword";
private static final String SHUTDOWN_PORT_PROP = "shutdownPort";
private static final String DEFAULT_MEMORY_PROP = "defaultMemory";
@Deprecated(since = "2.1.0")
private static final String MASTER_MEMORY_PROP = "masterMemory";
private static final String MANAGER_MEMORY_PROP = "managerMemory";
private static final String TSERVER_MEMORY_PROP = "tserverMemory";
private static final String ZOO_KEEPER_MEMORY_PROP = "zooKeeperMemory";
private static final String JDWP_ENABLED_PROP = "jdwpEnabled";
private static final String ZOO_KEEPER_PORT_PROP = "zooKeeperPort";
private static final String ZOO_KEEPER_STARTUP_TIME_PROP = "zooKeeperStartupTime";
private static final String NUM_T_SERVERS_PROP = "numTServers";
private static final String DIRECTORY_PROP = "directory";
private static final String INSTANCE_NAME_PROP = "instanceName";
private static final String EXISTING_ZOO_KEEPERS_PROP = "existingZooKeepers";
private static void printProperties() {
System.out.println("#mini Accumulo cluster runner properties.");
System.out.println("#");
System.out.println("#uncomment following propeties to use, propeties not"
+ " set will use default or random value");
System.out.println();
System.out.println("#" + INSTANCE_NAME_PROP + "=devTest");
System.out.println("#" + DIRECTORY_PROP + "=/tmp/mac1");
System.out.println("#" + ROOT_PASSWORD_PROP + "=secret");
System.out.println("#" + NUM_T_SERVERS_PROP + "=2");
System.out.println("#" + ZOO_KEEPER_PORT_PROP + "=40404");
System.out.println("#" + ZOO_KEEPER_STARTUP_TIME_PROP + "=39000");
System.out.println("#" + SHUTDOWN_PORT_PROP + "=41414");
System.out.println("#" + DEFAULT_MEMORY_PROP + "=128M");
System.out.println("#" + MANAGER_MEMORY_PROP + "=128M");
System.out.println("#" + TSERVER_MEMORY_PROP + "=128M");
System.out.println("#" + ZOO_KEEPER_MEMORY_PROP + "=128M");
System.out.println("#" + JDWP_ENABLED_PROP + "=false");
System.out.println("#" + EXISTING_ZOO_KEEPERS_PROP + "=localhost:2181");
System.out.println();
System.out.println("# Configuration normally placed in accumulo.properties can be added using"
+ " a site.* prefix.");
System.out.println("# For example the following line will set tserver.compaction.major.delay");
System.out.println();
System.out.println("#site.tserver.compaction.major.delay=60s");
}
public static class PropertiesConverter implements IStringConverter<Properties> {
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN",
justification = "code runs in same security context as user who provided input file name")
@Override
public Properties convert(String fileName) {
Properties prop = new Properties();
InputStream is;
try {
is = new FileInputStream(fileName);
try {
prop.load(is);
} finally {
is.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return prop;
}
}
private static final String FORMAT_STRING = " %-21s %s";
public static class Opts extends Help {
@Parameter(names = "-p", required = false, description = "properties file name",
converter = PropertiesConverter.class)
Properties prop = new Properties();
@Parameter(names = {"-c", "--printProperties"}, required = false,
description = "prints an example propeties file, redirect to file to use")
boolean printProps = false;
}
/**
* Runs the {@link MiniAccumuloCluster} given a -p argument with a property file. Establishes a
* shutdown port for asynchronous operation.
*
* @param args
* An optional -p argument can be specified with the path to a valid properties file.
*/
@SuppressFBWarnings(value = {"PATH_TRAVERSAL_IN", "UNENCRYPTED_SERVER_SOCKET"},
justification = "code runs in same security context as user who provided input file name; "
+ "socket need not be encrypted, since this class is provided for testing only")
public static void main(String[] args) throws IOException, InterruptedException {
Opts opts = new Opts();
opts.parseArgs(MiniAccumuloRunner.class.getName(), args);
if (opts.printProps) {
printProperties();
System.exit(0);
}
int shutdownPort = 4445;
final File miniDir;
if (opts.prop.containsKey(DIRECTORY_PROP))
miniDir = new File(opts.prop.getProperty(DIRECTORY_PROP));
else {
miniDir = Files.createTempDirectory(System.currentTimeMillis() + "").toFile();
}
String rootPass = opts.prop.containsKey(ROOT_PASSWORD_PROP)
? opts.prop.getProperty(ROOT_PASSWORD_PROP) : "secret";
MiniAccumuloConfig config = new MiniAccumuloConfig(miniDir, rootPass);
if (opts.prop.containsKey(INSTANCE_NAME_PROP))
config.setInstanceName(opts.prop.getProperty(INSTANCE_NAME_PROP));
if (opts.prop.containsKey(NUM_T_SERVERS_PROP))
config.setNumTservers(Integer.parseInt(opts.prop.getProperty(NUM_T_SERVERS_PROP)));
if (opts.prop.containsKey(ZOO_KEEPER_PORT_PROP))
config.setZooKeeperPort(Integer.parseInt(opts.prop.getProperty(ZOO_KEEPER_PORT_PROP)));
if (opts.prop.containsKey(ZOO_KEEPER_STARTUP_TIME_PROP))
config.setZooKeeperStartupTime(
Long.parseLong(opts.prop.getProperty(ZOO_KEEPER_STARTUP_TIME_PROP)));
if (opts.prop.containsKey(EXISTING_ZOO_KEEPERS_PROP))
config.getImpl().setExistingZooKeepers(opts.prop.getProperty(EXISTING_ZOO_KEEPERS_PROP));
if (opts.prop.containsKey(JDWP_ENABLED_PROP))
config.setJDWPEnabled(Boolean.parseBoolean(opts.prop.getProperty(JDWP_ENABLED_PROP)));
if (opts.prop.containsKey(ZOO_KEEPER_MEMORY_PROP))
setMemoryOnConfig(config, opts.prop.getProperty(ZOO_KEEPER_MEMORY_PROP),
ServerType.ZOOKEEPER);
if (opts.prop.containsKey(TSERVER_MEMORY_PROP))
setMemoryOnConfig(config, opts.prop.getProperty(TSERVER_MEMORY_PROP),
ServerType.TABLET_SERVER);
if (opts.prop.containsKey(MASTER_MEMORY_PROP)) {
log.warn("{} is deprecated. Use {} instead.", MASTER_MEMORY_PROP, MANAGER_MEMORY_PROP);
setMemoryOnConfig(config, opts.prop.getProperty(MASTER_MEMORY_PROP), ServerType.MANAGER);
}
if (opts.prop.containsKey(MANAGER_MEMORY_PROP))
setMemoryOnConfig(config, opts.prop.getProperty(MANAGER_MEMORY_PROP), ServerType.MANAGER);
if (opts.prop.containsKey(DEFAULT_MEMORY_PROP))
setMemoryOnConfig(config, opts.prop.getProperty(DEFAULT_MEMORY_PROP));
if (opts.prop.containsKey(SHUTDOWN_PORT_PROP))
shutdownPort = Integer.parseInt(opts.prop.getProperty(SHUTDOWN_PORT_PROP));
Map<String,String> siteConfig = new HashMap<>();
for (Map.Entry<Object,Object> entry : opts.prop.entrySet()) {
String key = (String) entry.getKey();
if (key.startsWith("site."))
siteConfig.put(key.replaceFirst("site.", ""), (String) entry.getValue());
}
config.setSiteConfig(siteConfig);
final MiniAccumuloCluster accumulo = new MiniAccumuloCluster(config);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
accumulo.stop();
} catch (IOException e) {
log.error("IOException attempting to stop Accumulo.", e);
return;
} catch (InterruptedException e) {
log.error("InterruptedException attempting to stop Accumulo.", e);
return;
}
try {
FileUtils.deleteDirectory(miniDir);
} catch (IOException e) {
log.error("IOException attempting to clean up miniDir.", e);
return;
}
System.out.println("\nShut down gracefully on " + new Date());
}));
accumulo.start();
printInfo(accumulo, shutdownPort);
// start a socket on the shutdown port and block- anything connected to this port will activate
// the shutdown
try (ServerSocket shutdownServer = new ServerSocket(shutdownPort)) {
shutdownServer.accept().close();
}
System.exit(0);
}
private static boolean validateMemoryString(String memoryString) {
String unitsRegex = "[";
MemoryUnit[] units = MemoryUnit.values();
for (int i = 0; i < units.length; i++) {
unitsRegex += units[i].suffix();
if (i < units.length - 1)
unitsRegex += "|";
}
unitsRegex += "]";
Pattern p = Pattern.compile("\\d+" + unitsRegex);
return p.matcher(memoryString).matches();
}
private static void setMemoryOnConfig(MiniAccumuloConfig config, String memoryString) {
setMemoryOnConfig(config, memoryString, null);
}
private static void setMemoryOnConfig(MiniAccumuloConfig config, String memoryString,
ServerType serverType) {
if (!validateMemoryString(memoryString))
throw new IllegalArgumentException(memoryString + " is not a valid memory string");
long memSize = Long.parseLong(memoryString.substring(0, memoryString.length() - 1));
MemoryUnit memUnit = MemoryUnit.fromSuffix(memoryString.substring(memoryString.length() - 1));
if (serverType != null)
config.setMemory(serverType, memSize, memUnit);
else
config.setDefaultMemory(memSize, memUnit);
}
private static void printInfo(MiniAccumuloCluster accumulo, int shutdownPort) {
System.out.println("Mini Accumulo Cluster\n");
System.out.println(String.format(FORMAT_STRING, "Directory:",
accumulo.getConfig().getDir().getAbsoluteFile()));
System.out.println(String.format(FORMAT_STRING, "Logs:",
accumulo.getConfig().getImpl().getLogDir().getAbsoluteFile()));
System.out.println(
String.format(FORMAT_STRING, "Instance Name:", accumulo.getConfig().getInstanceName()));
System.out.println(
String.format(FORMAT_STRING, "Root Password:", accumulo.getConfig().getRootPassword()));
System.out.println(String.format(FORMAT_STRING, "ZooKeeper:", accumulo.getZooKeepers()));
for (Pair<ServerType,Integer> pair : accumulo.getDebugPorts()) {
System.out.println(String.format(FORMAT_STRING, pair.getFirst().prettyPrint() + " JDWP Host:",
"localhost:" + pair.getSecond()));
}
System.out.println(String.format(FORMAT_STRING, "Shutdown Port:", shutdownPort));
System.out.println();
System.out.println(" To connect with shell, use the following command : ");
System.out.println(" accumulo shell -zh " + accumulo.getZooKeepers() + " -zi "
+ accumulo.getConfig().getInstanceName() + " -u root ");
System.out.println("\n\nSuccessfully started on " + new Date());
}
}