Plugins: Add 'name' property to plugin descriptor file to determine plugin name

At the moment, when installing from an url, a user provides the plugin name on
the command line like:

* bin/plugin install [plugin-name] --url [url]

This can lead to problems when picking an already existing name from another
plugin, and can potentially overwrite plugins already installed with that name.

This, this PR introduces a mandatory `name` property to the plugin descriptor
file which replaces the name formerly provided by the user.

With the addition of the `name` property to the plugin descriptor file, the user
does not need to specify the plugin name any longer when installing from a file
or url. Because of this, all arguments to `plugin install` command are now
either treated as a symbolic name, a URL or a file without the need to specify
this with an explicit option.

The new syntax for `plugin install` is now:

bin/plugin install [name or url]

* downloads official plugin
bin/plugin install analysis-kuromoji

* downloads github plugin
bin/plugin install lmenezes/elasticsearch-kopf

* install from URL or file
bin/plugin install http://link.to/foo.zip
bin/plugin install file:/path/to/foo.zip

If the argument does not parse to a valid URL, it is assumed to be a name and the
download location is resolved like before. Regardless of the source location of
the plugin, it is extracted to a temporary directory and the `name` property from
the descriptor file is used to determine the final install location.

Relates to #12715
This commit is contained in:
Christoph Büscher 2015-08-07 16:05:39 +02:00
parent 66b0e7a6e1
commit 8454d49552
13 changed files with 148 additions and 67 deletions

View File

