plugin manager: new `timeout` option

When testing plugin manager with real downloads, it could happen that the test run forever. Fortunately, test suite will be interrupted after 20 minutes, but it could be useful not to fail the whole test suite but only warn in that case.

By default, plugin manager still wait indefinitely but it can be modified using new `--timeout` option:

```sh
bin/plugin --install elasticsearch/kibana --timeout 30s

bin/plugin --install elasticsearch/kibana --timeout 1h
```

Closes #4603.
Closes #4600.
This commit is contained in:
David Pilato 2014-01-01 23:31:32 +01:00
parent 2cb5cfecec
commit 0c7b494bb8
4 changed files with 84 additions and 52 deletions

View File

@ -123,6 +123,24 @@ bin/plugin --install mobz/elasticsearch-head --verbose
plugin --remove head --silent plugin --remove head --silent
----------------------------------- -----------------------------------
[float]
==== Timeout settings
By default, the `plugin` script will wait indefinitely when downloading before failing.
The timeout parameter can be used to explicitly specify how long it waits. Here is some examples of setting it to
different values:
[source,shell]
-----------------------------------
# Wait for 30 seconds before failing
bin/plugin --install mobz/elasticsearch-head --timeout 30s
# Wait for 1 minute before failing
bin/plugin --install mobz/elasticsearch-head --timeout 1m
# Wait forever (default)
bin/plugin --install mobz/elasticsearch-head --timeout 0
-----------------------------------
[float] [float]
[[known-plugins]] [[known-plugins]]

View File

@ -19,7 +19,10 @@
package org.elasticsearch.common.http.client; package org.elasticsearch.common.http.client;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticSearchTimeoutException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.unit.TimeValue;
import java.io.*; import java.io.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -33,9 +36,8 @@ public class HttpDownloadHelper {
private boolean useTimestamp = false; private boolean useTimestamp = false;
private boolean skipExisting = false; private boolean skipExisting = false;
private long maxTime = 0;
public boolean download(URL source, File dest, @Nullable DownloadProgress progress) throws IOException { public boolean download(URL source, File dest, @Nullable DownloadProgress progress, TimeValue timeout) throws Exception {
if (dest.exists() && skipExisting) { if (dest.exists() && skipExisting) {
return true; return true;
} }
@ -55,19 +57,20 @@ public class HttpDownloadHelper {
} }
GetThread getThread = new GetThread(source, dest, hasTimestamp, timestamp, progress); GetThread getThread = new GetThread(source, dest, hasTimestamp, timestamp, progress);
try {
getThread.setDaemon(true); getThread.setDaemon(true);
getThread.start(); getThread.start();
try { getThread.join(timeout.millis());
getThread.join(maxTime * 1000);
} catch (InterruptedException ie) {
// ignore
}
if (getThread.isAlive()) { if (getThread.isAlive()) {
String msg = "The GET operation took longer than " + maxTime throw new ElasticSearchTimeoutException("The GET operation took longer than " + timeout + ", stopping it.");
+ " seconds, stopping it."; }
}
catch (InterruptedException ie) {
return false;
} finally {
getThread.closeStreams(); getThread.closeStreams();
throw new IOException(msg);
} }
return getThread.wasSuccessful(); return getThread.wasSuccessful();
@ -329,16 +332,7 @@ public class HttpDownloadHelper {
} }
finished = !isInterrupted(); finished = !isInterrupted();
} finally { } finally {
try { IOUtils.close(os, is);
os.close();
} catch (IOException e) {
// ignore
}
try {
is.close();
} catch (IOException e) {
// ignore
}
// we have started to (over)write dest, but failed. // we have started to (over)write dest, but failed.
// Try to delete the garbage we'd otherwise leave // Try to delete the garbage we'd otherwise leave
@ -374,21 +368,16 @@ public class HttpDownloadHelper {
* Closes streams, interrupts the download, may delete the * Closes streams, interrupts the download, may delete the
* output file. * output file.
*/ */
void closeStreams() { void closeStreams() throws IOException {
interrupt(); interrupt();
try { if (success) {
os.close(); IOUtils.close(is, os);
} catch (IOException e) { } else {
// ignore IOUtils.closeWhileHandlingException(is, os);
} if (dest != null && dest.exists()) {
try {
is.close();
} catch (IOException e) {
// ignore
}
if (!success && dest.exists()) {
dest.delete(); dest.delete();
} }
} }
} }
}
} }

