List of existing plugins with Node Info API

We want to display information about loaded plugins in Node Info API using plugin option:

```sh
curl http://localhost:9200/_nodes?plugin=true
```

For example, on a 4 nodes cluster, it could provide the following output:

```javascript
{
  "ok" : true,
  "cluster_name" : "test-cluster-MacBook-Air-de-David.local",
  "nodes" : {
    "lodYfbFTRnmwE6rjWGGyQQ" : {
      "name" : "node1",
      "transport_address" : "inet[/172.18.58.139:9300]",
      "hostname" : "MacBook-Air-de-David.local",
      "version" : "0.90.0.Beta2-SNAPSHOT",
      "http_address" : "inet[/172.18.58.139:9200]",
      "plugins" : [ ]
    },
    "hJLXmY_NTrCytiIMbX4_1g" : {
      "name" : "node4",
      "transport_address" : "inet[/172.18.58.139:9303]",
      "hostname" : "MacBook-Air-de-David.local",
      "version" : "0.90.0.Beta2-SNAPSHOT",
      "http_address" : "inet[/172.18.58.139:9203]",
      "plugins" : [ {
        "name" : "test-plugin",
        "description" : "test-plugin description",
        "site" : true,
        "jvm" : false
      }, {
        "name" : "test-no-version-plugin",
        "description" : "test-no-version-plugin description",
        "site" : true,
        "jvm" : false
      }, {
        "name" : "dummy",
        "description" : "No description found for dummy.",
        "url" : "/_plugin/dummy/",
        "site" : false,
        "jvm" : true
      } ]
    },
    "bnoySsBfTrSzbDRZ0BFHvg" : {
      "name" : "node2",
      "transport_address" : "inet[/172.18.58.139:9301]",
      "hostname" : "MacBook-Air-de-David.local",
      "version" : "0.90.0.Beta2-SNAPSHOT",
      "http_address" : "inet[/172.18.58.139:9201]",
      "plugins" : [ {
        "name" : "dummy",
        "description" : "This is a description for a dummy test site plugin.",
        "url" : "/_plugin/dummy/",
        "site" : false,
        "jvm" : true
      } ]
    },
    "0Vwil01LSfK9YgRrMce3Ug" : {
      "name" : "node3",
      "transport_address" : "inet[/172.18.58.139:9302]",
      "hostname" : "MacBook-Air-de-David.local",
      "version" : "0.90.0.Beta2-SNAPSHOT",
      "http_address" : "inet[/172.18.58.139:9202]",
      "plugins" : [ {
        "name" : "test-plugin",
        "description" : "test-plugin description",
        "site" : true,
        "jvm" : false
      } ]
    }
  }
}
```

Information are cached for 10 seconds by default. Modify `plugins.info_refresh_interval` property if needed.
Setting `plugins.info_refresh_interval` to `-1` will cause infinite caching.
Setting `plugins.info_refresh_interval` to `0` will disable caching.

Closes #2668.
This commit is contained in:
David Pilato 2013-03-15 19:28:01 +01:00
parent f3e6fe094a
commit 36b92be212
19 changed files with 674 additions and 13 deletions

View File

