Plugins: Remove site plugins

Site plugins used to be used for things like kibana and marvel, but
there is no longer a need since kibana (and marvel as a kibana plugin)
uses node.js. This change removes site plugins, as well as the flag for
jvm plugins. Now all plugins are jvm plugins.
This commit is contained in:
Ryan Ernst 2016-01-16 21:54:05 -08:00
parent b4538a5676
commit 3b78267c71
42 changed files with 79 additions and 1033 deletions

View file

@ -112,9 +112,6 @@ public class PluginBuildPlugin extends BuildPlugin {
include 'config/**' include 'config/**'
include 'bin/**' include 'bin/**'
} }
from('src/site') {
include '_site/**'
}
} }
project.assemble.dependsOn(bundle) project.assemble.dependsOn(bundle)

View file

@ -36,15 +36,9 @@ class PluginPropertiesExtension {
@Input @Input
String description String description
@Input
boolean jvm = true
@Input @Input
String classname String classname
@Input
boolean site = false
@Input @Input
boolean isolated = true boolean isolated = true

View file

@ -51,11 +51,11 @@ class PluginPropertiesTask extends Copy {
if (extension.description == null) { if (extension.description == null) {
throw new InvalidUserDataException('description is a required setting for esplugin') throw new InvalidUserDataException('description is a required setting for esplugin')
} }
if (extension.jvm && extension.classname == null) { if (extension.classname == null) {
throw new InvalidUserDataException('classname is a required setting for esplugin with jvm=true') throw new InvalidUserDataException('classname is a required setting for esplugin')
} }
doFirst { doFirst {
if (extension.jvm && extension.isolated == false) { if (extension.isolated == false) {
String warning = "WARNING: Disabling plugin isolation in ${project.path} is deprecated and will be removed in the future" String warning = "WARNING: Disabling plugin isolation in ${project.path} is deprecated and will be removed in the future"
logger.warn("${'=' * warning.length()}\n${warning}\n${'=' * warning.length()}") logger.warn("${'=' * warning.length()}\n${warning}\n${'=' * warning.length()}")
} }
@ -74,10 +74,8 @@ class PluginPropertiesTask extends Copy {
'version': extension.version, 'version': extension.version,
'elasticsearchVersion': VersionProperties.elasticsearch, 'elasticsearchVersion': VersionProperties.elasticsearch,
'javaVersion': project.targetCompatibility as String, 'javaVersion': project.targetCompatibility as String,
'jvm': extension.jvm as String,
'site': extension.site as String,
'isolated': extension.isolated as String, 'isolated': extension.isolated as String,
'classname': extension.jvm ? extension.classname : 'NA' 'classname': extension.classname
] ]
} }
} }

View file

@ -2,26 +2,13 @@
# This file must exist as 'plugin-descriptor.properties' at # This file must exist as 'plugin-descriptor.properties' at
# the root directory of all plugins. # the root directory of all plugins.
# #
# A plugin can be 'site', 'jvm', or both. ### example plugin for "foo"
#
### example site plugin for "foo":
#
# foo.zip <-- zip file for the plugin, with this structure:
# _site/ <-- the contents that will be served
# plugin-descriptor.properties <-- example contents below:
#
# site=true
# description=My cool plugin
# version=1.0
#
### example jvm plugin for "foo"
# #
# foo.zip <-- zip file for the plugin, with this structure: # foo.zip <-- zip file for the plugin, with this structure:
# <arbitrary name1>.jar <-- classes, resources, dependencies # <arbitrary name1>.jar <-- classes, resources, dependencies
# <arbitrary nameN>.jar <-- any number of jars # <arbitrary nameN>.jar <-- any number of jars
# plugin-descriptor.properties <-- example contents below: # plugin-descriptor.properties <-- example contents below:
# #
# jvm=true
# classname=foo.bar.BazPlugin # classname=foo.bar.BazPlugin
# description=My cool plugin # description=My cool plugin
# version=2.0 # version=2.0
@ -38,21 +25,6 @@ version=${version}
# #
# 'name': the plugin name # 'name': the plugin name
name=${name} name=${name}
### mandatory elements for site plugins:
#
# 'site': set to true to indicate contents of the _site/
# directory in the root of the plugin should be served.
site=${site}
#
### mandatory elements for jvm plugins :
#
# 'jvm': true if the 'classname' class should be loaded
# from jar files in the root directory of the plugin.
# Note that only jar files in the root directory are
# added to the classpath for the plugin! If you need
# other resources, package them into a resources jar.
jvm=${jvm}
# #
# 'classname': the name of the class to load, fully-qualified. # 'classname': the name of the class to load, fully-qualified.
classname=${classname} classname=${classname}

View file

