From bd871f96c277ee457050979886feddd97b485d04 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Sat, 1 Feb 2014 22:12:00 +0100 Subject: [PATCH] Check that a plugin is Lucene compatible with the current running node using `lucene` property in `es-plugin.properties` file. * If plugin does not provide `lucene` property, we consider that the plugin is compatible. * If plugin provides `lucene` property, we try to load related Enum org.apache.lucene.util.Version. If this fails, it means that the node is too "old" comparing to the Lucene version the plugin was built for. * We compare then two first digits of current node lucene version against two first digits of plugin Lucene version. If not equal, it means that the plugin is too "old" for the current node. Plugin developers who wants to launch plugin check only have to add a `lucene` property in `es-plugin.properties` file. If you are using maven to build your plugin, you can do it like this: In `pom.xml`: ```xml 4.6.0 src/main/resources true ``` In `es-plugin.properties`, add: ```properties lucene=${lucene.version} ``` BTW, if you don't already have it, you can add the plugin version as well: ```properties version=${project.version} ``` You can disable that check using `plugins.check_lucene: false`. --- docs/reference/modules/plugins.asciidoc | 13 ++ pom.xml | 2 + .../elasticsearch/plugins/PluginsService.java | 89 ++++++++++++-- .../nodesinfo/SimpleNodesInfoTests.java | 116 ++---------------- .../plugin/PluginLuceneCheckerTests.java | 82 +++++++++++++ .../plugin/PluginManagerTests.java | 19 ++- .../lucene/current/CurrentLucenePlugin.java | 40 ++++++ .../lucene/current/es-plugin.properties | 21 ++++ .../lucene/newer/NewerLucenePlugin.java | 40 ++++++ .../plugin/lucene/newer/es-plugin.properties | 21 ++++ .../plugin/lucene/old/OldLucenePlugin.java | 40 ++++++ .../plugin/lucene/old/es-plugin.properties | 21 ++++ .../hamcrest/ElasticsearchAssertions.java | 101 +++++++++++++++ 13 files changed, 487 insertions(+), 118 deletions(-) create mode 100644 src/test/java/org/elasticsearch/plugin/PluginLuceneCheckerTests.java create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/current/CurrentLucenePlugin.java create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/current/es-plugin.properties create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/newer/NewerLucenePlugin.java create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/newer/es-plugin.properties create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/old/OldLucenePlugin.java create mode 100644 src/test/java/org/elasticsearch/plugin/lucene/old/es-plugin.properties diff --git a/docs/reference/modules/plugins.asciidoc b/docs/reference/modules/plugins.asciidoc index c43c2d7c43d..bd417f92574 100644 --- a/docs/reference/modules/plugins.asciidoc +++ b/docs/reference/modules/plugins.asciidoc @@ -142,6 +142,19 @@ bin/plugin --install mobz/elasticsearch-head --timeout 1m bin/plugin --install mobz/elasticsearch-head --timeout 0 ----------------------------------- +[float] +==== Lucene version dependent plugins + +coming[1.2.0] + +For some plugins, such as analysis plugins, a specific major Lucene version is +required to run. In that case, the plugin provides in its `es-plugin.properties` +file the Lucene version for which the plugin was built for. + +If present at startup the node will check the Lucene version before loading the plugin. + +You can disable that check using `plugins.check_lucene: false`. + [float] [[known-plugins]] === Known Plugins diff --git a/pom.xml b/pom.xml index 789db8949dd..ca670c6c951 100644 --- a/pom.xml +++ b/pom.xml @@ -335,7 +335,9 @@ **/*.json **/*.yml **/*.txt + **/*.properties + true ${basedir}/src/test/resources diff --git a/src/main/java/org/elasticsearch/plugins/PluginsService.java b/src/main/java/org/elasticsearch/plugins/PluginsService.java index a1b8c30de3f..9197348a9c2 100644 --- a/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -20,8 +20,10 @@ package org.elasticsearch.plugins; import com.google.common.collect.*; +import org.apache.lucene.util.Constants; import org.apache.lucene.util.IOUtils; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.node.info.PluginInfo; import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo; import org.elasticsearch.common.Strings; @@ -30,6 +32,7 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; @@ -50,7 +53,7 @@ import static org.elasticsearch.common.io.FileSystemUtils.isAccessibleDirectory; * */ public class PluginsService extends AbstractComponent { - private static final String ES_PLUGIN_PROPERTIES = "es-plugin.properties"; + public static final String ES_PLUGIN_PROPERTIES = "es-plugin.properties"; private final Environment environment; @@ -63,6 +66,7 @@ public class PluginsService extends AbstractComponent { private PluginsInfo cachedPluginsInfo; private final TimeValue refreshInterval; + private final boolean checkLucene; private long lastRefresh; static class OnModuleReference { @@ -83,6 +87,7 @@ public class PluginsService extends AbstractComponent { public PluginsService(Settings settings, Environment environment) { super(settings); this.environment = environment; + this.checkLucene = componentSettings.getAsBoolean("check_lucene", true); ImmutableList.Builder> tupleBuilder = ImmutableList.builder(); @@ -161,7 +166,6 @@ public class PluginsService extends AbstractComponent { this.onModuleReferences = onModuleReferences.immutableMap(); this.refreshInterval = componentSettings.getAsTime("info_refresh_interval", TimeValue.timeValueSeconds(10)); - } public ImmutableList> plugins() { @@ -494,22 +498,87 @@ public class PluginsService extends AbstractComponent { try { Class pluginClass = (Class) settings.getClassLoader().loadClass(className); Plugin plugin; - try { - plugin = pluginClass.getConstructor(Settings.class).newInstance(settings); - } catch (NoSuchMethodException e) { + + if (!checkLucene || checkLuceneCompatibility(pluginClass, settings, logger)) { try { - plugin = pluginClass.getConstructor().newInstance(); - } catch (NoSuchMethodException e1) { - throw new ElasticsearchException("No constructor for [" + pluginClass + "]. A plugin class must " + - "have either an empty default constructor or a single argument constructor accepting a " + - "Settings instance"); + plugin = pluginClass.getConstructor(Settings.class).newInstance(settings); + } catch (NoSuchMethodException e) { + try { + plugin = pluginClass.getConstructor().newInstance(); + } catch (NoSuchMethodException e1) { + throw new ElasticsearchException("No constructor for [" + pluginClass + "]. A plugin class must " + + "have either an empty default constructor or a single argument constructor accepting a " + + "Settings instance"); + } } + } else { + throw new ElasticsearchException("Plugin is incompatible with the current node"); } + return plugin; } catch (Throwable e) { throw new ElasticsearchException("Failed to load plugin class [" + className + "]", e); } } + + /** + *