@ -76,12 +76,15 @@ public class NodeInfo extends NodeOperationResponse {
@Nullable
private HttpInfo http;
@Nullable
private PluginsInfo plugins;
NodeInfo() {
}
public NodeInfo(@Nullable String hostname, Version version, DiscoveryNode node, @Nullable ImmutableMap<String, String> serviceAttributes, @Nullable Settings settings,
@Nullable OsInfo os, @Nullable ProcessInfo process, @Nullable JvmInfo jvm, @Nullable ThreadPoolInfo threadPool, @Nullable NetworkInfo network,
@Nullable TransportInfo transport, @Nullable HttpInfo http) {
@Nullable TransportInfo transport, @Nullable HttpInfo http, @Nullable PluginsInfo plugins) {
super(node);
this.hostname = hostname;
this.version = version;
@ -94,6 +97,7 @@ public class NodeInfo extends NodeOperationResponse {
this.network = network;
this.transport = transport;
this.http = http;
this.plugins = plugins;
}
/**
@ -174,6 +178,11 @@ public class NodeInfo extends NodeOperationResponse {
return http;
}
@Nullable
public PluginsInfo getPlugins() {
return this.plugins;
}
public static NodeInfo readNodeInfo(StreamInput in) throws IOException {
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.readFrom(in);
@ -219,6 +228,9 @@ public class NodeInfo extends NodeOperationResponse {
if (in.readBoolean()) {
http = HttpInfo.readHttpInfo(in);
}
if (in.readBoolean()) {
plugins = PluginsInfo.readPluginsInfo(in);
}
}
@Override
@ -289,5 +301,12 @@ public class NodeInfo extends NodeOperationResponse {
out.writeBoolean(true);
http.writeTo(out);
}
if (plugins == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
plugins.writeTo(out);
}
}
}

View File

@ -38,6 +38,7 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
private boolean network = false;
private boolean transport = false;
private boolean http = false;
private boolean plugin = false;
public NodesInfoRequest() {
}
@ -62,6 +63,7 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
network = false;
transport = false;
http = false;
plugin = false;
return this;
}
@ -77,6 +79,7 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
network = true;
transport = true;
http = true;
plugin = true;
return this;
}
@ -200,6 +203,23 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
return this;
}
/**
* Should information about plugins be returned
* @param plugin true if you want info
* @return The request
*/
public NodesInfoRequest plugin(boolean plugin) {
this.plugin = plugin;
return this;
}
/**
* @return true if information about plugins is requested
*/
public boolean plugin() {
return plugin;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
@ -211,6 +231,7 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
network = in.readBoolean();
transport = in.readBoolean();
http = in.readBoolean();
plugin = in.readBoolean();
}
@Override
@ -224,5 +245,6 @@ public class NodesInfoRequest extends NodesOperationRequest<NodesInfoRequest> {
out.writeBoolean(network);
out.writeBoolean(transport);
out.writeBoolean(http);
out.writeBoolean(plugin);
}
}

View File

@ -113,6 +113,10 @@ public class NodesInfoRequestBuilder extends NodesOperationRequestBuilder<NodesI
return this;
}
public NodesInfoRequestBuilder setPlugin(boolean plugin) {
request().plugin(plugin);
return this;
}
@Override
protected void doExecute(ActionListener<NodesInfoResponse> listener) {

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import java.io.IOException;
import java.util.Map;
@ -132,10 +133,26 @@ public class NodesInfoResponse extends NodesOperationResponse<NodeInfo> implemen
if (nodeInfo.getHttp() != null) {
nodeInfo.getHttp().toXContent(builder, params);
}
if (nodeInfo.getPlugins() != null) {
nodeInfo.getPlugins().toXContent(builder, params);
}
builder.endObject();
}
builder.endObject();
return builder;
}
@Override
public String toString() {
try {
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
toXContent(builder, EMPTY_PARAMS);
builder.endObject();
return builder.string();
} catch (IOException e) {
return "{ \"error\" : \"" + e.getMessage() + "\"}";
}
}
}

View File