@ -51,7 +51,7 @@ import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK; import static org.elasticsearch.rest.RestStatus.OK;
/** /**
* * A component to serve http requests, backed by rest handlers.
*/ */
public class HttpServer extends AbstractLifecycleComponent<HttpServer> { public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
@ -63,10 +63,6 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
private final NodeService nodeService; private final NodeService nodeService;
private final boolean disableSites;
private final PluginSiteFilter pluginSiteFilter = new PluginSiteFilter();
@Inject @Inject
public HttpServer(Settings settings, Environment environment, HttpServerTransport transport, public HttpServer(Settings settings, Environment environment, HttpServerTransport transport,
RestController restController, RestController restController,
@ -77,9 +73,6 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
this.restController = restController; this.restController = restController;
this.nodeService = nodeService; this.nodeService = nodeService;
nodeService.setHttpServer(this); nodeService.setHttpServer(this);
this.disableSites = this.settings.getAsBoolean("http.disable_sites", false);
transport.httpServerAdapter(new Dispatcher(this)); transport.httpServerAdapter(new Dispatcher(this));
} }
@ -126,27 +119,13 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
} }
public void internalDispatchRequest(final HttpRequest request, final HttpChannel channel) { public void internalDispatchRequest(final HttpRequest request, final HttpChannel channel) {
String rawPath = request.rawPath(); if (request.rawPath().equals("/favicon.ico")) {
if (rawPath.startsWith("/_plugin/")) {
RestFilterChain filterChain = restController.filterChain(pluginSiteFilter);
filterChain.continueProcessing(request, channel);
return;
} else if (rawPath.equals("/favicon.ico")) {
handleFavicon(request, channel); handleFavicon(request, channel);
return; return;
} }
restController.dispatchRequest(request, channel); restController.dispatchRequest(request, channel);
} }
class PluginSiteFilter extends RestFilter {
@Override
public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws IOException {
handlePluginSite((HttpRequest) request, (HttpChannel) channel);
}
}
void handleFavicon(HttpRequest request, HttpChannel channel) { void handleFavicon(HttpRequest request, HttpChannel channel) {
if (request.method() == RestRequest.Method.GET) { if (request.method() == RestRequest.Method.GET) {
try { try {
@ -163,129 +142,4 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
channel.sendResponse(new BytesRestResponse(FORBIDDEN)); channel.sendResponse(new BytesRestResponse(FORBIDDEN));
} }
} }
void handlePluginSite(HttpRequest request, HttpChannel channel) throws IOException {
if (disableSites) {
channel.sendResponse(new BytesRestResponse(FORBIDDEN));
return;
}
if (request.method() == RestRequest.Method.OPTIONS) {
// when we have OPTIONS request, simply send OK by default (with the Access Control Origin header which gets automatically added)
channel.sendResponse(new BytesRestResponse(OK));
return;
}
if (request.method() != RestRequest.Method.GET) {
channel.sendResponse(new BytesRestResponse(FORBIDDEN));
return;
}
// TODO for a "/_plugin" endpoint, we should have a page that lists all the plugins?
String path = request.rawPath().substring("/_plugin/".length());
int i1 = path.indexOf('/');
String pluginName;
String sitePath;
if (i1 == -1) {
pluginName = path;
sitePath = null;
// If a trailing / is missing, we redirect to the right page #2654
String redirectUrl = request.rawPath() + "/";
BytesRestResponse restResponse = new BytesRestResponse(RestStatus.MOVED_PERMANENTLY, "text/html", "<head><meta http-equiv=\"refresh\" content=\"0; URL=" + redirectUrl + "\"></head>");
restResponse.addHeader("Location", redirectUrl);
channel.sendResponse(restResponse);
return;
} else {
pluginName = path.substring(0, i1);
sitePath = path.substring(i1 + 1);
}
// we default to index.html, or what the plugin provides (as a unix-style path)
// this is a relative path under _site configured by the plugin.
if (sitePath.length() == 0) {
sitePath = "index.html";
} else {
// remove extraneous leading slashes, its not an absolute path.
while (sitePath.length() > 0 && sitePath.charAt(0) == '/') {
sitePath = sitePath.substring(1);
}
}
final Path siteFile = environment.pluginsFile().resolve(pluginName).resolve("_site");
final String separator = siteFile.getFileSystem().getSeparator();
// Convert file separators.
sitePath = sitePath.replace("/", separator);
Path file = siteFile.resolve(sitePath);
// return not found instead of forbidden to prevent malicious requests to find out if files exist or dont exist
if (!Files.exists(file) || FileSystemUtils.isHidden(file) || !file.toAbsolutePath().normalize().startsWith(siteFile.toAbsolutePath().normalize())) {
channel.sendResponse(new BytesRestResponse(NOT_FOUND));
return;
}
BasicFileAttributes attributes = Files.readAttributes(file, BasicFileAttributes.class);
if (!attributes.isRegularFile()) {
// If it's not a dir, we send a 403
if (!attributes.isDirectory()) {
channel.sendResponse(new BytesRestResponse(FORBIDDEN));
return;
}
// We don't serve dir but if index.html exists in dir we should serve it
file = file.resolve("index.html");
if (!Files.exists(file) || FileSystemUtils.isHidden(file) || !Files.isRegularFile(file)) {
channel.sendResponse(new BytesRestResponse(FORBIDDEN));
return;
}
}
try {
byte[] data = Files.readAllBytes(file);
channel.sendResponse(new BytesRestResponse(OK, guessMimeType(sitePath), data));
} catch (IOException e) {
channel.sendResponse(new BytesRestResponse(INTERNAL_SERVER_ERROR));
}
}
// TODO: Don't respond with a mime type that violates the request's Accept header
private String guessMimeType(String path) {
int lastDot = path.lastIndexOf('.');
if (lastDot == -1) {
return "";
}
String extension = path.substring(lastDot + 1).toLowerCase(Locale.ROOT);
String mimeType = DEFAULT_MIME_TYPES.get(extension);
if (mimeType == null) {
return "";
}
return mimeType;
}
static {
// This is not an exhaustive list, just the most common types. Call registerMimeType() to add more.
Map<String, String> mimeTypes = new HashMap<>();
mimeTypes.put("txt", "text/plain");
mimeTypes.put("css", "text/css");
mimeTypes.put("csv", "text/csv");
mimeTypes.put("htm", "text/html");
mimeTypes.put("html", "text/html");
mimeTypes.put("xml", "text/xml");
mimeTypes.put("js", "text/javascript"); // Technically it should be application/javascript (RFC 4329), but IE8 struggles with that
mimeTypes.put("xhtml", "application/xhtml+xml");
mimeTypes.put("json", "application/json");
mimeTypes.put("pdf", "application/pdf");
mimeTypes.put("zip", "application/zip");
mimeTypes.put("tar", "application/x-tar");
mimeTypes.put("gif", "image/gif");
mimeTypes.put("jpeg", "image/jpeg");
mimeTypes.put("jpg", "image/jpeg");
mimeTypes.put("tiff", "image/tiff");
mimeTypes.put("tif", "image/tiff");
mimeTypes.put("png", "image/png");
mimeTypes.put("svg", "image/svg+xml");
mimeTypes.put("ico", "image/vnd.microsoft.icon");
mimeTypes.put("mp3", "audio/mpeg");
DEFAULT_MIME_TYPES = unmodifiableMap(mimeTypes);
}
public static final Map<String, String> DEFAULT_MIME_TYPES;
} }

View file

@ -20,9 +20,9 @@ package org.elasticsearch.plugins;
public class DummyPluginInfo extends PluginInfo { public class DummyPluginInfo extends PluginInfo {
private DummyPluginInfo(String name, String description, boolean site, String version, boolean jvm, String classname, boolean isolated) { private DummyPluginInfo(String name, String description, String version, String classname, boolean isolated) {
super(name, description, site, version, jvm, classname, isolated); super(name, description, version, classname, isolated);
} }
public static final DummyPluginInfo INSTANCE = new DummyPluginInfo("dummy_plugin_name", "dummy plugin description", true, "dummy_plugin_version", true, "DummyPluginName", true); public static final DummyPluginInfo INSTANCE = new DummyPluginInfo("dummy_plugin_name", "dummy plugin description", "dummy_plugin_version", "DummyPluginName", true);
} }

View file

