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(); + } + }; }