@ -0,0 +1,120 @@
package org.elasticsearch.action.admin.cluster.node.info;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import java.io.IOException;
import java.io.Serializable;
public class PluginInfo implements Streamable, Serializable, ToXContent {
static final class Fields {
static final XContentBuilderString NAME = new XContentBuilderString("name");
static final XContentBuilderString DESCRIPTION = new XContentBuilderString("description");
static final XContentBuilderString URL = new XContentBuilderString("url");
static final XContentBuilderString JVM = new XContentBuilderString("jvm");
static final XContentBuilderString SITE = new XContentBuilderString("site");
}
private String name;
private String description;
private boolean site;
private boolean jvm;
public PluginInfo() {
}
/**
* Information about plugins
* @param name Its name
* @param description Its description
* @param site true if it's a site plugin
* @param jvm true if it's a jvm plugin
*/
public PluginInfo(String name, String description, boolean site, boolean jvm) {
this.name = name;
this.description = description;
this.site = site;
this.jvm = jvm;
}
/**
* @return Plugin's name
*/
public String getName() {
return name;
}
/**
* @return Plugin's description if any
*/
public String getDescription() {
return description;
}
/**
* @return true is it's a site plugin
*/
public boolean isSite() {
return site;
}
/**
* @return true if it's a plugin running in the jvm
*/
public boolean isJvm() {
return jvm;
}
/**
* We compute the URL for sites: "/_plugin/" + name + "/"
* @return
*/
public String getUrl() {
if (site) {
return ("/_plugin/" + name + "/");
} else {
return null;
}
}
public static PluginInfo readPluginInfo(StreamInput in) throws IOException {
PluginInfo info = new PluginInfo();
info.readFrom(in);
return info;
}
@Override
public void readFrom(StreamInput in) throws IOException {
this.name = in.readString();
this.description = in.readString();
this.site = in.readBoolean();
this.jvm = in.readBoolean();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeString(description);
out.writeBoolean(site);
out.writeBoolean(jvm);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Fields.NAME, name);
builder.field(Fields.DESCRIPTION, description);
if (site) {
builder.field(Fields.URL, getUrl());
}
builder.field(Fields.JVM, jvm);
builder.field(Fields.SITE, site);
builder.endObject();
return builder;
}
}

View File