Check that a plugin is Lucene compatible with the current running node using `lucene` property + * in `es-plugin.properties` file.

+ *

If plugin does not provide `lucene` property, we consider that the plugin is compatible.

+ *

If plugin provides `lucene` property, we try to load related Enum org.apache.lucene.util.Version. If this + * fails, it means that the node is too "old" comparing to the Lucene version the plugin was built for.

+ *

We compare then two first digits of current node lucene version against two first digits of plugin Lucene + * version. If not equal, it means that the plugin is too "old" for the current node.

+ * + * @param pluginClass Plugin class we are checking + * @return true if the plugin is Lucene compatible + */ + public static boolean checkLuceneCompatibility(Class pluginClass, Settings settings, ESLogger logger) { + String luceneVersion = null; + try { + // We try to read the es-plugin.properties file + // But as we can have several plugins in the same classloader, + // we have to find the right es-plugin.properties file + Enumeration pluginUrls = settings.getClassLoader().getResources(PluginsService.ES_PLUGIN_PROPERTIES); + + while (pluginUrls.hasMoreElements()) { + URL pluginUrl = pluginUrls.nextElement(); + try (InputStream is = pluginUrl.openStream()) { + Properties pluginProps = new Properties(); + pluginProps.load(is); + String plugin = pluginProps.getProperty("plugin"); + // If we don't have the expected plugin, let's continue to the next one + if (pluginClass.getName().equals(plugin)) { + luceneVersion = pluginProps.getProperty("lucene"); + break; + } + logger.debug("skipping [{}]", pluginUrl); + } + } + + if (luceneVersion != null) { + // We only keep the first two parts + String parts[] = luceneVersion.split("\\."); + + // Should fail if the running node is too old! + org.apache.lucene.util.Version luceneExpectedVersion = org.apache.lucene.util.Version.parseLeniently(parts[0]+"."+parts[1]); + + if (Version.CURRENT.luceneVersion.equals(luceneExpectedVersion)) { + logger.debug("starting analysis plugin for Lucene [{}].", luceneExpectedVersion); + return true; + } + } else { + logger.debug("lucene property is not set in plugin {} file. Skipping test.", PluginsService.ES_PLUGIN_PROPERTIES); + return true; + } + } catch (Throwable t) { + // We don't have the expected version... Let's fail after. + logger.debug("exception raised while checking plugin Lucene version.", t); + } + logger.error("cannot start plugin due to incorrect Lucene version: plugin [{}], node [{}].", + luceneVersion, Constants.LUCENE_MAIN_VERSION); + return false; + } } diff --git a/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java b/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java index 983af06b16b..f7c68889d67 100644 --- a/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java +++ b/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java @@ -19,22 +19,18 @@ package org.elasticsearch.nodesinfo; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; 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.common.settings.Settings; import org.elasticsearch.nodesinfo.plugin.dummy1.TestPlugin; import org.elasticsearch.nodesinfo.plugin.dummy2.TestNoVersionPlugin; import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; +import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; import org.junit.Test; import java.io.File; @@ -43,8 +39,6 @@ import java.net.URL; import java.util.Collections; import java.util.List; -import static com.google.common.base.Predicates.and; -import static com.google.common.base.Predicates.isNull; import static org.elasticsearch.client.Requests.clusterHealthRequest; import static org.elasticsearch.client.Requests.nodesInfoRequest; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; @@ -133,23 +127,23 @@ public class SimpleNodesInfoTests extends ElasticsearchIntegrationTest { NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().clear().setPlugins(true).execute().actionGet(); logger.info("--> full json answer, status " + response.toString()); - assertNodeContainsPlugins(response, server1NodeId, + ElasticsearchAssertions.assertNodeContainsPlugins(response, server1NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST, // No JVM Plugin Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No Site Plugin - assertNodeContainsPlugins(response, server2NodeId, + ElasticsearchAssertions.assertNodeContainsPlugins(response, server2NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST, // No JVM Plugin Lists.newArrayList(Fields.SITE_PLUGIN), // Site Plugin Lists.newArrayList(Fields.SITE_PLUGIN_DESCRIPTION), Lists.newArrayList(Fields.SITE_PLUGIN_VERSION)); - assertNodeContainsPlugins(response, server3NodeId, + ElasticsearchAssertions.assertNodeContainsPlugins(response, server3NodeId, Lists.newArrayList(TestPlugin.Fields.NAME), // JVM Plugin Lists.newArrayList(TestPlugin.Fields.DESCRIPTION), Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE), Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No site Plugin - assertNodeContainsPlugins(response, server4NodeId, + ElasticsearchAssertions.assertNodeContainsPlugins(response, server4NodeId, Lists.newArrayList(TestNoVersionPlugin.Fields.NAME), // JVM Plugin Lists.newArrayList(TestNoVersionPlugin.Fields.DESCRIPTION), Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE), @@ -158,63 +152,14 @@ public class SimpleNodesInfoTests extends ElasticsearchIntegrationTest { Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE)); } - private void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId, - List expectedJvmPluginNames, - List expectedJvmPluginDescriptions, - List expectedJvmVersions, - List expectedSitePluginNames, - List expectedSitePluginDescriptions, - List expectedSiteVersions) { - - assertThat(response.getNodesMap().get(nodeId), notNullValue()); - - PluginsInfo plugins = response.getNodesMap().get(nodeId).getPlugins(); - assertThat(plugins, notNullValue()); - - List pluginNames = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(nameFunction).toList(); - for (String expectedJvmPluginName : expectedJvmPluginNames) { - assertThat(pluginNames, hasItem(expectedJvmPluginName)); - } - - List pluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(descriptionFunction).toList(); - for (String expectedJvmPluginDescription : expectedJvmPluginDescriptions) { - assertThat(pluginDescriptions, hasItem(expectedJvmPluginDescription)); - } - - List jvmPluginVersions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(versionFunction).toList(); - for (String pluginVersion : expectedJvmVersions) { - assertThat(jvmPluginVersions, hasItem(pluginVersion)); - } - - FluentIterable jvmUrls = FluentIterable.from(plugins.getInfos()) - .filter(and(jvmPluginPredicate, Predicates.not(sitePluginPredicate))) - .filter(isNull()) - .transform(urlFunction); - assertThat(Iterables.size(jvmUrls), is(0)); - - List sitePluginNames = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(nameFunction).toList(); - for (String expectedSitePluginName : expectedSitePluginNames) { - assertThat(sitePluginNames, hasItem(expectedSitePluginName)); - } - - List sitePluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(descriptionFunction).toList(); - for (String sitePluginDescription : expectedSitePluginDescriptions) { - assertThat(sitePluginDescriptions, hasItem(sitePluginDescription)); - } - - List sitePluginUrls = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(urlFunction).toList(); - assertThat(sitePluginUrls, not(contains(nullValue()))); - - - List sitePluginVersions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(versionFunction).toList(); - for (String pluginVersion : expectedSiteVersions) { - assertThat(sitePluginVersions, hasItem(pluginVersion)); - } + public static String startNodeWithPlugins(int nodeId, String ... pluginClassNames) throws URISyntaxException { + return startNodeWithPlugins(ImmutableSettings.EMPTY, "/org/elasticsearch/nodesinfo/node" + Integer.toString(nodeId) + "/", pluginClassNames); } - private String startNodeWithPlugins(int nodeId, String ... pluginClassNames) throws URISyntaxException { - URL resource = SimpleNodesInfoTests.class.getResource("/org/elasticsearch/nodesinfo/node" + Integer.toString(nodeId) + "/"); + public static String startNodeWithPlugins(Settings nodeSettings, String pluginDir, String ... pluginClassNames) throws URISyntaxException { + URL resource = SimpleNodesInfoTests.class.getResource(pluginDir); ImmutableSettings.Builder settings = settingsBuilder(); + settings.put(nodeSettings); if (resource != null) { settings.put("path.plugins", new File(resource.toURI()).getAbsolutePath()); } @@ -228,45 +173,8 @@ public class SimpleNodesInfoTests extends ElasticsearchIntegrationTest { // We wait for a Green status client().admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet(); - String serverNodeId = cluster().getInstance(ClusterService.class, nodeName).state().nodes().localNodeId(); - logger.debug("--> server {} started" + serverNodeId); - return serverNodeId; + return cluster().getInstance(ClusterService.class, nodeName).state().nodes().localNodeId(); } - private Predicate jvmPluginPredicate = new Predicate() { - public boolean apply(PluginInfo pluginInfo) { - return pluginInfo.isJvm(); - } - }; - - private Predicate sitePluginPredicate = new Predicate() { - public boolean apply(PluginInfo pluginInfo) { - return pluginInfo.isSite(); - } - }; - - private Function nameFunction = new Function() { - public String apply(PluginInfo pluginInfo) { - return pluginInfo.getName(); - } - }; - - private Function descriptionFunction = new Function() { - public String apply(PluginInfo pluginInfo) { - return pluginInfo.getDescription(); - } - }; - - private Function urlFunction = new Function() { - public String apply(PluginInfo pluginInfo) { - return pluginInfo.getUrl(); - } - }; - - private Function versionFunction = new Function() { - public String apply(PluginInfo pluginInfo) { - return pluginInfo.getVersion(); - } - }; } diff --git a/src/test/java/org/elasticsearch/plugin/PluginLuceneCheckerTests.java b/src/test/java/org/elasticsearch/plugin/PluginLuceneCheckerTests.java new file mode 100644 index 00000000000..45bda11b0f2 --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/PluginLuceneCheckerTests.java @@ -0,0 +1,82 @@ +/* + * 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.plugin; + +import com.google.common.collect.Lists; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.nodesinfo.SimpleNodesInfoTests; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; +import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; +import org.junit.Test; + +import java.net.URISyntaxException; +import java.util.Collections; + +import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; + +/** + * + */ +@ClusterScope(scope= ElasticsearchIntegrationTest.Scope.TEST, numDataNodes=0, transportClientRatio = 0) +public class PluginLuceneCheckerTests extends ElasticsearchIntegrationTest { + + /** + * We check that no Lucene version checking is done + * when we set `"plugins.check_lucene":false` + */ + @Test + public void testDisableLuceneVersionCheckingPlugin() throws URISyntaxException { + String serverNodeId = SimpleNodesInfoTests.startNodeWithPlugins( + settingsBuilder().put("plugins.check_lucene", false).build(), + "/org/elasticsearch/plugin/lucene/"); + logger.info("--> server {} started" + serverNodeId); + + NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().clear().setPlugins(true).execute().actionGet(); + logger.info("--> full json answer, status " + response.toString()); + + ElasticsearchAssertions.assertNodeContainsPlugins(response, serverNodeId, + Lists.newArrayList("old-lucene"), Lists.newArrayList("old"), Lists.newArrayList("1.0.0"), // JVM Plugin + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No Site Plugin + } + + /** + * We check that with an old plugin (built on an old Lucene version) + * plugin is not loaded + * We check that with a recent plugin (built on current Lucene version) + * plugin is loaded + * We check that with a too recent plugin (built on an unknown Lucene version) + * plugin is not loaded + */ + @Test + public void testEnableLuceneVersionCheckingPlugin() throws URISyntaxException { + String serverNodeId = SimpleNodesInfoTests.startNodeWithPlugins( + settingsBuilder().put("plugins.check_lucene", true).build(), + "/org/elasticsearch/plugin/lucene/"); + logger.info("--> server {} started" + serverNodeId); + + NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().clear().setPlugins(true).execute().actionGet(); + logger.info("--> full json answer, status " + response.toString()); + + ElasticsearchAssertions.assertNodeContainsPlugins(response, serverNodeId, + Lists.newArrayList("current-lucene"), Lists.newArrayList("current"), Lists.newArrayList("2.0.0"), // JVM Plugin + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No Site Plugin + } +} diff --git a/src/test/java/org/elasticsearch/plugin/PluginManagerTests.java b/src/test/java/org/elasticsearch/plugin/PluginManagerTests.java index 1d038f9c1b5..c20701cc6de 100644 --- a/src/test/java/org/elasticsearch/plugin/PluginManagerTests.java +++ b/src/test/java/org/elasticsearch/plugin/PluginManagerTests.java @@ -22,6 +22,7 @@ import com.google.common.base.Predicate; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.action.admin.cluster.node.info.PluginInfo; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.settings.ImmutableSettings; @@ -46,8 +47,9 @@ import java.io.IOException; import java.net.URI; import java.util.concurrent.TimeUnit; -import static org.elasticsearch.test.ElasticsearchIntegrationTest.*; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -150,9 +152,18 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest { NodesInfoResponse nodesInfoResponse = client().admin().cluster().prepareNodesInfo().clear().setPlugins(true).get(); assertThat(nodesInfoResponse.getNodes().length, equalTo(1)); assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos(), notNullValue()); - assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos().size(), equalTo(1)); - assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos().get(0).getName(), equalTo(pluginName)); - assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos().get(0).isSite(), equalTo(true)); + assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos().size(), not(0)); + + boolean pluginFound = false; + + for (PluginInfo pluginInfo : nodesInfoResponse.getNodes()[0].getPlugins().getInfos()) { + if (pluginInfo.getName().equals(pluginName)) { + pluginFound = true; + break; + } + } + + assertThat(pluginFound, is(true)); } private void assertPluginAvailable(String pluginName) throws InterruptedException { diff --git a/src/test/java/org/elasticsearch/plugin/lucene/current/CurrentLucenePlugin.java b/src/test/java/org/elasticsearch/plugin/lucene/current/CurrentLucenePlugin.java new file mode 100644 index 00000000000..848fd2ebe24 --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/current/CurrentLucenePlugin.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.lucene.current; + +import org.elasticsearch.plugins.AbstractPlugin; + +public class CurrentLucenePlugin extends AbstractPlugin { + /** + * The name of the plugin. + */ + @Override + public String name() { + return "current-lucene"; + } + + /** + * The description of the plugin. + */ + @Override + public String description() { + return "current"; + } +} diff --git a/src/test/java/org/elasticsearch/plugin/lucene/current/es-plugin.properties b/src/test/java/org/elasticsearch/plugin/lucene/current/es-plugin.properties new file mode 100644 index 00000000000..fb009c621a1 --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/current/es-plugin.properties @@ -0,0 +1,21 @@ +################################################################ +# 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. +################################################################ +plugin=org.elasticsearch.plugin.lucene.current.CurrentLucenePlugin +version=2.0.0 +lucene=${lucene.version} diff --git a/src/test/java/org/elasticsearch/plugin/lucene/newer/NewerLucenePlugin.java b/src/test/java/org/elasticsearch/plugin/lucene/newer/NewerLucenePlugin.java new file mode 100644 index 00000000000..7ceaec320d7 --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/newer/NewerLucenePlugin.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.lucene.newer; + +import org.elasticsearch.plugins.AbstractPlugin; + +public class NewerLucenePlugin extends AbstractPlugin { + /** + * The name of the plugin. + */ + @Override + public String name() { + return "newer-lucene"; + } + + /** + * The description of the plugin. + */ + @Override + public String description() { + return "newer"; + } +} diff --git a/src/test/java/org/elasticsearch/plugin/lucene/newer/es-plugin.properties b/src/test/java/org/elasticsearch/plugin/lucene/newer/es-plugin.properties new file mode 100644 index 00000000000..1e41549909b --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/newer/es-plugin.properties @@ -0,0 +1,21 @@ +################################################################ +# 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. +################################################################ +plugin=org.elasticsearch.plugin.lucene.newer.NewerLucenePlugin +version=3.0.0 +lucene=99.0.0 diff --git a/src/test/java/org/elasticsearch/plugin/lucene/old/OldLucenePlugin.java b/src/test/java/org/elasticsearch/plugin/lucene/old/OldLucenePlugin.java new file mode 100644 index 00000000000..7e01f466854 --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/old/OldLucenePlugin.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.lucene.old; + +import org.elasticsearch.plugins.AbstractPlugin; + +public class OldLucenePlugin extends AbstractPlugin { + /** + * The name of the plugin. + */ + @Override + public String name() { + return "old-lucene"; + } + + /** + * The description of the plugin. + */ + @Override + public String description() { + return "old"; + } +} diff --git a/src/test/java/org/elasticsearch/plugin/lucene/old/es-plugin.properties b/src/test/java/org/elasticsearch/plugin/lucene/old/es-plugin.properties new file mode 100644 index 00000000000..8344e919e8d --- /dev/null +++ b/src/test/java/org/elasticsearch/plugin/lucene/old/es-plugin.properties @@ -0,0 +1,21 @@ +################################################################ +# 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. +################################################################ +plugin=org.elasticsearch.plugin.lucene.old.OldLucenePlugin +version=1.0.0 +lucene=3.0.0 diff --git a/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java index f247fd2b77b..63418df7d84 100644 --- a/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java +++ b/src/test/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java @@ -18,7 +18,11 @@ */ package org.elasticsearch.test.hamcrest; +import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.elasticsearch.ElasticsearchException; @@ -31,6 +35,9 @@ import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequestBuilder; 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.action.admin.indices.delete.DeleteIndexRequestBuilder; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; @@ -58,6 +65,7 @@ import org.elasticsearch.test.engine.MockInternalEngine; import org.elasticsearch.test.store.MockDirectoryHelper; import org.hamcrest.Matcher; import org.hamcrest.Matchers; +import org.junit.Assert; import java.io.IOException; import java.lang.reflect.Constructor; @@ -65,6 +73,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.TimeUnit; +import static com.google.common.base.Predicates.isNull; import static org.elasticsearch.test.ElasticsearchTestCase.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; @@ -575,4 +584,96 @@ public class ElasticsearchAssertions { } } + public static void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId, + List expectedJvmPluginNames, + List expectedJvmPluginDescriptions, + List expectedJvmVersions, + List expectedSitePluginNames, + List expectedSitePluginDescriptions, + List expectedSiteVersions) { + + Assert.assertThat(response.getNodesMap().get(nodeId), notNullValue()); + + PluginsInfo plugins = response.getNodesMap().get(nodeId).getPlugins(); + Assert.assertThat(plugins, notNullValue()); + + List pluginNames = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(nameFunction).toList(); + for (String expectedJvmPluginName : expectedJvmPluginNames) { + Assert.assertThat(pluginNames, hasItem(expectedJvmPluginName)); + } + + List pluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(descriptionFunction).toList(); + for (String expectedJvmPluginDescription : expectedJvmPluginDescriptions) { + Assert.assertThat(pluginDescriptions, hasItem(expectedJvmPluginDescription)); + } + + List jvmPluginVersions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(versionFunction).toList(); + for (String pluginVersion : expectedJvmVersions) { + Assert.assertThat(jvmPluginVersions, hasItem(pluginVersion)); + } + + FluentIterable jvmUrls = FluentIterable.from(plugins.getInfos()) + .filter(Predicates.and(jvmPluginPredicate, Predicates.not(sitePluginPredicate))) + .filter(isNull()) + .transform(urlFunction); + Assert.assertThat(Iterables.size(jvmUrls), is(0)); + + List sitePluginNames = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(nameFunction).toList(); + Assert.assertThat(sitePluginNames.isEmpty(), is(expectedSitePluginNames.isEmpty())); + for (String expectedSitePluginName : expectedSitePluginNames) { + Assert.assertThat(sitePluginNames, hasItem(expectedSitePluginName)); + } + + List sitePluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(descriptionFunction).toList(); + Assert.assertThat(sitePluginDescriptions.isEmpty(), is(expectedSitePluginDescriptions.isEmpty())); + for (String sitePluginDescription : expectedSitePluginDescriptions) { + Assert.assertThat(sitePluginDescriptions, hasItem(sitePluginDescription)); + } + + List sitePluginUrls = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(urlFunction).toList(); + Assert.assertThat(sitePluginUrls, not(contains(nullValue()))); + + + List sitePluginVersions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(versionFunction).toList(); + Assert.assertThat(sitePluginVersions.isEmpty(), is(expectedSiteVersions.isEmpty())); + for (String pluginVersion : expectedSiteVersions) { + Assert.assertThat(sitePluginVersions, hasItem(pluginVersion)); + } + } + + private static Predicate jvmPluginPredicate = new Predicate() { + public boolean apply(PluginInfo pluginInfo) { + return pluginInfo.isJvm(); + } + }; + + private static Predicate sitePluginPredicate = new Predicate() { + public boolean apply(PluginInfo pluginInfo) { + return pluginInfo.isSite(); + } + }; + + private static Function nameFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getName(); + } + }; + + private static Function descriptionFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getDescription(); + } + }; + + private static Function urlFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getUrl(); + } + }; + + private static Function versionFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getVersion(); + } + }; }