Merge pull request #12775 from cbuescher/fix/12715

Use 'name' from plugin descriptor file to determine plugin name
This commit is contained in:
Christoph Büscher 2015-08-19 12:33:34 +02:00
commit 4410287f42
13 changed files with 148 additions and 67 deletions

View File

@ -86,7 +86,11 @@ public class PluginInfo implements Streamable, ToXContent {
try (InputStream stream = Files.newInputStream(descriptor)) {
props.load(stream);
}
String name = dir.getFileName().toString();
String name = props.getProperty("name");
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Property [name] is missing in [" + descriptor + "]");
}
PluginManager.checkForForbiddenName(name);
String description = props.getProperty("description");
if (description == null) {
throw new IllegalArgumentException("Property [description] is missing for plugin [" + name + "]");
@ -95,6 +99,7 @@ public class PluginInfo implements Streamable, ToXContent {
if (version == null) {
throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]");
}
boolean jvm = Boolean.parseBoolean(props.getProperty("jvm"));
boolean site = Boolean.parseBoolean(props.getProperty("site"));
if (jvm == false && site == false) {

View File

@ -91,11 +91,11 @@ public class PluginManager {
).build();
private final Environment environment;
private String url;
private URL url;
private OutputMode outputMode;
private TimeValue timeout;
public PluginManager(Environment environment, String url, OutputMode outputMode, TimeValue timeout) {
public PluginManager(Environment environment, URL url, OutputMode outputMode, TimeValue timeout) {
this.environment = environment;
this.url = url;
this.outputMode = outputMode;
@ -103,8 +103,8 @@ public class PluginManager {
}
public void downloadAndExtract(String name, Terminal terminal) throws IOException {
if (name == null) {
throw new IllegalArgumentException("plugin name must be supplied with install [name].");
if (name == null && url == null) {
throw new IllegalArgumentException("plugin name or url must be supplied with install.");
}
if (!Files.exists(environment.pluginsFile())) {
@ -116,8 +116,14 @@ public class PluginManager {
throw new IOException("plugin directory " + environment.pluginsFile() + " is read only");
}
PluginHandle pluginHandle = PluginHandle.parse(name);
PluginHandle pluginHandle;
if (name != null) {
pluginHandle = PluginHandle.parse(name);
checkForForbiddenName(pluginHandle.name);
} else {
// if we have no name but url, use temporary name that will be overwritten later
pluginHandle = new PluginHandle("temp_name" + new Random().nextInt(), null, null);
}
Path pluginFile = download(pluginHandle, terminal);
extract(pluginHandle, terminal, pluginFile);
@ -138,7 +144,7 @@ public class PluginManager {
// first, try directly from the URL provided
if (url != null) {
URL pluginUrl = new URL(url);
URL pluginUrl = url;
boolean isSecureProcotol = "https".equalsIgnoreCase(pluginUrl.getProtocol());
boolean isAuthInfoSet = !Strings.isNullOrEmpty(pluginUrl.getUserInfo());
if (isAuthInfoSet && !isSecureProcotol) {
@ -204,10 +210,6 @@ public class PluginManager {
}
private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFile) throws IOException {
final Path extractLocation = pluginHandle.extractedDir(environment);
if (Files.exists(extractLocation)) {
throw new IOException("plugin directory " + extractLocation.toAbsolutePath() + " already exists. To update the plugin, uninstall it first using 'remove " + pluginHandle.name + "' command");
}
// unzip plugin to a staging temp dir, named for the plugin
Path tmp = Files.createTempDirectory(environment.tmpFile(), null);
@ -226,6 +228,13 @@ public class PluginManager {
jarHellCheck(root, info.isIsolated());
}
// update name in handle based on 'name' property found in descriptor file
pluginHandle = new PluginHandle(info.getName(), pluginHandle.version, pluginHandle.user);
final Path extractLocation = pluginHandle.extractedDir(environment);
if (Files.exists(extractLocation)) {
throw new IOException("plugin directory " + extractLocation.toAbsolutePath() + " already exists. To update the plugin, uninstall it first using 'remove " + pluginHandle.name + "' command");
}
// install plugin
FileSystemUtils.copyDirectoryRecursively(root, extractLocation);
terminal.println("Installed %s into %s", pluginHandle.name, extractLocation.toAbsolutePath());
@ -395,7 +404,7 @@ public class PluginManager {
}
}
private static void checkForForbiddenName(String name) {
static void checkForForbiddenName(String name) {
if (!hasLength(name) || BLACKLIST.contains(name.toLowerCase(Locale.ROOT))) {
throw new IllegalArgumentException("Illegal plugin name: " + name);
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.plugins;
import com.google.common.base.Strings;
import org.apache.commons.cli.CommandLine;
import org.elasticsearch.common.cli.CliTool;
import org.elasticsearch.common.cli.CliToolConfig;
@ -32,7 +33,8 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.node.internal.InternalSettingsPreparer;
import org.elasticsearch.plugins.PluginManager.OutputMode;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
@ -166,19 +168,29 @@ public class PluginManagerCliParser extends CliTool {
private static final String NAME = "install";
private static final CliToolConfig.Cmd CMD = cmd(NAME, Install.class)
.options(option("u", "url").required(false).hasArg(true))
.options(option("t", "timeout").required(false).hasArg(false))
.build();
static Command parse(Terminal terminal, CommandLine cli) {
String[] args = cli.getArgs();
// install [plugin-name/url]
if ((args == null) || (args.length == 0)) {
return exitCmd(ExitStatus.USAGE, terminal, "plugin name is missing (type -h for help)");
return exitCmd(ExitStatus.USAGE, terminal, "plugin name or url is missing (type -h for help)");
}
String name = args[0];
URL optionalPluginUrl = null;
// try parsing cli argument as URL
try {
optionalPluginUrl = new URL(name);
name = null;
} catch (MalformedURLException e) {
// we tried to parse the cli argument as url and failed
// continue treating it as a symbolic plugin name like `analysis-icu` etc.
}
String name = args[0];
TimeValue timeout = TimeValue.parseTimeValue(cli.getOptionValue("t"), DEFAULT_TIMEOUT, "cli");
String url = cli.getOptionValue("u");
OutputMode outputMode = OutputMode.DEFAULT;
if (cli.hasOption("s")) {
@ -188,15 +200,15 @@ public class PluginManagerCliParser extends CliTool {
outputMode = OutputMode.VERBOSE;
}
return new Install(terminal, name, outputMode, url, timeout);
return new Install(terminal, name, outputMode, optionalPluginUrl, timeout);
}
final String name;
private OutputMode outputMode;
final String url;
final URL url;
final TimeValue timeout;
Install(Terminal terminal, String name, OutputMode outputMode, String url, TimeValue timeout) {
Install(Terminal terminal, String name, OutputMode outputMode, URL url, TimeValue timeout) {
super(terminal);
this.name = name;
this.outputMode = outputMode;
@ -207,7 +219,11 @@ public class PluginManagerCliParser extends CliTool {
@Override
public ExitStatus execute(Settings settings, Environment env) throws Exception {
PluginManager pluginManager = new PluginManager(env, url, outputMode, timeout);
if (name != null) {
terminal.println("-> Installing " + Strings.nullToEmpty(name) + "...");
} else {
terminal.println("-> Installing from " + url + "...");
}
pluginManager.downloadAndExtract(name, terminal);
return ExitStatus.OK;
}

View File

@ -4,13 +4,13 @@ NAME
SYNOPSIS
plugin install <name>
plugin install <name or url>
DESCRIPTION
This command installs an elasticsearch plugin
<name> can be one of the official plugins, or refer to a github repository, or to one of the official plugins
The argument can be a <name> of one of the official plugins, or refer to a github repository
The notation of just specifying a plugin name, downloads an officially supported plugin.
@ -20,6 +20,8 @@ DESCRIPTION
The notation of 'username/repository' refers to a github repository.
The argument can be an valid <url> which points to a download or file location for the plugin to be loaded from.
EXAMPLES
plugin install analysis-kuromoji
@ -28,6 +30,10 @@ EXAMPLES
plugin install lmenezes/elasticsearch-kopf
plugin install http://download.elasticsearch.org/elasticsearch/elasticsearch-analysis-kuromoji/elasticsearch-analysis-kuromoji-2.7.0.zip
plugin install file:/path/to/plugin/elasticsearch-analysis-kuromoji-2.7.0.zip
OFFICIAL PLUGINS
The following plugins are officially supported and can be installed by just referring to their name
@ -49,8 +55,6 @@ OFFICIAL PLUGINS
OPTIONS
-u,--url URL to retrieve the plugin from
-t,--timeout Timeout until the plugin download is abort
-v,--verbose Verbose output

View File

@ -21,6 +21,7 @@ package org.elasticsearch.plugins;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo;
import org.elasticsearch.test.ESTestCase;
@ -53,13 +54,14 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertEquals("fake-plugin", info.getName());
assertEquals("my_plugin", info.getName());
assertEquals("fake desc", info.getDescription());
assertEquals("1.0", info.getVersion());
assertEquals("FakePlugin", info.getClassname());
@ -69,9 +71,28 @@ public class PluginInfoTests extends ESTestCase {
assertNull(info.getUrl());
}
public void testReadFromPropertiesDescriptionMissing() throws Exception {
public void testReadFromPropertiesNameMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir);
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected missing name exception");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Property [name] is missing in"));
}
writeProperties(pluginDir, "name", "");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected missing name exception");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Property [name] is missing in"));
}
}
public void testReadFromPropertiesDescriptionMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir, "name", "fake-plugin");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected missing description exception");
@ -82,7 +103,7 @@ public class PluginInfoTests extends ESTestCase {
public void testReadFromPropertiesVersionMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir, "description", "fake desc");
writeProperties(pluginDir, "description", "fake desc", "name", "fake-plugin");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected missing version exception");
@ -95,7 +116,8 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"version", "1.0");
"version", "1.0",
"name", "my_plugin");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected jvm or site exception");
@ -108,6 +130,7 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"jvm", "true");
try {
@ -122,6 +145,7 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"elasticsearch.version", Version.CURRENT.toString(),
"version", "1.0",
"jvm", "true");
@ -134,9 +158,11 @@ public class PluginInfoTests extends ESTestCase {
}
public void testReadFromPropertiesJavaVersionIncompatible() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
String pluginName = "fake-plugin";
Path pluginDir = createTempDir().resolve(pluginName);
writeProperties(pluginDir,
"description", "fake desc",
"name", pluginName,
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", "1000000.0",
"classname", "FakePlugin",
@ -146,7 +172,7 @@ public class PluginInfoTests extends ESTestCase {
PluginInfo.readFromProperties(pluginDir);
fail("expected incompatible java version exception");
} catch (IllegalStateException e) {
assertTrue(e.getMessage(), e.getMessage().contains("fake-plugin requires Java"));
assertTrue(e.getMessage(), e.getMessage().contains(pluginName + " requires Java"));
}
}
@ -156,6 +182,7 @@ public class PluginInfoTests extends ESTestCase {
"description", "fake desc",
"version", "1.0",
"jvm", "true",
"name", "my_plugin",
"elasticsearch.version", "bogus");
try {
PluginInfo.readFromProperties(pluginDir);
@ -169,6 +196,7 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"jvm", "true",
"elasticsearch.version", Version.V_1_7_0.toString());
@ -184,6 +212,7 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
@ -201,6 +230,7 @@ public class PluginInfoTests extends ESTestCase {
Files.createDirectories(pluginDir.resolve("_site"));
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"site", "true");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
@ -213,6 +243,7 @@ public class PluginInfoTests extends ESTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"site", "true");
try {

View File

@ -175,11 +175,13 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin");
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", "fake-plugin",
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin");
assertStatus("install --url " + pluginUrl, USAGE);
assertStatus("install", USAGE);
}
@Test
@ -194,6 +196,7 @@ public class PluginManagerIT extends ESIntegTestCase {
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
@ -205,7 +208,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginBinDir = binDir.resolve(pluginName);
Path pluginConfigDir = env.configFile().resolve(pluginName);
assertStatusOk("install " + pluginName + " --url " + pluginUrl + " --verbose");
assertStatusOk("install " + pluginUrl + " --verbose");
terminal.getTerminalOutput().clear();
assertStatusOk("list");
@ -239,6 +242,7 @@ public class PluginManagerIT extends ESIntegTestCase {
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
@ -248,7 +252,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Environment env = initialSettings.v2();
Path pluginConfigDir = env.configFile().resolve(pluginName);
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
/*
First time, our plugin contains:
@ -275,13 +279,14 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version1".getBytes(StandardCharsets.UTF_8));
pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "2.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin");
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertFileContent(pluginConfigDir, "test.txt", "version1");
assertFileContent(pluginConfigDir, "test.txt.new", "version2");
@ -311,13 +316,14 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version2".getBytes(StandardCharsets.UTF_8));
pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "3.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin");
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertFileContent(pluginConfigDir, "test.txt", "version1");
assertFileContent(pluginConfigDir, "test2.txt", "version1");
@ -339,6 +345,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.createFile(pluginDir.resolve("bin").resolve("tool"));;
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", "fake-plugin",
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
@ -349,7 +356,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Path binDir = env.binFile();
Path pluginBinDir = binDir.resolve(pluginName);
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertThatPluginIsListed(pluginName);
assertDirectoryExists(pluginBinDir);
}
@ -373,12 +380,13 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginDir = createTempDir().resolve(pluginName);
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin");
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertThatPluginIsListed(pluginName);
}
@ -390,9 +398,10 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.createFile(pluginDir.resolve("_site").resolve("somefile"));
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"site", "true");
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertThatPluginIsListed(pluginName);
// We want to check that Plugin Manager moves content to _site
assertFileExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site"));
@ -408,7 +417,7 @@ public class PluginManagerIT extends ESIntegTestCase {
"description", "fake desc",
"version", "1.0",
"site", "true");
assertStatus(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl),
assertStatus(String.format(Locale.ROOT, "install %s --verbose", pluginUrl),
ExitStatus.IO_ERROR);
assertThatPluginIsNotListed(pluginName);
assertFileNotExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site"));
@ -419,7 +428,7 @@ public class PluginManagerIT extends ESIntegTestCase {
if (pluginCoordinates == null) {
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginDescriptor));
} else {
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginDescriptor, pluginCoordinates));
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginCoordinates));
}
assertThatPluginIsListed(pluginName);
@ -496,6 +505,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginDir = createTempDir().resolve(pluginName);
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
@ -561,7 +571,7 @@ public class PluginManagerIT extends ESIntegTestCase {
@Test
public void testThatBasicAuthIsRejectedOnHttp() throws Exception {
assertStatus(String.format(Locale.ROOT, "install foo --url http://user:pass@localhost:12345/foo.zip --verbose"), CliTool.ExitStatus.IO_ERROR);
assertStatus(String.format(Locale.ROOT, "install http://user:pass@localhost:12345/foo.zip --verbose"), CliTool.ExitStatus.IO_ERROR);
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Basic auth is only supported for HTTPS!")));
}
@ -598,7 +608,7 @@ public class PluginManagerIT extends ESIntegTestCase {
Channel channel = serverBootstrap.bind(new InetSocketAddress("localhost", 0));
int port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
// IO_ERROR because there is no real file delivered...
assertStatus(String.format(Locale.ROOT, "install foo --url https://user:pass@localhost:%s/foo.zip --verbose --timeout 1s", port), ExitStatus.IO_ERROR);
assertStatus(String.format(Locale.ROOT, "install https://user:pass@localhost:%s/foo.zip --verbose --timeout 1s", port), ExitStatus.IO_ERROR);
// ensure that we did not try any other data source like download.elastic.co, in case we specified our own local URL
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("download.elastic.co"))));

View File

@ -1,3 +1,4 @@
site=true
description=anotherplugin
version=1.0
name=anotherplugin

View File

@ -1,3 +1,4 @@
site=true
description=dummy
version=1.0
name=dummy

View File

@ -1,3 +1,4 @@
site=true
description=subdir
version=1.0
name=subdir

View File

@ -87,8 +87,6 @@
<run-script script="@{home}/bin/plugin">
<nested>
<arg value="install"/>
<arg value="@{name}"/>
<arg value="-u"/>
<arg value="${url}"/>
</nested>
</run-script>

View File

@ -36,6 +36,9 @@ description=${project.description}
# 'version': plugin's version
version=${project.version}
#
# 'name': the plugin name
name=${elasticsearch.plugin.name}
### mandatory elements for site plugins:
#
# 'site': set to true to indicate contents of the _site/

View File

@ -84,15 +84,15 @@ A plugin can also be downloaded directly from a custom location by specifying th
[source,shell]
-----------------------------------
sudo bin/plugin install [plugin-name] --url [url] <1>
sudo bin/plugin install [url] <1>
-----------------------------------
<1> Both the URL and the plugin name must be specified.
<1> must be a valid URL, the plugin name is determined from its descriptor.
For instance, to install a plugin from your local file system, you could run:
[source,shell]
-----------------------------------
sudo bin/plugin install my_plugin --url file:/path/to/plugin.zip
sudo bin/plugin install file:/path/to/plugin.zip
-----------------------------------
[[listing-removing]]

View File

@ -22,6 +22,7 @@
<properties>
<elasticsearch.assembly.descriptor>${elasticsearch.tools.directory}/plugin-metadata/plugin-assembly.xml</elasticsearch.assembly.descriptor>
<elasticsearch.assembly.appendId>false</elasticsearch.assembly.appendId>
<elasticsearch.plugin.name>${project.artifactId}</elasticsearch.plugin.name>
<elasticsearch.plugin.jvm>true</elasticsearch.plugin.jvm>
<elasticsearch.plugin.isolated>true</elasticsearch.plugin.isolated>
<elasticsearch.plugin.site>false</elasticsearch.plugin.site>
@ -365,6 +366,7 @@
<rules>
<requireProperty>
<property>elasticsearch.plugin.classname</property>
<property>elasticsearch.plugin.name</property>
</requireProperty>
</rules>
</configuration>