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:
parent
f3e6fe094a
commit
36b92be212
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() + "\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
Loading…
Reference in New Issue