Simplify Plugin Manager for official plugins

Plugin Manager can now use another simplified form when a user wants to install an official plugin hosted at elasticsearch download service.

The form we use is:

```sh
bin/plugin install pluginname
```

As plugins share now the same version as elasticsearch, we can automatically guess what is the exact current version of the plugin manager script.

Also, download service will now use `/org.elasticsearch.plugins/pluginName/pluginName-version.zip` URL path to download a plugin.

If the older form is provided (`user/plugin/version` or `user/plugin`), we will still use:

 * elasticsearch download service at `/user/plugin/plugin-version.zip`
 * maven central with groupIp=user, artifactId=plugin and version=version
 * github with user=user, repoName=plugin and tag=version
 * github with user=user, repoName=plugin and branch=master if no version is set

Note that community plugin providers can use other download services by using `--url` option.

If you try to use the new form with a non core elasticsearch plugin, the plugin manager will reject
it and will give you all known core plugins.

```
Usage:
    -u, --url     [plugin location]   : Set exact URL to download the plugin from
    -i, --install [plugin name]       : Downloads and installs listed plugins [*]
    -t, --timeout [duration]          : Timeout setting: 30s, 1m, 1h... (infinite by default)
    -r, --remove  [plugin name]       : Removes listed plugins
    -l, --list                        : List installed plugins
    -v, --verbose                     : Prints verbose messages
    -s, --silent                      : Run in silent mode
    -h, --help                        : Prints this help message

 [*] Plugin name could be:
     elasticsearch-plugin-name    for Elasticsearch 2.0 Core plugin (download from download.elastic.co)
     elasticsearch/plugin/version for elasticsearch commercial plugins (download from download.elastic.co)
     groupId/artifactId/version   for community plugins (download from maven central or oss sonatype)
     username/repository          for site plugins (download from github master)

Elasticsearch Core plugins:
 - elasticsearch-analysis-icu
 - elasticsearch-analysis-kuromoji
 - elasticsearch-analysis-phonetic
 - elasticsearch-analysis-smartcn
 - elasticsearch-analysis-stempel
 - elasticsearch-cloud-aws
 - elasticsearch-cloud-azure
 - elasticsearch-cloud-gce
 - elasticsearch-delete-by-query
 - elasticsearch-lang-javascript
 - elasticsearch-lang-python
```
This commit is contained in:
David Pilato 2015-06-22 13:22:54 +02:00
parent 992716ac5c
commit d57de59158
5 changed files with 149 additions and 21 deletions

View File

