Remove groovy scripting language (#21607)

* Scripting: Remove groovy scripting language

Groovy was deprecated in 5.0. This change removes it, along with the
legacy default language infrastructure in scripting.
This commit is contained in:
Ryan Ernst 2016-11-22 19:24:12 -08:00 committed by GitHub
parent dbdcf9e95c
commit 6940b2b8c7
40 changed files with 40 additions and 2022 deletions

View File

@ -113,15 +113,6 @@ public class QueryRewriteContext implements ParseFieldMatcherSupplier {
return new QueryParseContext(indicesQueriesRegistry, parser, indexSettings.getParseFieldMatcher());
}
/**
* Returns a new {@link QueryParseContext} like {@link #newParseContext(XContentParser)} with the only diffence, that
* the default script language will default to what has been set in the 'script.legacy.default_lang' setting.
*/
public QueryParseContext newParseContextWithLegacyScriptLanguage(XContentParser parser) {
String defaultScriptLanguage = ScriptSettings.getLegacyDefaultLang(indexSettings.getNodeSettings());
return new QueryParseContext(defaultScriptLanguage, indicesQueriesRegistry, parser, indexSettings.getParseFieldMatcher());
}
public long nowInMillis() {
return nowInMillis.getAsLong();
}

View File

@ -32,17 +32,6 @@ import java.util.function.Function;
public class ScriptSettings {
static final String LEGACY_DEFAULT_LANG = "groovy";
/**
* The default script language to use for scripts that are stored in documents that have no script lang set explicitly.
* This setting is legacy setting and only applies for indices created on ES versions prior to version 5.0
*
* This constant will be removed in the next major release.
*/
@Deprecated
public static final String LEGACY_SCRIPT_SETTING = "script.legacy.default_lang";
private static final Map<ScriptType, Setting<Boolean>> SCRIPT_TYPE_SETTING_MAP;
static {
@ -58,7 +47,6 @@ public class ScriptSettings {
private final Map<ScriptContext, Setting<Boolean>> scriptContextSettingMap;
private final List<Setting<Boolean>> scriptLanguageSettings;
private final Setting<String> defaultLegacyScriptLanguageSetting;
public ScriptSettings(ScriptEngineRegistry scriptEngineRegistry, ScriptContextRegistry scriptContextRegistry) {
Map<ScriptContext, Setting<Boolean>> scriptContextSettingMap = contextSettings(scriptContextRegistry);
@ -66,13 +54,6 @@ public class ScriptSettings {
List<Setting<Boolean>> scriptLanguageSettings = languageSettings(SCRIPT_TYPE_SETTING_MAP, scriptContextSettingMap, scriptEngineRegistry, scriptContextRegistry);
this.scriptLanguageSettings = Collections.unmodifiableList(scriptLanguageSettings);
this.defaultLegacyScriptLanguageSetting = new Setting<>(LEGACY_SCRIPT_SETTING, LEGACY_DEFAULT_LANG, setting -> {
if (!LEGACY_DEFAULT_LANG.equals(setting) && !scriptEngineRegistry.getRegisteredLanguages().containsKey(setting)) {
throw new IllegalArgumentException("unregistered default language [" + setting + "]");
}
return setting;
}, Property.NodeScope);
}
private static Map<ScriptContext, Setting<Boolean>> contextSettings(ScriptContextRegistry scriptContextRegistry) {
@ -169,19 +150,10 @@ public class ScriptSettings {
settings.addAll(SCRIPT_TYPE_SETTING_MAP.values());
settings.addAll(scriptContextSettingMap.values());
settings.addAll(scriptLanguageSettings);
settings.add(defaultLegacyScriptLanguageSetting);
return settings;
}
public Iterable<Setting<Boolean>> getScriptLanguageSettings() {
return scriptLanguageSettings;
}
public Setting<String> getDefaultLegacyScriptLanguageSetting() {
return defaultLegacyScriptLanguageSetting;
}
public static String getLegacyDefaultLang(Settings settings) {
return settings.get(LEGACY_SCRIPT_SETTING, ScriptSettings.LEGACY_DEFAULT_LANG);
}
}

View File

@ -297,7 +297,7 @@ public class QueryDSLDocumentationTests extends ESTestCase {
parameters.put("param1", 5);
scriptQuery(
new Script(
ScriptType.FILE, "groovy", "mygroovyscript",
ScriptType.FILE, "coollang", "myscript",
parameters)
);

View File

@ -1,64 +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.index.query;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptSettings;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.test.ESTestCase;
import static java.util.Collections.emptyList;
public class QueryRewriteContextTests extends ESTestCase {
public void testNewParseContextWithLegacyScriptLanguage() throws Exception {
String defaultLegacyScriptLanguage = randomAsciiOfLength(4);
IndexMetaData.Builder indexMetadata = new IndexMetaData.Builder("index");
indexMetadata.settings(Settings.builder().put("index.version.created", Version.CURRENT)
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 1)
);
final long nowInMills = randomPositiveLong();
IndicesQueriesRegistry indicesQueriesRegistry = new SearchModule(Settings.EMPTY, false, emptyList()).getQueryParserRegistry();
IndexSettings indexSettings = new IndexSettings(indexMetadata.build(),
Settings.builder().put(ScriptSettings.LEGACY_SCRIPT_SETTING, defaultLegacyScriptLanguage).build());
QueryRewriteContext queryRewriteContext =
new QueryRewriteContext(indexSettings, null, null, indicesQueriesRegistry, null, null, null, () -> nowInMills);
// verify that the default script language in the query parse context is equal to defaultLegacyScriptLanguage variable:
QueryParseContext queryParseContext =
queryRewriteContext.newParseContextWithLegacyScriptLanguage(XContentHelper.createParser(new BytesArray("{}")));
assertEquals(defaultLegacyScriptLanguage, queryParseContext.getDefaultScriptLanguage());
// verify that the script query's script language is equal to defaultLegacyScriptLanguage variable:
XContentParser parser = XContentHelper.createParser(new BytesArray("{\"script\" : {\"script\": \"return true\"}}"));
queryParseContext = queryRewriteContext.newParseContextWithLegacyScriptLanguage(parser);
ScriptQueryBuilder queryBuilder = (ScriptQueryBuilder) queryParseContext.parseInnerQueryBuilder().get();
assertEquals(defaultLegacyScriptLanguage, queryBuilder.script().getLang());
}
}

View File

@ -221,7 +221,7 @@ public class ScriptServiceTests extends ESTestCase {
builder.put("script.file", "true");
}
buildScriptService(builder.build());
createFileScripts("groovy", "mustache", "dtest");
createFileScripts("mustache", "dtest");
for (ScriptContext scriptContext : scriptContexts) {
// only file scripts are accepted by default
@ -292,7 +292,7 @@ public class ScriptServiceTests extends ESTestCase {
}
buildScriptService(builder.build());
createFileScripts("groovy", "expression", "mustache", "dtest");
createFileScripts("expression", "mustache", "dtest");
for (ScriptType scriptType : ScriptType.values()) {
//make sure file scripts have a different name than inline ones.

View File

@ -34,39 +34,6 @@ import static org.hamcrest.Matchers.equalTo;
public class ScriptSettingsTests extends ESTestCase {
public void testDefaultLegacyLanguageIsPainless() {
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngineService()));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList());
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry);
assertThat(scriptSettings.getDefaultLegacyScriptLanguageSetting().get(Settings.EMPTY),
equalTo(ScriptSettings.LEGACY_DEFAULT_LANG));
}
public void testCustomLegacyDefaultLanguage() {
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngineService()));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList());
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry);
String defaultLanguage = CustomScriptEngineService.NAME;
Settings settings = Settings.builder().put(ScriptSettings.LEGACY_SCRIPT_SETTING, defaultLanguage).build();
assertThat(scriptSettings.getDefaultLegacyScriptLanguageSetting().get(settings), equalTo(defaultLanguage));
}
public void testInvalidLegacyDefaultLanguage() {
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngineService()));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.emptyList());
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry);
Settings settings = Settings.builder().put(ScriptSettings.LEGACY_SCRIPT_SETTING, "C++").build();
try {
scriptSettings.getDefaultLegacyScriptLanguageSetting().get(settings);
fail("should have seen unregistered default language");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("unregistered default language [C++]"));
}
}
public void testSettingsAreProperlyPropogated() {
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.singletonList(new CustomScriptEngineService()));

View File

@ -5,21 +5,6 @@ Here is how you can use
{ref}/search-aggregations-metrics-scripted-metric-aggregation.html[Scripted Metric Aggregation]
with Java API.
Don't forget to add Groovy in your classpath if you want to run Groovy scripts in an embedded data node
(for unit tests for example).
For example, with Maven, add this dependency to your `pom.xml` file:
[source,xml]
--------------------------------------------------
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.3.2</version>
<classifier>indy</classifier>
</dependency>
--------------------------------------------------
===== Prepare aggregation request
Here is an example on how to create the aggregation request:

View File

@ -153,7 +153,6 @@ If everything goes well, you should see a bunch of messages that look like below
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [aggs-matrix-stats]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [ingest-common]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [lang-expression]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [lang-groovy]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [lang-mustache]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [lang-painless]
[2016-09-16T14:17:51,967][INFO ][o.e.p.PluginsService ] [6-bjhwl] loaded module [percolator]

View File

@ -33,6 +33,7 @@ way to reindex old indices is to use the `reindex` API.
* <<breaking_60_settings_changes>>
* <<breaking_60_plugins_changes>>
* <<breaking_60_indices_changes>>
* <<breaking_60_scripting_changes>>
include::migrate_6_0/cat.asciidoc[]
@ -51,3 +52,5 @@ include::migrate_6_0/settings.asciidoc[]
include::migrate_6_0/plugins.asciidoc[]
include::migrate_6_0/indices.asciidoc[]
include::migrate_6_0/scripting.asciidoc[]

View File

@ -0,0 +1,7 @@
[[breaking_60_scripting_changes]]
=== Scripting changes
==== Groovy language removed
The groovy scripting language was deprecated in elasticsearch 5.0 and is now removed.
Use painless instead.

View File

@ -26,10 +26,6 @@ and give the most flexibility.
|yes
|built-in
|<<modules-scripting-groovy, `groovy`>>
|<<modules-scripting-security, no>>
|built-in
|=======================================================================
[float]
@ -79,8 +75,6 @@ include::scripting/fields.asciidoc[]
include::scripting/security.asciidoc[]
include::scripting/groovy.asciidoc[]
include::scripting/painless.asciidoc[]
include::scripting/painless-syntax.asciidoc[]

View File

@ -198,14 +198,14 @@ GET my_index/_search
"script_fields": {
"source": {
"script": {
"lang": "groovy",
"inline": "_source.title + ' ' + _source.first_name + ' ' + _source.last_name" <2>
"lang": "painless",
"inline": "params._source.title + ' ' + params._source.first_name + ' ' + params._source.last_name" <2>
}
},
"stored_fields": {
"script": {
"lang": "groovy",
"inline": "_fields['first_name'].value + ' ' + _fields['last_name'].value"
"lang": "painless",
"inline": "params._fields['first_name'].value + ' ' + params._fields['last_name'].value"
}
}
}

View File

@ -1,150 +0,0 @@
[[modules-scripting-groovy]]
=== Groovy Scripting Language
deprecated[5.0.0,Groovy will be replaced by the new scripting language <<modules-scripting-painless, `Painless`>>]
Groovy is available in Elasticsearch by default. Although
limited by the <<java-security-manager,Java Security Manager>>, it is not a
sandboxed language and only `file` scripts may be used by default.
Enabling `inline` or `stored` Groovy scripting is a security risk and should
only be considered if your Elasticsearch cluster is protected from the outside
world. Even a simple `while (true) { }` loop could behave as a denial-of-
service attack on your cluster.
See <<modules-scripting-security, Scripting and Security>> for details
on security issues with scripts, including how to customize class
whitelisting.
[float]
=== Doc value properties and methods
Doc values in Groovy support the following properties and methods (depending
on the underlying field type):
`doc['field_name'].value`::
The native value of the field. For example, if its a short type, it will be short.
`doc['field_name'].values`::
The native array values of the field. For example, if its a short type,
it will be short[]. Remember, a field can have several values within a
single doc. Returns an empty array if the field has no values.
`doc['field_name'].empty`::
A boolean indicating if the field has no values within the doc.
`doc['field_name'].lat`::
The latitude of a geo point type, or `null`.
`doc['field_name'].lon`::
The longitude of a geo point type, or `null`.
`doc['field_name'].lats`::
The latitudes of a geo point type, or an empty array.
`doc['field_name'].lons`::
The longitudes of a geo point type, or an empty array.
`doc['field_name'].arcDistance(lat, lon)`::
The `arc` distance (in meters) of this geo point field from the provided lat/lon.
`doc['field_name'].arcDistanceWithDefault(lat, lon, default)`::
The `arc` distance (in meters) of this geo point field from the provided lat/lon with a default value
for empty fields.
`doc['field_name'].planeDistance(lat, lon)`::
The `plane` distance (in meters) of this geo point field from the provided lat/lon.
`doc['field_name'].planeDistanceWithDefault(lat, lon, default)`::
The `plane` distance (in meters) of this geo point field from the provided lat/lon with a default value
for empty fields.
`doc['field_name'].geohashDistance(geohash)`::
The `arc` distance (in meters) of this geo point field from the provided geohash.
`doc['field_name'].geohashDistanceWithDefault(geohash, default)`::
The `arc` distance (in meters) of this geo point field from the provided geohash with a default value
for empty fields.
[float]
=== Groovy Built In Functions
There are several built in functions that can be used within scripts.
They include:
[cols="<,<",options="header",]
|=======================================================================
|Function |Description
|`sin(a)` |Returns the trigonometric sine of an angle.
|`cos(a)` |Returns the trigonometric cosine of an angle.
|`tan(a)` |Returns the trigonometric tangent of an angle.
|`asin(a)` |Returns the arc sine of a value.
|`acos(a)` |Returns the arc cosine of a value.
|`atan(a)` |Returns the arc tangent of a value.
|`toRadians(angdeg)` |Converts an angle measured in degrees to an
approximately equivalent angle measured in radians
|`toDegrees(angrad)` |Converts an angle measured in radians to an
approximately equivalent angle measured in degrees.
|`exp(a)` |Returns Euler's number _e_ raised to the power of value.
|`log(a)` |Returns the natural logarithm (base _e_) of a value.
|`log10(a)` |Returns the base 10 logarithm of a value.
|`sqrt(a)` |Returns the correctly rounded positive square root of a
value.
|`cbrt(a)` |Returns the cube root of a double value.
|`IEEEremainder(f1, f2)` |Computes the remainder operation on two
arguments as prescribed by the IEEE 754 standard.
|`ceil(a)` |Returns the smallest (closest to negative infinity) value
that is greater than or equal to the argument and is equal to a
mathematical integer.
|`floor(a)` |Returns the largest (closest to positive infinity) value
that is less than or equal to the argument and is equal to a
mathematical integer.
|`rint(a)` |Returns the value that is closest in value to the argument
and is equal to a mathematical integer.
|`atan2(y, x)` |Returns the angle _theta_ from the conversion of
rectangular coordinates (_x_, _y_) to polar coordinates (r,_theta_).
|`pow(a, b)` |Returns the value of the first argument raised to the
power of the second argument.
|`round(a)` |Returns the closest _int_ to the argument.
|`random()` |Returns a random _double_ value.
|`abs(a)` |Returns the absolute value of a value.
|`max(a, b)` |Returns the greater of two values.
|`min(a, b)` |Returns the smaller of two values.
|`ulp(d)` |Returns the size of an ulp of the argument.
|`signum(d)` |Returns the signum function of the argument.
|`sinh(x)` |Returns the hyperbolic sine of a value.
|`cosh(x)` |Returns the hyperbolic cosine of a value.
|`tanh(x)` |Returns the hyperbolic tangent of a value.
|`hypot(x, y)` |Returns sqrt(_x2_ + _y2_) without intermediate overflow
or underflow.
|=======================================================================

View File

@ -1,7 +1,7 @@
[[modules-scripting-native]]
=== Native (Java) Scripts
Sometimes `groovy` and <<modules-scripting-expression, expression>> aren't enough. For those times you can
Sometimes `painless` and <<modules-scripting-expression, expression>> aren't enough. For those times you can
implement a native script.
The best way to implement a native script is to write a plugin and install it.

View File

@ -14,16 +14,16 @@ to run scripts on your box or not, and apply the appropriate safety measures.
=== Enabling dynamic scripting
The `script.*` settings allow for <<security-script-fine,fine-grained>>
control of which script languages (e.g `groovy`, `painless`) are allowed to
control of which script languages (e.g `painless`) are allowed to
run in which context ( e.g. `search`, `aggs`, `update`), and where the script
source is allowed to come from (i.e. `inline`, `stored`, `file`).
For instance, the following setting enables `stored` `update` scripts for
`groovy`:
`painless`:
[source,yaml]
----------------
script.engine.groovy.inline.update: true
script.engine.painless.inline.update: true
----------------
Less fine-grained settings exist which allow you to enable or disable scripts
@ -128,9 +128,9 @@ script.inline: false <1>
script.stored: false <1>
script.file: false <1>
script.engine.groovy.inline: true <2>
script.engine.groovy.stored.search: true <3>
script.engine.groovy.stored.aggs: true <3>
script.engine.painless.inline: true <2>
script.engine.painless.stored.search: true <3>
script.engine.painless.stored.aggs: true <3>
script.engine.mustache.stored.search: true <4>
-----------------------------------
@ -184,7 +184,7 @@ will return the following exception:
{
"reason": {
"type": "script_exception",
"reason": "failed to run inline script [use(java.math.BigInteger); new BigInteger(1)] using lang [groovy]",
"reason": "failed to run inline script [use(java.math.BigInteger); new BigInteger(1)] using lang [painless]",
"caused_by": {
"type": "no_class_def_found_error",
"reason": "java/math/BigInteger",
@ -197,30 +197,6 @@ will return the following exception:
}
------------------------------
However, classloader issues may also result in more difficult to interpret
exceptions. For instance, this script:
[source,groovy]
------------------------------
use(groovy.time.TimeCategory); new Date(123456789).format('HH')
------------------------------
Returns the following exception:
[source,js]
------------------------------
{
"reason": {
"type": "script_exception",
"reason": "failed to run inline script [use(groovy.time.TimeCategory); new Date(123456789).format('HH')] using lang [groovy]",
"caused_by": {
"type": "missing_property_exception",
"reason": "No such property: groovy for class: 8d45f5c1a07a1ab5dda953234863e283a7586240"
}
}
}
------------------------------
[float]
== Dealing with Java Security Manager issues
@ -262,16 +238,6 @@ grant {
};
----------------------------------
Here is an example of how to enable the `groovy.time.TimeCategory` class:
[source,js]
----------------------------------
grant {
permission org.elasticsearch.script.ClassPermission "java.lang.Class";
permission org.elasticsearch.script.ClassPermission "groovy.time.TimeCategory";
};
----------------------------------
[TIP]
======================================

View File

@ -48,7 +48,7 @@ GET my_index/_search
`lang`::
Specifies the language the script is written in. Defaults to `groovy` but
Specifies the language the script is written in. Defaults to `painless` but
may be set to any of languages listed in <<modules-scripting>>. The
default language may be changed in the `elasticsearch.yml` config file by
setting `script.default_lang` to the appropriate language.
@ -63,7 +63,7 @@ GET my_index/_search
directory (see <<modules-scripting-file-scripts, File Scripts>>).
+
While languages like `expression` and `painless` can be used out of the box as
inline or stored scripts, other languages like `groovy` can only be
inline or stored scripts, other languages can only be
specified as `file` unless you first adjust the default
<<modules-scripting-security,scripting security settings>>.
@ -134,7 +134,7 @@ the following example creates a Groovy script called `calculate-score`:
[source,sh]
--------------------------------------------------
cat "log(_score * 2) + my_modifier" > config/scripts/calculate-score.groovy
cat "Math.log(_score * 2) + my_modifier" > config/scripts/calculate-score.painless
--------------------------------------------------
This script can be used as follows:
@ -146,7 +146,7 @@ GET my_index/_search
"query": {
"script": {
"script": {
"lang": "groovy", <1>
"lang": "painless", <1>
"file": "calculate-score", <2>
"params": {
"my_modifier": 2
@ -161,7 +161,7 @@ GET my_index/_search
The `script` directory may contain sub-directories, in which case the
hierarchy of directories is flattened and concatenated with underscores. A
script in `group1/group2/my_script.groovy` should use `group1_group2_myscript`
script in `group1/group2/my_script.painless` should use `group1_group2_myscript`
as the `file` name.
@ -190,14 +190,14 @@ Scripts may be stored in and retrieved from the cluster state using the
<1> The `lang` represents the script language.
<2> The `id` is a unique identifier or script name.
This example stores a Groovy script called `calculate-score` in the cluster
This example stores a Painless script called `calculate-score` in the cluster
state:
[source,js]
-----------------------------------
POST _scripts/groovy/calculate-score
POST _scripts/painless/calculate-score
{
"script": "log(_score * 2) + my_modifier"
"script": "Math.log(_score * 2) + params.my_modifier"
}
-----------------------------------
// CONSOLE
@ -206,7 +206,7 @@ This same script can be retrieved with:
[source,js]
-----------------------------------
GET _scripts/groovy/calculate-score
GET _scripts/painless/calculate-score
-----------------------------------
// CONSOLE
// TEST[continued]
@ -220,7 +220,7 @@ GET _search
"query": {
"script": {
"script": {
"lang": "groovy",
"lang": "painless",
"stored": "calculate-score",
"params": {
"my_modifier": 2
@ -237,7 +237,7 @@ And deleted with:
[source,js]
-----------------------------------
DELETE _scripts/groovy/calculate-score
DELETE _scripts/painless/calculate-score
-----------------------------------
// CONSOLE
// TEST[continued]

View File

@ -1,77 +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 'Groovy scripting integration for Elasticsearch'
classname 'org.elasticsearch.script.groovy.GroovyPlugin'
}
dependencies {
compile 'org.codehaus.groovy:groovy:2.4.6:indy'
}
integTest {
cluster {
setting 'script.inline', 'true'
setting 'script.stored', 'true'
setting 'script.max_compilations_per_minute', '1000'
}
}
thirdPartyAudit.excludes = [
// classes are missing, we bring in a minimal groovy dist
// for example we do not need ivy, scripts arent allowed to download code
'com.thoughtworks.xstream.XStream',
'groovyjarjarasm.asm.util.Textifiable',
// commons-cli is referenced by groovy, even though they supposedly
// jarjar it. Since we don't use the cli, we don't need the dep.
'org.apache.commons.cli.CommandLine',
'org.apache.commons.cli.CommandLineParser',
'org.apache.commons.cli.GnuParser',
'org.apache.commons.cli.HelpFormatter',
'org.apache.commons.cli.Option',
'org.apache.commons.cli.OptionBuilder',
'org.apache.commons.cli.Options',
'org.apache.commons.cli.Parser',
'org.apache.commons.cli.PosixParser',
'org.apache.ivy.Ivy',
'org.apache.ivy.core.event.IvyListener',
'org.apache.ivy.core.event.download.PrepareDownloadEvent',
'org.apache.ivy.core.event.resolve.StartResolveEvent',
'org.apache.ivy.core.module.descriptor.Configuration',
'org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor',
'org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor',
'org.apache.ivy.core.module.descriptor.DefaultExcludeRule',
'org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor',
'org.apache.ivy.core.module.id.ArtifactId',
'org.apache.ivy.core.module.id.ModuleId',
'org.apache.ivy.core.module.id.ModuleRevisionId',
'org.apache.ivy.core.report.ResolveReport',
'org.apache.ivy.core.resolve.ResolveOptions',
'org.apache.ivy.core.settings.IvySettings',
'org.apache.ivy.plugins.matcher.ExactPatternMatcher',
'org.apache.ivy.plugins.matcher.PatternMatcher',
'org.apache.ivy.plugins.resolver.IBiblioResolver',
'org.apache.ivy.util.DefaultMessageLogger',
'org.apache.ivy.util.Message',
'org.fusesource.jansi.Ansi$Attribute',
'org.fusesource.jansi.Ansi$Color',
'org.fusesource.jansi.Ansi',
'org.fusesource.jansi.AnsiRenderWriter',
]

View File

@ -1 +0,0 @@
af78e80fab591a6dcf2d6ccb8bf34d1e888291be

View File

@ -1,15 +0,0 @@
/*
* Licensed 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.
*
*/

View File

@ -1,5 +0,0 @@
Apache Groovy
Copyright 2003-2016 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -1,35 +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.script.groovy;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptEngineRegistry;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptModule;
public class GroovyPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptEngineService getScriptEngineService(Settings settings) {
return new GroovyScriptEngineService(settings);
}
}

View File

@ -1,384 +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.script.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import groovy.lang.Script;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Scorer;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.codehaus.groovy.control.messages.Message;
import org.elasticsearch.SpecialPermission;
import org.elasticsearch.bootstrap.BootstrapInfo;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.hash.MessageDigests;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ClassPermission;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.ScoreAccessor;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.lookup.LeafSearchLookup;
import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
/**
* Provides the infrastructure for Groovy as a scripting language for Elasticsearch
*/
public class GroovyScriptEngineService extends AbstractComponent implements ScriptEngineService {
/**
* The name of the scripting engine/language.
*/
public static final String NAME = "groovy";
/**
* The name of the Groovy compiler setting to use associated with activating <code>invokedynamic</code> support.
*/
public static final String GROOVY_INDY_SETTING_NAME = "indy";
/**
* Classloader used as a parent classloader for all Groovy scripts
*/
private final ClassLoader loader;
public GroovyScriptEngineService(Settings settings) {
super(settings);
deprecationLogger.deprecated("[groovy] scripts are deprecated, use [painless] scripts instead");
// Creates the classloader here in order to isolate Groovy-land code
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SpecialPermission());
}
this.loader = AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> {
// snapshot our context (which has permissions for classes), since the script has none
AccessControlContext context = AccessController.getContext();
return new ClassLoader(getClass().getClassLoader()) {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (sm != null) {
try {
context.checkPermission(new ClassPermission(name));
} catch (SecurityException e) {
throw new ClassNotFoundException(name, e);
}
}
return super.loadClass(name, resolve);
}
};
});
}
@Override
public void close() throws IOException {
// Nothing to do here
}
@Override
public String getType() {
return NAME;
}
@Override
public String getExtension() {
return NAME;
}
@Override
public Object compile(String scriptName, String scriptSource, Map<String, String> params) {
// Create the script class name
String className = MessageDigests.toHexString(MessageDigests.sha1().digest(scriptSource.getBytes(StandardCharsets.UTF_8)));
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SpecialPermission());
}
return AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try {
GroovyCodeSource codeSource = new GroovyCodeSource(scriptSource, className, BootstrapInfo.UNTRUSTED_CODEBASE);
codeSource.setCachable(false);
CompilerConfiguration configuration = new CompilerConfiguration()
.addCompilationCustomizers(new ImportCustomizer().addStarImports("org.joda.time").addStaticStars("java.lang.Math"))
.addCompilationCustomizers(new GroovyBigDecimalTransformer(CompilePhase.CONVERSION));
// always enable invokeDynamic, not the crazy softreference-based stuff
configuration.getOptimizationOptions().put(GROOVY_INDY_SETTING_NAME, true);
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(loader, configuration);
return groovyClassLoader.parseClass(codeSource);
} catch (Exception e) {
if (logger.isTraceEnabled()) {
logger.trace("Exception compiling Groovy script:", e);
}
throw convertToScriptException("Error compiling script " + className, scriptSource, e);
}
});
}
/**
* Return a script object with the given vars from the compiled script object
*/
@SuppressWarnings("unchecked")
private Script createScript(Object compiledScript, Map<String, Object> vars) throws ReflectiveOperationException {
Class<?> scriptClass = (Class<?>) compiledScript;
Script scriptObject = (Script) scriptClass.getConstructor().newInstance();
Binding binding = new Binding();
binding.getVariables().putAll(vars);
scriptObject.setBinding(binding);
return scriptObject;
}
@Override
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> vars) {
deprecationLogger.deprecated("[groovy] scripts are deprecated, use [painless] scripts instead");
try {
Map<String, Object> allVars = new HashMap<>();
if (vars != null) {
allVars.putAll(vars);
}
return new GroovyScript(compiledScript, createScript(compiledScript.compiled(), allVars), this.logger);
} catch (ReflectiveOperationException e) {
throw convertToScriptException("Failed to build executable script", compiledScript.name(), e);
}
}
@Override
public SearchScript search(final CompiledScript compiledScript, final SearchLookup lookup, @Nullable final Map<String, Object> vars) {
deprecationLogger.deprecated("[groovy] scripts are deprecated, use [painless] scripts instead");
return new SearchScript() {
@Override
public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException {
final LeafSearchLookup leafLookup = lookup.getLeafSearchLookup(context);
Map<String, Object> allVars = new HashMap<>();
allVars.putAll(leafLookup.asMap());
if (vars != null) {
allVars.putAll(vars);
}
Script scriptObject;
try {
scriptObject = createScript(compiledScript.compiled(), allVars);
} catch (ReflectiveOperationException e) {
throw convertToScriptException("Failed to build search script", compiledScript.name(), e);
}
return new GroovyScript(compiledScript, scriptObject, leafLookup, logger);
}
@Override
public boolean needsScores() {
// TODO: can we reliably know if a groovy script makes use of _score
return true;
}
};
}
/**
* Converts a {@link Throwable} to a {@link ScriptException}
*/
private ScriptException convertToScriptException(String message, String source, Throwable cause) {
List<String> stack = new ArrayList<>();
if (cause instanceof MultipleCompilationErrorsException) {
@SuppressWarnings({"unchecked"})
List<Message> errors = (List<Message>) ((MultipleCompilationErrorsException) cause).getErrorCollector().getErrors();
for (Message error : errors) {
try (StringWriter writer = new StringWriter()) {
error.write(new PrintWriter(writer));
stack.add(writer.toString());
} catch (IOException e1) {
logger.error("failed to write compilation error message to the stack", e1);
}
}
} else if (cause instanceof CompilationFailedException) {
CompilationFailedException error = (CompilationFailedException) cause;
stack.add(error.getMessage());
}
throw new ScriptException(message, cause, stack, source, NAME);
}
public static final class GroovyScript implements ExecutableScript, LeafSearchScript {
private final CompiledScript compiledScript;
private final Script script;
private final LeafSearchLookup lookup;
private final Map<String, Object> variables;
private final Logger logger;
public GroovyScript(CompiledScript compiledScript, Script script, Logger logger) {
this(compiledScript, script, null, logger);
}
@SuppressWarnings("unchecked")
public GroovyScript(CompiledScript compiledScript, Script script, @Nullable LeafSearchLookup lookup, Logger logger) {
this.compiledScript = compiledScript;
this.script = script;
this.lookup = lookup;
this.logger = logger;
this.variables = script.getBinding().getVariables();
}
@Override
public void setScorer(Scorer scorer) {
this.variables.put("_score", new ScoreAccessor(scorer));
}
@Override
public void setDocument(int doc) {
if (lookup != null) {
lookup.setDocument(doc);
}
}
@Override
public void setNextVar(String name, Object value) {
variables.put(name, value);
}
@Override
public void setSource(Map<String, Object> source) {
if (lookup != null) {
lookup.source().setSource(source);
}
}
@Override
public Object run() {
try {
// NOTE: we truncate the stack because IndyInterface has security issue (needs getClassLoader)
// we don't do a security check just as a tradeoff, it cannot really escalate to anything.
return AccessController.doPrivileged((PrivilegedAction<Object>) script::run);
} catch (final AssertionError ae) {
if (ae instanceof GroovyBugError) {
// we encountered a bug in Groovy; we wrap this so it does not go to the uncaught exception handler and tear us down
final String message = "encountered bug in Groovy while executing script [" + compiledScript.name() + "]";
throw new ScriptException(message, ae, Collections.emptyList(), compiledScript.toString(), compiledScript.lang());
}
// Groovy asserts are not java asserts, and cannot be disabled, so we do a best-effort trying to determine if this is a
// Groovy assert (in which case we wrap it and throw), or a real Java assert, in which case we rethrow it as-is, likely
// resulting in the uncaughtExceptionHandler handling it.
final StackTraceElement[] elements = ae.getStackTrace();
if (elements.length > 0 && "org.codehaus.groovy.runtime.InvokerHelper".equals(elements[0].getClassName())) {
logger.debug((Supplier<?>) () -> new ParameterizedMessage("failed to run {}", compiledScript), ae);
throw new ScriptException("error evaluating " + compiledScript.name(), ae, emptyList(), "", compiledScript.lang());
}
throw ae;
} catch (Exception | NoClassDefFoundError e) {
logger.trace((Supplier<?>) () -> new ParameterizedMessage("failed to run {}", compiledScript), e);
throw new ScriptException("error evaluating " + compiledScript.name(), e, emptyList(), "", compiledScript.lang());
}
}
@Override
public long runAsLong() {
return ((Number) run()).longValue();
}
@Override
public double runAsDouble() {
return ((Number) run()).doubleValue();
}
}
/**
* A compilation customizer that is used to transform a number like 1.23,
* which would normally be a BigDecimal, into a double value.
*/
private class GroovyBigDecimalTransformer extends CompilationCustomizer {
private GroovyBigDecimalTransformer(CompilePhase phase) {
super(phase);
}
@Override
public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException {
new BigDecimalExpressionTransformer(source).visitClass(classNode);
}
}
/**
* Groovy expression transformer that converts BigDecimals to doubles
*/
private class BigDecimalExpressionTransformer extends ClassCodeExpressionTransformer {
private final SourceUnit source;
private BigDecimalExpressionTransformer(SourceUnit source) {
this.source = source;
}
@Override
protected SourceUnit getSourceUnit() {
return this.source;
}
@Override
public Expression transform(Expression expr) {
Expression newExpr = expr;
if (expr instanceof ConstantExpression) {
ConstantExpression constExpr = (ConstantExpression) expr;
Object val = constExpr.getValue();
if (val != null && val instanceof BigDecimal) {
newExpr = new ConstantExpression(((BigDecimal) val).doubleValue());
}
}
return super.transform(newExpr);
}
}
}

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.
*/
grant {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
// needed by IndyInterface
permission java.lang.RuntimePermission "getClassLoader";
// needed by groovy engine
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.reflect";
// Allow executing groovy scripts with codesource of /untrusted
permission groovy.security.GroovyCodeSourcePermission "/untrusted";
// Standard set of classes
permission org.elasticsearch.script.ClassPermission "<<STANDARD>>";
// groovy runtime (TODO: clean these up if possible)
permission org.elasticsearch.script.ClassPermission "groovy.grape.GrabAnnotationTransformation";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Binding";
permission org.elasticsearch.script.ClassPermission "groovy.lang.GroovyObject";
permission org.elasticsearch.script.ClassPermission "groovy.lang.GString";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Script";
permission org.elasticsearch.script.ClassPermission "groovy.util.GroovyCollections";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.ast.builder.AstBuilderTransformation";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.reflection.ClassInfo";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.GStringImpl";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.ValueRecorder";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.AssertionRenderer";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.ScriptBytecodeAdapter";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.vmplugin.v7.IndyInterface";
permission org.elasticsearch.script.ClassPermission "sun.reflect.ConstructorAccessorImpl";
permission org.elasticsearch.script.ClassPermission "sun.reflect.MethodAccessorImpl";
permission org.elasticsearch.script.ClassPermission "jdk.internal.reflect.ConstructorAccessorImpl";
permission org.elasticsearch.script.ClassPermission "jdk.internal.reflect.MethodAccessorImpl";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Closure";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.GeneratedClosure";
permission org.elasticsearch.script.ClassPermission "groovy.lang.MetaClass";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Range";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Reference";
};

View File

@ -1,164 +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.script.groovy;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESIntegTestCase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
public class GroovyIndexedScriptTests extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(GroovyPlugin.class);
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal));
builder.put("script.engine.groovy.stored.update", "false");
builder.put("script.engine.groovy.stored.search", "true");
builder.put("script.engine.groovy.stored.aggs", "true");
builder.put("script.engine.groovy.inline.aggs", "false");
return builder.build();
}
public void testFieldIndexedScript() throws ExecutionException, InterruptedException {
client().admin().cluster().preparePutStoredScript()
.setId("script1")
.setScriptLang(GroovyScriptEngineService.NAME)
.setSource(new BytesArray("{ \"script\" : \"2\"}"))
.get();
client().admin().cluster().preparePutStoredScript()
.setId("script2")
.setScriptLang(GroovyScriptEngineService.NAME)
.setSource(new BytesArray("{ \"script\" : \"factor * 2\"}"))
.get();
List<IndexRequestBuilder> builders = new ArrayList<>();
builders.add(client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}"));
builders.add(client().prepareIndex("test", "scriptTest", "2").setSource("{\"theField\":\"foo 2\"}"));
builders.add(client().prepareIndex("test", "scriptTest", "3").setSource("{\"theField\":\"foo 3\"}"));
builders.add(client().prepareIndex("test", "scriptTest", "4").setSource("{\"theField\":\"foo 4\"}"));
builders.add(client().prepareIndex("test", "scriptTest", "5").setSource("{\"theField\":\"bar\"}"));
indexRandom(true, builders);
Map<String, Object> script2Params = new HashMap<>();
script2Params.put("factor", 3);
SearchResponse searchResponse = client()
.prepareSearch()
.setSource(
new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).size(1)
.scriptField("test1",
new Script(ScriptType.STORED, GroovyScriptEngineService.NAME, "script1", Collections.emptyMap()))
.scriptField("test2",
new Script(ScriptType.STORED, GroovyScriptEngineService.NAME, "script2", script2Params)))
.setIndices("test").setTypes("scriptTest").get();
assertHitCount(searchResponse, 5);
assertTrue(searchResponse.getHits().hits().length == 1);
SearchHit sh = searchResponse.getHits().getAt(0);
assertThat(sh.field("test1").getValue(), equalTo(2));
assertThat(sh.field("test2").getValue(), equalTo(6));
}
// Relates to #10397
public void testUpdateScripts() {
createIndex("test_index");
ensureGreen("test_index");
client().prepareIndex("test_index", "test_type", "1").setSource("{\"foo\":\"bar\"}").get();
flush("test_index");
int iterations = randomIntBetween(2, 11);
for (int i = 1; i < iterations; i++) {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang(GroovyScriptEngineService.NAME)
.setId("script1")
.setSource(new BytesArray("{\"script\":\"" + i + "\"}")));
SearchResponse searchResponse = client()
.prepareSearch()
.setSource(
new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).scriptField("test_field",
new Script(ScriptType.STORED, GroovyScriptEngineService.NAME, "script1", Collections.emptyMap())))
.setIndices("test_index")
.setTypes("test_type").get();
assertHitCount(searchResponse, 1);
SearchHit sh = searchResponse.getHits().getAt(0);
assertThat(sh.field("test_field").getValue(), equalTo(i));
}
}
public void testDisabledUpdateIndexedScriptsOnly() {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang(GroovyScriptEngineService.NAME)
.setId("script1")
.setSource(new BytesArray("{\"script\":\"2\"}")));
client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}").get();
try {
client().prepareUpdate("test", "scriptTest", "1")
.setScript(new Script(ScriptType.STORED, GroovyScriptEngineService.NAME, "script1", Collections.emptyMap())).get();
fail("update script should have been rejected");
} catch (Exception e) {
assertThat(e.getMessage(), containsString("failed to execute script"));
assertThat(ExceptionsHelper.detailedMessage(e),
containsString("scripts of type [stored], operation [update] and lang [groovy] are disabled"));
}
}
public void testDisabledAggsDynamicScripts() {
//dynamic scripts don't need to be enabled for an indexed script to be indexed and later on executed
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang(GroovyScriptEngineService.NAME)
.setId("script1")
.setSource(new BytesArray("{\"script\":\"2\"}")));
client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}").get();
refresh();
SearchResponse searchResponse = client()
.prepareSearch("test")
.setSource(
new SearchSourceBuilder().aggregation(AggregationBuilders.terms("test").script(
new Script(ScriptType.STORED, GroovyScriptEngineService.NAME, "script1", Collections.emptyMap())))).get();
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getAggregations().get("test"), notNullValue());
}
}

View File

@ -1,156 +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.script.groovy;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.ScriptSortBuilder.ScriptSortType;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.test.ESIntegTestCase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery;
import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertOrderedSearchHits;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
import static org.hamcrest.Matchers.equalTo;
/**
* Various tests for Groovy scripting
*/
// TODO: refactor into unit test or proper rest tests
public class GroovyScriptTests extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(GroovyPlugin.class);
}
public void testGroovyBigDecimalTransformation() {
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefreshPolicy(IMMEDIATE).get();
// Test that something that would usually be a BigDecimal is transformed into a Double
assertScript("def n = 1.23; assert n instanceof Double; return n;");
assertScript("def n = 1.23G; assert n instanceof Double; return n;");
assertScript("def n = BigDecimal.ONE; assert n instanceof BigDecimal; return n;");
}
public void assertScript(String scriptString) {
Script script = new Script(ScriptType.INLINE, GroovyScriptEngineService.NAME, scriptString, Collections.emptyMap());
SearchResponse resp = client().prepareSearch("test")
.setSource(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).sort(SortBuilders.
scriptSort(script, ScriptSortType.NUMBER)))
.get();
assertNoFailures(resp);
}
public void testGroovyExceptionSerialization() throws Exception {
List<IndexRequestBuilder> reqs = new ArrayList<>();
for (int i = 0; i < randomIntBetween(50, 500); i++) {
reqs.add(client().prepareIndex("test", "doc", "" + i).setSource("foo", "bar"));
}
indexRandom(true, false, reqs);
try {
client().prepareSearch("test")
.setQuery(
constantScoreQuery(scriptQuery(new Script(ScriptType.INLINE, GroovyScriptEngineService.NAME, "1 == not_found",
Collections.emptyMap())))).get();
fail("should have thrown an exception");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString()+ "should not contained NotSerializableTransportException",
e.toString().contains("NotSerializableTransportException"), equalTo(false));
assertThat(e.toString()+ "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString()+ "should have contained not_found",
e.toString().contains("No such property: not_found"), equalTo(true));
}
try {
client().prepareSearch("test")
.setQuery(constantScoreQuery(scriptQuery(
new Script(ScriptType.INLINE, GroovyScriptEngineService.NAME, "null.foo", Collections.emptyMap())))).get();
fail("should have thrown an exception");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should not contained NotSerializableTransportException",
e.toString().contains("NotSerializableTransportException"), equalTo(false));
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString()+ "should have contained a NullPointerException",
e.toString().contains("NullPointerException[Cannot get property 'foo' on null object]"), equalTo(true));
}
}
public void testGroovyScriptAccess() {
client().prepareIndex("test", "doc", "1").setSource("foo", "quick brow fox jumped over the lazy dog", "bar", 1).get();
client().prepareIndex("test", "doc", "2").setSource("foo", "fast jumping spiders", "bar", 2).get();
client().prepareIndex("test", "doc", "3").setSource("foo", "dog spiders that can eat a dog", "bar", 3).get();
refresh();
// doc[] access
SearchResponse resp = client().prepareSearch("test").setQuery(functionScoreQuery(scriptFunction(
new Script(ScriptType.INLINE, GroovyScriptEngineService.NAME, "doc['bar'].value", Collections.emptyMap())))
.boostMode(CombineFunction.REPLACE)).get();
assertNoFailures(resp);
assertOrderedSearchHits(resp, "3", "2", "1");
}
public void testScoreAccess() {
client().prepareIndex("test", "doc", "1").setSource("foo", "quick brow fox jumped over the lazy dog", "bar", 1).get();
client().prepareIndex("test", "doc", "2").setSource("foo", "fast jumping spiders", "bar", 2).get();
client().prepareIndex("test", "doc", "3").setSource("foo", "dog spiders that can eat a dog", "bar", 3).get();
refresh();
// _score can be accessed
SearchResponse resp = client().prepareSearch("test").setQuery(functionScoreQuery(matchQuery("foo", "dog"),
scriptFunction(new Script(ScriptType.INLINE, GroovyScriptEngineService.NAME, "_score", Collections.emptyMap())))
.boostMode(CombineFunction.REPLACE)).get();
assertNoFailures(resp);
assertSearchHits(resp, "3", "1");
// _score is comparable
// NOTE: it is important to use 0.0 instead of 0 instead Groovy will do an integer comparison
// and if the score if between 0 and 1 it will be considered equal to 0 due to the cast
resp = client()
.prepareSearch("test")
.setQuery(
functionScoreQuery(matchQuery("foo", "dog"), scriptFunction(
new Script(ScriptType.INLINE,
GroovyScriptEngineService.NAME, "_score > 0.0 ? _score : 0", Collections.emptyMap())))
.boostMode(CombineFunction.REPLACE)).get();
assertNoFailures(resp);
assertSearchHits(resp, "3", "1");
}
}

View File

@ -1,177 +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.script.groovy;
import groovy.lang.MissingPropertyException;
import org.apache.lucene.util.Constants;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESTestCase;
import java.nio.file.Path;
import java.security.PrivilegedActionException;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Tests for the Groovy security permissions
*/
public class GroovySecurityTests extends ESTestCase {
private GroovyScriptEngineService se;
@Override
public void setUp() throws Exception {
super.setUp();
se = new GroovyScriptEngineService(Settings.EMPTY);
// otherwise will exit your VM and other bad stuff
assumeTrue("test requires security manager to be enabled", System.getSecurityManager() != null);
}
@Override
public void tearDown() throws Exception {
se.close();
super.tearDown();
}
public void testEvilGroovyScripts() throws Exception {
// Plain test
assertSuccess("");
// field access (via map)
assertSuccess("def foo = doc['foo'].value; if (foo == null) { return 5; }");
// field access (via list)
assertSuccess("def foo = mylist[0]; if (foo == null) { return 5; }");
// field access (via array)
assertSuccess("def foo = myarray[0]; if (foo == null) { return 5; }");
// field access (via object)
assertSuccess("def foo = myobject.primitive.toString(); if (foo == null) { return 5; }");
assertSuccess("def foo = myobject.object.toString(); if (foo == null) { return 5; }");
assertSuccess("def foo = myobject.list[0].primitive.toString(); if (foo == null) { return 5; }");
// List
assertSuccess("def list = [doc['foo'].value, 3, 4]; def v = list.get(1); list.add(10)");
// Ranges
assertSuccess("def range = 1..doc['foo'].value; def v = range.get(0)");
// Maps
assertSuccess("def v = doc['foo'].value; def m = [:]; m.put(\"value\", v)");
// Times
assertSuccess("def t = Instant.now().getMillis()");
// GroovyCollections
assertSuccess("def n = [1,2,3]; GroovyCollections.max(n)");
// Groovy closures
assertSuccess("[1, 2, 3, 4].findAll { it % 2 == 0 }");
assertSuccess("def buckets=[ [2, 4, 6, 8], [10, 12, 16, 14], [18, 22, 20, 24] ]; buckets[-3..-1].every { it.every { i -> i % 2 == 0 } }");
assertSuccess("def val = \"\"; [1, 2, 3, 4].each { val += it }; val");
// Groovy uses reflection to invoke closures. These reflective calls are optimized by the JVM after "sun.reflect.inflationThreshold"
// invocations. After the inflation step, access to sun.reflect.MethodAccessorImpl is required from the security manager. This test,
// assuming a inflation threshold below 100 (15 is current value on Oracle JVMs), checks that the relevant permission is available.
assertSuccess("(1..100).collect{ it + 1 }");
// Fail cases:
assertFailure("pr = Runtime.getRuntime().exec(\"touch /tmp/gotcha\"); pr.waitFor()", MissingPropertyException.class);
// infamous:
assertFailure("java.lang.Math.class.forName(\"java.lang.Runtime\")", PrivilegedActionException.class);
// filtered directly by our classloader
assertFailure("getClass().getClassLoader().loadClass(\"java.lang.Runtime\").availableProcessors()", PrivilegedActionException.class);
// unfortunately, we have access to other classloaders (due to indy mechanism needing getClassLoader permission)
// but we can't do much with them directly at least.
assertFailure("myobject.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").availableProcessors()", SecurityException.class);
assertFailure("d = new DateTime(); d.getClass().getDeclaredMethod(\"year\").setAccessible(true)", SecurityException.class);
assertFailure("d = new DateTime(); d.\"${'get' + 'Class'}\"()." +
"\"${'getDeclared' + 'Method'}\"(\"year\").\"${'set' + 'Accessible'}\"(false)", SecurityException.class);
assertFailure("Class.forName(\"org.joda.time.DateTime\").getDeclaredMethod(\"year\").setAccessible(true)", MissingPropertyException.class);
assertFailure("Eval.me('2 + 2')", MissingPropertyException.class);
assertFailure("Eval.x(5, 'x + 2')", MissingPropertyException.class);
assertFailure("d = new Date(); java.lang.reflect.Field f = Date.class.getDeclaredField(\"fastTime\");" +
" f.setAccessible(true); f.get(\"fastTime\")", MultipleCompilationErrorsException.class);
assertFailure("def methodName = 'ex'; Runtime.\"${'get' + 'Runtime'}\"().\"${methodName}ec\"(\"touch /tmp/gotcha2\")", MissingPropertyException.class);
assertFailure("t = new Thread({ println 3 });", MultipleCompilationErrorsException.class);
// test a directory we normally have access to, but the groovy script does not.
Path dir = createTempDir();
// TODO: figure out the necessary escaping for windows paths here :)
if (!Constants.WINDOWS) {
assertFailure("new File(\"" + dir + "\").exists()", MultipleCompilationErrorsException.class);
}
}
public void testGroovyScriptsThatThrowErrors() throws Exception {
assertFailure("assert false, \"msg\";", AssertionError.class);
assertFailure("def foo=false; assert foo;", AssertionError.class);
// Groovy's asserts require org.codehaus.groovy.runtime.InvokerHelper, so they are denied
assertFailure("def foo=false; assert foo, \"msg2\";", NoClassDefFoundError.class);
}
public void testGroovyBugError() {
// this script throws a GroovyBugError because our security manager permissions prevent Groovy from accessing this private field
// and Groovy does not handle it gracefully; this test will likely start failing if the bug is fixed upstream so that a
// GroovyBugError no longer surfaces here in which case the script should be replaced with another script that intentionally
// surfaces a GroovyBugError
assertFailure("[1, 2].size", AssertionError.class);
}
/** runs a script */
private void doTest(String script) {
Map<String, Object> vars = new HashMap<String, Object>();
// we add a "mock document" containing a single field "foo" that returns 4 (abusing a jdk class with a getValue() method)
vars.put("doc", Collections.singletonMap("foo", new AbstractMap.SimpleEntry<Object,Integer>(null, 4)));
vars.put("mylist", Arrays.asList("foo"));
vars.put("myarray", Arrays.asList("foo"));
vars.put("myobject", new MyObject());
se.executable(new CompiledScript(ScriptType.INLINE, "test", "js", se.compile(null, script, Collections.emptyMap())), vars).run();
}
public static class MyObject {
public int getPrimitive() { return 0; }
public Object getObject() { return "value"; }
public List<Object> getList() { return Arrays.asList(new MyObject()); }
}
/** asserts that a script runs without exception */
private void assertSuccess(String script) {
doTest(script);
}
/** asserts that a script triggers the given exceptionclass */
private void assertFailure(String script, Class<? extends Throwable> exceptionClass) {
try {
doTest(script);
fail("did not get expected exception");
} catch (ScriptException expected) {
Throwable cause = expected.getCause();
assertNotNull(cause);
if (exceptionClass.isAssignableFrom(cause.getClass()) == false) {
throw new AssertionError("unexpected exception: " + cause, expected);
}
}
}
}

View File

@ -1,42 +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.script.groovy;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.yaml.parser.ClientYamlTestParseException;
import java.io.IOException;
public class LangGroovyClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
public LangGroovyClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
super(testCandidate);
}
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, ClientYamlTestParseException {
return ESClientYamlSuiteTestCase.createParameters();
}
}

View File

@ -1,13 +0,0 @@
# Integration tests for Groovy scripts
#
"Groovy loaded":
- do:
cluster.state: {}
# Get master node id
- set: { master_node: master }
- do:
nodes.info: {}
- match: { nodes.$master.modules.0.name: lang-groovy }

View File

@ -1,87 +0,0 @@
---
"Update Script":
- skip:
features: groovy_scripting
- do:
index:
index: test_1
type: test
id: 1
body:
foo: bar
count: 1
- do:
update:
index: test_1
type: test
id: 1
body:
script:
lang: groovy
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }
- match: { _index: test_1 }
- match: { _type: test }
- match: { _id: "1" }
- match: { _version: 2 }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: xxx }
- match: { _source.count: 1 }
- do:
update:
index: test_1
type: test
id: 1
body:
script:
lang: groovy
inline: "ctx._source.foo = 'yyy'"
- match: { _index: test_1 }
- match: { _type: test }
- match: { _id: "1" }
- match: { _version: 3 }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: yyy }
- match: { _source.count: 1 }
- do:
catch: /script_lang not supported \[doesnotexist\]/
update:
index: test_1
type: test
id: 1
body:
script:
inline: "1"
lang: "doesnotexist"
params: { bar: 'xxx' }
- do:
catch: /script_lang not supported \[doesnotexist\]/
update:
index: test_1
type: test
id: 1
body:
script:
lang: doesnotexist
inline: "1"

View File

@ -1,73 +0,0 @@
---
"Indexed script":
- skip:
features: groovy_scripting
- do:
put_script:
id: "1"
lang: "groovy"
body: { "script": "_score * doc[\"myParent.weight\"].value" }
- match: { acknowledged: true }
- do:
get_script:
id: "1"
lang: "groovy"
- match: { found: true }
- match: { lang: groovy }
- match: { _id: "1" }
- match: { "script": "_score * doc[\"myParent.weight\"].value" }
- do:
catch: missing
get_script:
id: "2"
lang: "groovy"
- match: { found: false }
- match: { lang: groovy }
- match: { _id: "2" }
- is_false: script
- do:
delete_script:
id: "1"
lang: "groovy"
- match: { acknowledged: true }
- do:
catch: missing
delete_script:
id: "non_existing"
lang: "groovy"
- do:
catch: request
put_script:
id: "1"
lang: "groovy"
body: { "script": "_score * foo bar + doc[\"myParent.weight\"].value" }
- do:
catch: /script_exception,.+Error.compiling.script.*/
put_script:
id: "1"
lang: "groovy"
body: { "script": "_score * foo bar + doc[\"myParent.weight\"].value" }
- do:
catch: request
put_script:
id: "1"
lang: "foobar"
body: { "script" : "_score * doc[\"myParent.weight\"].value" }
- do:
catch: /script_lang.not.supported/
put_script:
id: "1"
lang: "foobar"
body: { "script" : "_score * doc[\"myParent.weight\"].value" }

View File

@ -1,69 +0,0 @@
---
"Script upsert":
- skip:
features: groovy_scripting
- do:
update:
index: test_1
type: test
id: 1
body:
script:
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }
lang: "groovy"
upsert: { foo: baz }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: baz }
- do:
update:
index: test_1
type: test
id: 1
body:
script:
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }
lang: "groovy"
upsert: { foo: baz }
- do:
get:
index: test_1
type: test
id: 1
- match: { _source.foo: xxx }
- do:
update:
index: test_1
type: test
id: 2
body:
script:
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }
lang: "groovy"
upsert: { foo: baz }
scripted_upsert: true
- do:
get:
index: test_1
type: test
id: 2
- match: { _source.foo: xxx }

View File

@ -1,72 +0,0 @@
setup:
- do:
indices.create:
index: test
- do:
index:
index: test
type: test
id: 1
body: { foo: bar }
refresh: wait_for
---
teardown:
- do:
cluster.put_settings:
body:
transient:
script.max_compilations_per_minute: null
---
"circuit breaking with too many scripts":
- do:
cluster.put_settings:
body:
transient:
script.max_compilations_per_minute: 1
- do:
search:
index: test
type: test
body:
size: 1
script_fields:
myfield:
script:
inline: "\"aoeu\""
lang: groovy
- match: { hits.total: 1 }
- do:
catch: /Too many dynamic script compilations within one minute/
search:
index: test
type: test
body:
size: 1
script_fields:
myfield:
script:
inline: "\"aoeuaoeu\""
lang: groovy
---
"no bad settings":
- do:
catch: /Failed to parse value \[-1\] for setting \[script.max_compilations_per_minute\] must be >= 0/
cluster.put_settings:
body:
transient:
script.max_compilations_per_minute: -1
- do:
catch: /Failed to parse value \[99999999999\] for setting \[script.max_compilations_per_minute\]/
cluster.put_settings:
body:
transient:
script.max_compilations_per_minute: 99999999999

View File

@ -1,48 +0,0 @@
---
"Missing document (partial doc)":
- do:
catch: missing
update:
index: test_1
type: test
id: 1
body: { doc: { foo: bar } }
- do:
update:
index: test_1
type: test
id: 1
body: { doc: { foo: bar } }
ignore: 404
---
"Missing document (script)":
- skip:
features: groovy_scripting
- do:
catch: missing
update:
index: test_1
type: test
id: 1
body:
script:
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }
lang: "groovy"
- do:
update:
index: test_1
type: test
id: 1
ignore: 404
body:
script:
inline: "ctx._source.foo = bar"
params: { bar: 'xxx' }

View File

@ -521,7 +521,7 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
currentFieldName = sourceParser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("query".equals(currentFieldName)) {
QueryParseContext queryParseContext = context.newParseContextWithLegacyScriptLanguage(sourceParser);
QueryParseContext queryParseContext = context.newParseContext(sourceParser);
return parseQuery(context, mapUnmappedFieldsAsString, queryParseContext, sourceParser);
} else {
sourceParser.skipChildren();

View File

@ -1,173 +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.percolator;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.TestUtil;
import org.elasticsearch.Version;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.env.Environment;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESIntegTestCase;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
import static org.elasticsearch.percolator.PercolatorTestUtil.preparePercolate;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, numClientNodes = 0)
@LuceneTestCase.SuppressFileSystems("ExtrasFS")
// Can'r run as IT as the test cluster is immutable and this test adds nodes during the test
public class PercolatorBackwardsCompatibilityTests extends ESIntegTestCase {
private static final String INDEX_NAME = "percolator_index";
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Arrays.asList(PercolatorPlugin.class, FoolMeScriptLang.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return Collections.singleton(PercolatorPlugin.class);
}
public void testOldPercolatorIndex() throws Exception {
setupNode();
// verify cluster state:
ClusterState state = client().admin().cluster().prepareState().get().getState();
assertThat(state.metaData().indices().size(), equalTo(1));
assertThat(state.metaData().indices().get(INDEX_NAME), notNullValue());
assertThat(state.metaData().indices().get(INDEX_NAME).getCreationVersion(), equalTo(Version.V_2_0_0));
assertThat(state.metaData().indices().get(INDEX_NAME).getUpgradedVersion(), equalTo(Version.CURRENT));
assertThat(state.metaData().indices().get(INDEX_NAME).getMappings().size(), equalTo(2));
assertThat(state.metaData().indices().get(INDEX_NAME).getMappings().get(".percolator"), notNullValue());
// important: verify that the query field in the .percolator mapping is of type object (from 5.x this is of type percolator)
MappingMetaData mappingMetaData = state.metaData().indices().get(INDEX_NAME).getMappings().get(".percolator");
assertThat(XContentMapValues.extractValue("properties.query.type", mappingMetaData.sourceAsMap()), equalTo("object"));
assertThat(state.metaData().indices().get(INDEX_NAME).getMappings().get("message"), notNullValue());
// verify existing percolator queries:
SearchResponse searchResponse = client().prepareSearch(INDEX_NAME)
.setTypes(".percolator")
.addSort("_uid", SortOrder.ASC)
.get();
assertThat(searchResponse.getHits().getTotalHits(), equalTo(4L));
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
assertThat(searchResponse.getHits().getAt(1).id(), equalTo("2"));
assertThat(searchResponse.getHits().getAt(2).id(), equalTo("3"));
assertThat(searchResponse.getHits().getAt(3).id(), equalTo("4"));
assertThat(XContentMapValues.extractValue("query.script.script.inline",
searchResponse.getHits().getAt(3).sourceAsMap()), equalTo("return true"));
// we don't upgrade the script definitions so that they include explicitly the lang,
// because we read / parse the query at search time.
assertThat(XContentMapValues.extractValue("query.script.script.lang",
searchResponse.getHits().getAt(3).sourceAsMap()), nullValue());
// verify percolate response
PercolateResponse percolateResponse = preparePercolate(client())
.setIndices(INDEX_NAME)
.setDocumentType("message")
.setPercolateDoc(new PercolateSourceBuilder.DocBuilder().setDoc("{}"))
.get();
assertThat(percolateResponse.getCount(), equalTo(1L));
assertThat(percolateResponse.getMatches().length, equalTo(1));
assertThat(percolateResponse.getMatches()[0].getId().string(), equalTo("4"));
percolateResponse = preparePercolate(client())
.setIndices(INDEX_NAME)
.setDocumentType("message")
.setPercolateDoc(new PercolateSourceBuilder.DocBuilder().setDoc("message", "the quick brown fox jumps over the lazy dog"))
.get();
assertThat(percolateResponse.getCount(), equalTo(3L));
assertThat(percolateResponse.getMatches().length, equalTo(3));
assertThat(percolateResponse.getMatches()[0].getId().string(), equalTo("1"));
assertThat(percolateResponse.getMatches()[1].getId().string(), equalTo("2"));
assertThat(percolateResponse.getMatches()[2].getId().string(), equalTo("4"));
// add an extra query and verify the results
client().prepareIndex(INDEX_NAME, ".percolator", "5")
.setSource(jsonBuilder().startObject().field("query", matchQuery("message", "fox jumps")).endObject())
.get();
refresh();
percolateResponse = preparePercolate(client())
.setIndices(INDEX_NAME)
.setDocumentType("message")
.setPercolateDoc(new PercolateSourceBuilder.DocBuilder().setDoc("message", "the quick brown fox jumps over the lazy dog"))
.get();
assertThat(percolateResponse.getCount(), equalTo(4L));
assertThat(percolateResponse.getMatches().length, equalTo(4));
assertThat(percolateResponse.getMatches()[0].getId().string(), equalTo("1"));
assertThat(percolateResponse.getMatches()[1].getId().string(), equalTo("2"));
assertThat(percolateResponse.getMatches()[2].getId().string(), equalTo("4"));
}
private void setupNode() throws Exception {
Path clusterDir = createTempDir();
try (InputStream stream = PercolatorBackwardsCompatibilityTests.class.
getResourceAsStream("/indices/percolator/bwc_index_2.0.0.zip")) {
TestUtil.unzip(stream, clusterDir);
}
Settings.Builder nodeSettings = Settings.builder()
.put(Environment.PATH_DATA_SETTING.getKey(), clusterDir);
internalCluster().startNode(nodeSettings.build());
ensureGreen(INDEX_NAME);
}
// Fool the script service that this is the groovy script language,
// so that we can run a script that has no lang defined implicetely against the legacy language:
public static class FoolMeScriptLang extends MockScriptPlugin {
@Override
protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {
return Collections.singletonMap("return true", (vars) -> true);
}
@Override
public String pluginScriptLang() {
return "groovy";
}
}
}

View File

@ -59,7 +59,7 @@ public class SimpleExecutableScript implements ExecutableScript {
return new HashMap<>((Map<?, ?>) value);
}
}
// Others just return the objects plain (groovy, painless)
// Others just return the objects plain (painless)
return value;
}
}

View File

@ -372,7 +372,7 @@ public class InstallPluginCommandTests extends ESTestCase {
public void testBuiltinModule() throws Exception {
Tuple<Path, Environment> env = createEnv(fs, temp);
Path pluginDir = createPluginDir(temp);
String pluginZip = createPlugin("lang-groovy", pluginDir);
String pluginZip = createPlugin("lang-painless", pluginDir);
UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1()));
assertTrue(e.getMessage(), e.getMessage().contains("is a system module"));
assertInstallCleaned(env.v2());

View File

@ -45,7 +45,7 @@
},
"lang": {
"type": "string",
"description": "The script language (default: groovy)"
"description": "The script language (default: painless)"
},
"parent": {
"type": "string",

View File

@ -24,7 +24,6 @@ List projects = [
'modules:aggs-matrix-stats',
'modules:ingest-common',
'modules:lang-expression',
'modules:lang-groovy',
'modules:lang-mustache',
'modules:lang-painless',
'modules:transport-netty4',

View File

@ -38,7 +38,6 @@ public final class Features {
private static final List<String> SUPPORTED = unmodifiableList(Arrays.asList(
"catch_unauthorized",
"embedded_stash_key",
"groovy_scripting",
"headers",
"stash_in_path",
"warnings",