@ -0,0 +1,70 @@
package org.elasticsearch.action.admin.cluster.node.info;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class PluginsInfo implements Streamable, Serializable, ToXContent {
static final class Fields {
static final XContentBuilderString PLUGINS = new XContentBuilderString("plugins");
}
private List<PluginInfo> infos;
public PluginsInfo() {
infos = new ArrayList<PluginInfo>();
}
public PluginsInfo(int size) {
infos = new ArrayList<PluginInfo>(size);
}
public List<PluginInfo> getInfos() {
return infos;
}
public void add(PluginInfo info) {
infos.add(info);
}
public static PluginsInfo readPluginsInfo(StreamInput in) throws IOException {
PluginsInfo infos = new PluginsInfo();
infos.readFrom(in);
return infos;
}
@Override
public void readFrom(StreamInput in) throws IOException {
int plugins_size = in.readInt();
for (int i = 0; i < plugins_size; i++) {
infos.add(PluginInfo.readPluginInfo(in));
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeInt(infos.size());
for (PluginInfo plugin : infos) {
plugin.writeTo(out);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray(Fields.PLUGINS);
for (PluginInfo pluginInfo : infos) {
pluginInfo.toXContent(builder, params);
}
builder.endArray();
return builder;
}
}

View File

@ -97,7 +97,8 @@ public class TransportNodesInfoAction extends TransportNodesOperationAction<Node
@Override
protected NodeInfo nodeOperation(NodeInfoRequest nodeRequest) throws ElasticSearchException {
NodesInfoRequest request = nodeRequest.request;
return nodeService.info(request.settings(), request.os(), request.process(), request.jvm(), request.threadPool(), request.network(), request.transport(), request.http());
return nodeService.info(request.settings(), request.os(), request.process(), request.jvm(), request.threadPool(),
request.network(), request.transport(), request.http(), request.plugin());
}
@Override

View File

@ -34,6 +34,7 @@ import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.http.HttpServer;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.monitor.MonitorService;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -53,6 +54,8 @@ public class NodeService extends AbstractComponent {
private final IndicesService indicesService;
private final PluginsService pluginService;
@Nullable
private HttpServer httpServer;
@ -64,7 +67,9 @@ public class NodeService extends AbstractComponent {
private final Version version;
@Inject
public NodeService(Settings settings, ThreadPool threadPool, MonitorService monitorService, Discovery discovery, ClusterService clusterService, TransportService transportService, IndicesService indicesService) {
public NodeService(Settings settings, ThreadPool threadPool, MonitorService monitorService, Discovery discovery,
ClusterService clusterService, TransportService transportService, IndicesService indicesService,
PluginsService pluginService) {
super(settings);
this.threadPool = threadPool;
this.monitorService = monitorService;
@ -77,6 +82,7 @@ public class NodeService extends AbstractComponent {
this.hostname = address.getHostName();
}
this.version = Version.CURRENT;
this.pluginService = pluginService;
}
public void setHttpServer(@Nullable HttpServer httpServer) {
@ -117,11 +123,13 @@ public class NodeService extends AbstractComponent {
threadPool.info(),
monitorService.networkService().info(),
transportService.info(),
httpServer == null ? null : httpServer.info()
httpServer == null ? null : httpServer.info(),
pluginService == null ? null : pluginService.info()
);
}
public NodeInfo info(boolean settings, boolean os, boolean process, boolean jvm, boolean threadPool, boolean network, boolean transport, boolean http) {
public NodeInfo info(boolean settings, boolean os, boolean process, boolean jvm, boolean threadPool,
boolean network, boolean transport, boolean http, boolean plugin) {
return new NodeInfo(hostname, version, clusterService.state().nodes().localNode(), serviceAttributes,
settings ? this.settings : null,
os ? monitorService.osService().info() : null,
@ -130,7 +138,8 @@ public class NodeService extends AbstractComponent {
threadPool ? this.threadPool.info() : null,
network ? monitorService.networkService().info() : null,
transport ? transportService.info() : null,
http ? (httpServer == null ? null : httpServer.info()) : null
http ? (httpServer == null ? null : httpServer.info()) : null,
plugin ? (pluginService == null ? null : pluginService.info()) : null
);
}

View File

@ -19,11 +19,10 @@
package org.elasticsearch.plugins;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.admin.cluster.node.info.PluginInfo;
import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.component.AbstractComponent;
@ -31,10 +30,12 @@ import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.CloseableIndexComponent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
@ -47,6 +48,7 @@ import static com.google.common.collect.Maps.newHashMap;
*
*/
public class PluginsService extends AbstractComponent {
private static final String ES_PLUGIN_PROPERTIES = "es-plugin.properties";
private final Environment environment;
@ -54,6 +56,10 @@ public class PluginsService extends AbstractComponent {
private final ImmutableMap<Plugin, List<OnModuleReference>> onModuleReferences;
private PluginsInfo cachedPluginsInfo;
private final TimeValue refreshInterval;
private long lastRefresh;
static class OnModuleReference {
public final Class<? extends Module> moduleClass;
public final Method onModuleMethod;
@ -128,6 +134,9 @@ public class PluginsService extends AbstractComponent {
}
}
this.onModuleReferences = onModuleReferences.immutableMap();
this.refreshInterval = componentSettings.getAsTime("info_refresh_interval", TimeValue.timeValueSeconds(10));
}
public ImmutableMap<String, Plugin> plugins() {
@ -240,6 +249,93 @@ public class PluginsService extends AbstractComponent {
return services;
}
/**
* Get information about plugins (jvm and site plugins).
* Information are cached for 10 seconds by default. Modify `plugins.info_refresh_interval` property if needed.
* Setting `plugins.info_refresh_interval` to `-1` will cause infinite caching.
* Setting `plugins.info_refresh_interval` to `0` will disable caching.
* @return List of plugins information
*/
synchronized public PluginsInfo info() {
if (refreshInterval.millis() != 0) {
if (cachedPluginsInfo != null &&
(refreshInterval.millis() < 0 || (System.currentTimeMillis() - lastRefresh) < refreshInterval.millis())) {
if (logger.isTraceEnabled()) logger.trace("using cache to retrieve plugins info");
return cachedPluginsInfo;
}
lastRefresh = System.currentTimeMillis();
}
if (logger.isTraceEnabled()) logger.trace("starting to fetch info on plugins");
cachedPluginsInfo = new PluginsInfo();
// We create a map to have only unique values
Set<String> plugins = new HashSet<String>();
for (Plugin plugin : plugins().values()) {
// We should detect if the plugin has also an embedded _site structure
File siteFile = new File(new File(environment.pluginsFile(), plugin.name()), "_site");
boolean isSite = siteFile.exists() && siteFile.isDirectory();
if (logger.isTraceEnabled()) logger.trace("found a jvm plugin [{}], [{}]{}",
plugin.name(), plugin.description(), isSite ? ": with _site structure" : "");
cachedPluginsInfo.add(new PluginInfo(plugin.name(), plugin.description(), isSite, true));
plugins.add(plugin.name());
}
File pluginsFile = environment.pluginsFile();
if (!pluginsFile.exists()) {
return cachedPluginsInfo;
}
if (!pluginsFile.isDirectory()) {
return cachedPluginsInfo;
}
File[] pluginsFiles = pluginsFile.listFiles();
if (pluginsFiles != null) {
for (File plugin : pluginsFiles) {
// We skip already known jvm plugins
if (!plugins.contains(plugin.getName())) {
File sitePluginDir = new File(plugin, "_site");
if (sitePluginDir.exists()) {
String name = plugin.getName();
String description = "No description found for " + name + ".";
// We check if es-plugin.properties exists in plugin/_site dir
File pluginPropFile = new File(sitePluginDir, ES_PLUGIN_PROPERTIES);
if (pluginPropFile.exists()) {
Properties pluginProps = new Properties();
InputStream is = null;
try {
is = new FileInputStream(pluginPropFile.getAbsolutePath());
pluginProps.load(is);
description = pluginProps.getProperty("description");
} catch (Exception e) {
logger.warn("failed to load plugin description from [" +
pluginPropFile.getAbsolutePath() + "]", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// ignore
}
}
}
}
if (logger.isTraceEnabled()) logger.trace("found a site plugin [{}], [{}]",
name, description);
cachedPluginsInfo.add(new PluginInfo(name, description, true, false));
}
}
}
}
return cachedPluginsInfo;
}
private void loadPluginsIntoClassLoader() {
File pluginsFile = environment.pluginsFile();
if (!pluginsFile.exists()) {
@ -302,7 +398,7 @@ public class PluginsService extends AbstractComponent {
Map<String, Plugin> plugins = newHashMap();
Enumeration<URL> pluginUrls = null;
try {
pluginUrls = settings.getClassLoader().getResources("es-plugin.properties");
pluginUrls = settings.getClassLoader().getResources(ES_PLUGIN_PROPERTIES);
} catch (IOException e) {
logger.warn("failed to find plugins from classpath", e);
return ImmutableMap.of();

View File

@ -73,6 +73,9 @@ public class RestNodesInfoAction extends BaseRestHandler {
controller.registerHandler(RestRequest.Method.GET, "/_nodes/http", new RestHttpHandler());
controller.registerHandler(RestRequest.Method.GET, "/_nodes/{nodeId}/http", new RestHttpHandler());
controller.registerHandler(RestRequest.Method.GET, "/_nodes/plugin", new RestPluginHandler());
controller.registerHandler(RestRequest.Method.GET, "/_nodes/{nodeId}/plugin", new RestPluginHandler());
this.settingsFilter = settingsFilter;
}
@ -97,6 +100,7 @@ public class RestNodesInfoAction extends BaseRestHandler {
nodesInfoRequest.network(request.paramAsBoolean("network", nodesInfoRequest.network()));
nodesInfoRequest.transport(request.paramAsBoolean("transport", nodesInfoRequest.transport()));
nodesInfoRequest.http(request.paramAsBoolean("http", nodesInfoRequest.http()));
nodesInfoRequest.plugin(request.paramAsBoolean("plugin", nodesInfoRequest.plugin()));
executeNodeRequest(request, channel, nodesInfoRequest);
}
@ -201,4 +205,13 @@ public class RestNodesInfoAction extends BaseRestHandler {
executeNodeRequest(request, channel, nodesInfoRequest);
}
}
class RestPluginHandler implements RestHandler {
@Override
public void handleRequest(final RestRequest request, final RestChannel channel) {
NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(RestActions.splitNodes(request.param("nodeId")));
nodesInfoRequest.clear().plugin(true);
executeNodeRequest(request, channel, nodesInfoRequest);
}
}
}

View File

@ -21,23 +21,39 @@ package org.elasticsearch.test.integration.nodesinfo;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.action.admin.cluster.node.info.PluginInfo;
import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.node.internal.InternalNode;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.elasticsearch.test.integration.nodesinfo.plugin.dummy1.TestPlugin;
import org.elasticsearch.test.integration.nodesinfo.plugin.dummy2.TestNoVersionPlugin;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import static org.elasticsearch.client.Requests.clusterHealthRequest;
import static org.elasticsearch.client.Requests.nodesInfoRequest;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.*;
/**
*
*/
public class SimpleNodesInfoTests extends AbstractNodesTests {
static final class Fields {
static final String SITE_PLUGIN = "dummy";
static final String SITE_PLUGIN_DESCRIPTION = "This is a description for a dummy test site plugin.";
static final String SITE_PLUGIN_NO_DESCRIPTION = "No description found for dummy.";
static final String JVM_PLUGIN_NO_DESCRIPTION = "No description found for test-no-version-plugin.";
}
@AfterMethod
public void closeNodes() {
closeAllNodes();
@ -81,4 +97,112 @@ public class SimpleNodesInfoTests extends AbstractNodesTests {
assertThat(response.getNodes().length, equalTo(1));
assertThat(response.getNodesMap().get(server2NodeId), notNullValue());
}
/**
* Use case is to start 4 nodes:
* <ul>
* <li>1 : no plugin</li>
* <li>2 : one site plugin (with a es-plugin.properties file)</li>
* <li>3 : one java plugin</li>
* <li>4 : one site plugin and 2 java plugins (included the previous one)</li>
* </ul>
* We test here that NodeInfo API with plugin option give us the right results.
* @throws URISyntaxException
*/
@Test
public void testNodeInfoPlugin() throws URISyntaxException {
// We start four nodes
// The first has no plugin
String server1NodeId = startNodeWithPlugins("node1");
// The second has one site plugin with a es-plugin.properties file (description and version)
String server2NodeId = startNodeWithPlugins("node2");
// The third has one java plugin
String server3NodeId = startNodeWithPlugins("node3");
// The fourth has one java plugin and one site plugin
String server4NodeId = startNodeWithPlugins("node4");
ClusterHealthResponse clusterHealth = client("node4").admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet();
logger.info("--> done cluster_health, status " + clusterHealth.getStatus());
NodesInfoResponse response = client("node1").admin().cluster().prepareNodesInfo().setPlugin(true).execute().actionGet();
logger.info("--> full json answer, status " + response.toString());
checkPlugin(response, server1NodeId, 0, 0);
checkPlugin(response, server2NodeId, 1, 0);
checkPlugin(response, server3NodeId, 0, 1);
checkPlugin(response, server4NodeId, 1, 2); // Note that we have now 2 JVM plugins as we have already loaded one with node3
}
/**
* We check infos
* @param response Response
* @param nodeId NodeId we want to check
* @param expectedSitePlugins Number of site plugins expected
* @param expectedJvmPlugins Number of jvm plugins expected
*/
private void checkPlugin(NodesInfoResponse response, String nodeId,
int expectedSitePlugins, int expectedJvmPlugins) {
assertThat(response.getNodesMap().get(nodeId), notNullValue());
PluginsInfo plugins = response.getNodesMap().get(nodeId).getPlugins();
assertThat(plugins, notNullValue());
int num_site_plugins = 0;
int num_jvm_plugins = 0;
for (PluginInfo pluginInfo : plugins.getInfos()) {
// It should be a site or a jvm plugin
assertThat(pluginInfo.isJvm() || pluginInfo.isSite(), is(true));
if (pluginInfo.isSite() && !pluginInfo.isJvm()) {
// Let's do some tests for site plugins
assertThat(pluginInfo.getName(), isOneOf(Fields.SITE_PLUGIN,
TestNoVersionPlugin.Fields.NAME));
assertThat(pluginInfo.getDescription(),
isOneOf(Fields.SITE_PLUGIN_DESCRIPTION,
Fields.SITE_PLUGIN_NO_DESCRIPTION,
Fields.JVM_PLUGIN_NO_DESCRIPTION));
assertThat(pluginInfo.getUrl(), notNullValue());
num_site_plugins++;
}
if (pluginInfo.isJvm() && !pluginInfo.isSite()) {
// Let's do some tests for site plugins
assertThat(pluginInfo.getName(),
isOneOf(TestPlugin.Fields.NAME, TestNoVersionPlugin.Fields.NAME));
assertThat(pluginInfo.getDescription(),
isOneOf(TestPlugin.Fields.DESCRIPTION, TestNoVersionPlugin.Fields.DESCRIPTION));
assertThat(pluginInfo.getUrl(), nullValue());
num_jvm_plugins++;
}
// On node4, test-no-version-plugin has an embedded _site structure
if (pluginInfo.isJvm() && pluginInfo.isSite()) {
assertThat(pluginInfo.getName(),
is(TestNoVersionPlugin.Fields.NAME));
assertThat(pluginInfo.getDescription(),
is(TestNoVersionPlugin.Fields.DESCRIPTION));
assertThat(pluginInfo.getUrl(), notNullValue());
num_jvm_plugins++;
}
}
assertThat(num_site_plugins, is(expectedSitePlugins));
assertThat(num_jvm_plugins, is(expectedJvmPlugins));
}
private String startNodeWithPlugins(String name) throws URISyntaxException {
URL resource = SimpleNodesInfoTests.class.getResource("/org/elasticsearch/test/integration/nodesinfo/" + name + "/");
ImmutableSettings.Builder settings = settingsBuilder();
if (resource != null) {
settings.put("path.plugins", new File(resource.toURI()).getAbsolutePath());
}
startNode(name, settings);
String serverNodeId = ((InternalNode) node(name)).injector()
.getInstance(ClusterService.class).state().nodes().localNodeId();
logger.debug("--> server {} started" + serverNodeId);
return serverNodeId;
}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.test.integration.nodesinfo.plugin.dummy1;
import org.elasticsearch.plugins.AbstractPlugin;
public class TestPlugin extends AbstractPlugin {
static final public class Fields {
static public final String NAME = "test-plugin";
static public final String DESCRIPTION = NAME + " description";
}
@Override
public String name() {
return Fields.NAME;
}
@Override
public String description() {
return Fields.DESCRIPTION;
}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.test.integration.nodesinfo.plugin.dummy2;
import org.elasticsearch.plugins.AbstractPlugin;
public class TestNoVersionPlugin extends AbstractPlugin {
static final public class Fields {
static public final String NAME = "test-no-version-plugin";
static public final String DESCRIPTION = NAME + " description";
}
@Override
public String name() {
return Fields.NAME;
}
@Override
public String description() {
return Fields.DESCRIPTION;
}
}

View File

@ -0,0 +1,20 @@
################################################################
# Licensed to ElasticSearch and Shay Banon under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. ElasticSearch licenses this
# file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
################################################################
description=This is a description for a dummy test site plugin.

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Dummy Site Plugin on Node 3</title>
</head>
<body>
<p>Welcome to this dummy elasticsearch plugin</p>
</body>
</html>

View File

@ -0,0 +1,20 @@
################################################################
# Licensed to ElasticSearch and Shay Banon under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. ElasticSearch licenses this
# file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
################################################################
plugin=org.elasticsearch.test.integration.nodesinfo.plugin.dummy1.TestPlugin

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Dummy Site Plugin on Node 3</title>
</head>
<body>
<p>Welcome to this dummy elasticsearch plugin</p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Dummy Site Plugin on Node 3</title>
</head>
<body>
<p>Welcome to this dummy elasticsearch plugin</p>
</body>
</html>

View File

@ -0,0 +1,19 @@
################################################################
# Licensed to ElasticSearch and Shay Banon under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. ElasticSearch licenses this
# file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
################################################################
plugin=org.elasticsearch.test.integration.nodesinfo.plugin.dummy2.TestNoVersionPlugin