@ -73,7 +73,7 @@ public class PluginManager {
// By default timeout is 0 which means no timeout
public static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueMillis(0);
private static final ImmutableSet<Object> BLACKLIST = ImmutableSet.builder()
private static final ImmutableSet<String> BLACKLIST = ImmutableSet.<String>builder()
.add("elasticsearch",
"elasticsearch.bat",
"elasticsearch.in.sh",
@ -81,6 +81,21 @@ public class PluginManager {
"plugin.bat",
"service.bat").build();
private static final ImmutableSet<String> OFFICIAL_PLUGINS = ImmutableSet.<String>builder()
.add(
"elasticsearch-analysis-icu",
"elasticsearch-analysis-kuromoji",
"elasticsearch-analysis-phonetic",
"elasticsearch-analysis-smartcn",
"elasticsearch-analysis-stempel",
"elasticsearch-cloud-aws",
"elasticsearch-cloud-azure",
"elasticsearch-cloud-gce",
"elasticsearch-delete-by-query",
"elasticsearch-lang-javascript",
"elasticsearch-lang-python"
).build();
private final Environment environment;
private String url;
private OutputMode outputMode;
@ -133,6 +148,10 @@ public class PluginManager {
// ignore
log("Failed: " + ExceptionsHelper.detailedMessage(e));
}
} else {
if (PluginHandle.isOfficialPlugin(pluginHandle.repo, pluginHandle.user, pluginHandle.version)) {
checkForOfficialPlugins(pluginHandle.name);
}
}
if (!downloaded) {
@ -384,6 +403,15 @@ public class PluginManager {
}
}
protected static void checkForOfficialPlugins(String name) {
// We make sure that users can use only new short naming for official plugins only
if (!OFFICIAL_PLUGINS.contains(name)) {
throw new IllegalArgumentException(name +
" is not an official plugin so you should install it using elasticsearch/" +
name + "/latest naming form.");
}
}
public Path[] getListInstalledPlugins() throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(environment.pluginsFile())) {
return Iterators.toArray(stream.iterator(), Path.class);
@ -597,9 +625,15 @@ public class PluginManager {
SysOut.println(" -h, --help : Prints this help message");
SysOut.newline();
SysOut.println(" [*] Plugin name could be:");
SysOut.println(" elasticsearch/plugin/version for official elasticsearch plugins (download from download.elasticsearch.org)");
SysOut.println(" elasticsearch-plugin-name for Elasticsearch 2.0 Core plugin (download from download.elastic.co)");
SysOut.println(" elasticsearch/plugin/version for elasticsearch commercial plugins (download from download.elastic.co)");
SysOut.println(" groupId/artifactId/version for community plugins (download from maven central or oss sonatype)");
SysOut.println(" username/repository for site plugins (download from github master)");
SysOut.newline();
SysOut.println("Elasticsearch Core plugins:");
for (String o : OFFICIAL_PLUGINS) {
SysOut.println(" - " + o);
}
if (message != null) {
SysOut.newline();
@ -652,17 +686,26 @@ public class PluginManager {
List<URL> urls() {
List<URL> urls = new ArrayList<>();
if (version != null) {
// Elasticsearch download service
addUrl(urls, "http://download.elasticsearch.org/" + user + "/" + repo + "/" + repo + "-" + version + ".zip");
// Maven central repository
addUrl(urls, "http://search.maven.org/remotecontent?filepath=" + user.replace('.', '/') + "/" + repo + "/" + version + "/" + repo + "-" + version + ".zip");
// Sonatype repository
addUrl(urls, "https://oss.sonatype.org/service/local/repositories/releases/content/" + user.replace('.', '/') + "/" + repo + "/" + version + "/" + repo + "-" + version + ".zip");
// Github repository
addUrl(urls, "https://github.com/" + user + "/" + repo + "/archive/" + version + ".zip");
// Elasticsearch new download service uses groupId org.elasticsearch.plugins from 2.0.0
if (user == null) {
// TODO Update to https
addUrl(urls, String.format(Locale.ROOT, "http://download.elastic.co/org.elasticsearch.plugins/%1$s/%1$s-%2$s.zip", repo, version));
} else {
// Elasticsearch old download service
// TODO Update to https
addUrl(urls, String.format(Locale.ROOT, "http://download.elastic.co/%1$s/%2$s/%2$s-%3$s.zip", user, repo, version));
// Maven central repository
addUrl(urls, String.format(Locale.ROOT, "http://search.maven.org/remotecontent?filepath=%1$s/%2$s/%3$s/%2$s-%3$s.zip", user.replace('.', '/'), repo, version));
// Sonatype repository
addUrl(urls, String.format(Locale.ROOT, "https://oss.sonatype.org/service/local/repositories/releases/content/%1$s/%2$s/%3$s/%2$s-%3$s.zip", user.replace('.', '/'), repo, version));
// Github repository
addUrl(urls, String.format(Locale.ROOT, "https://github.com/%1$s/%2$s/archive/%3$s.zip", user, repo, version));
}
}
if (user != null) {
// Github repository for master branch (assume site)
addUrl(urls, String.format(Locale.ROOT, "https://github.com/%1$s/%2$s/archive/master.zip", user, repo));
}
// Github repository for master branch (assume site)
addUrl(urls, "https://github.com/" + user + "/" + repo + "/archive/master.zip");
return urls;
}
@ -708,6 +751,10 @@ public class PluginManager {
}
}
if (isOfficialPlugin(repo, user, version)) {
return new PluginHandle(repo, Version.CURRENT.number(), null, repo);
}
if (repo.startsWith("elasticsearch-")) {
// remove elasticsearch- prefix
String endname = repo.substring("elasticsearch-".length());
@ -722,6 +769,10 @@ public class PluginManager {
return new PluginHandle(repo, version, user, repo);
}
static boolean isOfficialPlugin(String repo, String user, String version) {
return version == null && user == null && !Strings.isNullOrEmpty(repo);
}
}
}

View File

@ -54,9 +54,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertDire
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.*;
@ClusterScope(scope = Scope.TEST, numDataNodes = 0, transportClientRatio = 0.0)
@LuceneTestCase.SuppressFileSystems("*") // TODO: clean up this test to allow extra files
@ -513,6 +511,27 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
}
}
@Test
public void testOfficialPluginName_ThrowsException() throws IOException {
PluginManager.checkForOfficialPlugins("elasticsearch-analysis-icu");
PluginManager.checkForOfficialPlugins("elasticsearch-analysis-kuromoji");
PluginManager.checkForOfficialPlugins("elasticsearch-analysis-phonetic");
PluginManager.checkForOfficialPlugins("elasticsearch-analysis-smartcn");
PluginManager.checkForOfficialPlugins("elasticsearch-analysis-stempel");
PluginManager.checkForOfficialPlugins("elasticsearch-cloud-aws");
PluginManager.checkForOfficialPlugins("elasticsearch-cloud-azure");
PluginManager.checkForOfficialPlugins("elasticsearch-cloud-gce");
PluginManager.checkForOfficialPlugins("elasticsearch-delete-by-query");
PluginManager.checkForOfficialPlugins("elasticsearch-lang-javascript");
PluginManager.checkForOfficialPlugins("elasticsearch-lang-python");
try {
PluginManager.checkForOfficialPlugins("elasticsearch-mapper-attachment");
fail("elasticsearch-mapper-attachment should not be allowed");
} catch (IllegalArgumentException e) {
// We expect that error
}
}
/**
* Retrieve a URL string that represents the resource with the given {@code resourceName}.

View File

@ -20,16 +20,18 @@
package org.elasticsearch.plugins;
import com.google.common.io.Files;
import org.elasticsearch.Version;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
/**
@ -55,4 +57,14 @@ public class PluginManagerUnitTests extends ElasticsearchTestCase {
assertThat(configDirPath, is(expectedDirPath));
}
@Test
public void testSimplifiedNaming() throws IOException {
String pluginName = randomAsciiOfLength(10);
PluginManager.PluginHandle handle = PluginManager.PluginHandle.parse(pluginName);
assertThat(handle.urls(), hasSize(1));
URL expected = new URL("http", "download.elastic.co", "/org.elasticsearch.plugins/" + pluginName + "/" +
pluginName + "-" + Version.CURRENT.number() + ".zip");
assertThat(handle.urls().get(0), is(expected));
}
}

View File

@ -731,3 +731,42 @@ to prevent clashes with the watcher plugin
=== Percolator stats
Changed the `percolate.getTime` stat (total time spent on percolating) to `percolate.time` state.
=== Plugin Manager for official plugins
Some of the elasticsearch official plugins have been moved to elasticsearch repository and will be released at the
same time as elasticsearch itself, using the same version number.
In that case, the plugin manager can now use a simpler form to identify an official plugin. Instead of:
[source,sh]
---------------
bin/plugin install elasticsearch/plugin_name/version
---------------
You can use:
[source,sh]
---------------
bin/plugin install plugin_name
---------------
The plugin manager will recognize this form and will be able to download the right version for your elasticsearch
version.
For older versions of elasticsearch, you still have to use the older form.
For the record, official plugins which can use this new simplified form are:
* elasticsearch-analysis-icu
* elasticsearch-analysis-kuromoji
* elasticsearch-analysis-phonetic
* elasticsearch-analysis-smartcn
* elasticsearch-analysis-stempel
* elasticsearch-cloud-aws
* elasticsearch-cloud-azure
* elasticsearch-cloud-gce
* elasticsearch-delete-by-query
* elasticsearch-lang-javascript
* elasticsearch-lang-python

View File

@ -14,19 +14,26 @@ and more.
==== Installing plugins
Installing plugins can either be done manually by placing them under the
`plugins` directory, or using the `plugin` script. Several plugins can
be found under the https://github.com/elasticsearch[elasticsearch]
organization in GitHub, starting with `elasticsearch-`.
`plugins` directory, or using the `plugin` script.
Installing plugins typically take the following form:
[source,shell]
-----------------------------------
bin/plugin --install plugin_name
-----------------------------------
The plugin will be automatically downloaded in this case from `download.elastic.co` download service using the
same version as your elasticsearch version.
For older version of elasticsearch (prior to 2.0.0) or community plugins, you would use the following form:
[source,shell]
-----------------------------------
bin/plugin --install <org>/<user/component>/<version>
-----------------------------------
The plugins will be
automatically downloaded in this case from `download.elastic.co`,
The plugins will be automatically downloaded in this case from `download.elastic.co` (for older plugins),
and in case they don't exist there, from maven (central and sonatype).
Note that when the plugin is located in maven central or sonatype