@ -42,19 +42,14 @@ public class PluginInfo implements Streamable, ToXContent {
static final XContentBuilderString NAME = new XContentBuilderString("name"); static final XContentBuilderString NAME = new XContentBuilderString("name");
static final XContentBuilderString DESCRIPTION = new XContentBuilderString("description"); static final XContentBuilderString DESCRIPTION = new XContentBuilderString("description");
static final XContentBuilderString URL = new XContentBuilderString("url"); static final XContentBuilderString URL = new XContentBuilderString("url");
static final XContentBuilderString SITE = new XContentBuilderString("site");
static final XContentBuilderString VERSION = new XContentBuilderString("version"); static final XContentBuilderString VERSION = new XContentBuilderString("version");
static final XContentBuilderString JVM = new XContentBuilderString("jvm");
static final XContentBuilderString CLASSNAME = new XContentBuilderString("classname"); static final XContentBuilderString CLASSNAME = new XContentBuilderString("classname");
static final XContentBuilderString ISOLATED = new XContentBuilderString("isolated"); static final XContentBuilderString ISOLATED = new XContentBuilderString("isolated");
} }
private String name; private String name;
private String description; private String description;
private boolean site;
private String version; private String version;
private boolean jvm;
private String classname; private String classname;
private boolean isolated; private boolean isolated;
@ -66,15 +61,11 @@ public class PluginInfo implements Streamable, ToXContent {
* *
* @param name Its name * @param name Its name
* @param description Its description * @param description Its description
* @param site true if it's a site plugin
* @param jvm true if it's a jvm plugin
* @param version Version number * @param version Version number
*/ */
PluginInfo(String name, String description, boolean site, String version, boolean jvm, String classname, boolean isolated) { PluginInfo(String name, String description, String version, String classname, boolean isolated) {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.site = site;
this.jvm = jvm;
this.version = version; this.version = version;
this.classname = classname; this.classname = classname;
this.isolated = isolated; this.isolated = isolated;
@ -101,43 +92,28 @@ public class PluginInfo implements Streamable, ToXContent {
throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]"); throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]");
} }
boolean jvm = Boolean.parseBoolean(props.getProperty("jvm")); String esVersionString = props.getProperty("elasticsearch.version");
boolean site = Boolean.parseBoolean(props.getProperty("site")); if (esVersionString == null) {
if (jvm == false && site == false) { throw new IllegalArgumentException("Property [elasticsearch.version] is missing for plugin [" + name + "]");
throw new IllegalArgumentException("Plugin [" + name + "] must be at least a jvm or site plugin");
} }
boolean isolated = true; Version esVersion = Version.fromString(esVersionString);
String classname = "NA"; if (esVersion.equals(Version.CURRENT) == false) {
if (jvm) { throw new IllegalArgumentException("Plugin [" + name + "] is incompatible with Elasticsearch [" + Version.CURRENT.toString() +
String esVersionString = props.getProperty("elasticsearch.version"); "]. Was designed for version [" + esVersionString + "]");
if (esVersionString == null) { }
throw new IllegalArgumentException("Property [elasticsearch.version] is missing for jvm plugin [" + name + "]"); String javaVersionString = props.getProperty("java.version");
} if (javaVersionString == null) {
Version esVersion = Version.fromString(esVersionString); throw new IllegalArgumentException("Property [java.version] is missing for plugin [" + name + "]");
if (esVersion.equals(Version.CURRENT) == false) { }
throw new IllegalArgumentException("Plugin [" + name + "] is incompatible with Elasticsearch [" + Version.CURRENT.toString() + JarHell.checkVersionFormat(javaVersionString);
"]. Was designed for version [" + esVersionString + "]"); JarHell.checkJavaVersion(name, javaVersionString);
} boolean isolated = Boolean.parseBoolean(props.getProperty("isolated", "true"));
String javaVersionString = props.getProperty("java.version"); String classname = props.getProperty("classname");
if (javaVersionString == null) { if (classname == null) {
throw new IllegalArgumentException("Property [java.version] is missing for jvm plugin [" + name + "]"); throw new IllegalArgumentException("Property [classname] is missing for plugin [" + name + "]");
}
JarHell.checkVersionFormat(javaVersionString);
JarHell.checkJavaVersion(name, javaVersionString);
isolated = Boolean.parseBoolean(props.getProperty("isolated", "true"));
classname = props.getProperty("classname");
if (classname == null) {
throw new IllegalArgumentException("Property [classname] is missing for jvm plugin [" + name + "]");
}
} }
if (site) { return new PluginInfo(name, description, version, classname, isolated);
if (!Files.exists(dir.resolve("_site"))) {
throw new IllegalArgumentException("Plugin [" + name + "] is a site plugin but has no '_site/' directory");
}
}
return new PluginInfo(name, description, site, version, jvm, classname, isolated);
} }
/** /**
@ -155,46 +131,19 @@ public class PluginInfo implements Streamable, ToXContent {
} }
/** /**
* @return true if it's a site plugin * @return true if plugin has isolated classloader
*/
public boolean isSite() {
return site;
}
/**
* @return true if it's a plugin running in the jvm
*/
public boolean isJvm() {
return jvm;
}
/**
* @return true if jvm plugin has isolated classloader
*/ */
public boolean isIsolated() { public boolean isIsolated() {
return isolated; return isolated;
} }
/** /**
* @return jvm plugin's classname * @return plugin's classname
*/ */
public String getClassname() { public String getClassname() {
return classname; return classname;
} }
/**
* We compute the URL for sites: "/_plugin/" + name + "/"
*
* @return relative URL for site plugin
*/
public String getUrl() {
if (site) {
return ("/_plugin/" + name + "/");
} else {
return null;
}
}
/** /**
* @return Version number for the plugin * @return Version number for the plugin
*/ */
@ -212,8 +161,6 @@ public class PluginInfo implements Streamable, ToXContent {
public void readFrom(StreamInput in) throws IOException { public void readFrom(StreamInput in) throws IOException {
this.name = in.readString(); this.name = in.readString();
this.description = in.readString(); this.description = in.readString();
this.site = in.readBoolean();
this.jvm = in.readBoolean();
this.version = in.readString(); this.version = in.readString();
this.classname = in.readString(); this.classname = in.readString();
this.isolated = in.readBoolean(); this.isolated = in.readBoolean();
@ -223,8 +170,6 @@ public class PluginInfo implements Streamable, ToXContent {
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
out.writeString(name); out.writeString(name);
out.writeString(description); out.writeString(description);
out.writeBoolean(site);
out.writeBoolean(jvm);
out.writeString(version); out.writeString(version);
out.writeString(classname); out.writeString(classname);
out.writeBoolean(isolated); out.writeBoolean(isolated);
@ -236,15 +181,8 @@ public class PluginInfo implements Streamable, ToXContent {
builder.field(Fields.NAME, name); builder.field(Fields.NAME, name);
builder.field(Fields.VERSION, version); builder.field(Fields.VERSION, version);
builder.field(Fields.DESCRIPTION, description); builder.field(Fields.DESCRIPTION, description);
if (site) { builder.field(Fields.CLASSNAME, classname);
builder.field(Fields.URL, getUrl()); builder.field(Fields.ISOLATED, isolated);
}
builder.field(Fields.JVM, jvm);
if (jvm) {
builder.field(Fields.CLASSNAME, classname);
builder.field(Fields.ISOLATED, isolated);
}
builder.field(Fields.SITE, site);
builder.endObject(); builder.endObject();
return builder; return builder;
@ -274,14 +212,9 @@ public class PluginInfo implements Streamable, ToXContent {
.append("- Plugin information:\n") .append("- Plugin information:\n")
.append("Name: ").append(name).append("\n") .append("Name: ").append(name).append("\n")
.append("Description: ").append(description).append("\n") .append("Description: ").append(description).append("\n")
.append("Site: ").append(site).append("\n")
.append("Version: ").append(version).append("\n") .append("Version: ").append(version).append("\n")
.append("JVM: ").append(jvm).append("\n"); .append(" * Classname: ").append(classname).append("\n")
.append(" * Isolated: ").append(isolated);
if (jvm) {
information.append(" * Classname: ").append(classname).append("\n");
information.append(" * Isolated: ").append(isolated);
}
return information.toString(); return information.toString();
} }

View file

@ -258,9 +258,7 @@ public class PluginManager {
} }
// check for jar hell before any copying // check for jar hell before any copying
if (info.isJvm()) { jarHellCheck(root, info.isIsolated());
jarHellCheck(root, info.isIsolated());
}
// read optional security policy (extra permissions) // read optional security policy (extra permissions)
// if it exists, confirm or warn the user // if it exists, confirm or warn the user

View file

@ -98,7 +98,7 @@ public class PluginsService extends AbstractComponent {
// first we load plugins that are on the classpath. this is for tests and transport clients // first we load plugins that are on the classpath. this is for tests and transport clients
for (Class<? extends Plugin> pluginClass : classpathPlugins) { for (Class<? extends Plugin> pluginClass : classpathPlugins) {
Plugin plugin = loadPlugin(pluginClass, settings); Plugin plugin = loadPlugin(pluginClass, settings);
PluginInfo pluginInfo = new PluginInfo(plugin.name(), plugin.description(), false, "NA", true, pluginClass.getName(), false); PluginInfo pluginInfo = new PluginInfo(plugin.name(), plugin.description(), "NA", pluginClass.getName(), false);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("plugin loaded from classpath [{}]", pluginInfo); logger.trace("plugin loaded from classpath [{}]", pluginInfo);
} }
@ -136,18 +136,10 @@ public class PluginsService extends AbstractComponent {
plugins = Collections.unmodifiableList(pluginsLoaded); plugins = Collections.unmodifiableList(pluginsLoaded);
// We need to build a List of jvm and site plugins for checking mandatory plugins // We need to build a List of plugins for checking mandatory plugins
Map<String, Plugin> jvmPlugins = new HashMap<>(); Set<String> pluginsNames = new HashSet<>();
List<String> sitePlugins = new ArrayList<>();
for (Tuple<PluginInfo, Plugin> tuple : plugins) { for (Tuple<PluginInfo, Plugin> tuple : plugins) {
PluginInfo info = tuple.v1(); pluginsNames.add(tuple.v1().getName());
if (info.isJvm()) {
jvmPlugins.put(info.getName(), tuple.v2());
}
if (info.isSite()) {
sitePlugins.add(info.getName());
}
} }
// Checking expected plugins // Checking expected plugins
@ -155,7 +147,7 @@ public class PluginsService extends AbstractComponent {
if (mandatoryPlugins != null) { if (mandatoryPlugins != null) {
Set<String> missingPlugins = new HashSet<>(); Set<String> missingPlugins = new HashSet<>();
for (String mandatoryPlugin : mandatoryPlugins) { for (String mandatoryPlugin : mandatoryPlugins) {
if (!jvmPlugins.containsKey(mandatoryPlugin) && !sitePlugins.contains(mandatoryPlugin) && !missingPlugins.contains(mandatoryPlugin)) { if (!pluginsNames.contains(mandatoryPlugin) && !missingPlugins.contains(mandatoryPlugin)) {
missingPlugins.add(mandatoryPlugin); missingPlugins.add(mandatoryPlugin);
} }
} }
@ -175,10 +167,11 @@ public class PluginsService extends AbstractComponent {
jvmPluginNames.add(pluginInfo.getName()); jvmPluginNames.add(pluginInfo.getName());
} }
logger.info("modules {}, plugins {}, sites {}", moduleNames, jvmPluginNames, sitePlugins); logger.info("modules {}, plugins {}", moduleNames, jvmPluginNames);
Map<Plugin, List<OnModuleReference>> onModuleReferences = new HashMap<>(); Map<Plugin, List<OnModuleReference>> onModuleReferences = new HashMap<>();
for (Plugin plugin : jvmPlugins.values()) { for (Tuple<PluginInfo, Plugin> pluginEntry : plugins) {
Plugin plugin = pluginEntry.v2();
List<OnModuleReference> list = new ArrayList<>(); List<OnModuleReference> list = new ArrayList<>();
for (Method method : plugin.getClass().getMethods()) { for (Method method : plugin.getClass().getMethods()) {
if (!method.getName().equals("onModule")) { if (!method.getName().equals("onModule")) {
@ -304,9 +297,6 @@ public class PluginsService extends AbstractComponent {
continue; // skip over .DS_Store etc continue; // skip over .DS_Store etc
} }
PluginInfo info = PluginInfo.readFromProperties(module); PluginInfo info = PluginInfo.readFromProperties(module);
if (!info.isJvm()) {
throw new IllegalStateException("modules must be jvm plugins: " + info);
}
if (!info.isIsolated()) { if (!info.isIsolated()) {
throw new IllegalStateException("modules must be isolated: " + info); throw new IllegalStateException("modules must be isolated: " + info);
} }
@ -353,17 +343,14 @@ public class PluginsService extends AbstractComponent {
} }
List<URL> urls = new ArrayList<>(); List<URL> urls = new ArrayList<>();
if (info.isJvm()) { try (DirectoryStream<Path> jarStream = Files.newDirectoryStream(plugin, "*.jar")) {
// a jvm plugin: gather urls for jar files for (Path jar : jarStream) {
try (DirectoryStream<Path> jarStream = Files.newDirectoryStream(plugin, "*.jar")) { // normalize with toRealPath to get symlinks out of our hair
for (Path jar : jarStream) { urls.add(jar.toRealPath().toUri().toURL());
// normalize with toRealPath to get symlinks out of our hair
urls.add(jar.toRealPath().toUri().toURL());
}
} }
} }
final Bundle bundle; final Bundle bundle;
if (info.isJvm() && info.isIsolated() == false) { if (info.isIsolated() == false) {
bundle = bundles.get(0); // purgatory bundle = bundles.get(0); // purgatory
} else { } else {
bundle = new Bundle(); bundle = new Bundle();
@ -395,15 +382,10 @@ public class PluginsService extends AbstractComponent {
// create a child to load the plugins in this bundle // create a child to load the plugins in this bundle
ClassLoader loader = URLClassLoader.newInstance(bundle.urls.toArray(new URL[0]), getClass().getClassLoader()); ClassLoader loader = URLClassLoader.newInstance(bundle.urls.toArray(new URL[0]), getClass().getClassLoader());
for (PluginInfo pluginInfo : bundle.plugins) { for (PluginInfo pluginInfo : bundle.plugins) {
final Plugin plugin; // reload lucene SPI with any new services from the plugin
if (pluginInfo.isJvm()) { reloadLuceneSPI(loader);
// reload lucene SPI with any new services from the plugin final Class<? extends Plugin> pluginClass = loadPluginClass(pluginInfo.getClassname(), loader);
reloadLuceneSPI(loader); final Plugin plugin = loadPlugin(pluginClass, settings);
Class<? extends Plugin> pluginClass = loadPluginClass(pluginInfo.getClassname(), loader);
plugin = loadPlugin(pluginClass, settings);
} else {
plugin = new SitePlugin(pluginInfo.getName(), pluginInfo.getDescription());
}
plugins.add(new Tuple<>(pluginInfo, plugin)); plugins.add(new Tuple<>(pluginInfo, plugin));
} }
} }

View file

@ -1,41 +0,0 @@
/*
* Licensed to Elasticsearch 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.plugins;
/** A site-only plugin, just serves resources */
final class SitePlugin extends Plugin {
final String name;
final String description;
SitePlugin(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public String name() {
return name;
}
@Override
public String description() {
return description;
}
}

View file

@ -84,8 +84,6 @@ public class RestPluginsAction extends AbstractCatAction {
table.addCell("name", "alias:n;desc:node name"); table.addCell("name", "alias:n;desc:node name");
table.addCell("component", "alias:c;desc:component"); table.addCell("component", "alias:c;desc:component");
table.addCell("version", "alias:v;desc:component version"); table.addCell("version", "alias:v;desc:component version");
table.addCell("type", "alias:t;desc:type (j for JVM, s for Site)");
table.addCell("url", "alias:u;desc:url for site plugins");
table.addCell("description", "alias:d;default:false;desc:plugin details"); table.addCell("description", "alias:d;default:false;desc:plugin details");
table.endHeaders(); table.endHeaders();
return table; return table;
@ -104,22 +102,6 @@ public class RestPluginsAction extends AbstractCatAction {
table.addCell(node.name()); table.addCell(node.name());
table.addCell(pluginInfo.getName()); table.addCell(pluginInfo.getName());
table.addCell(pluginInfo.getVersion()); table.addCell(pluginInfo.getVersion());
String type;
if (pluginInfo.isSite()) {
if (pluginInfo.isJvm()) {
type = "j/s";
} else {
type = "s";
}
} else {
if (pluginInfo.isJvm()) {
type = "j";
} else {
type = "";
}
}
table.addCell(type);
table.addCell(pluginInfo.getUrl());
table.addCell(pluginInfo.getDescription()); table.addCell(pluginInfo.getDescription());
table.endRow(); table.endRow();
} }

View file

@ -40,17 +40,13 @@ public class PluginInfoTests extends ESTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
PluginInfo info = PluginInfo.readFromProperties(pluginDir); PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertEquals("my_plugin", info.getName()); assertEquals("my_plugin", info.getName());
assertEquals("fake desc", info.getDescription()); assertEquals("fake desc", info.getDescription());
assertEquals("1.0", info.getVersion()); assertEquals("1.0", info.getVersion());
assertEquals("FakePlugin", info.getClassname()); assertEquals("FakePlugin", info.getClassname());
assertTrue(info.isJvm());
assertTrue(info.isIsolated()); assertTrue(info.isIsolated());
assertFalse(info.isSite());
assertNull(info.getUrl());
} }
public void testReadFromPropertiesNameMissing() throws Exception { public void testReadFromPropertiesNameMissing() throws Exception {
@ -94,27 +90,12 @@ public class PluginInfoTests extends ESTestCase {
} }
} }
public void testReadFromPropertiesJvmAndSiteMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
PluginTestUtil.writeProperties(pluginDir,
"description", "fake desc",
"version", "1.0",
"name", "my_plugin");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected jvm or site exception");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("must be at least a jvm or site plugin"));
}
}
public void testReadFromPropertiesElasticsearchVersionMissing() throws Exception { public void testReadFromPropertiesElasticsearchVersionMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin"); Path pluginDir = createTempDir().resolve("fake-plugin");
PluginTestUtil.writeProperties(pluginDir, PluginTestUtil.writeProperties(pluginDir,
"description", "fake desc", "description", "fake desc",
"name", "my_plugin", "name", "my_plugin",
"version", "1.0", "version", "1.0");
"jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
fail("expected missing elasticsearch version exception"); fail("expected missing elasticsearch version exception");
@ -129,8 +110,7 @@ public class PluginInfoTests extends ESTestCase {
"description", "fake desc", "description", "fake desc",
"name", "my_plugin", "name", "my_plugin",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"version", "1.0", "version", "1.0");
"jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
fail("expected missing java version exception"); fail("expected missing java version exception");
@ -148,8 +128,7 @@ public class PluginInfoTests extends ESTestCase {
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", "1000000.0", "java.version", "1000000.0",
"classname", "FakePlugin", "classname", "FakePlugin",
"version", "1.0", "version", "1.0");
"jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
fail("expected incompatible java version exception"); fail("expected incompatible java version exception");
@ -167,8 +146,7 @@ public class PluginInfoTests extends ESTestCase {
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", "1.7.0_80", "java.version", "1.7.0_80",
"classname", "FakePlugin", "classname", "FakePlugin",
"version", "1.0", "version", "1.0");
"jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
fail("expected bad java version format exception"); fail("expected bad java version format exception");
@ -182,7 +160,6 @@ public class PluginInfoTests extends ESTestCase {
PluginTestUtil.writeProperties(pluginDir, PluginTestUtil.writeProperties(pluginDir,
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"jvm", "true",
"name", "my_plugin", "name", "my_plugin",
"elasticsearch.version", "bogus"); "elasticsearch.version", "bogus");
try { try {
@ -199,7 +176,6 @@ public class PluginInfoTests extends ESTestCase {
"description", "fake desc", "description", "fake desc",
"name", "my_plugin", "name", "my_plugin",
"version", "1.0", "version", "1.0",
"jvm", "true",
"elasticsearch.version", Version.V_1_7_0.toString()); "elasticsearch.version", Version.V_1_7_0.toString());
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
@ -216,8 +192,7 @@ public class PluginInfoTests extends ESTestCase {
"name", "my_plugin", "name", "my_plugin",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"));
"jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
fail("expected old elasticsearch version exception"); fail("expected old elasticsearch version exception");
@ -226,42 +201,13 @@ public class PluginInfoTests extends ESTestCase {
} }
} }
public void testReadFromPropertiesSitePlugin() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
Files.createDirectories(pluginDir.resolve("_site"));
PluginTestUtil.writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"site", "true");
PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertTrue(info.isSite());
assertFalse(info.isJvm());
assertEquals("NA", info.getClassname());
}
public void testReadFromPropertiesSitePluginWithoutSite() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
PluginTestUtil.writeProperties(pluginDir,
"description", "fake desc",
"name", "my_plugin",
"version", "1.0",
"site", "true");
try {
PluginInfo.readFromProperties(pluginDir);
fail("didn't get expected exception");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("site plugin but has no '_site"));
}
}
public void testPluginListSorted() { public void testPluginListSorted() {
PluginsAndModules pluginsInfo = new PluginsAndModules(); PluginsAndModules pluginsInfo = new PluginsAndModules();
pluginsInfo.addPlugin(new PluginInfo("c", "foo", true, "dummy", true, "dummyclass", true)); pluginsInfo.addPlugin(new PluginInfo("c", "foo", "dummy", "dummyclass", true));
pluginsInfo.addPlugin(new PluginInfo("b", "foo", true, "dummy", true, "dummyclass", true)); pluginsInfo.addPlugin(new PluginInfo("b", "foo", "dummy", "dummyclass", true));
pluginsInfo.addPlugin(new PluginInfo("e", "foo", true, "dummy", true, "dummyclass", true)); pluginsInfo.addPlugin(new PluginInfo("e", "foo", "dummy", "dummyclass", true));
pluginsInfo.addPlugin(new PluginInfo("a", "foo", true, "dummy", true, "dummyclass", true)); pluginsInfo.addPlugin(new PluginInfo("a", "foo", "dummy", "dummyclass", true));
pluginsInfo.addPlugin(new PluginInfo("d", "foo", true, "dummy", true, "dummyclass", true)); pluginsInfo.addPlugin(new PluginInfo("d", "foo", "dummy", "dummyclass", true));
final List<PluginInfo> infos = pluginsInfo.getPluginInfos(); final List<PluginInfo> infos = pluginsInfo.getPluginInfos();
List<String> names = infos.stream().map((input) -> input.getName()).collect(Collectors.toList()); List<String> names = infos.stream().map((input) -> input.getName()).collect(Collectors.toList());

View file

@ -1,131 +0,0 @@
/*
* Licensed to Elasticsearch 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.plugins;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.ESIntegTestCase.Scope;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.elasticsearch.rest.RestStatus.FORBIDDEN;
import static org.elasticsearch.rest.RestStatus.MOVED_PERMANENTLY;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasStatus;
import static org.hamcrest.Matchers.containsString;
/**
* We want to test site plugins
*/
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1)
public class SitePluginIT extends ESIntegTestCase {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Path pluginDir = getDataPath("/org/elasticsearch/test_plugins");
return settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("path.plugins", pluginDir.toAbsolutePath())
.put("force.http.enabled", true)
.build();
}
@Override
public HttpRequestBuilder httpClient() {
RequestConfig.Builder builder = RequestConfig.custom().setRedirectsEnabled(false);
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(builder.build()).build();
return new HttpRequestBuilder(httpClient).httpTransport(internalCluster().getDataNodeInstance(HttpServerTransport.class));
}
public void testRedirectSitePlugin() throws Exception {
// We use an HTTP Client to test redirection
HttpResponse response = httpClient().method("GET").path("/_plugin/dummy").execute();
assertThat(response, hasStatus(MOVED_PERMANENTLY));
assertThat(response.getBody(), containsString("/_plugin/dummy/"));
// We test the real URL
response = httpClient().method("GET").path("/_plugin/dummy/").execute();
assertThat(response, hasStatus(OK));
assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>"));
}
/**
* Test direct access to an existing file (index.html)
*/
public void testAnyPage() throws Exception {
HttpResponse response = httpClient().path("/_plugin/dummy/index.html").execute();
assertThat(response, hasStatus(OK));
assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>"));
}
/**
* Test normalizing of path
*/
public void testThatPathsAreNormalized() throws Exception {
// more info: https://www.owasp.org/index.php/Path_Traversal
List<String> notFoundUris = new ArrayList<>();
notFoundUris.add("/_plugin/dummy/../../../../../log4j.properties");
notFoundUris.add("/_plugin/dummy/../../../../../%00log4j.properties");
notFoundUris.add("/_plugin/dummy/..%c0%af..%c0%af..%c0%af..%c0%af..%c0%aflog4j.properties");
notFoundUris.add("/_plugin/dummy/%2E%2E/%2E%2E/%2E%2E/%2E%2E/index.html");
notFoundUris.add("/_plugin/dummy/%2e%2e/%2e%2e/%2e%2e/%2e%2e/index.html");
notFoundUris.add("/_plugin/dummy/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2findex.html");
notFoundUris.add("/_plugin/dummy/%2E%2E/%2E%2E/%2E%2E/%2E%2E/index.html");
notFoundUris.add("/_plugin/dummy/..%5C..%5C..%5C..%5C..%5Clog4j.properties");
for (String uri : notFoundUris) {
HttpResponse response = httpClient().path(uri).execute();
String message = String.format(Locale.ROOT, "URI [%s] expected to be not found", uri);
assertThat(message, response, hasStatus(NOT_FOUND));
}
// using relative path inside of the plugin should work
HttpResponse response = httpClient().path("/_plugin/dummy/dir1/../dir1/../index.html").execute();
assertThat(response, hasStatus(OK));
assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>"));
}
/**
* Test case for #4845: https://github.com/elasticsearch/elasticsearch/issues/4845
* Serving _site plugins do not pick up on index.html for sub directories
*/
public void testWelcomePageInSubDirs() throws Exception {
HttpResponse response = httpClient().path("/_plugin/subdir/dir/").execute();
assertThat(response, hasStatus(OK));
assertThat(response.getBody(), containsString("<title>Dummy Site Plugin (subdir)</title>"));
response = httpClient().path("/_plugin/subdir/dir_without_index/").execute();
assertThat(response, hasStatus(FORBIDDEN));
response = httpClient().path("/_plugin/subdir/dir_without_index/page.html").execute();
assertThat(response, hasStatus(OK));
assertThat(response.getBody(), containsString("<title>Dummy Site Plugin (page)</title>"));
}
}

View file

@ -1,88 +0,0 @@
/*
* Licensed to Elasticsearch 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.plugins;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import java.nio.file.Path;
import static org.apache.lucene.util.Constants.WINDOWS;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasStatus;
@ClusterScope(scope = SUITE, numDataNodes = 1)
public class SitePluginRelativePathConfigIT extends ESIntegTestCase {
private final Path root = PathUtils.get(".").toAbsolutePath().getRoot();
@Override
protected Settings nodeSettings(int nodeOrdinal) {
String cwdToRoot = getRelativePath(PathUtils.get(".").toAbsolutePath());
Path pluginDir = PathUtils.get(cwdToRoot, relativizeToRootIfNecessary(getDataPath("/org/elasticsearch/test_plugins")).toString());
Path tempDir = createTempDir();
boolean useRelativeInMiddleOfPath = randomBoolean();
if (useRelativeInMiddleOfPath) {
pluginDir = PathUtils.get(tempDir.toString(), getRelativePath(tempDir), pluginDir.toString());
}
return settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("path.plugins", pluginDir)
.put("force.http.enabled", true)
.build();
}
public void testThatRelativePathsDontAffectPlugins() throws Exception {
HttpResponse response = httpClient().method("GET").path("/_plugin/dummy/").execute();
assertThat(response, hasStatus(OK));
}
private Path relativizeToRootIfNecessary(Path path) {
if (WINDOWS) {
return root.relativize(path);
}
return path;
}
private String getRelativePath(Path path) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.getNameCount(); i++) {
sb.append("..");
sb.append(path.getFileSystem().getSeparator());
}
return sb.toString();
}
@Override
public HttpRequestBuilder httpClient() {
CloseableHttpClient httpClient = HttpClients.createDefault();
return new HttpRequestBuilder(httpClient).httpTransport(internalCluster().getDataNodeInstance(HttpServerTransport.class));
}
}

View file

@ -3,8 +3,6 @@
The Elasticsearch repository contains examples of: The Elasticsearch repository contains examples of:
* a https://github.com/elastic/elasticsearch/tree/master/plugins/site-example[site plugin]
for serving static HTML, JavaScript, and CSS.
* a https://github.com/elastic/elasticsearch/tree/master/plugins/jvm-example[Java plugin] * a https://github.com/elastic/elasticsearch/tree/master/plugins/jvm-example[Java plugin]
which contains Java code. which contains Java code.
@ -12,20 +10,6 @@ These examples provide the bare bones needed to get started. For more
information about how to write a plugin, we recommend looking at the plugins information about how to write a plugin, we recommend looking at the plugins
listed in this documentation for inspiration. listed in this documentation for inspiration.
[NOTE]
.Site plugins
====================================
The example site plugin mentioned above contains all of the scaffolding needed
for integrating with Gradle builds. If you don't plan on using Gradle, then all
you really need in your plugin is:
* The `plugin-descriptor.properties` file
* The `_site/` directory
* The `_site/index.html` file
====================================
[float] [float]
=== Plugin descriptor file === Plugin descriptor file
@ -43,7 +27,7 @@ instance, see
https://github.com/elastic/elasticsearch/blob/master/plugins/site-example/build.gradle[`/plugins/site-example/build.gradle`]. https://github.com/elastic/elasticsearch/blob/master/plugins/site-example/build.gradle[`/plugins/site-example/build.gradle`].
[float] [float]
==== Mandatory elements for all plugins ==== Mandatory elements for plugins
[cols="<,<,<",options="header",] [cols="<,<,<",options="header",]
@ -56,23 +40,6 @@ https://github.com/elastic/elasticsearch/blob/master/plugins/site-example/build.
|`name` |String | the plugin name |`name` |String | the plugin name
|=======================================================================
[float]
==== Mandatory elements for Java plugins
[cols="<,<,<",options="header",]
|=======================================================================
|Element | Type | Description
|`jvm` |Boolean | true if the `classname` class should be loaded
from jar files in the root directory of the plugin.
Note that only jar files in the root directory are added to the classpath for the plugin!
If you need other resources, package them into a resources jar.
|`classname` |String | the name of the class to load, fully-qualified. |`classname` |String | the name of the class to load, fully-qualified.
|`java.version` |String | version of java the code is built against. |`java.version` |String | version of java the code is built against.
@ -83,6 +50,9 @@ of nonnegative decimal integers separated by "."'s and may have leading zeros.
|======================================================================= |=======================================================================
Note that only jar files in the root directory are added to the classpath for the plugin!
If you need other resources, package them into a resources jar.
[IMPORTANT] [IMPORTANT]
.Plugin release lifecycle .Plugin release lifecycle
============================================== ==============================================
@ -94,20 +64,6 @@ in the presence of plugins with the incorrect `elasticsearch.version`.
============================================== ==============================================
[float]
==== Mandatory elements for Site plugins
[cols="<,<,<",options="header",]
|=======================================================================
|Element | Type | Description
|`site` |Boolean | true to indicate contents of the `_site/`
directory in the root of the plugin should be served.
|=======================================================================
[float] [float]
=== Testing your plugin === Testing your plugin

View file

@ -27,7 +27,7 @@ consult this table:
|2.x |3.x |<<restart-upgrade,Full cluster restart>> |2.x |3.x |<<restart-upgrade,Full cluster restart>>
|======================================================================= |=======================================================================
TIP: Take plugins into consideration as well when upgrading. Most plugins will have to be upgraded alongside Elasticsearch, although some plugins accessed primarily through the browser (`_site` plugins) may continue to work given that API changes are compatible. TIP: Take plugins into consideration as well when upgrading. Plugins must be upgraded alongside Elasticsearch.
include::backup.asciidoc[] include::backup.asciidoc[]

View file

@ -39,8 +39,5 @@ subprojects {
if (esplugin.isolated == false) { if (esplugin.isolated == false) {
throw new InvalidModelException("Modules cannot disable isolation") throw new InvalidModelException("Modules cannot disable isolation")
} }
if (esplugin.jvm == false) {
throw new InvalidModelException("Modules must be jvm plugins")
}
} }
} }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.modules.0.name: lang-expression } - match: { nodes.$master.modules.0.name: lang-expression }
- match: { nodes.$master.modules.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.modules.0.name: lang-groovy } - match: { nodes.$master.modules.0.name: lang-groovy }
- match: { nodes.$master.modules.0.jvm: true }

View file

@ -11,7 +11,6 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.modules.0.name: lang-mustache } - match: { nodes.$master.modules.0.name: lang-mustache }
- match: { nodes.$master.modules.0.jvm: true }
--- ---
"Indexed template": "Indexed template":

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: discovery-azure } - match: { nodes.$master.plugins.0.name: discovery-azure }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: discovery-ec2 } - match: { nodes.$master.plugins.0.name: discovery-ec2 }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: discovery-gce } - match: { nodes.$master.plugins.0.name: discovery-gce }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: discovery-multicast } - match: { nodes.$master.plugins.0.name: discovery-multicast }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: jvm-example } - match: { nodes.$master.plugins.0.name: jvm-example }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: lang-plan-a } - match: { nodes.$master.plugins.0.name: lang-plan-a }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: mapper-attachments } - match: { nodes.$master.plugins.0.name: mapper-attachments }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: repository-azure } - match: { nodes.$master.plugins.0.name: repository-azure }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -13,7 +13,6 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: repository-hdfs } - match: { nodes.$master.plugins.0.name: repository-hdfs }
- match: { nodes.$master.plugins.0.jvm: true }
--- ---
# #
# Check that we can't use file:// repositories or anything like that # Check that we can't use file:// repositories or anything like that

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: repository-s3 } - match: { nodes.$master.plugins.0.name: repository-s3 }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -1,27 +0,0 @@
/*
* Licensed to Elasticsearch 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.
*/
esplugin {
description 'Demonstrates how to serve resources via elasticsearch.'
jvm false
site true
}
// no unit tests
test.enabled = false

View file

@ -1,6 +0,0 @@
<html>
<head>
<title>Page title</title>
</head>
<body>Page body</body>
</html>

View file

@ -1,59 +0,0 @@
/*
* Licensed to Elasticsearch 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.example;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ExternalTestCluster;
import org.elasticsearch.test.TestCluster;
import org.elasticsearch.test.rest.client.RestResponse;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* verifies content is actually served for the site plugin
*/
public class SiteContentsIT extends ESIntegTestCase {
// TODO: probably a better way to test, but we don't want to really
// define a fake rest spec or anything?
public void test() throws Exception {
TestCluster cluster = cluster();
assumeTrue("this test will not work from an IDE unless you pass tests.cluster pointing to a running instance", cluster instanceof ExternalTestCluster);
ExternalTestCluster externalCluster = (ExternalTestCluster) cluster;
try (CloseableHttpClient httpClient = HttpClients.createMinimal(new PoolingHttpClientConnectionManager(15, TimeUnit.SECONDS))) {
for (InetSocketAddress address : externalCluster.httpAddresses()) {
RestResponse restResponse = new RestResponse(
new HttpRequestBuilder(httpClient)
.host(NetworkAddress.formatAddress(address.getAddress())).port(address.getPort())
.path("/_plugin/site-example/")
.method("GET").execute());
assertEquals(200, restResponse.getStatusCode());
String body = restResponse.getBodyAsString();
assertTrue("unexpected body contents: " + body, body.contains("<body>Page body</body>"));
}
}
}
}

View file

@ -1,41 +0,0 @@
/*
* Licensed to Elasticsearch 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.example;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import java.io.IOException;
public class SiteRestIT extends ESRestTestCase {
public SiteRestIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
}
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
}
}

View file

@ -1,15 +0,0 @@
# Integration tests for Example site plugin
#
"Example site loaded":
- do:
cluster.state: {}
# Get master node id
- set: { master_node: master }
- do:
nodes.info: {}
- match: { nodes.$master.plugins.0.name: site-example }
- match: { nodes.$master.plugins.0.jvm: false }
- match: { nodes.$master.plugins.0.site: true }

View file

@ -11,4 +11,3 @@
nodes.info: {} nodes.info: {}
- match: { nodes.$master.plugins.0.name: store-smb } - match: { nodes.$master.plugins.0.name: store-smb }
- match: { nodes.$master.plugins.0.jvm: true }

View file

@ -196,7 +196,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
assertStatus("install", USAGE); assertStatus("install", USAGE);
} }
@ -216,7 +215,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Path binDir = environment.binFile(); Path binDir = environment.binFile();
@ -260,7 +258,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Path pluginConfigDir = environment.configFile().resolve(pluginName); Path pluginConfigDir = environment.configFile().resolve(pluginName);
@ -296,7 +293,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "2.0", "version", "2.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl)); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl));
@ -361,7 +357,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
Path binDir = environment.binFile(); Path binDir = environment.binFile();
@ -392,16 +387,13 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
System.err.println("install " + pluginUrl + " --verbose"); System.err.println("install " + pluginUrl + " --verbose");
ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl + " --verbose")); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl + " --verbose"));
assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Name: fake-plugin"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Name: fake-plugin")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Description: fake desc"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Description: fake desc")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Site: false")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Version: 1.0"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Version: 1.0")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("JVM: true")));
assertThatPluginIsListed(pluginName); assertThatPluginIsListed(pluginName);
} }
@ -414,7 +406,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl)); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl));
assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK));
@ -447,7 +438,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"isolated", "false", "isolated", "false",
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
// install // install
@ -465,63 +455,20 @@ public class PluginManagerTests extends ESIntegTestCase {
assertTrue(foundExpectedMessage); assertTrue(foundExpectedMessage);
} }
public void testInstallSitePluginVerbose() throws IOException {
String pluginName = "fake-plugin";
Path pluginDir = createTempDir().resolve(pluginName);
Files.createDirectories(pluginDir.resolve("_site"));
Files.createFile(pluginDir.resolve("_site").resolve("somefile"));
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"site", "true");
ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl + " --verbose"));
assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Name: fake-plugin")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Description: fake desc")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Site: true")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("Version: 1.0")));
assertThat(terminal.getTerminalOutput(), hasItem(containsString("JVM: false")));
assertThatPluginIsListed(pluginName);
// We want to check that Plugin Manager moves content to _site
assertFileExists(environment.pluginsFile().resolve(pluginName).resolve("_site"));
}
public void testInstallSitePlugin() throws IOException {
String pluginName = "fake-plugin";
Path pluginDir = createTempDir().resolve(pluginName);
Files.createDirectories(pluginDir.resolve("_site"));
Files.createFile(pluginDir.resolve("_site").resolve("somefile"));
String pluginUrl = createPlugin(pluginDir,
"description", "fake desc",
"name", pluginName,
"version", "1.0",
"site", "true");
ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl));
assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK));
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Name: fake-plugin"))));
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Description:"))));
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Site:"))));
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Version:"))));
assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("JVM:"))));
assertThatPluginIsListed(pluginName);
// We want to check that Plugin Manager moves content to _site
assertFileExists(environment.pluginsFile().resolve(pluginName).resolve("_site"));
}
public void testInstallPluginWithBadChecksum() throws IOException { public void testInstallPluginWithBadChecksum() throws IOException {
String pluginName = "fake-plugin"; String pluginName = "fake-plugin";
Path pluginDir = createTempDir().resolve(pluginName); Path pluginDir = createTempDir().resolve(pluginName);
Files.createDirectories(pluginDir.resolve("_site"));
Files.createFile(pluginDir.resolve("_site").resolve("somefile"));
String pluginUrl = createPluginWithBadChecksum(pluginDir, String pluginUrl = createPluginWithBadChecksum(pluginDir,
"description", "fake desc", "description", "fake desc",
"version", "1.0", "name", pluginName,
"site", "true"); "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"classname", "FakePlugin");
assertStatus(String.format(Locale.ROOT, "install %s --verbose", pluginUrl), assertStatus(String.format(Locale.ROOT, "install %s --verbose", pluginUrl),
ExitStatus.IO_ERROR); ExitStatus.IO_ERROR);
assertThatPluginIsNotListed(pluginName); assertThatPluginIsNotListed(pluginName);
assertFileNotExists(environment.pluginsFile().resolve(pluginName).resolve("_site")); assertFileNotExists(environment.pluginsFile().resolve(pluginName));
} }
private void singlePluginInstallAndRemove(String pluginDescriptor, String pluginName, String pluginCoordinates) throws IOException { private void singlePluginInstallAndRemove(String pluginDescriptor, String pluginName, String pluginCoordinates) throws IOException {
@ -606,7 +553,6 @@ public class PluginManagerTests extends ESIntegTestCase {
"version", "1.0.0", "version", "1.0.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
// We want to remove plugin with plugin short name // We want to remove plugin with plugin short name

View file

@ -263,12 +263,6 @@ fi
install_and_check_plugin repository s3 aws-java-sdk-core-*.jar install_and_check_plugin repository s3 aws-java-sdk-core-*.jar
} }
@test "[$GROUP] install site example" {
# Doesn't use install_and_check_plugin because this is a site plugin
install_plugin site-example $(readlink -m site-example-*.zip)
assert_file_exist "$ESHOME/plugins/site-example/_site/index.html"
}
@test "[$GROUP] install store-smb plugin" { @test "[$GROUP] install store-smb plugin" {
install_and_check_plugin store smb install_and_check_plugin store smb
} }

View file

@ -10,7 +10,5 @@
name .+ \n name .+ \n
component .+ \n component .+ \n
version .+ \n version .+ \n
type .+ \n
url .+ \n
description .+ \n description .+ \n
$/ $/

View file

@ -34,7 +34,6 @@ List projects = [
'plugins:repository-hdfs', 'plugins:repository-hdfs',
'plugins:repository-s3', 'plugins:repository-s3',
'plugins:jvm-example', 'plugins:jvm-example',
'plugins:site-example',
'plugins:store-smb', 'plugins:store-smb',
'qa:evil-tests', 'qa:evil-tests',
'qa:smoke-test-client', 'qa:smoke-test-client',

View file

@ -154,11 +154,9 @@ public class BootstrapForTesting {
try (InputStream stream = url.openStream()) { try (InputStream stream = url.openStream()) {
properties.load(stream); properties.load(stream);
} }
if (Boolean.parseBoolean(properties.getProperty("jvm"))) { String clazz = properties.getProperty("classname");
String clazz = properties.getProperty("classname"); if (clazz != null) {
if (clazz != null) { Class.forName(clazz);
Class.forName(clazz);
}
} }
} }
} catch (Exception e) { } catch (Exception e) {

View file

@ -731,82 +731,6 @@ public class ElasticsearchAssertions {
return response; return response;
} }
public static void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId,
List<String> expectedJvmPluginNames,
List<String> expectedJvmPluginDescriptions,
List<String> expectedJvmVersions,
List<String> expectedSitePluginNames,
List<String> expectedSitePluginDescriptions,
List<String> expectedSiteVersions) {
Assert.assertThat(response.getNodesMap().get(nodeId), notNullValue());
PluginsAndModules plugins = response.getNodesMap().get(nodeId).getPlugins();
Assert.assertThat(plugins, notNullValue());
List<String> pluginNames = filterAndMap(plugins, jvmPluginPredicate, nameFunction);
for (String expectedJvmPluginName : expectedJvmPluginNames) {
Assert.assertThat(pluginNames, hasItem(expectedJvmPluginName));
}
List<String> pluginDescriptions = filterAndMap(plugins, jvmPluginPredicate, descriptionFunction);
for (String expectedJvmPluginDescription : expectedJvmPluginDescriptions) {
Assert.assertThat(pluginDescriptions, hasItem(expectedJvmPluginDescription));
}
List<String> jvmPluginVersions = filterAndMap(plugins, jvmPluginPredicate, versionFunction);
for (String pluginVersion : expectedJvmVersions) {
Assert.assertThat(jvmPluginVersions, hasItem(pluginVersion));
}
boolean anyHaveUrls =
plugins
.getPluginInfos()
.stream()
.filter(jvmPluginPredicate.and(sitePluginPredicate.negate()))
.map(urlFunction)
.anyMatch(p -> p != null);
assertFalse(anyHaveUrls);
List<String> sitePluginNames = filterAndMap(plugins, sitePluginPredicate, nameFunction);
Assert.assertThat(sitePluginNames.isEmpty(), is(expectedSitePluginNames.isEmpty()));
for (String expectedSitePluginName : expectedSitePluginNames) {
Assert.assertThat(sitePluginNames, hasItem(expectedSitePluginName));
}
List<String> sitePluginDescriptions = filterAndMap(plugins, sitePluginPredicate, descriptionFunction);
Assert.assertThat(sitePluginDescriptions.isEmpty(), is(expectedSitePluginDescriptions.isEmpty()));
for (String sitePluginDescription : expectedSitePluginDescriptions) {
Assert.assertThat(sitePluginDescriptions, hasItem(sitePluginDescription));
}
List<String> sitePluginUrls = filterAndMap(plugins, sitePluginPredicate, urlFunction);
Assert.assertThat(sitePluginUrls, not(contains(nullValue())));
List<String> sitePluginVersions = filterAndMap(plugins, sitePluginPredicate, versionFunction);
Assert.assertThat(sitePluginVersions.isEmpty(), is(expectedSiteVersions.isEmpty()));
for (String pluginVersion : expectedSiteVersions) {
Assert.assertThat(sitePluginVersions, hasItem(pluginVersion));
}
}
private static List<String> filterAndMap(PluginsAndModules pluginsInfo, Predicate<PluginInfo> predicate, Function<PluginInfo, String> function) {
return pluginsInfo.getPluginInfos().stream().filter(predicate).map(function).collect(Collectors.toList());
}
private static Predicate<PluginInfo> jvmPluginPredicate = p -> p.isJvm();
private static Predicate<PluginInfo> sitePluginPredicate = p -> p.isSite();
private static Function<PluginInfo, String> nameFunction = p -> p.getName();
private static Function<PluginInfo, String> descriptionFunction = p -> p.getDescription();
private static Function<PluginInfo, String> urlFunction = p -> p.getUrl();
private static Function<PluginInfo, String> versionFunction = p -> p.getVersion();
/** /**
* Check if a file exists * Check if a file exists
*/ */