From 69d7f1a78af182c9fb97fa8057194f38af67befd Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Thu, 30 Jul 2015 10:28:18 +0200 Subject: [PATCH] Startup: Remove getopt parsing in shell script, use java CLITool In order to ensure, we have the same experience across operating systems and shells, this commit uses the java CLI parser instead of the shell getopt parsing to parse arguments. This also allows for support for paths, which contain spaces. Also commons-cli depdency was upgraded to 1.3.1 and tests have been added. Changes * new exit code, OK_AND_EXIT, allowing to tell the caller to exit, as everything went as expected (e.g. when running a version output) BWC breaking: * execute() returns an ExitStatus instead of an integer, otherwise there is no possibility to signal by a command, if the JVM should be exited after a run. This affects plugins, that have command line tools * -v used to be version, but is a verbose flag by default in the current CLI infra, must be -V or --version now * -X has been removed - the current implementation was useless anyway, as it prefixed those properties with "es.". You should use ES_JAVA_OPTS/JAVA_OPTS for JVM configuration --- .../elasticsearch/bootstrap/Bootstrap.java | 12 +- .../bootstrap/BootstrapCLIParser.java | 167 ++++++++++++ .../org/elasticsearch/common/cli/CliTool.java | 37 ++- .../common/cli/CliToolConfig.java | 45 +++- .../plugins/PluginManagerCliParser.java | 2 +- .../bootstrap/elasticsearch-start.help | 26 ++ .../bootstrap/elasticsearch-version.help | 16 ++ .../bootstrap/elasticsearch.help | 22 ++ .../bootstrap/BootstrapCliParserTests.java | 246 ++++++++++++++++++ .../common/cli/CliToolTests.java | 90 +++++-- .../plugins/PluginManagerCliTests.java | 9 +- .../plugins/PluginManagerTests.java | 48 +++- .../licenses/commons-cli-1.2.jar.sha1 | 1 - .../licenses/commons-cli-1.3.1.jar.sha1 | 1 + .../src/main/resources/bin/elasticsearch | 135 +--------- .../src/main/resources/bin/elasticsearch.bat | 2 +- docs/reference/migration/migrate_2_0.asciidoc | 7 + pom.xml | 2 +- 18 files changed, 672 insertions(+), 196 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java create mode 100644 core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-start.help create mode 100644 core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-version.help create mode 100644 core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch.help create mode 100644 core/src/test/java/org/elasticsearch/bootstrap/BootstrapCliParserTests.java delete mode 100644 distribution/licenses/commons-cli-1.2.jar.sha1 create mode 100644 distribution/licenses/commons-cli-1.3.1.jar.sha1 diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 36ba3bdc7a6..d110942388f 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -19,12 +19,13 @@ package org.elasticsearch.bootstrap; -import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.Constants; +import org.apache.lucene.util.StringHelper; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.Version; import org.elasticsearch.common.PidFile; import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.common.cli.CliTool; import org.elasticsearch.common.cli.Terminal; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.CreationException; @@ -222,10 +223,17 @@ public class Bootstrap { } public static void main(String[] args) { + BootstrapCLIParser bootstrapCLIParser = new BootstrapCLIParser(); + CliTool.ExitStatus status = bootstrapCLIParser.execute(args); + + if (CliTool.ExitStatus.OK != status) { + System.exit(status.status()); + } + System.setProperty("es.logger.prefix", ""); INSTANCE = new Bootstrap(); - boolean foreground = System.getProperty("es.foreground", System.getProperty("es-foreground")) != null; + boolean foreground = !"false".equals(System.getProperty("es.foreground", System.getProperty("es-foreground"))); // handle the wrapper system property, if its a service, don't run as a service if (System.getProperty("wrapper.service", "XXX").equalsIgnoreCase("true")) { foreground = false; diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java new file mode 100644 index 00000000000..1e3f8396279 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java @@ -0,0 +1,167 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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.elasticsearch.bootstrap; + +import com.google.common.base.Strings; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.elasticsearch.Build; +import org.elasticsearch.common.cli.CliTool; +import org.elasticsearch.common.cli.CliToolConfig; +import org.elasticsearch.common.cli.Terminal; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.monitor.jvm.JvmInfo; + +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd; +import static org.elasticsearch.common.cli.CliToolConfig.Builder.optionBuilder; + +public class BootstrapCLIParser extends CliTool { + + private static final CliToolConfig CONFIG = CliToolConfig.config("elasticsearch", BootstrapCLIParser.class) + .cmds(Start.CMD, Version.CMD) + .build(); + + public BootstrapCLIParser() { + super(CONFIG); + } + + public BootstrapCLIParser(Terminal terminal) { + super(CONFIG, terminal); + } + + @Override + protected Command parse(String cmdName, CommandLine cli) throws Exception { + switch (cmdName.toLowerCase(Locale.ROOT)) { + case Start.NAME: + return Start.parse(terminal, cli); + case Version.NAME: + return Version.parse(terminal, cli); + default: + assert false : "should never get here, if the user enters an unknown command, an error message should be shown before parse is called"; + return null; + } + } + + static class Version extends CliTool.Command { + + private static final String NAME = "version"; + + private static final CliToolConfig.Cmd CMD = cmd(NAME, Version.class).build(); + + public static Command parse(Terminal terminal, CommandLine cli) { + return new Version(terminal); + } + + public Version(Terminal terminal) { + super(terminal); + } + + @Override + public ExitStatus execute(Settings settings, Environment env) throws Exception { + terminal.println("Version: %s, Build: %s/%s, JVM: %s", org.elasticsearch.Version.CURRENT, Build.CURRENT.hashShort(), Build.CURRENT.timestamp(), JvmInfo.jvmInfo().version()); + return ExitStatus.OK_AND_EXIT; + } + } + + static class Start extends CliTool.Command { + + private static final String NAME = "start"; + + private static final CliToolConfig.Cmd CMD = cmd(NAME, Start.class) + .options( + optionBuilder("d", "daemonize").hasArg(false).required(false), + optionBuilder("p", "pidfile").hasArg(true).required(false), + optionBuilder("V", "version").hasArg(false).required(false), + Option.builder("D").argName("property=value").valueSeparator('=').numberOfArgs(2) + ) + .stopAtNonOption(true) // needed to parse the --foo.bar options, so this parser must be lenient + .build(); + + public static Command parse(Terminal terminal, CommandLine cli) { + if (cli.hasOption("V")) { + return Version.parse(terminal, cli); + } + + if (cli.hasOption("d")) { + System.setProperty("es.foreground", "false"); + } + + String pidFile = cli.getOptionValue("pidfile"); + if (!Strings.isNullOrEmpty(pidFile)) { + System.setProperty("es.pidfile", pidFile); + } + + if (cli.hasOption("D")) { + Properties properties = cli.getOptionProperties("D"); + for (Map.Entry entry : properties.entrySet()) { + String key = (String) entry.getKey(); + String propertyName = key.startsWith("es.") ? key : "es." + key; + System.setProperty(propertyName, entry.getValue().toString()); + } + } + + // hacky way to extract all the fancy extra args, there is no CLI tool helper for this + Iterator iterator = cli.getArgList().iterator(); + while (iterator.hasNext()) { + String arg = iterator.next(); + if (!arg.startsWith("--")) { + throw new IllegalArgumentException("Parameter [" + arg + "]does not start with --"); + } + // if there is no = sign, we have to get the next argu + arg = arg.replace("--", ""); + if (arg.contains("=")) { + String[] splitArg = arg.split("=", 2); + String key = splitArg[0]; + String value = splitArg[1]; + System.setProperty("es." + key, value); + } else { + if (iterator.hasNext()) { + String value = iterator.next(); + if (value.startsWith("--")) { + throw new IllegalArgumentException("Parameter [" + arg + "] needs value"); + } + System.setProperty("es." + arg, value); + } else { + throw new IllegalArgumentException("Parameter [" + arg + "] needs value"); + } + } + } + + return new Start(terminal); + } + + public Start(Terminal terminal) { + super(terminal); + + } + + @Override + public ExitStatus execute(Settings settings, Environment env) throws Exception { + return ExitStatus.OK; + } + } + +} diff --git a/core/src/main/java/org/elasticsearch/common/cli/CliTool.java b/core/src/main/java/org/elasticsearch/common/cli/CliTool.java index b0784a57ca7..6b11e65147f 100644 --- a/core/src/main/java/org/elasticsearch/common/cli/CliTool.java +++ b/core/src/main/java/org/elasticsearch/common/cli/CliTool.java @@ -22,6 +22,7 @@ package org.elasticsearch.common.cli; import com.google.common.base.Preconditions; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.GnuParser; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; @@ -54,6 +55,7 @@ public abstract class CliTool { // based on sysexits.h public static enum ExitStatus { OK(0), + OK_AND_EXIT(0), USAGE(64), /* command line usage error */ DATA_ERROR(65), /* data format error */ NO_INPUT(66), /* cannot open input */ @@ -77,6 +79,16 @@ public abstract class CliTool { public int status() { return status; } + + public static ExitStatus fromStatus(int status) { + for (ExitStatus exitStatus : values()) { + if (exitStatus.status() == status) { + return exitStatus; + } + } + + return null; + } } protected final Terminal terminal; @@ -98,14 +110,14 @@ public abstract class CliTool { env = tuple.v2(); } - public final int execute(String... args) { + public final ExitStatus execute(String... args) { // first lets see if the user requests tool help. We're doing it only if // this is a multi-command tool. If it's a single command tool, the -h/--help // option will be taken care of on the command level if (!config.isSingle() && args.length > 0 && (args[0].equals("-h") || args[0].equals("--help"))) { config.printUsage(terminal); - return ExitStatus.OK.status; + return ExitStatus.OK_AND_EXIT; } CliToolConfig.Cmd cmd; @@ -116,14 +128,14 @@ public abstract class CliTool { if (args.length == 0) { terminal.printError("command not specified"); config.printUsage(terminal); - return ExitStatus.USAGE.status; + return ExitStatus.USAGE; } String cmdName = args[0]; cmd = config.cmd(cmdName); if (cmd == null) { terminal.printError("unknown command [%s]. Use [-h] option to list available commands", cmdName); - return ExitStatus.USAGE.status; + return ExitStatus.USAGE; } // we now remove the command name from the args @@ -140,20 +152,19 @@ public abstract class CliTool { try { command = parse(cmd, args); - return command.execute(settings, env).status; - + return command.execute(settings, env); } catch (IOException ioe) { terminal.printError(ioe); - return ExitStatus.IO_ERROR.status; + return ExitStatus.IO_ERROR; } catch (IllegalArgumentException ilae) { terminal.printError(ilae); - return ExitStatus.USAGE.status; + return ExitStatus.USAGE; } catch (Throwable t) { terminal.printError(t); if (command == null) { - return ExitStatus.USAGE.status; + return ExitStatus.USAGE; } - return ExitStatus.CODE_ERROR.status; + return ExitStatus.CODE_ERROR; } } @@ -163,12 +174,12 @@ public abstract class CliTool { } public Command parse(CliToolConfig.Cmd cmd, String[] args) throws Exception { - CommandLineParser parser = new GnuParser(); + CommandLineParser parser = new DefaultParser(); CommandLine cli = parser.parse(CliToolConfig.OptionsSource.HELP.options(), args, true); if (cli.hasOption("h")) { return helpCmd(cmd); } - cli = parser.parse(cmd.options(), args); + cli = parser.parse(cmd.options(), args, cmd.isStopAtNonOption()); Terminal.Verbosity verbosity = Terminal.Verbosity.resolve(cli); terminal.verbosity(verbosity); return parse(cmd.name(), cli); @@ -210,7 +221,7 @@ public abstract class CliTool { @Override public ExitStatus execute(Settings settings, Environment env) throws Exception { cmd.printUsage(terminal); - return ExitStatus.OK; + return ExitStatus.OK_AND_EXIT; } } diff --git a/core/src/main/java/org/elasticsearch/common/cli/CliToolConfig.java b/core/src/main/java/org/elasticsearch/common/cli/CliToolConfig.java index 0ba4f849be2..2e3d755793e 100644 --- a/core/src/main/java/org/elasticsearch/common/cli/CliToolConfig.java +++ b/core/src/main/java/org/elasticsearch/common/cli/CliToolConfig.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.cli; import com.google.common.collect.ImmutableMap; +import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; @@ -90,6 +91,10 @@ public class CliToolConfig { return new OptionBuilder(shortName, longName); } + public static Option.Builder optionBuilder(String shortName, String longName) { + return Option.builder(shortName).argName(longName).longOpt(longName); + } + public static OptionGroupBuilder optionGroup(boolean required) { return new OptionGroupBuilder(required); } @@ -131,11 +136,13 @@ public class CliToolConfig { private final String name; private final Class cmdType; private final Options options; + private final boolean stopAtNonOption; - private Cmd(String name, Class cmdType, Options options) { + private Cmd(String name, Class cmdType, Options options, boolean stopAtNonOption) { this.name = name; this.cmdType = cmdType; this.options = options; + this.stopAtNonOption = stopAtNonOption; OptionsSource.VERBOSITY.populate(options); } @@ -148,16 +155,11 @@ public class CliToolConfig { } public Options options() { - // TODO Remove this when commons-cli 1.3 will be released - // and replace by return options; - // See https://issues.apache.org/jira/browse/CLI-183 - Options copy = new Options(); - for (Object oOption : options.getOptions()) { - Option option = (Option) oOption; - copy.addOption(option); - } - OptionsSource.VERBOSITY.populate(copy); - return copy; + return options; + } + + public boolean isStopAtNonOption() { + return stopAtNonOption; } public void printUsage(Terminal terminal) { @@ -169,6 +171,7 @@ public class CliToolConfig { private final String name; private final Class cmdType; private Options options = new Options(); + private boolean stopAtNonOption = false; private Builder(String name, Class cmdType) { this.name = name; @@ -182,6 +185,13 @@ public class CliToolConfig { return this; } + public Builder options(Option.Builder... optionBuilders) { + for (int i = 0; i < optionBuilders.length; i++) { + options.addOption(optionBuilders[i].build()); + } + return this; + } + public Builder optionGroups(OptionGroupBuilder... optionGroupBuilders) { for (OptionGroupBuilder builder : optionGroupBuilders) { options.addOptionGroup(builder.build()); @@ -189,8 +199,19 @@ public class CliToolConfig { return this; } + /** + * @param stopAtNonOption if true an unrecognized argument stops + * the parsing and the remaining arguments are added to the + * args list. If false an unrecognized + * argument triggers a ParseException. + */ + public Builder stopAtNonOption(boolean stopAtNonOption) { + this.stopAtNonOption = stopAtNonOption; + return this; + } + public Cmd build() { - return new Cmd(name, cmdType, options); + return new Cmd(name, cmdType, options, stopAtNonOption); } } } diff --git a/core/src/main/java/org/elasticsearch/plugins/PluginManagerCliParser.java b/core/src/main/java/org/elasticsearch/plugins/PluginManagerCliParser.java index ecf208526ca..3732e8bda08 100644 --- a/core/src/main/java/org/elasticsearch/plugins/PluginManagerCliParser.java +++ b/core/src/main/java/org/elasticsearch/plugins/PluginManagerCliParser.java @@ -51,7 +51,7 @@ public class PluginManagerCliParser extends CliTool { public static void main(String[] args) { Tuple initialSettings = InternalSettingsPreparer.prepareSettings(EMPTY, true, Terminal.DEFAULT); LogConfigurator.configure(initialSettings.v1()); - int status = new PluginManagerCliParser().execute(args); + int status = new PluginManagerCliParser().execute(args).status(); System.exit(status); } diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-start.help b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-start.help new file mode 100644 index 00000000000..5d20a49c5f0 --- /dev/null +++ b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-start.help @@ -0,0 +1,26 @@ +NAME + + start - start Elasticsearcion + +SYNOPSIS + + elasticsearch start + +DESCRIPTION + + This command starts Elasticsearch. You can configure it to run in the foreground, write a pid file + and configure arbitrary options that override file-based configuration. + +OPTIONS + + -h,--help Shows this message + + -p,--pidfile Creates a pid file in the specified path on start + + -d,--daemonize Starts Elasticsearch in the background + + -Dproperty=value Configures an Elasticsearch specific property, like -Dnetwork.host=127.0.0.1 + + --property=value Configures an elasticsearch specific property, like --network.host 127.0.0.1 + --property value + diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-version.help b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-version.help new file mode 100644 index 00000000000..00f2a33401c --- /dev/null +++ b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch-version.help @@ -0,0 +1,16 @@ +NAME + + version - Show version information and exit + +SYNOPSIS + + elasticsearch version + +DESCRIPTION + + This command shows Elasticsearch version, timestamp and build information as well as JVM info + +OPTIONS + + -h,--help Shows this message + diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch.help b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch.help new file mode 100644 index 00000000000..8d6834dae26 --- /dev/null +++ b/core/src/main/resources/org/elasticsearch/bootstrap/elasticsearch.help @@ -0,0 +1,22 @@ +NAME + + elasticsearch - Manages elasticsearch + +SYNOPSIS + + elasticsearch + +DESCRIPTION + + Start elasticsearch and manage plugins + +COMMANDS + + start Start elasticsearch + + version Show version information and exit + +NOTES + + [*] For usage help on specific commands please type "elasticsearch -h" + diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCliParserTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCliParserTests.java new file mode 100644 index 00000000000..8aceb1278ba --- /dev/null +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCliParserTests.java @@ -0,0 +1,246 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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.elasticsearch.bootstrap; + +import com.google.common.base.Joiner; +import org.elasticsearch.Build; +import org.elasticsearch.Version; +import org.elasticsearch.common.cli.CliTool.ExitStatus; +import org.elasticsearch.common.cli.CliToolTestCase; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.Streams; +import org.elasticsearch.monitor.jvm.JvmInfo; +import org.hamcrest.Matcher; +import org.junit.After; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK_AND_EXIT; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.USAGE; +import static org.hamcrest.Matchers.*; + +public class BootstrapCliParserTests extends CliToolTestCase { + + private CaptureOutputTerminal terminal = new CaptureOutputTerminal(); + private List propertiesToClear = new ArrayList<>(); + + @After + public void clearProperties() { + for (String property : propertiesToClear) { + System.clearProperty(property); + } + } + + public void testThatVersionIsReturned() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + ExitStatus status = parser.execute(args("version")); + assertStatus(status, OK_AND_EXIT); + + assertThatTerminalOutput(containsString(Version.CURRENT.toString())); + assertThatTerminalOutput(containsString(Build.CURRENT.hashShort())); + assertThatTerminalOutput(containsString(Build.CURRENT.timestamp())); + assertThatTerminalOutput(containsString(JvmInfo.jvmInfo().version())); + } + + public void testThatVersionIsReturnedAsStartParameter() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + ExitStatus status = parser.execute(args("start -V")); + assertStatus(status, OK_AND_EXIT); + + assertThatTerminalOutput(containsString(Version.CURRENT.toString())); + assertThatTerminalOutput(containsString(Build.CURRENT.hashShort())); + assertThatTerminalOutput(containsString(Build.CURRENT.timestamp())); + assertThatTerminalOutput(containsString(JvmInfo.jvmInfo().version())); + + CaptureOutputTerminal terminal = new CaptureOutputTerminal(); + parser = new BootstrapCLIParser(terminal); + status = parser.execute(args("start --version")); + assertStatus(status, OK_AND_EXIT); + + assertThatTerminalOutput(containsString(Version.CURRENT.toString())); + assertThatTerminalOutput(containsString(Build.CURRENT.hashShort())); + assertThatTerminalOutput(containsString(Build.CURRENT.timestamp())); + assertThatTerminalOutput(containsString(JvmInfo.jvmInfo().version())); + } + + public void testThatPidFileCanBeConfigured() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.pidfile"); + + ExitStatus status = parser.execute(args("start --pidfile")); // missing pid file + assertStatus(status, USAGE); + + // good cases + status = parser.execute(args("start --pidfile /tmp/pid")); + assertStatus(status, OK); + assertSystemProperty("es.pidfile", "/tmp/pid"); + + System.clearProperty("es.pidfile"); + status = parser.execute(args("start -p /tmp/pid")); + assertStatus(status, OK); + assertSystemProperty("es.pidfile", "/tmp/pid"); + } + + public void testThatParsingDaemonizeWorks() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.foreground"); + + ExitStatus status = parser.execute(args("start -d")); + assertStatus(status, OK); + assertThat(System.getProperty("es.foreground"), is("false")); + } + + public void testThatNotDaemonizingDoesNotConfigureProperties() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.foreground"); + + ExitStatus status = parser.execute(args("start")); + assertStatus(status, OK); + assertThat(System.getProperty("es.foreground"), is(nullValue())); + } + + public void testThatJavaPropertyStyleArgumentsCanBeParsed() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.foo", "es.spam"); + + ExitStatus status = parser.execute(args("start -Dfoo=bar -Dspam=eggs")); + assertStatus(status, OK); + assertSystemProperty("es.foo", "bar"); + assertSystemProperty("es.spam", "eggs"); + } + + public void testThatJavaPropertyStyleArgumentsWithEsPrefixAreNotPrefixedTwice() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.spam", "es.pidfile"); + + ExitStatus status = parser.execute(args("start -Des.pidfile=/path/to/foo/elasticsearch/distribution/zip/target/integ-tests/es.pid -Dspam=eggs")); + assertStatus(status, OK); + assertThat(System.getProperty("es.es.pidfile"), is(nullValue())); + assertSystemProperty("es.pidfile", "/path/to/foo/elasticsearch/distribution/zip/target/integ-tests/es.pid"); + assertSystemProperty("es.spam", "eggs"); + } + + public void testThatUnknownLongOptionsCanBeParsed() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.network.host", "es.my.option"); + + ExitStatus status = parser.execute(args("start --network.host 127.0.0.1 --my.option=true")); + assertStatus(status, OK); + assertSystemProperty("es.network.host", "127.0.0.1"); + assertSystemProperty("es.my.option", "true"); + } + + public void testThatUnknownLongOptionsNeedAValue() throws Exception { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.network.host"); + + ExitStatus status = parser.execute(args("start --network.host")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Parameter [network.host] needs value")); + + status = parser.execute(args("start --network.host --foo")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Parameter [network.host] needs value")); + } + + public void testParsingErrors() { + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + + // unknown params + ExitStatus status = parser.execute(args("version --unknown-param /tmp/pid")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Unrecognized option: --unknown-param")); + + // single dash in extra params + terminal = new CaptureOutputTerminal(); + parser = new BootstrapCLIParser(terminal); + status = parser.execute(args("start -network.host 127.0.0.1")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Parameter [-network.host]does not start with --")); + + // never ended parameter + terminal = new CaptureOutputTerminal(); + parser = new BootstrapCLIParser(terminal); + status = parser.execute(args("start --network.host")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Parameter [network.host] needs value")); + + // free floating value + terminal = new CaptureOutputTerminal(); + parser = new BootstrapCLIParser(terminal); + status = parser.execute(args("start 127.0.0.1")); + assertStatus(status, USAGE); + assertThatTerminalOutput(containsString("Parameter [127.0.0.1]does not start with --")); + } + + public void testHelpWorks() throws Exception { + List> tuples = new ArrayList<>(); + tuples.add(new Tuple<>("version --help", "elasticsearch-version.help")); + tuples.add(new Tuple<>("version -h", "elasticsearch-version.help")); + tuples.add(new Tuple<>("start --help", "elasticsearch-start.help")); + tuples.add(new Tuple<>("start -h", "elasticsearch-start.help")); + tuples.add(new Tuple<>("--help", "elasticsearch.help")); + tuples.add(new Tuple<>("-h", "elasticsearch.help")); + + for (Tuple tuple : tuples) { + terminal = new CaptureOutputTerminal(); + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + ExitStatus status = parser.execute(args(tuple.v1())); + assertStatus(status, OK_AND_EXIT); + + String expectedDocs = Streams.copyToStringFromClasspath("/org/elasticsearch/bootstrap/" + tuple.v2()); + String returnedDocs = Joiner.on("").join(terminal.getTerminalOutput()); + assertThat(returnedDocs.trim(), is(expectedDocs.trim())); + } + } + + public void testThatSpacesInParametersAreSupported() throws Exception { + // emulates: bin/elasticsearch --node.name "'my node with spaces'" --pidfile "'/tmp/my pid.pid'" + BootstrapCLIParser parser = new BootstrapCLIParser(terminal); + registerProperties("es.pidfile", "es.my.param"); + + ExitStatus status = parser.execute("start", "--pidfile", "foo with space", "--my.param", "my awesome neighbour"); + assertStatus(status, OK); + assertSystemProperty("es.pidfile", "foo with space"); + assertSystemProperty("es.my.param", "my awesome neighbour"); + + } + + private void registerProperties(String ... systemProperties) { + propertiesToClear.addAll(Arrays.asList(systemProperties)); + } + + private void assertSystemProperty(String name, String expectedValue) { + String msg = String.format(Locale.ROOT, "Expected property %s to be %s, terminal output was %s", name, expectedValue, terminal.getTerminalOutput()); + assertThat(msg, System.getProperty(name), is(expectedValue)); + } + + private void assertStatus(ExitStatus status, ExitStatus expectedStatus) { + assertThat(String.format(Locale.ROOT, "Expected status to be [%s], but was [%s], terminal output was %s", expectedStatus, status, terminal.getTerminalOutput()), status, is(expectedStatus)); + } + + private void assertThatTerminalOutput(Matcher matcher) { + assertThat(terminal.getTerminalOutput(), hasItem(matcher)); + } +} diff --git a/core/src/test/java/org/elasticsearch/common/cli/CliToolTests.java b/core/src/test/java/org/elasticsearch/common/cli/CliToolTests.java index eea1d0614a1..748e417caf3 100644 --- a/core/src/test/java/org/elasticsearch/common/cli/CliToolTests.java +++ b/core/src/test/java/org/elasticsearch/common/cli/CliToolTests.java @@ -33,6 +33,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.USAGE; import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd; import static org.hamcrest.Matchers.*; @@ -49,12 +51,12 @@ public class CliToolTests extends CliToolTestCase { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) { executed.set(true); - return CliTool.ExitStatus.OK; + return OK; } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); - int status = tool.execute(); - assertStatus(status, CliTool.ExitStatus.OK); + CliTool.ExitStatus status = tool.execute(); + assertStatus(status, OK); assertCommandHasBeenExecuted(executed); } @@ -70,7 +72,7 @@ public class CliToolTests extends CliToolTestCase { } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); - int status = tool.execute(); + CliTool.ExitStatus status = tool.execute(); assertStatus(status, CliTool.ExitStatus.USAGE); assertCommandHasBeenExecuted(executed); } @@ -87,7 +89,7 @@ public class CliToolTests extends CliToolTestCase { } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); - int status = tool.execute(); + CliTool.ExitStatus status = tool.execute(); assertStatus(status, CliTool.ExitStatus.IO_ERROR); assertCommandHasBeenExecuted(executed); } @@ -104,7 +106,7 @@ public class CliToolTests extends CliToolTestCase { } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); - int status = tool.execute(); + CliTool.ExitStatus status = tool.execute(); assertStatus(status, CliTool.ExitStatus.CODE_ERROR); assertCommandHasBeenExecuted(executed); } @@ -124,14 +126,14 @@ public class CliToolTests extends CliToolTestCase { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { executed[index].set(true); - return CliTool.ExitStatus.OK; + return OK; } }; } MultiCmdTool tool = new MultiCmdTool("tool", terminal, cmds); int cmdIndex = randomIntBetween(0, count-1); - int status = tool.execute("cmd" + cmdIndex); - assertThat(status, is(CliTool.ExitStatus.OK.status())); + CliTool.ExitStatus status = tool.execute("cmd" + cmdIndex); + assertThat(status, is(OK)); for (int i = 0; i < executed.length; i++) { assertThat(executed[i].get(), is(i == cmdIndex)); } @@ -152,13 +154,13 @@ public class CliToolTests extends CliToolTestCase { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { executed[index].set(true); - return CliTool.ExitStatus.OK; + return OK; } }; } MultiCmdTool tool = new MultiCmdTool("tool", terminal, cmds); - int status = tool.execute("cmd" + count); // "cmd" + count doesn't exist - assertThat(status, is(CliTool.ExitStatus.USAGE.status())); + CliTool.ExitStatus status = tool.execute("cmd" + count); // "cmd" + count doesn't exist + assertThat(status, is(CliTool.ExitStatus.USAGE)); for (int i = 0; i < executed.length; i++) { assertThat(executed[i].get(), is(false)); } @@ -176,8 +178,8 @@ public class CliToolTests extends CliToolTestCase { } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); - int status = tool.execute(args("-h")); - assertStatus(status, CliTool.ExitStatus.OK); + CliTool.ExitStatus status = tool.execute(args("-h")); + assertStatus(status, CliTool.ExitStatus.OK_AND_EXIT); assertThat(terminal.getTerminalOutput(), hasSize(3)); assertThat(terminal.getTerminalOutput(), hasItem(containsString("cmd1 help"))); } @@ -189,18 +191,18 @@ public class CliToolTests extends CliToolTestCase { cmds[0] = new NamedCommand("cmd0", terminal) { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { - return CliTool.ExitStatus.OK; + return OK; } }; cmds[1] = new NamedCommand("cmd1", terminal) { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { - return CliTool.ExitStatus.OK; + return OK; } }; MultiCmdTool tool = new MultiCmdTool("tool", terminal, cmds); - int status = tool.execute(args("-h")); - assertStatus(status, CliTool.ExitStatus.OK); + CliTool.ExitStatus status = tool.execute(args("-h")); + assertStatus(status, CliTool.ExitStatus.OK_AND_EXIT); assertThat(terminal.getTerminalOutput(), hasSize(3)); assertThat(terminal.getTerminalOutput(), hasItem(containsString("tool help"))); } @@ -212,18 +214,18 @@ public class CliToolTests extends CliToolTestCase { cmds[0] = new NamedCommand("cmd0", terminal) { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { - return CliTool.ExitStatus.OK; + return OK; } }; cmds[1] = new NamedCommand("cmd1", terminal) { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) throws Exception { - return CliTool.ExitStatus.OK; + return OK; } }; MultiCmdTool tool = new MultiCmdTool("tool", terminal, cmds); - int status = tool.execute(args("cmd1 -h")); - assertStatus(status, CliTool.ExitStatus.OK); + CliTool.ExitStatus status = tool.execute(args("cmd1 -h")); + assertStatus(status, CliTool.ExitStatus.OK_AND_EXIT); assertThat(terminal.getTerminalOutput(), hasSize(3)); assertThat(terminal.getTerminalOutput(), hasItem(containsString("cmd1 help"))); } @@ -264,7 +266,7 @@ public class CliToolTests extends CliToolTestCase { @Override public CliTool.ExitStatus execute(Settings settings, Environment env) { executed.set(true); - return CliTool.ExitStatus.OK; + return OK; } }; SingleCmdTool tool = new SingleCmdTool("tool", terminal, cmd); @@ -298,7 +300,7 @@ public class CliToolTests extends CliToolTestCase { public CliTool.ExitStatus execute(Settings settings, Environment env) { promptedSecretValue.set(settings.get("foo.password")); promptedTextValue.set(settings.get("replace")); - return CliTool.ExitStatus.OK; + return OK; } }; @@ -316,8 +318,44 @@ public class CliToolTests extends CliToolTestCase { assertThat(promptedTextValue.get(), is("replaced")); } - private void assertStatus(int status, CliTool.ExitStatus expectedStatus) { - assertThat(status, is(expectedStatus.status())); + @Test + public void testStopAtNonOptionParsing() throws Exception { + final CliToolConfig.Cmd lenientCommand = cmd("lenient", CliTool.Command.Exit.class).stopAtNonOption(true).build(); + final CliToolConfig.Cmd strictCommand = cmd("strict", CliTool.Command.Exit.class).stopAtNonOption(false).build(); + final CliToolConfig config = CliToolConfig.config("elasticsearch", CliTool.class).cmds(lenientCommand, strictCommand).build(); + + final CaptureOutputTerminal terminal = new CaptureOutputTerminal(); + final CliTool cliTool = new CliTool(config, terminal) { + @Override + protected Command parse(String cmdName, CommandLine cli) throws Exception { + return new NamedCommand(cmdName, terminal) { + @Override + public ExitStatus execute(Settings settings, Environment env) throws Exception { + return OK; + } + }; + } + }; + + // known parameters, no error + assertStatus(cliTool.execute(args("lenient --verbose")), OK); + assertStatus(cliTool.execute(args("lenient -v")), OK); + + // unknown parameters, no error + assertStatus(cliTool.execute(args("lenient --unknown")), OK); + assertStatus(cliTool.execute(args("lenient -u")), OK); + + // unknown parameters, error + assertStatus(cliTool.execute(args("strict --unknown")), USAGE); + assertThat(terminal.getTerminalOutput(), hasItem(containsString("Unrecognized option: --unknown"))); + + terminal.getTerminalOutput().clear(); + assertStatus(cliTool.execute(args("strict -u")), USAGE); + assertThat(terminal.getTerminalOutput(), hasItem(containsString("Unrecognized option: -u"))); + } + + private void assertStatus(CliTool.ExitStatus status, CliTool.ExitStatus expectedStatus) { + assertThat(status, is(expectedStatus)); } private void assertCommandHasBeenExecuted(AtomicReference executed) { diff --git a/core/src/test/java/org/elasticsearch/plugins/PluginManagerCliTests.java b/core/src/test/java/org/elasticsearch/plugins/PluginManagerCliTests.java index 7c81c1479d8..c0003a36fd6 100644 --- a/core/src/test/java/org/elasticsearch/plugins/PluginManagerCliTests.java +++ b/core/src/test/java/org/elasticsearch/plugins/PluginManagerCliTests.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK_AND_EXIT; import static org.hamcrest.Matchers.*; public class PluginManagerCliTests extends CliToolTestCase { @@ -36,22 +37,22 @@ public class PluginManagerCliTests extends CliToolTestCase { @Test public void testHelpWorks() throws IOException { CliToolTestCase.CaptureOutputTerminal terminal = new CliToolTestCase.CaptureOutputTerminal(); - assertThat(new PluginManagerCliParser(terminal).execute(args("--help")), is(OK.status())); + assertThat(new PluginManagerCliParser(terminal).execute(args("--help")), is(OK_AND_EXIT)); assertHelp(terminal, "/org/elasticsearch/plugins/plugin.help"); terminal.getTerminalOutput().clear(); - assertThat(new PluginManagerCliParser(terminal).execute(args("install -h")), is(OK.status())); + assertThat(new PluginManagerCliParser(terminal).execute(args("install -h")), is(OK_AND_EXIT)); assertHelp(terminal, "/org/elasticsearch/plugins/plugin-install.help"); for (String plugin : PluginManager.OFFICIAL_PLUGINS) { assertThat(terminal.getTerminalOutput(), hasItem(containsString(plugin))); } terminal.getTerminalOutput().clear(); - assertThat(new PluginManagerCliParser(terminal).execute(args("remove --help")), is(OK.status())); + assertThat(new PluginManagerCliParser(terminal).execute(args("remove --help")), is(OK_AND_EXIT)); assertHelp(terminal, "/org/elasticsearch/plugins/plugin-remove.help"); terminal.getTerminalOutput().clear(); - assertThat(new PluginManagerCliParser(terminal).execute(args("list -h")), is(OK.status())); + assertThat(new PluginManagerCliParser(terminal).execute(args("list -h")), is(OK_AND_EXIT)); assertHelp(terminal, "/org/elasticsearch/plugins/plugin-list.help"); } diff --git a/core/src/test/java/org/elasticsearch/plugins/PluginManagerTests.java b/core/src/test/java/org/elasticsearch/plugins/PluginManagerTests.java index 042e209a444..5065eb49f20 100644 --- a/core/src/test/java/org/elasticsearch/plugins/PluginManagerTests.java +++ b/core/src/test/java/org/elasticsearch/plugins/PluginManagerTests.java @@ -18,12 +18,14 @@ */ package org.elasticsearch.plugins; +import com.google.common.base.Joiner; import org.apache.http.impl.client.HttpClients; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.Version; -import org.elasticsearch.common.cli.CliTool; +import org.elasticsearch.common.cli.CliTool.ExitStatus; import org.elasticsearch.common.cli.CliToolTestCase.CaptureOutputTerminal; import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.node.internal.InternalSettingsPreparer; @@ -50,15 +52,16 @@ import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import static org.elasticsearch.common.cli.CliTool.ExitStatus.OK_AND_EXIT; import static org.elasticsearch.common.cli.CliTool.ExitStatus.USAGE; import static org.elasticsearch.common.cli.CliToolTestCase.args; import static org.elasticsearch.common.io.FileSystemUtilsTests.assertFileContent; import static org.elasticsearch.common.settings.Settings.settingsBuilder; +import static org.elasticsearch.plugins.PluginInfoTests.writeProperties; import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertDirectoryExists; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists; import static org.hamcrest.Matchers.*; -import static org.elasticsearch.plugins.PluginInfoTests.writeProperties; @ClusterScope(scope = Scope.TEST, numDataNodes = 0, transportClientRatio = 0.0) @LuceneTestCase.SuppressFileSystems("*") // TODO: clean up this test to allow extra files @@ -434,15 +437,13 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest { @Test public void testRemovePlugin_NullName_ThrowsException() throws IOException { - int status = new PluginManagerCliParser(terminal).execute(args("remove ")); - assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(USAGE.status())); + assertStatus("remove ", USAGE); } @Test public void testRemovePluginWithURLForm() throws Exception { - int status = new PluginManagerCliParser(terminal).execute(args("remove file://whatever")); + assertStatus("remove file://whatever", USAGE); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Illegal plugin name"))); - assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(USAGE.status())); } @Test @@ -479,6 +480,33 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest { } } + @Test + public void testHelpWorks() throws IOException { + assertStatus("--help", OK_AND_EXIT); + assertHelp("/org/elasticsearch/plugins/plugin.help"); + + terminal.getTerminalOutput().clear(); + assertStatus("install -h", OK_AND_EXIT); + assertHelp("/org/elasticsearch/plugins/plugin-install.help"); + for (String plugin : PluginManager.OFFICIAL_PLUGINS) { + assertThat(terminal.getTerminalOutput(), hasItem(containsString(plugin))); + } + + terminal.getTerminalOutput().clear(); + assertStatus("remove --help", OK_AND_EXIT); + assertHelp("/org/elasticsearch/plugins/plugin-remove.help"); + + terminal.getTerminalOutput().clear(); + assertStatus("list -h", OK_AND_EXIT); + assertHelp("/org/elasticsearch/plugins/plugin-list.help"); + } + + private void assertHelp(String classPath) throws IOException { + String expectedDocs = Streams.copyToStringFromClasspath(classPath); + String returnedDocs = Joiner.on("").join(terminal.getTerminalOutput()); + assertThat(returnedDocs.trim(), is(expectedDocs.trim())); + } + private Tuple buildInitialSettings() throws IOException { Settings settings = settingsBuilder() .put("discovery.zen.ping.multicast.enabled", false) @@ -488,12 +516,12 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest { } private void assertStatusOk(String command) { - assertStatus(command, CliTool.ExitStatus.OK); + assertStatus(command, ExitStatus.OK); } - private void assertStatus(String command, CliTool.ExitStatus exitStatus) { - int status = new PluginManagerCliParser(terminal).execute(args(command)); - assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(exitStatus.status())); + private void assertStatus(String command, ExitStatus exitStatus) { + ExitStatus status = new PluginManagerCliParser(terminal).execute(args(command)); + assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(exitStatus)); } private void assertThatPluginIsListed(String pluginName) { diff --git a/distribution/licenses/commons-cli-1.2.jar.sha1 b/distribution/licenses/commons-cli-1.2.jar.sha1 deleted file mode 100644 index 6dacb321cdf..00000000000 --- a/distribution/licenses/commons-cli-1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2bf96b7aa8b611c177d329452af1dc933e14501c diff --git a/distribution/licenses/commons-cli-1.3.1.jar.sha1 b/distribution/licenses/commons-cli-1.3.1.jar.sha1 new file mode 100644 index 00000000000..fc366d027f5 --- /dev/null +++ b/distribution/licenses/commons-cli-1.3.1.jar.sha1 @@ -0,0 +1 @@ +1303efbc4b181e5a58bf2e967dc156a3132b97c0 diff --git a/distribution/src/main/resources/bin/elasticsearch b/distribution/src/main/resources/bin/elasticsearch index f05f3d30100..fb090758931 100755 --- a/distribution/src/main/resources/bin/elasticsearch +++ b/distribution/src/main/resources/bin/elasticsearch @@ -1,16 +1,5 @@ #!/bin/sh -# OPTIONS: -# -d daemonize (run in background) -# -p pidfile write PID to -# -h -# --help print command line options -# -v print elasticsearch version, then exit -# -D prop set JAVA system property -# -X prop set non-standard JAVA system property -# --prop=val -# --prop val set elasticsearch property (i.e. -Des.=) - # CONTROLLING STARTUP: # # This script relies on few environment variables to determine startup @@ -132,120 +121,16 @@ case `uname` in ;; esac -launch_service() -{ - pidpath=$1 - daemonized=$2 - props=$3 - es_parms="-Delasticsearch" +export HOSTNAME=`hostname -s` - if [ "x$pidpath" != "x" ]; then - es_parms="$es_parms -Des.pidfile=$pidpath" - fi - - # Make sure we dont use any predefined locale, as we check some exception message strings and rely on english language - # As those strings are created by the OS, they are dependant on the configured locale - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - - export HOSTNAME=`hostname -s` - - # The es-foreground option will tell Elasticsearch not to close stdout/stderr, but it's up to us not to daemonize. - if [ "x$daemonized" = "x" ]; then - es_parms="$es_parms -Des.foreground=yes" - eval exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms "\"-Des.path.home=$ES_HOME\"" -cp "\"$ES_CLASSPATH\"" $props \ - org.elasticsearch.bootstrap.Elasticsearch - # exec without running it in the background, makes it replace this shell, we'll never get here... - # no need to return something - else - # Startup Elasticsearch, background it, and write the pid. - eval exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms "\"-Des.path.home=$ES_HOME\"" -cp "\"$ES_CLASSPATH\"" $props \ - org.elasticsearch.bootstrap.Elasticsearch <&- & - return $? - fi -} - -# Print command line usage / help -usage() { - echo "Usage: $0 [-vdh] [-p pidfile] [-D prop] [-X prop]" - echo "Start elasticsearch." - echo " -d daemonize (run in background)" - echo " -p pidfile write PID to " - echo " -h" - echo " --help print command line options" - echo " -v print elasticsearch version, then exit" - echo " -D prop set JAVA system property" - echo " -X prop set non-standard JAVA system property" - echo " --prop=val" - echo " --prop val set elasticsearch property (i.e. -Des.=)" -} - -# Parse any long getopt options and put them into properties before calling getopt below -# Be dash compatible to make sure running under ubuntu works -ARGV="" -while [ $# -gt 0 ] -do - case $1 in - --help) ARGV="$ARGV -h"; shift;; - --*=*) properties="$properties -Des.${1#--}" - shift 1 - ;; - --*) [ $# -le 1 ] && { - echo "Option requires an argument: '$1'." - shift - continue - } - properties="$properties -Des.${1#--}=$2" - shift 2 - ;; - *) ARGV="$ARGV $1" ; shift - esac -done - -# Parse any command line options. -args=`getopt vdhp:D:X: $ARGV` -eval set -- "$args" - -while true; do - case $1 in - -v) - eval "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms "\"-Des.path.home=$ES_HOME\"" -cp "\"$ES_CLASSPATH\"" $props \ - org.elasticsearch.Version - exit 0 - ;; - -p) - pidfile="$2" - shift 2 - ;; - -d) - daemonized="yes" - shift - ;; - -h) - usage - exit 0 - ;; - -D) - properties="$properties -D$2" - shift 2 - ;; - -X) - properties="$properties -X$2" - shift 2 - ;; - --) - shift - break - ;; - *) - echo "Error parsing argument $1!" >&2 - usage - exit 1 - ;; - esac -done - -# Start up the service -launch_service "$pidfile" "$daemonized" "$properties" +# manual parsing to find out, if process should be detached +daemonized=`echo $* | grep -E -- '(^-d |-d$| -d |--daemonize$|--daemonize )'` +if [ -z "$daemonized" ] ; then + eval exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS "\"-Des.path.home=$ES_HOME\"" -cp "\"$ES_CLASSPATH\"" \ + org.elasticsearch.bootstrap.Elasticsearch start $* +else + eval exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS "\"-Des.path.home=$ES_HOME\"" -cp "\"$ES_CLASSPATH\"" \ + org.elasticsearch.bootstrap.Elasticsearch start $* <&- & +fi exit $? diff --git a/distribution/src/main/resources/bin/elasticsearch.bat b/distribution/src/main/resources/bin/elasticsearch.bat index fb39780d01f..a0079fc967c 100644 --- a/distribution/src/main/resources/bin/elasticsearch.bat +++ b/distribution/src/main/resources/bin/elasticsearch.bat @@ -43,6 +43,6 @@ IF ERRORLEVEL 1 ( EXIT /B %ERRORLEVEL% ) -"%JAVA_HOME%\bin\java" %JAVA_OPTS% %ES_JAVA_OPTS% %ES_PARAMS% !newparams! -cp "%ES_CLASSPATH%" "org.elasticsearch.bootstrap.Elasticsearch" +"%JAVA_HOME%\bin\java" %JAVA_OPTS% %ES_JAVA_OPTS% %ES_PARAMS% !newparams! -cp "%ES_CLASSPATH%" "org.elasticsearch.bootstrap.Elasticsearch" start ENDLOCAL diff --git a/docs/reference/migration/migrate_2_0.asciidoc b/docs/reference/migration/migrate_2_0.asciidoc index b841a12cf63..c8aa72dd433 100644 --- a/docs/reference/migration/migrate_2_0.asciidoc +++ b/docs/reference/migration/migrate_2_0.asciidoc @@ -820,6 +820,13 @@ For the record, official plugins which can use this new simplified form are: * elasticsearch-lang-javascript * elasticsearch-lang-python +=== `/bin/elasticsearch` version needs `-V` parameter + +Due to switching to elasticsearchs internal command line parsing +infrastructure for the pluginmanager and the elasticsearch start up +script, the `-v` parameter now stands for `--verbose`, where as `-V` or +`--version` can be used to show the Elasticsearch version and exit. + === Aliases Fields used in alias filters no longer have to exist in the mapping upon alias creation time. Alias filters are now diff --git a/pom.xml b/pom.xml index 8092781a4cf..58b9f70d8fd 100644 --- a/pom.xml +++ b/pom.xml @@ -409,7 +409,7 @@ commons-cli commons-cli - 1.2 + 1.3.1