@ -52,7 +52,7 @@ public class PluginInfo implements Streamable, ToXContent {
private String description; private String description;
private boolean site; private boolean site;
private String version; private String version;
private boolean jvm; private boolean jvm;
private String classname; private String classname;
private boolean isolated; private boolean isolated;
@ -86,7 +86,11 @@ public class PluginInfo implements Streamable, ToXContent {
try (InputStream stream = Files.newInputStream(descriptor)) { try (InputStream stream = Files.newInputStream(descriptor)) {
props.load(stream); 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"); String description = props.getProperty("description");
if (description == null) { if (description == null) {
throw new IllegalArgumentException("Property [description] is missing for plugin [" + name + "]"); throw new IllegalArgumentException("Property [description] is missing for plugin [" + name + "]");
@ -95,6 +99,7 @@ public class PluginInfo implements Streamable, ToXContent {
if (version == null) { if (version == null) {
throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]"); throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]");
} }
boolean jvm = Boolean.parseBoolean(props.getProperty("jvm")); boolean jvm = Boolean.parseBoolean(props.getProperty("jvm"));
boolean site = Boolean.parseBoolean(props.getProperty("site")); boolean site = Boolean.parseBoolean(props.getProperty("site"));
if (jvm == false && site == false) { if (jvm == false && site == false) {
@ -122,7 +127,7 @@ public class PluginInfo implements Streamable, ToXContent {
throw new IllegalArgumentException("Property [classname] is missing for jvm plugin [" + name + "]"); throw new IllegalArgumentException("Property [classname] is missing for jvm plugin [" + name + "]");
} }
} }
if (site) { if (site) {
if (!Files.exists(dir.resolve("_site"))) { if (!Files.exists(dir.resolve("_site"))) {
throw new IllegalArgumentException("Plugin [" + name + "] is a site plugin but has no '_site/' directory"); throw new IllegalArgumentException("Plugin [" + name + "] is a site plugin but has no '_site/' directory");
@ -159,14 +164,14 @@ public class PluginInfo implements Streamable, ToXContent {
public boolean isJvm() { public boolean isJvm() {
return jvm; return jvm;
} }
/** /**
* @return true if jvm plugin has isolated classloader * @return true if jvm plugin has isolated classloader
*/ */
public boolean isIsolated() { public boolean isIsolated() {
return isolated; return isolated;
} }
/** /**
* @return jvm plugin's classname * @return jvm plugin's classname
*/ */

View File

@ -91,11 +91,11 @@ public class PluginManager {
).build(); ).build();
private final Environment environment; private final Environment environment;
private String url; private URL url;
private OutputMode outputMode; private OutputMode outputMode;
private TimeValue timeout; 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.environment = environment;
this.url = url; this.url = url;
this.outputMode = outputMode; this.outputMode = outputMode;
@ -103,8 +103,8 @@ public class PluginManager {
} }
public void downloadAndExtract(String name, Terminal terminal) throws IOException { public void downloadAndExtract(String name, Terminal terminal) throws IOException {
if (name == null) { if (name == null && url == null) {
throw new IllegalArgumentException("plugin name must be supplied with install [name]."); throw new IllegalArgumentException("plugin name or url must be supplied with install.");
} }
if (!Files.exists(environment.pluginsFile())) { if (!Files.exists(environment.pluginsFile())) {
@ -116,8 +116,14 @@ public class PluginManager {
throw new IOException("plugin directory " + environment.pluginsFile() + " is read only"); throw new IOException("plugin directory " + environment.pluginsFile() + " is read only");
} }
PluginHandle pluginHandle = PluginHandle.parse(name); PluginHandle pluginHandle;
checkForForbiddenName(pluginHandle.name); 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); Path pluginFile = download(pluginHandle, terminal);
extract(pluginHandle, terminal, pluginFile); extract(pluginHandle, terminal, pluginFile);
@ -138,7 +144,7 @@ public class PluginManager {
// first, try directly from the URL provided // first, try directly from the URL provided
if (url != null) { if (url != null) {
URL pluginUrl = new URL(url); URL pluginUrl = url;
boolean isSecureProcotol = "https".equalsIgnoreCase(pluginUrl.getProtocol()); boolean isSecureProcotol = "https".equalsIgnoreCase(pluginUrl.getProtocol());
boolean isAuthInfoSet = !Strings.isNullOrEmpty(pluginUrl.getUserInfo()); boolean isAuthInfoSet = !Strings.isNullOrEmpty(pluginUrl.getUserInfo());
if (isAuthInfoSet && !isSecureProcotol) { if (isAuthInfoSet && !isSecureProcotol) {
@ -204,14 +210,10 @@ public class PluginManager {
} }
private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFile) throws IOException { 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 // unzip plugin to a staging temp dir, named for the plugin
Path tmp = Files.createTempDirectory(environment.tmpFile(), null); Path tmp = Files.createTempDirectory(environment.tmpFile(), null);
Path root = tmp.resolve(pluginHandle.name); Path root = tmp.resolve(pluginHandle.name);
unzipPlugin(pluginFile, root); unzipPlugin(pluginFile, root);
// find the actual root (in case its unzipped with extra directory wrapping) // find the actual root (in case its unzipped with extra directory wrapping)
@ -226,6 +228,13 @@ public class PluginManager {
jarHellCheck(root, info.isIsolated()); 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 // install plugin
FileSystemUtils.copyDirectoryRecursively(root, extractLocation); FileSystemUtils.copyDirectoryRecursively(root, extractLocation);
terminal.println("Installed %s into %s", pluginHandle.name, extractLocation.toAbsolutePath()); terminal.println("Installed %s into %s", pluginHandle.name, extractLocation.toAbsolutePath());
@ -334,7 +343,7 @@ public class PluginManager {
private void unzipPlugin(Path zip, Path target) throws IOException { private void unzipPlugin(Path zip, Path target) throws IOException {
Files.createDirectories(target); Files.createDirectories(target);
try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) { try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) {
ZipEntry entry; ZipEntry entry;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
@ -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))) { if (!hasLength(name) || BLACKLIST.contains(name.toLowerCase(Locale.ROOT))) {
throw new IllegalArgumentException("Illegal plugin name: " + name); throw new IllegalArgumentException("Illegal plugin name: " + name);
} }

View File

@ -20,6 +20,7 @@
package org.elasticsearch.plugins; package org.elasticsearch.plugins;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.elasticsearch.common.cli.CliTool; import org.elasticsearch.common.cli.CliTool;
import org.elasticsearch.common.cli.CliToolConfig; import org.elasticsearch.common.cli.CliToolConfig;
@ -32,7 +33,8 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.node.internal.InternalSettingsPreparer; import org.elasticsearch.node.internal.InternalSettingsPreparer;
import org.elasticsearch.plugins.PluginManager.OutputMode; import org.elasticsearch.plugins.PluginManager.OutputMode;
import java.io.IOException; import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale; import java.util.Locale;
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd; 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 String NAME = "install";
private static final CliToolConfig.Cmd CMD = cmd(NAME, Install.class) 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)) .options(option("t", "timeout").required(false).hasArg(false))
.build(); .build();
static Command parse(Terminal terminal, CommandLine cli) { static Command parse(Terminal terminal, CommandLine cli) {
String[] args = cli.getArgs(); String[] args = cli.getArgs();
// install [plugin-name/url]
if ((args == null) || (args.length == 0)) { 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"); TimeValue timeout = TimeValue.parseTimeValue(cli.getOptionValue("t"), DEFAULT_TIMEOUT, "cli");
String url = cli.getOptionValue("u");
OutputMode outputMode = OutputMode.DEFAULT; OutputMode outputMode = OutputMode.DEFAULT;
if (cli.hasOption("s")) { if (cli.hasOption("s")) {
@ -188,15 +200,15 @@ public class PluginManagerCliParser extends CliTool {
outputMode = OutputMode.VERBOSE; outputMode = OutputMode.VERBOSE;
} }
return new Install(terminal, name, outputMode, url, timeout); return new Install(terminal, name, outputMode, optionalPluginUrl, timeout);
} }
final String name; final String name;
private OutputMode outputMode; private OutputMode outputMode;
final String url; final URL url;
final TimeValue timeout; 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); super(terminal);
this.name = name; this.name = name;
this.outputMode = outputMode; this.outputMode = outputMode;
@ -207,7 +219,11 @@ public class PluginManagerCliParser extends CliTool {
@Override @Override
public ExitStatus execute(Settings settings, Environment env) throws Exception { public ExitStatus execute(Settings settings, Environment env) throws Exception {
PluginManager pluginManager = new PluginManager(env, url, outputMode, timeout); PluginManager pluginManager = new PluginManager(env, url, outputMode, timeout);
terminal.println("-> Installing " + Strings.nullToEmpty(name) + "..."); if (name != null) {
terminal.println("-> Installing " + Strings.nullToEmpty(name) + "...");
} else {
terminal.println("-> Installing from " + url + "...");
}
pluginManager.downloadAndExtract(name, terminal); pluginManager.downloadAndExtract(name, terminal);
return ExitStatus.OK; return ExitStatus.OK;
} }

View File

@ -4,13 +4,13 @@ NAME
SYNOPSIS SYNOPSIS
plugin install <name> plugin install <name or url>
DESCRIPTION DESCRIPTION
This command installs an elasticsearch plugin 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. 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 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 EXAMPLES
plugin install analysis-kuromoji plugin install analysis-kuromoji
@ -28,6 +30,10 @@ EXAMPLES
plugin install lmenezes/elasticsearch-kopf 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 OFFICIAL PLUGINS
The following plugins are officially supported and can be installed by just referring to their name The following plugins are officially supported and can be installed by just referring to their name
@ -49,8 +55,6 @@ OFFICIAL PLUGINS
OPTIONS OPTIONS
-u,--url URL to retrieve the plugin from
-t,--timeout Timeout until the plugin download is abort -t,--timeout Timeout until the plugin download is abort
-v,--verbose Verbose output -v,--verbose Verbose output

View File

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

View File

@ -175,11 +175,13 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginDir = createTempDir().resolve("fake-plugin"); Path pluginDir = createTempDir().resolve("fake-plugin");
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", "fake-plugin",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
assertStatus("install --url " + pluginUrl, USAGE); assertStatus("install", USAGE);
} }
@Test @Test
@ -191,21 +193,22 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.createFile(pluginDir.resolve("bin").resolve("tool")); Files.createFile(pluginDir.resolve("bin").resolve("tool"));
Files.createDirectories(pluginDir.resolve("config")); Files.createDirectories(pluginDir.resolve("config"));
Files.createFile(pluginDir.resolve("config").resolve("file")); Files.createFile(pluginDir.resolve("config").resolve("file"));
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Environment env = initialSettings.v2(); Environment env = initialSettings.v2();
Path binDir = env.binFile(); Path binDir = env.binFile();
Path pluginBinDir = binDir.resolve(pluginName); Path pluginBinDir = binDir.resolve(pluginName);
Path pluginConfigDir = env.configFile().resolve(pluginName); Path pluginConfigDir = env.configFile().resolve(pluginName);
assertStatusOk("install " + pluginName + " --url " + pluginUrl + " --verbose"); assertStatusOk("install " + pluginUrl + " --verbose");
terminal.getTerminalOutput().clear(); terminal.getTerminalOutput().clear();
assertStatusOk("list"); assertStatusOk("list");
@ -236,19 +239,20 @@ public class PluginManagerIT extends ESIntegTestCase {
// create config/test.txt with contents 'version1' // create config/test.txt with contents 'version1'
Files.createDirectories(pluginDir.resolve("config")); Files.createDirectories(pluginDir.resolve("config"));
Files.write(pluginDir.resolve("config").resolve("test.txt"), "version1".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("test.txt"), "version1".getBytes(StandardCharsets.UTF_8));
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Environment env = initialSettings.v2(); Environment env = initialSettings.v2();
Path pluginConfigDir = env.configFile().resolve(pluginName); 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: 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)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version1".getBytes(StandardCharsets.UTF_8));
pluginUrl = createPlugin(pluginDir, pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "2.0", "version", "2.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "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", "version1");
assertFileContent(pluginConfigDir, "test.txt.new", "version2"); 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)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version2".getBytes(StandardCharsets.UTF_8));
pluginUrl = createPlugin(pluginDir, pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "3.0", "version", "3.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "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", "version1");
assertFileContent(pluginConfigDir, "test2.txt", "version1"); assertFileContent(pluginConfigDir, "test2.txt", "version1");
@ -339,17 +345,18 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.createFile(pluginDir.resolve("bin").resolve("tool"));; Files.createFile(pluginDir.resolve("bin").resolve("tool"));;
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", "fake-plugin",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Environment env = initialSettings.v2(); Environment env = initialSettings.v2();
Path binDir = env.binFile(); Path binDir = env.binFile();
Path pluginBinDir = binDir.resolve(pluginName); 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); assertThatPluginIsListed(pluginName);
assertDirectoryExists(pluginBinDir); assertDirectoryExists(pluginBinDir);
} }
@ -373,12 +380,13 @@ public class PluginManagerIT extends ESIntegTestCase {
Path pluginDir = createTempDir().resolve(pluginName); Path pluginDir = createTempDir().resolve(pluginName);
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "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); assertThatPluginIsListed(pluginName);
} }
@ -389,10 +397,11 @@ public class PluginManagerIT extends ESIntegTestCase {
Files.createDirectories(pluginDir.resolve("_site")); Files.createDirectories(pluginDir.resolve("_site"));
Files.createFile(pluginDir.resolve("_site").resolve("somefile")); Files.createFile(pluginDir.resolve("_site").resolve("somefile"));
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"version", "1.0", "name", pluginName,
"site", "true"); "version", "1.0",
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl)); "site", "true");
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
assertThatPluginIsListed(pluginName); assertThatPluginIsListed(pluginName);
// We want to check that Plugin Manager moves content to _site // We want to check that Plugin Manager moves content to _site
assertFileExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site")); assertFileExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site"));
@ -408,7 +417,7 @@ public class PluginManagerIT extends ESIntegTestCase {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"site", "true"); "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); ExitStatus.IO_ERROR);
assertThatPluginIsNotListed(pluginName); assertThatPluginIsNotListed(pluginName);
assertFileNotExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site")); assertFileNotExists(initialSettings.v2().pluginsFile().resolve(pluginName).resolve("_site"));
@ -419,7 +428,7 @@ public class PluginManagerIT extends ESIntegTestCase {
if (pluginCoordinates == null) { if (pluginCoordinates == null) {
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginDescriptor)); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginDescriptor));
} else { } else {
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginDescriptor, pluginCoordinates)); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginCoordinates));
} }
assertThatPluginIsListed(pluginName); assertThatPluginIsListed(pluginName);
@ -493,15 +502,16 @@ public class PluginManagerIT extends ESIntegTestCase {
@Test @Test
public void testRemovePlugin() throws Exception { public void testRemovePlugin() throws Exception {
String pluginName = "plugintest"; String pluginName = "plugintest";
Path pluginDir = createTempDir().resolve(pluginName); Path pluginDir = createTempDir().resolve(pluginName);
String pluginUrl = createPlugin(pluginDir, String pluginUrl = createPlugin(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", pluginName,
"version", "1.0.0", "version", "1.0.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
// We want to remove plugin with plugin short name // We want to remove plugin with plugin short name
singlePluginInstallAndRemove("plugintest", "plugintest", pluginUrl); singlePluginInstallAndRemove("plugintest", "plugintest", pluginUrl);
@ -561,7 +571,7 @@ public class PluginManagerIT extends ESIntegTestCase {
@Test @Test
public void testThatBasicAuthIsRejectedOnHttp() throws Exception { 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!"))); 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)); Channel channel = serverBootstrap.bind(new InetSocketAddress("localhost", 0));
int port = ((InetSocketAddress) channel.getLocalAddress()).getPort(); int port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
// IO_ERROR because there is no real file delivered... // 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 // 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")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("download.elastic.co"))));

View File

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

View File

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

View File

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

View File

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

View File

@ -36,6 +36,9 @@ description=${project.description}
# 'version': plugin's version # 'version': plugin's version
version=${project.version} version=${project.version}
# #
# 'name': the plugin name
name=${elasticsearch.plugin.name}
### mandatory elements for site plugins: ### mandatory elements for site plugins:
# #
# 'site': set to true to indicate contents of the _site/ # '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] [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: For instance, to install a plugin from your local file system, you could run:
[source,shell] [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]] [[listing-removing]]

View File

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