View File

@ -21,12 +21,14 @@ package org.elasticsearch.plugins;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.ElasticSearchTimeoutException;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.http.client.HttpDownloadHelper; import org.elasticsearch.common.http.client.HttpDownloadHelper;
import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.node.internal.InternalSettingsPreparer; import org.elasticsearch.node.internal.InternalSettingsPreparer;
@ -60,15 +62,20 @@ public class PluginManager {
DEFAULT, SILENT, VERBOSE DEFAULT, SILENT, VERBOSE
} }
// By default timeout is 0 which means no timeout
public static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueMillis(0);
private final Environment environment; private final Environment environment;
private String url; private String url;
private OutputMode outputMode; private OutputMode outputMode;
private TimeValue timeout;
public PluginManager(Environment environment, String url, OutputMode outputMode) { public PluginManager(Environment environment, String url, OutputMode outputMode, TimeValue timeout) {
this.environment = environment; this.environment = environment;
this.url = url; this.url = url;
this.outputMode = outputMode; this.outputMode = outputMode;
this.timeout = timeout;
TrustManager[] trustAllCerts = new TrustManager[]{ TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() { new X509TrustManager() {
@ -124,9 +131,11 @@ public class PluginManager {
URL pluginUrl = new URL(url); URL pluginUrl = new URL(url);
log("Trying " + pluginUrl.toExternalForm() + "..."); log("Trying " + pluginUrl.toExternalForm() + "...");
try { try {
downloadHelper.download(pluginUrl, pluginFile, progress); downloadHelper.download(pluginUrl, pluginFile, progress, this.timeout);
downloaded = true; downloaded = true;
} catch (IOException e) { } catch (ElasticSearchTimeoutException e) {
throw e;
} catch (Exception e) {
// ignore // ignore
log("Failed: " + ExceptionsHelper.detailedMessage(e)); log("Failed: " + ExceptionsHelper.detailedMessage(e));
} }
@ -137,9 +146,11 @@ public class PluginManager {
for (URL url : pluginHandle.urls()) { for (URL url : pluginHandle.urls()) {
log("Trying " + url.toExternalForm() + "..."); log("Trying " + url.toExternalForm() + "...");
try { try {
downloadHelper.download(url, pluginFile, progress); downloadHelper.download(url, pluginFile, progress, this.timeout);
downloaded = true; downloaded = true;
break; break;
} catch (ElasticSearchTimeoutException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
debug("Failed: " + ExceptionsHelper.detailedMessage(e)); debug("Failed: " + ExceptionsHelper.detailedMessage(e));
} }
@ -308,6 +319,7 @@ public class PluginManager {
String url = null; String url = null;
OutputMode outputMode = OutputMode.DEFAULT; OutputMode outputMode = OutputMode.DEFAULT;
String pluginName = null; String pluginName = null;
TimeValue timeout = DEFAULT_TIMEOUT;
int action = ACTION.NONE; int action = ACTION.NONE;
if (args.length < 1) { if (args.length < 1) {
@ -332,7 +344,9 @@ public class PluginManager {
|| command.equals("install") || command.equals("-install")) { || command.equals("install") || command.equals("-install")) {
pluginName = args[++c]; pluginName = args[++c];
action = ACTION.INSTALL; action = ACTION.INSTALL;
} else if (command.equals("-t") || command.equals("--timeout")
|| command.equals("timeout") || command.equals("-timeout")) {
timeout = TimeValue.parseTimeValue(args[++c], DEFAULT_TIMEOUT);
} else if (command.equals("-r") || command.equals("--remove") } else if (command.equals("-r") || command.equals("--remove")
// Deprecated commands // Deprecated commands
|| command.equals("remove") || command.equals("-remove")) { || command.equals("remove") || command.equals("-remove")) {
@ -357,7 +371,7 @@ public class PluginManager {
if (action > ACTION.NONE) { if (action > ACTION.NONE) {
int exitCode = EXIT_CODE_ERROR; // we fail unless it's reset int exitCode = EXIT_CODE_ERROR; // we fail unless it's reset
PluginManager pluginManager = new PluginManager(initialSettings.v2(), url, outputMode); PluginManager pluginManager = new PluginManager(initialSettings.v2(), url, outputMode, timeout);
switch (action) { switch (action) {
case ACTION.INSTALL: case ACTION.INSTALL:
try { try {
@ -413,6 +427,7 @@ public class PluginManager {
System.out.println("Usage:"); System.out.println("Usage:");
System.out.println(" -u, --url [plugin location] : Set exact URL to download the plugin from"); System.out.println(" -u, --url [plugin location] : Set exact URL to download the plugin from");
System.out.println(" -i, --install [plugin name] : Downloads and installs listed plugins [*]"); System.out.println(" -i, --install [plugin name] : Downloads and installs listed plugins [*]");
System.out.println(" -t, --timeout [duration] : Timeout setting: 30s, 1m, 1h... (infinite by default)");
System.out.println(" -r, --remove [plugin name] : Removes listed plugins"); System.out.println(" -r, --remove [plugin name] : Removes listed plugins");
System.out.println(" -l, --list : List installed plugins"); System.out.println(" -l, --list : List installed plugins");
System.out.println(" -v, --verbose : Prints verbose messages"); System.out.println(" -v, --verbose : Prints verbose messages");

View File

@ -19,11 +19,13 @@
package org.elasticsearch.plugin; package org.elasticsearch.plugin;
import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.ElasticSearchTimeoutException;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.node.internal.InternalSettingsPreparer; import org.elasticsearch.node.internal.InternalSettingsPreparer;
@ -123,13 +125,17 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
downloadAndExtract(pluginName, "file://" + url.getFile()); downloadAndExtract(pluginName, "file://" + url.getFile());
} }
/**
* We build a plugin manager instance which wait only for 30 seconds before
* raising an ElasticSearchTimeoutException
*/
private static PluginManager pluginManager(String pluginUrl) { private static PluginManager pluginManager(String pluginUrl) {
Tuple<Settings, Environment> initialSettings = InternalSettingsPreparer.prepareSettings( Tuple<Settings, Environment> initialSettings = InternalSettingsPreparer.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());
} }
return new PluginManager(initialSettings.v2(), pluginUrl, PluginManager.OutputMode.SILENT); return new PluginManager(initialSettings.v2(), pluginUrl, PluginManager.OutputMode.SILENT, TimeValue.timeValueSeconds(30));
} }
private static void downloadAndExtract(String pluginName, String pluginUrl) throws IOException { private static void downloadAndExtract(String pluginName, String pluginUrl) throws IOException {
@ -208,6 +214,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
private void singlePluginInstallAndRemove(String pluginShortName, String pluginCoordinates) throws IOException { private void singlePluginInstallAndRemove(String pluginShortName, String pluginCoordinates) throws IOException {
PluginManager pluginManager = pluginManager(pluginCoordinates); PluginManager pluginManager = pluginManager(pluginCoordinates);
try {
pluginManager.downloadAndExtract(pluginShortName); pluginManager.downloadAndExtract(pluginShortName);
File[] plugins = pluginManager.getListInstalledPlugins(); File[] plugins = pluginManager.getListInstalledPlugins();
assertThat(plugins, notNullValue()); assertThat(plugins, notNullValue());
@ -218,6 +225,9 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
plugins = pluginManager.getListInstalledPlugins(); plugins = pluginManager.getListInstalledPlugins();
assertThat(plugins, notNullValue()); assertThat(plugins, notNullValue());
assertThat(plugins.length, is(0)); assertThat(plugins.length, is(0));
} catch (ElasticSearchTimeoutException e) {
logger.warn("--> timeout exception raised while downloading plugin [{}]. Skipping test.", pluginShortName);
}
} }
/** /**