Plugin Manager should support -remove group/artifact/version naming
When installing a plugin, we use: ```sh bin/plugin --install groupid/artifactid/version ``` But when removing the plugin, we only support: ```sh bin/plugin --remove dirname ``` where `dirname` is the directory name of the plugin under `/plugins` dir. Closes #3421.
This commit is contained in:
parent
f3c0e39380
commit
764aa54f2d
|
@ -89,6 +89,19 @@ will not start.
|
||||||
A list of the currently loaded plugins can be retrieved using the
|
A list of the currently loaded plugins can be retrieved using the
|
||||||
<<cluster-nodes-info,Node Info API>>.
|
<<cluster-nodes-info,Node Info API>>.
|
||||||
|
|
||||||
|
[float]
|
||||||
|
==== Removing plugins
|
||||||
|
|
||||||
|
Removing plugins can either be done manually by removing them under the
|
||||||
|
`plugins` directory, or using the `plugin` script.
|
||||||
|
|
||||||
|
Removing plugins typically take the following form:
|
||||||
|
|
||||||
|
[source,shell]
|
||||||
|
-----------------------------------
|
||||||
|
plugin --remove <pluginname>
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
=== Known Plugins
|
=== Known Plugins
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,9 @@ import javax.net.ssl.X509TrustManager;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Enumeration;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
@ -97,7 +96,13 @@ public class PluginManager {
|
||||||
throw new IOException("plugin directory " + environment.pluginsFile() + " is read only");
|
throw new IOException("plugin directory " + environment.pluginsFile() + " is read only");
|
||||||
}
|
}
|
||||||
|
|
||||||
File pluginFile = new File(environment.pluginsFile(), name + ".zip");
|
PluginHandle pluginHandle = PluginHandle.parse(name);
|
||||||
|
File pluginFile = pluginHandle.distroFile(environment);
|
||||||
|
// extract the plugin
|
||||||
|
File extractLocation = pluginHandle.extractedDir(environment);
|
||||||
|
if (extractLocation.exists()) {
|
||||||
|
throw new IOException("plugin directory " + extractLocation.getAbsolutePath() + " already exists. To update the plugin, uninstall it first using -remove " + name + " command");
|
||||||
|
}
|
||||||
|
|
||||||
// first, try directly from the URL provided
|
// first, try directly from the URL provided
|
||||||
boolean downloaded = false;
|
boolean downloaded = false;
|
||||||
|
@ -116,92 +121,17 @@ public class PluginManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, try as a path name...
|
|
||||||
if (!downloaded) {
|
if (!downloaded) {
|
||||||
if (name.indexOf('/') != -1) {
|
// We try all possible locations
|
||||||
// github repo
|
for (URL url: pluginHandle.urls()) {
|
||||||
String[] elements = name.split("/");
|
System.out.println("Trying " + url.toExternalForm() + "...");
|
||||||
String userName = elements[0];
|
try {
|
||||||
String repoName = elements[1];
|
downloadHelper.download(url, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
||||||
String version = null;
|
downloaded = true;
|
||||||
if (elements.length > 2) {
|
break;
|
||||||
version = elements[2];
|
} catch (Exception e) {
|
||||||
}
|
if (verbose) {
|
||||||
// the installation file should not include the userName, just the repoName
|
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e));
|
||||||
name = repoName;
|
|
||||||
if (name.startsWith("elasticsearch-")) {
|
|
||||||
// remove elasticsearch- prefix
|
|
||||||
name = name.substring("elasticsearch-".length());
|
|
||||||
} else if (name.startsWith("es-")) {
|
|
||||||
// remove es- prefix
|
|
||||||
name = name.substring("es-".length());
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the plugin file name to reflect the extracted name
|
|
||||||
pluginFile = new File(environment.pluginsFile(), name + ".zip");
|
|
||||||
|
|
||||||
if (version != null) {
|
|
||||||
URL pluginUrl = new URL("http://download.elasticsearch.org/" + userName + "/" + repoName + "/" + repoName + "-" + version + ".zip");
|
|
||||||
System.out.println("Trying " + pluginUrl.toExternalForm() + "...");
|
|
||||||
try {
|
|
||||||
downloadHelper.download(pluginUrl, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
|
||||||
downloaded = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!downloaded) {
|
|
||||||
// try maven, see if its there... (both central and sonatype)
|
|
||||||
pluginUrl = new URL("http://search.maven.org/remotecontent?filepath=" + userName.replace('.', '/') + "/" + repoName + "/" + version + "/" + repoName + "-" + version + ".zip");
|
|
||||||
System.out.println("Trying " + pluginUrl.toExternalForm() + "...");
|
|
||||||
try {
|
|
||||||
downloadHelper.download(pluginUrl, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
|
||||||
downloaded = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!downloaded) {
|
|
||||||
pluginUrl = new URL("https://oss.sonatype.org/service/local/repositories/releases/content/" + userName.replace('.', '/') + "/" + repoName + "/" + version + "/" + repoName + "-" + version + ".zip");
|
|
||||||
System.out.println("Trying " + pluginUrl.toExternalForm() + "...");
|
|
||||||
try {
|
|
||||||
downloadHelper.download(pluginUrl, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
|
||||||
downloaded = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!downloaded) {
|
|
||||||
// try it as a site plugin tagged
|
|
||||||
pluginUrl = new URL("https://github.com/" + userName + "/" + repoName + "/archive/v" + version + ".zip");
|
|
||||||
System.out.println("Trying " + pluginUrl.toExternalForm() + "... (assuming site plugin)");
|
|
||||||
try {
|
|
||||||
downloadHelper.download(pluginUrl, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
|
||||||
downloaded = true;
|
|
||||||
} catch (Exception e1) {
|
|
||||||
// ignore
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// assume site plugin, download master....
|
|
||||||
URL pluginUrl = new URL("https://github.com/" + userName + "/" + repoName + "/archive/master.zip");
|
|
||||||
System.out.println("Trying " + pluginUrl.toExternalForm() + "... (assuming site plugin)");
|
|
||||||
try {
|
|
||||||
downloadHelper.download(pluginUrl, pluginFile, new HttpDownloadHelper.VerboseProgress(System.out));
|
|
||||||
downloaded = true;
|
|
||||||
} catch (Exception e2) {
|
|
||||||
// ignore
|
|
||||||
if (verbose) {
|
|
||||||
System.out.println("Failed: " + ExceptionsHelper.detailedMessage(e2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,11 +141,6 @@ public class PluginManager {
|
||||||
throw new IOException("failed to download out of all possible locations..., use -verbose to get detailed information");
|
throw new IOException("failed to download out of all possible locations..., use -verbose to get detailed information");
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the plugin
|
|
||||||
File extractLocation = new File(environment.pluginsFile(), name);
|
|
||||||
if (extractLocation.exists()) {
|
|
||||||
throw new IOException("plugin directory " + extractLocation.getAbsolutePath() + " already exists. To update the plugin, uninstall it first using -remove " + name + " command");
|
|
||||||
}
|
|
||||||
ZipFile zipFile = null;
|
ZipFile zipFile = null;
|
||||||
try {
|
try {
|
||||||
zipFile = new ZipFile(pluginFile);
|
zipFile = new ZipFile(pluginFile);
|
||||||
|
@ -259,7 +184,7 @@ public class PluginManager {
|
||||||
|
|
||||||
File binFile = new File(extractLocation, "bin");
|
File binFile = new File(extractLocation, "bin");
|
||||||
if (binFile.exists() && binFile.isDirectory()) {
|
if (binFile.exists() && binFile.isDirectory()) {
|
||||||
File toLocation = new File(new File(environment.homeFile(), "bin"), name);
|
File toLocation = pluginHandle.binDir(environment);
|
||||||
System.out.println("Found bin, moving to " + toLocation.getAbsolutePath());
|
System.out.println("Found bin, moving to " + toLocation.getAbsolutePath());
|
||||||
FileSystemUtils.deleteRecursively(toLocation);
|
FileSystemUtils.deleteRecursively(toLocation);
|
||||||
binFile.renameTo(toLocation);
|
binFile.renameTo(toLocation);
|
||||||
|
@ -282,22 +207,38 @@ public class PluginManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removePlugin(String name) throws IOException {
|
public void removePlugin(String name) throws IOException {
|
||||||
File pluginToDelete = new File(environment.pluginsFile(), name);
|
PluginHandle pluginHandle = PluginHandle.parse(name);
|
||||||
|
boolean removed = false;
|
||||||
|
|
||||||
|
File pluginToDelete = pluginHandle.extractedDir(environment);
|
||||||
if (pluginToDelete.exists()) {
|
if (pluginToDelete.exists()) {
|
||||||
FileSystemUtils.deleteRecursively(pluginToDelete, true);
|
FileSystemUtils.deleteRecursively(pluginToDelete, true);
|
||||||
|
removed = true;
|
||||||
}
|
}
|
||||||
pluginToDelete = new File(environment.pluginsFile(), name + ".zip");
|
pluginToDelete = pluginHandle.distroFile(environment);
|
||||||
if (pluginToDelete.exists()) {
|
if (pluginToDelete.exists()) {
|
||||||
pluginToDelete.delete();
|
pluginToDelete.delete();
|
||||||
|
removed = true;
|
||||||
}
|
}
|
||||||
File binLocation = new File(new File(environment.homeFile(), "bin"), name);
|
File binLocation = pluginHandle.binDir(environment);
|
||||||
if (binLocation.exists()) {
|
if (binLocation.exists()) {
|
||||||
FileSystemUtils.deleteRecursively(binLocation);
|
FileSystemUtils.deleteRecursively(binLocation);
|
||||||
|
removed = true;
|
||||||
}
|
}
|
||||||
|
if (removed) {
|
||||||
|
System.out.println("Removed " + name);
|
||||||
|
} else {
|
||||||
|
System.out.println("Plugin " + name + " not found. Run plugin --list to get list of installed plugins.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public File[] getListInstalledPlugins() {
|
||||||
|
File[] plugins = environment.pluginsFile().listFiles();
|
||||||
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void listInstalledPlugins() {
|
public void listInstalledPlugins() {
|
||||||
File[] plugins = environment.pluginsFile().listFiles();
|
File[] plugins = getListInstalledPlugins();
|
||||||
System.out.println("Installed plugins:");
|
System.out.println("Installed plugins:");
|
||||||
if (plugins == null || plugins.length == 0) {
|
if (plugins == null || plugins.length == 0) {
|
||||||
System.out.println(" - No plugin detected in " + environment.pluginsFile().getAbsolutePath());
|
System.out.println(" - No plugin detected in " + environment.pluginsFile().getAbsolutePath());
|
||||||
|
@ -461,4 +402,95 @@ public class PluginManager {
|
||||||
System.out.println(" " + message);
|
System.out.println(" " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to extract properly user name, repository name, version and plugin name
|
||||||
|
* from plugin name given by a user.
|
||||||
|
*/
|
||||||
|
static class PluginHandle {
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final String version;
|
||||||
|
final String user;
|
||||||
|
final String repo;
|
||||||
|
|
||||||
|
PluginHandle(String name, String version, String user, String repo) {
|
||||||
|
this.name = name;
|
||||||
|
this.version = version;
|
||||||
|
this.user = user;
|
||||||
|
this.repo = repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<URL> urls() {
|
||||||
|
List<URL> urls = new ArrayList<URL>();
|
||||||
|
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/v" + version + ".zip");
|
||||||
|
}
|
||||||
|
// Github repository for master branch (assume site)
|
||||||
|
addUrl(urls, "https://github.com/" + user + "/" + repo + "/archive/master.zip");
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addUrl(List<URL> urls, String url) {
|
||||||
|
try {
|
||||||
|
URL _url = new URL(url);
|
||||||
|
urls.add(new URL(url));
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
// We simply ignore malformed URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File distroFile(Environment env) {
|
||||||
|
return new File(env.pluginsFile(), name + ".zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
File extractedDir(Environment env) {
|
||||||
|
return new File(env.pluginsFile(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
File binDir(Environment env) {
|
||||||
|
return new File(new File(env.homeFile(), "bin"), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PluginHandle parse(String name) {
|
||||||
|
String[] elements = name.split("/");
|
||||||
|
// We first consider the simplest form: pluginname
|
||||||
|
String repo = elements[0];
|
||||||
|
String user = null;
|
||||||
|
String version = null;
|
||||||
|
|
||||||
|
// We consider the form: username/pluginname
|
||||||
|
if (elements.length > 1) {
|
||||||
|
user = elements[0];
|
||||||
|
repo = elements[1];
|
||||||
|
|
||||||
|
// We consider the form: username/pluginname/version
|
||||||
|
if (elements.length > 2) {
|
||||||
|
version = elements[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repo.startsWith("elasticsearch-")) {
|
||||||
|
// remove elasticsearch- prefix
|
||||||
|
String endname = repo.substring("elasticsearch-".length());
|
||||||
|
return new PluginHandle(endname, version, user, repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("es-")) {
|
||||||
|
// remove es- prefix
|
||||||
|
String endname = repo.substring("es-".length());
|
||||||
|
return new PluginHandle(endname, version, user, repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PluginHandle(repo, version, user, repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,10 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
|
||||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
|
||||||
|
|
||||||
public class PluginManagerTests extends AbstractNodesTests {
|
public class PluginManagerTests extends AbstractNodesTests {
|
||||||
|
|
||||||
|
@ -102,14 +101,17 @@ public class PluginManagerTests extends AbstractNodesTests {
|
||||||
assertPluginAvailable(node, pluginName);
|
assertPluginAvailable(node, pluginName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void downloadAndExtract(String pluginName, String pluginUrl) throws Exception {
|
private static PluginManager pluginManager(String pluginUrl) {
|
||||||
Tuple<Settings, Environment> initialSettings = InternalSettingsPerparer.prepareSettings(
|
Tuple<Settings, Environment> initialSettings = InternalSettingsPerparer.prepareSettings(
|
||||||
ImmutableSettings.settingsBuilder().build(), false);
|
ImmutableSettings.settingsBuilder().build(), false);
|
||||||
if (!initialSettings.v2().pluginsFile().exists()) {
|
if (!initialSettings.v2().pluginsFile().exists()) {
|
||||||
FileSystemUtils.mkdirs(initialSettings.v2().pluginsFile());
|
FileSystemUtils.mkdirs(initialSettings.v2().pluginsFile());
|
||||||
}
|
}
|
||||||
PluginManager pluginManager = new PluginManager(initialSettings.v2(), pluginUrl);
|
return new PluginManager(initialSettings.v2(), pluginUrl);
|
||||||
pluginManager.downloadAndExtract(pluginName, false);
|
}
|
||||||
|
|
||||||
|
private static void downloadAndExtract(String pluginName, String pluginUrl) throws IOException {
|
||||||
|
pluginManager(pluginUrl).downloadAndExtract(pluginName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node startNode() {
|
private Node startNode() {
|
||||||
|
@ -152,4 +154,30 @@ public class PluginManagerTests extends AbstractNodesTests {
|
||||||
private void deletePluginsFolder() {
|
private void deletePluginsFolder() {
|
||||||
FileSystemUtils.deleteRecursively(new File(PLUGIN_DIR));
|
FileSystemUtils.deleteRecursively(new File(PLUGIN_DIR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void singlePluginInstallAndRemove(String pluginName, String pluginCoordinates) throws IOException {
|
||||||
|
PluginManager pluginManager = pluginManager(pluginCoordinates);
|
||||||
|
pluginManager.downloadAndExtract("plugin", false);
|
||||||
|
File[] plugins = pluginManager.getListInstalledPlugins();
|
||||||
|
assertThat(plugins, notNullValue());
|
||||||
|
assertThat(plugins.length, is(1));
|
||||||
|
|
||||||
|
// We remove it
|
||||||
|
pluginManager.removePlugin(pluginName);
|
||||||
|
plugins = pluginManager.getListInstalledPlugins();
|
||||||
|
assertThat(plugins, notNullValue());
|
||||||
|
assertThat(plugins.length, is(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemovePlugin() throws Exception {
|
||||||
|
// We want to remove plugin with plugin short name
|
||||||
|
singlePluginInstallAndRemove("plugin", "file://".concat(PluginManagerTests.class.getResource("plugin_without_folders.zip").getFile()));
|
||||||
|
|
||||||
|
// We want to remove plugin with groupid/artifactid/version form
|
||||||
|
singlePluginInstallAndRemove("groupid/plugin/1.0.0", "file://".concat(PluginManagerTests.class.getResource("plugin_without_folders.zip").getFile()));
|
||||||
|
|
||||||
|
// We want to remove plugin with groupid/artifactid form
|
||||||
|
singlePluginInstallAndRemove("groupid/plugin", "file://".concat(PluginManagerTests.class.getResource("plugin_without_folders.zip").getFile()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue