Scripting: allow plugins to define custom operations that they use scripts for

Plugins can now define multiple operations/contexts that they use scripts for. Fine-grained settings can then be used to enable/disable scripts based on each single registered context.

Also added a new generic category called `plugin`, which will be used as a default when the context is not specified. This allows us to restore backwards compatibility for plugins on `ScriptService` by restoring the old methods that don't require the script context and making them internally use the `plugin` context, as they can only be called from plugins.

Closes #10347
Closes #10419
This commit is contained in:
javanna 2015-04-01 17:20:46 +02:00 committed by Luca Cavanna
parent 5d4a4fc3ff
commit 7bd7ea8f13
24 changed files with 685 additions and 401 deletions

View File

@ -300,12 +300,17 @@ supported operations are:
| `mapping` |Mappings (script transform feature) | `mapping` |Mappings (script transform feature)
| `search` |Search api, Percolator api and Suggester api (e.g filters, script_fields) | `search` |Search api, Percolator api and Suggester api (e.g filters, script_fields)
| `update` |Update api | `update` |Update api
| `plugin` |Any plugin that makes use of scripts under the generic `plugin` category
|======================================================================= |=======================================================================
Plugins can also define custom operations that they use scripts for instead
of using the generic `plugin` category. Those operations can be referred to
in the following form: `${pluginName}_${operation}`.
The following example disables scripting for `update` and `mapping` operations, The following example disables scripting for `update` and `mapping` operations,
regardless of the script source, for any engine. Scripts can still be regardless of the script source, for any engine. Scripts can still be
executed from sandboxed languages as part of `aggregations` and `search` executed from sandboxed languages as part of `aggregations`, `search`
operations though, as the above defaults still get applied. and plugins execution though, as the above defaults still get applied.
[source,yaml] [source,yaml]
----------------------------------- -----------------------------------
@ -325,14 +330,17 @@ script.engine.groovy.file.aggs: on
script.engine.groovy.file.mapping: on script.engine.groovy.file.mapping: on
script.engine.groovy.file.search: on script.engine.groovy.file.search: on
script.engine.groovy.file.update: on script.engine.groovy.file.update: on
script.engine.groovy.file.plugin: on
script.engine.groovy.indexed.aggs: on script.engine.groovy.indexed.aggs: on
script.engine.groovy.indexed.mapping: off script.engine.groovy.indexed.mapping: off
script.engine.groovy.indexed.search: on script.engine.groovy.indexed.search: on
script.engine.groovy.indexed.update: off script.engine.groovy.indexed.update: off
script.engine.groovy.indexed.plugin: off
script.engine.groovy.inline.aggs: on script.engine.groovy.inline.aggs: on
script.engine.groovy.inline.mapping: off script.engine.groovy.inline.mapping: off
script.engine.groovy.inline.search: off script.engine.groovy.inline.search: off
script.engine.groovy.inline.update: off script.engine.groovy.inline.update: off
script.engine.groovy.inline.plugin: off
----------------------------------- -----------------------------------

View File

@ -94,7 +94,7 @@ public class UpdateHelper extends AbstractComponent {
ctx.put("op", "create"); ctx.put("op", "create");
ctx.put("_source", upsertDoc); ctx.put("_source", upsertDoc);
try { try {
ExecutableScript script = scriptService.executable(request.scriptLang, request.script, request.scriptType, ScriptContext.UPDATE, request.scriptParams); ExecutableScript script = scriptService.executable(request.scriptLang, request.script, request.scriptType, ScriptContext.Standard.UPDATE, request.scriptParams);
script.setNextVar("ctx", ctx); script.setNextVar("ctx", ctx);
script.run(); script.run();
// we need to unwrap the ctx... // we need to unwrap the ctx...
@ -193,7 +193,7 @@ public class UpdateHelper extends AbstractComponent {
ctx.put("_source", sourceAndContent.v2()); ctx.put("_source", sourceAndContent.v2());
try { try {
ExecutableScript script = scriptService.executable(request.scriptLang, request.script, request.scriptType, ScriptContext.UPDATE, request.scriptParams); ExecutableScript script = scriptService.executable(request.scriptLang, request.script, request.scriptType, ScriptContext.Standard.UPDATE, request.scriptParams);
script.setNextVar("ctx", ctx); script.setNextVar("ctx", ctx);
script.run(); script.run();
// we need to unwrap the ctx... // we need to unwrap the ctx...

View File

@ -64,8 +64,9 @@ import org.elasticsearch.index.mapper.internal.VersionFieldMapper;
import org.elasticsearch.index.mapper.object.ObjectMapper; import org.elasticsearch.index.mapper.object.ObjectMapper;
import org.elasticsearch.index.mapper.object.RootObjectMapper; import org.elasticsearch.index.mapper.object.RootObjectMapper;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptService.ScriptType;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -79,7 +80,6 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static org.elasticsearch.script.ScriptService.*;
/** /**
* *
@ -760,7 +760,7 @@ public class DocumentMapper implements ToXContent {
public Map<String, Object> transformSourceAsMap(Map<String, Object> sourceAsMap) { public Map<String, Object> transformSourceAsMap(Map<String, Object> sourceAsMap) {
try { try {
// We use the ctx variable and the _source name to be consistent with the update api. // We use the ctx variable and the _source name to be consistent with the update api.
ExecutableScript executable = scriptService.executable(language, script, scriptType, ScriptContext.MAPPING, parameters); ExecutableScript executable = scriptService.executable(language, script, scriptType, ScriptContext.Standard.MAPPING, parameters);
Map<String, Object> ctx = new HashMap<>(1); Map<String, Object> ctx = new HashMap<>(1);
ctx.put("_source", sourceAsMap); ctx.put("_source", sourceAsMap);
executable.setNextVar("ctx", ctx); executable.setNextVar("ctx", ctx);

View File

@ -134,7 +134,7 @@ public class ScriptFilterParser implements FilterParser {
public ScriptFilter(String scriptLang, String script, ScriptService.ScriptType scriptType, Map<String, Object> params, ScriptService scriptService, SearchLookup searchLookup) { public ScriptFilter(String scriptLang, String script, ScriptService.ScriptType scriptType, Map<String, Object> params, ScriptService scriptService, SearchLookup searchLookup) {
this.script = script; this.script = script;
this.params = params; this.params = params;
this.searchScript = scriptService.search(searchLookup, scriptLang, script, scriptType, ScriptContext.SEARCH, newHashMap(params)); this.searchScript = scriptService.search(searchLookup, scriptLang, script, scriptType, ScriptContext.Standard.SEARCH, newHashMap(params));
} }
@Override @Override

View File

@ -26,8 +26,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustacheScriptEngineService; import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import java.io.IOException; import java.io.IOException;
@ -77,7 +77,7 @@ public class TemplateQueryParser implements QueryParser {
public Query parse(QueryParseContext parseContext) throws IOException { public Query parse(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
TemplateContext templateContext = parse(parser, PARAMS, parametersToTypes); TemplateContext templateContext = parse(parser, PARAMS, parametersToTypes);
ExecutableScript executable = this.scriptService.executable(MustacheScriptEngineService.NAME, templateContext.template(), templateContext.scriptType(), ScriptContext.SEARCH, templateContext.params()); ExecutableScript executable = this.scriptService.executable(MustacheScriptEngineService.NAME, templateContext.template(), templateContext.scriptType(), ScriptContext.Standard.SEARCH, templateContext.params());
BytesReference querySource = (BytesReference) executable.run(); BytesReference querySource = (BytesReference) executable.run();

View File

@ -87,7 +87,7 @@ public class ScriptScoreFunctionParser implements ScoreFunctionParser {
SearchScript searchScript; SearchScript searchScript;
try { try {
searchScript = parseContext.scriptService().search(parseContext.lookup(), scriptParameterParser.lang(), script, scriptType, ScriptContext.SEARCH, vars); searchScript = parseContext.scriptService().search(parseContext.lookup(), scriptParameterParser.lang(), script, scriptType, ScriptContext.Standard.SEARCH, vars);
return new ScriptScoreFunction(script, vars, searchScript); return new ScriptScoreFunction(script, vars, searchScript);
} catch (Exception e) { } catch (Exception e) {
throw new QueryParsingException(parseContext.index(), NAMES[0] + " the script could not be loaded", e); throw new QueryParsingException(parseContext.index(), NAMES[0] + " the script could not be loaded", e);

View File

@ -19,20 +19,95 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import java.util.Locale; import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Strings;
/** /**
* Operation/api that uses a script as part of its execution. * Context of an operation that uses scripts as part of its execution.
* Note that the suggest api is considered part of search for simplicity, as well as the percolate api.
*/ */
public enum ScriptContext { public interface ScriptContext {
MAPPING,
UPDATE,
SEARCH,
AGGS;
@Override /**
public String toString() { * @return the name of the operation
return name().toLowerCase(Locale.ROOT); */
String getKey();
/**
* Standard operations that make use of scripts as part of their execution.
* Note that the suggest api is considered part of search for simplicity, as well as the percolate api.
*/
enum Standard implements ScriptContext {
AGGS("aggs"), MAPPING("mapping"), SEARCH("search"), UPDATE("update"),
/**
* Generic custom operation exposed via plugin
*
* @deprecated create a new {@link org.elasticsearch.script.ScriptContext.Plugin} instance instead
*/
@Deprecated
GENERIC_PLUGIN("plugin");
private final String key;
Standard(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
@Override
public String toString() {
return getKey();
}
}
/**
* Custom operation exposed via plugin, which makes use of scripts as part of its execution
*/
final class Plugin implements ScriptContext {
private final String pluginName;
private final String operation;
private final String key;
/**
* Creates a new custom scripts based operation exposed via plugin.
* The name of the plugin combined with the operation name can be used to enable/disable scripts via fine-grained settings.
*
* @param pluginName the name of the plugin
* @param operation the name of the operation
*/
public Plugin(String pluginName, String operation) {
if (Strings.hasLength(pluginName) == false) {
throw new ElasticsearchIllegalArgumentException("plugin name cannot be empty when registering a custom script context");
}
if (Strings.hasLength(operation) == false) {
throw new ElasticsearchIllegalArgumentException("operation name cannot be empty when registering a custom script context");
}
this.pluginName = pluginName;
this.operation = operation;
this.key = pluginName + "_" + operation;
}
public final String getPluginName() {
return pluginName;
}
public final String getOperation() {
return operation;
}
@Override
public final String getKey() {
return key;
}
@Override
public final String toString() {
return getKey();
}
} }
} }

View File

@ -0,0 +1,90 @@
/*
* 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;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import java.util.Map;
/**
* Registry for operations that use scripts as part of their execution. Can be standard operations of custom defined ones (via plugin).
* Allows plugins to register custom operations that they use scripts for, via {@link ScriptModule#registerScriptContext(org.elasticsearch.script.ScriptContext.Plugin)}.
* Scripts can be enabled/disabled via fine-grained settings for each single registered operation.
*/
public final class ScriptContextRegistry {
static final ImmutableSet<String> RESERVED_SCRIPT_CONTEXTS = reservedScriptContexts();
private final ImmutableMap<String, ScriptContext> scriptContexts;
ScriptContextRegistry(Iterable<ScriptContext.Plugin> customScriptContexts) {
Map<String, ScriptContext> scriptContexts = Maps.newHashMap();
for (ScriptContext.Standard scriptContext : ScriptContext.Standard.values()) {
scriptContexts.put(scriptContext.getKey(), scriptContext);
}
for (ScriptContext.Plugin customScriptContext : customScriptContexts) {
validateScriptContext(customScriptContext);
ScriptContext previousContext = scriptContexts.put(customScriptContext.getKey(), customScriptContext);
if (previousContext != null) {
throw new ElasticsearchIllegalArgumentException("script context [" + customScriptContext.getKey() + "] cannot be registered twice");
}
}
this.scriptContexts = ImmutableMap.copyOf(scriptContexts);
}
/**
* @return a list that contains all the supported {@link ScriptContext}s, both standard ones and registered via plugins
*/
ImmutableCollection<ScriptContext> scriptContexts() {
return scriptContexts.values();
}
/**
* @return <tt>true</tt> if the provided {@link ScriptContext} is supported, <tt>false</tt> otherwise
*/
boolean isSupportedContext(ScriptContext scriptContext) {
return scriptContexts.containsKey(scriptContext.getKey());
}
//script contexts can be used in fine-grained settings, we need to be careful with what we allow here
private void validateScriptContext(ScriptContext.Plugin scriptContext) {
if (RESERVED_SCRIPT_CONTEXTS.contains(scriptContext.getPluginName())) {
throw new ElasticsearchIllegalArgumentException("[" + scriptContext.getPluginName() + "] is a reserved name, it cannot be registered as a custom script context");
}
if (RESERVED_SCRIPT_CONTEXTS.contains(scriptContext.getOperation())) {
throw new ElasticsearchIllegalArgumentException("[" + scriptContext.getOperation() + "] is a reserved name, it cannot be registered as a custom script context");
}
}
private static ImmutableSet<String> reservedScriptContexts() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (ScriptService.ScriptType scriptType : ScriptService.ScriptType.values()) {
builder.add(scriptType.toString());
}
for (ScriptContext.Standard scriptContext : ScriptContext.Standard.values()) {
builder.add(scriptContext.getKey());
}
builder.add("script").add("engine");
return builder.build();
}
}

View File

@ -42,45 +42,45 @@ public class ScriptModes {
final ImmutableMap<String, ScriptMode> scriptModes; final ImmutableMap<String, ScriptMode> scriptModes;
ScriptModes(Map<String, ScriptEngineService> scriptEngines, Settings settings) { ScriptModes(Map<String, ScriptEngineService> scriptEngines, ScriptContextRegistry scriptContextRegistry, Settings settings) {
//filter out the native engine as we don't want to apply fine grained settings to it. //filter out the native engine as we don't want to apply fine grained settings to it.
//native scripts are always on as they are static by definition. //native scripts are always on as they are static by definition.
Map<String, ScriptEngineService> filteredEngines = Maps.newHashMap(scriptEngines); Map<String, ScriptEngineService> filteredEngines = Maps.newHashMap(scriptEngines);
filteredEngines.remove(NativeScriptEngineService.NAME); filteredEngines.remove(NativeScriptEngineService.NAME);
this.scriptModes = buildScriptModeSettingsMap(settings, filteredEngines); this.scriptModes = buildScriptModeSettingsMap(settings, filteredEngines, scriptContextRegistry);
} }
private ImmutableMap<String, ScriptMode> buildScriptModeSettingsMap(Settings settings, Map<String, ScriptEngineService> scriptEngines) { private static ImmutableMap<String, ScriptMode> buildScriptModeSettingsMap(Settings settings, Map<String, ScriptEngineService> scriptEngines, ScriptContextRegistry scriptContextRegistry) {
HashMap<String, ScriptMode> scriptModesMap = Maps.newHashMap(); HashMap<String, ScriptMode> scriptModesMap = Maps.newHashMap();
//file scripts are enabled by default, for any language //file scripts are enabled by default, for any language
addGlobalScriptTypeModes(scriptEngines.keySet(), ScriptType.FILE, ScriptMode.ON, scriptModesMap); addGlobalScriptTypeModes(scriptEngines.keySet(), scriptContextRegistry, ScriptType.FILE, ScriptMode.ON, scriptModesMap);
//indexed scripts are enabled by default only for sandboxed languages //indexed scripts are enabled by default only for sandboxed languages
addGlobalScriptTypeModes(scriptEngines.keySet(), ScriptType.INDEXED, ScriptMode.SANDBOX, scriptModesMap); addGlobalScriptTypeModes(scriptEngines.keySet(), scriptContextRegistry, ScriptType.INDEXED, ScriptMode.SANDBOX, scriptModesMap);
//dynamic scripts are enabled by default only for sandboxed languages //dynamic scripts are enabled by default only for sandboxed languages
addGlobalScriptTypeModes(scriptEngines.keySet(), ScriptType.INLINE, ScriptMode.SANDBOX, scriptModesMap); addGlobalScriptTypeModes(scriptEngines.keySet(), scriptContextRegistry, ScriptType.INLINE, ScriptMode.SANDBOX, scriptModesMap);
processSourceBasedGlobalSettings(settings, scriptEngines, scriptModesMap); processSourceBasedGlobalSettings(settings, scriptEngines, scriptContextRegistry, scriptModesMap);
processOperationBasedGlobalSettings(settings, scriptEngines, scriptModesMap); processOperationBasedGlobalSettings(settings, scriptEngines, scriptContextRegistry, scriptModesMap);
processEngineSpecificSettings(settings, scriptEngines, scriptModesMap); processEngineSpecificSettings(settings, scriptEngines, scriptContextRegistry, scriptModesMap);
return ImmutableMap.copyOf(scriptModesMap); return ImmutableMap.copyOf(scriptModesMap);
} }
private static void processSourceBasedGlobalSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, Map<String, ScriptMode> scriptModes) { private static void processSourceBasedGlobalSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, ScriptContextRegistry scriptContextRegistry, Map<String, ScriptMode> scriptModes) {
//read custom source based settings for all operations (e.g. script.indexed: on) //read custom source based settings for all operations (e.g. script.indexed: on)
for (ScriptType scriptType : ScriptType.values()) { for (ScriptType scriptType : ScriptType.values()) {
String scriptTypeSetting = settings.get(SCRIPT_SETTINGS_PREFIX + scriptType); String scriptTypeSetting = settings.get(SCRIPT_SETTINGS_PREFIX + scriptType);
if (Strings.hasLength(scriptTypeSetting)) { if (Strings.hasLength(scriptTypeSetting)) {
ScriptMode scriptTypeMode = ScriptMode.parse(scriptTypeSetting); ScriptMode scriptTypeMode = ScriptMode.parse(scriptTypeSetting);
addGlobalScriptTypeModes(scriptEngines.keySet(), scriptType, scriptTypeMode, scriptModes); addGlobalScriptTypeModes(scriptEngines.keySet(), scriptContextRegistry, scriptType, scriptTypeMode, scriptModes);
} }
} }
} }
private static void processOperationBasedGlobalSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, Map<String, ScriptMode> scriptModes) { private static void processOperationBasedGlobalSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, ScriptContextRegistry scriptContextRegistry, Map<String, ScriptMode> scriptModes) {
//read custom op based settings for all sources (e.g. script.aggs: off) //read custom op based settings for all sources (e.g. script.aggs: off)
//op based settings take precedence over source based settings, hence they get expanded later //op based settings take precedence over source based settings, hence they get expanded later
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
ScriptMode scriptMode = getScriptContextMode(settings, SCRIPT_SETTINGS_PREFIX, scriptContext); ScriptMode scriptMode = getScriptContextMode(settings, SCRIPT_SETTINGS_PREFIX, scriptContext);
if (scriptMode != null) { if (scriptMode != null) {
addGlobalScriptContextModes(scriptEngines.keySet(), scriptContext, scriptMode, scriptModes); addGlobalScriptContextModes(scriptEngines.keySet(), scriptContext, scriptMode, scriptModes);
@ -88,16 +88,15 @@ public class ScriptModes {
} }
} }
private static void processEngineSpecificSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, Map<String, ScriptMode> scriptModes) { private static void processEngineSpecificSettings(Settings settings, Map<String, ScriptEngineService> scriptEngines, ScriptContextRegistry scriptContextRegistry, Map<String, ScriptMode> scriptModes) {
Map<String, Settings> langGroupedSettings = settings.getGroups(ENGINE_SETTINGS_PREFIX, true); Map<String, Settings> langGroupedSettings = settings.getGroups(ENGINE_SETTINGS_PREFIX, true);
for (Map.Entry<String, Settings> langSettings : langGroupedSettings.entrySet()) { for (Map.Entry<String, Settings> langSettings : langGroupedSettings.entrySet()) {
//read engine specific settings that refer to a non existing script lang will be ignored //read engine specific settings that refer to a non existing script lang will be ignored
ScriptEngineService scriptEngineService = scriptEngines.get(langSettings.getKey()); ScriptEngineService scriptEngineService = scriptEngines.get(langSettings.getKey());
if (scriptEngineService != null) { if (scriptEngineService != null) {
String enginePrefix = ScriptModes.ENGINE_SETTINGS_PREFIX + "." + langSettings.getKey() + ".";
for (ScriptType scriptType : ScriptType.values()) { for (ScriptType scriptType : ScriptType.values()) {
String scriptTypePrefix = scriptType + "."; String scriptTypePrefix = scriptType + ".";
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
ScriptMode scriptMode = getScriptContextMode(langSettings.getValue(), scriptTypePrefix, scriptContext); ScriptMode scriptMode = getScriptContextMode(langSettings.getValue(), scriptTypePrefix, scriptContext);
if (scriptMode != null) { if (scriptMode != null) {
addScriptMode(scriptEngineService, scriptType, scriptContext, scriptMode, scriptModes); addScriptMode(scriptEngineService, scriptType, scriptContext, scriptMode, scriptModes);
@ -109,16 +108,16 @@ public class ScriptModes {
} }
private static ScriptMode getScriptContextMode(Settings settings, String prefix, ScriptContext scriptContext) { private static ScriptMode getScriptContextMode(Settings settings, String prefix, ScriptContext scriptContext) {
String settingValue = settings.get(prefix + scriptContext); String settingValue = settings.get(prefix + scriptContext.getKey());
if (Strings.hasLength(settingValue)) { if (Strings.hasLength(settingValue)) {
return ScriptMode.parse(settingValue); return ScriptMode.parse(settingValue);
} }
return null; return null;
} }
private static void addGlobalScriptTypeModes(Set<String> langs, ScriptType scriptType, ScriptMode scriptMode, Map<String, ScriptMode> scriptModes) { private static void addGlobalScriptTypeModes(Set<String> langs, ScriptContextRegistry scriptContextRegistry, ScriptType scriptType, ScriptMode scriptMode, Map<String, ScriptMode> scriptModes) {
for (String lang : langs) { for (String lang : langs) {
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
addScriptMode(lang, scriptType, scriptContext, scriptMode, scriptModes); addScriptMode(lang, scriptType, scriptContext, scriptMode, scriptModes);
} }
} }
@ -141,7 +140,7 @@ public class ScriptModes {
} }
private static void addScriptMode(String lang, ScriptType scriptType, ScriptContext scriptContext, ScriptMode scriptMode, Map<String, ScriptMode> scriptModes) { private static void addScriptMode(String lang, ScriptType scriptType, ScriptContext scriptContext, ScriptMode scriptMode, Map<String, ScriptMode> scriptModes) {
scriptModes.put(ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext, scriptMode); scriptModes.put(ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext.getKey(), scriptMode);
} }
/** /**
@ -150,7 +149,7 @@ public class ScriptModes {
* *
* @param lang the language that the script is written in * @param lang the language that the script is written in
* @param scriptType the type of the script * @param scriptType the type of the script
* @param scriptContext the api that requires the execution of the script * @param scriptContext the operation that requires the execution of the script
* @return whether scripts are on, off, or enabled only for sandboxed languages * @return whether scripts are on, off, or enabled only for sandboxed languages
*/ */
public ScriptMode getScriptMode(String lang, ScriptType scriptType, ScriptContext scriptContext) { public ScriptMode getScriptMode(String lang, ScriptType scriptType, ScriptContext scriptContext) {
@ -158,9 +157,9 @@ public class ScriptModes {
if (NativeScriptEngineService.NAME.equals(lang)) { if (NativeScriptEngineService.NAME.equals(lang)) {
return ScriptMode.ON; return ScriptMode.ON;
} }
ScriptMode scriptMode = scriptModes.get(ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext); ScriptMode scriptMode = scriptModes.get(ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext.getKey());
if (scriptMode == null) { if (scriptMode == null) {
throw new ElasticsearchIllegalArgumentException("script mode not found for lang [" + lang + "], script_type [" + scriptType + "], operation [" + scriptContext + "]"); throw new ElasticsearchIllegalArgumentException("script mode not found for lang [" + lang + "], script_type [" + scriptType + "], operation [" + scriptContext.getKey() + "]");
} }
return scriptMode; return scriptMode;
} }

View File

@ -46,6 +46,8 @@ public class ScriptModule extends AbstractModule {
private final Map<String, Class<? extends NativeScriptFactory>> scripts = Maps.newHashMap(); private final Map<String, Class<? extends NativeScriptFactory>> scripts = Maps.newHashMap();
private final List<ScriptContext.Plugin> customScriptContexts = Lists.newArrayList();
public ScriptModule(Settings settings) { public ScriptModule(Settings settings) {
this.settings = settings; this.settings = settings;
} }
@ -58,6 +60,14 @@ public class ScriptModule extends AbstractModule {
scripts.put(name, script); scripts.put(name, script);
} }
/**
* Registers a custom script context that can be used by plugins to categorize the different operations that they use scripts for.
* Fine-grained settings allow to enable/disable scripts per context.
*/
public void registerScriptContext(ScriptContext.Plugin scriptContext) {
customScriptContexts.add(scriptContext);
}
@Override @Override
protected void configure() { protected void configure() {
MapBinder<String, NativeScriptFactory> scriptsBinder MapBinder<String, NativeScriptFactory> scriptsBinder
@ -105,6 +115,7 @@ public class ScriptModule extends AbstractModule {
multibinder.addBinding().to(scriptEngine).asEagerSingleton(); multibinder.addBinding().to(scriptEngine).asEagerSingleton();
} }
bind(ScriptContextRegistry.class).toInstance(new ScriptContextRegistry(customScriptContexts));
bind(ScriptService.class).asEagerSingleton(); bind(ScriptService.class).asEagerSingleton();
} }
} }

View File

@ -103,6 +103,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
private final FileWatcher fileWatcher; private final FileWatcher fileWatcher;
private final ScriptModes scriptModes; private final ScriptModes scriptModes;
private final ScriptContextRegistry scriptContextRegistry;
private Client client = null; private Client client = null;
@ -113,7 +114,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
@Inject @Inject
public ScriptService(Settings settings, Environment env, Set<ScriptEngineService> scriptEngines, public ScriptService(Settings settings, Environment env, Set<ScriptEngineService> scriptEngines,
ResourceWatcherService resourceWatcherService, NodeSettingsService nodeSettingsService) throws IOException { ResourceWatcherService resourceWatcherService, NodeSettingsService nodeSettingsService, ScriptContextRegistry scriptContextRegistry) throws IOException {
super(settings); super(settings);
if (Strings.hasLength(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING))) { if (Strings.hasLength(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING))) {
@ -122,6 +123,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
} }
this.scriptEngines = scriptEngines; this.scriptEngines = scriptEngines;
this.scriptContextRegistry = scriptContextRegistry;
int cacheMaxSize = settings.getAsInt(SCRIPT_CACHE_SIZE_SETTING, 100); int cacheMaxSize = settings.getAsInt(SCRIPT_CACHE_SIZE_SETTING, 100);
TimeValue cacheExpire = settings.getAsTime(SCRIPT_CACHE_EXPIRE_SETTING, null); TimeValue cacheExpire = settings.getAsTime(SCRIPT_CACHE_EXPIRE_SETTING, null);
logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire); logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire);
@ -150,7 +152,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
this.scriptEnginesByLang = enginesByLangBuilder.build(); this.scriptEnginesByLang = enginesByLangBuilder.build();
this.scriptEnginesByExt = enginesByExtBuilder.build(); this.scriptEnginesByExt = enginesByExtBuilder.build();
this.scriptModes = new ScriptModes(this.scriptEnginesByLang, settings); this.scriptModes = new ScriptModes(this.scriptEnginesByLang, scriptContextRegistry, settings);
// add file watcher for static scripts // add file watcher for static scripts
scriptsDirectory = env.configFile().resolve("scripts"); scriptsDirectory = env.configFile().resolve("scripts");
@ -212,6 +214,18 @@ public class ScriptService extends AbstractComponent implements Closeable {
return scriptEngineService; return scriptEngineService;
} }
/**
* Checks if a script can be executed and compiles it if needed, or returns the previously compiled and cached script.
* Doesn't require to specify a script context in order to maintain backwards compatibility, internally uses
* the {@link org.elasticsearch.script.ScriptContext.Standard#GENERIC_PLUGIN} default context, assuming that it can only be called from plugins.
*
* @deprecated use the method variant that accepts the {@link ScriptContext} argument too: {@link #compile(String, String, ScriptType, ScriptContext)}
*/
@Deprecated
public CompiledScript compile(String lang, String script, ScriptType scriptType) {
return compile(lang, script, scriptType, ScriptContext.Standard.GENERIC_PLUGIN);
}
/** /**
* Checks if a script can be executed and compiles it if needed, or returns the previously compiled and cached script. * Checks if a script can be executed and compiles it if needed, or returns the previously compiled and cached script.
*/ */
@ -226,7 +240,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
ScriptEngineService scriptEngineService = getScriptEngineServiceForLang(lang); ScriptEngineService scriptEngineService = getScriptEngineServiceForLang(lang);
if (canExecuteScript(lang, scriptEngineService, scriptType, scriptContext) == false) { if (canExecuteScript(lang, scriptEngineService, scriptType, scriptContext) == false) {
throw new ScriptException("scripts of type [" + scriptType + "], operation [" + scriptContext + "] and lang [" + lang + "] are disabled"); throw new ScriptException("scripts of type [" + scriptType + "], operation [" + scriptContext.getKey() + "] and lang [" + lang + "] are disabled");
} }
return compileInternal(lang, script, scriptType); return compileInternal(lang, script, scriptType);
} }
@ -380,6 +394,18 @@ public class ScriptService extends AbstractComponent implements Closeable {
} }
} }
/**
* Compiles (or retrieves from cache) and executes the provided script.
* Doesn't require to specify a script context in order to maintain backwards compatibility, internally uses
* the {@link org.elasticsearch.script.ScriptContext.Standard#GENERIC_PLUGIN} default context, assuming that it can only be called from plugins.
*
* @deprecated use the method variant that accepts the {@link ScriptContext} argument too: {@link #executable(String, String, ScriptType, ScriptContext, Map)}
*/
@Deprecated
public ExecutableScript executable(String lang, String script, ScriptType scriptType, Map<String, Object> vars) {
return executable(lang, script, scriptType, ScriptContext.Standard.GENERIC_PLUGIN, vars);
}
/** /**
* Compiles (or retrieves from cache) and executes the provided script * Compiles (or retrieves from cache) and executes the provided script
*/ */
@ -394,6 +420,18 @@ public class ScriptService extends AbstractComponent implements Closeable {
return getScriptEngineServiceForLang(compiledScript.lang()).executable(compiledScript.compiled(), vars); return getScriptEngineServiceForLang(compiledScript.lang()).executable(compiledScript.compiled(), vars);
} }
/**
* Compiles (or retrieves from cache) and executes the provided search script
* Doesn't require to specify a script context in order to maintain backwards compatibility, internally uses
* the {@link org.elasticsearch.script.ScriptContext.Standard#GENERIC_PLUGIN} default context, assuming that it can only be called from plugins.
*
* @deprecated use the method variant that accepts the {@link ScriptContext} argument too: {@link #search(SearchLookup, String, String, ScriptType, ScriptContext, Map)}
*/
@Deprecated
public SearchScript search(SearchLookup lookup, String lang, String script, ScriptType scriptType, @Nullable Map<String, Object> vars) {
return search(lookup, lang, script, scriptType, ScriptContext.Standard.GENERIC_PLUGIN, vars);
}
/** /**
* Compiles (or retrieves from cache) and executes the provided search script * Compiles (or retrieves from cache) and executes the provided search script
*/ */
@ -403,7 +441,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
} }
private boolean isAnyScriptContextEnabled(String lang, ScriptEngineService scriptEngineService, ScriptType scriptType) { private boolean isAnyScriptContextEnabled(String lang, ScriptEngineService scriptEngineService, ScriptType scriptType) {
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
if (canExecuteScript(lang, scriptEngineService, scriptType, scriptContext)) { if (canExecuteScript(lang, scriptEngineService, scriptType, scriptContext)) {
return true; return true;
} }
@ -413,6 +451,9 @@ public class ScriptService extends AbstractComponent implements Closeable {
private boolean canExecuteScript(String lang, ScriptEngineService scriptEngineService, ScriptType scriptType, ScriptContext scriptContext) { private boolean canExecuteScript(String lang, ScriptEngineService scriptEngineService, ScriptType scriptType, ScriptContext scriptContext) {
assert lang != null; assert lang != null;
if (scriptContextRegistry.isSupportedContext(scriptContext) == false) {
throw new ElasticsearchIllegalArgumentException("script context [" + scriptContext.getKey() + "] not supported");
}
ScriptMode mode = scriptModes.getScriptMode(lang, scriptType, scriptContext); ScriptMode mode = scriptModes.getScriptMode(lang, scriptType, scriptContext);
switch (mode) { switch (mode) {
case ON: case ON:

View File

@ -71,8 +71,9 @@ import org.elasticsearch.indices.IndicesWarmer.TerminationHandle;
import org.elasticsearch.indices.IndicesWarmer.WarmerContext; import org.elasticsearch.indices.IndicesWarmer.WarmerContext;
import org.elasticsearch.indices.cache.query.IndicesQueryCache; import org.elasticsearch.indices.cache.query.IndicesQueryCache;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContextRegistry;
import org.elasticsearch.script.mustache.MustacheScriptEngineService; import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.search.dfs.CachedDfSource; import org.elasticsearch.search.dfs.CachedDfSource;
import org.elasticsearch.search.dfs.DfsPhase; import org.elasticsearch.search.dfs.DfsPhase;
@ -652,7 +653,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
final ExecutableScript executable; final ExecutableScript executable;
if (hasLength(request.templateName())) { if (hasLength(request.templateName())) {
executable = this.scriptService.executable(MustacheScriptEngineService.NAME, request.templateName(), request.templateType(), ScriptContext.SEARCH, request.templateParams()); executable = this.scriptService.executable(MustacheScriptEngineService.NAME, request.templateName(), request.templateType(), ScriptContext.Standard.SEARCH, request.templateParams());
} else { } else {
if (!hasLength(request.templateSource())) { if (!hasLength(request.templateSource())) {
return; return;
@ -692,7 +693,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
if (!hasLength(templateContext.template())) { if (!hasLength(templateContext.template())) {
throw new ElasticsearchParseException("Template must have [template] field configured"); throw new ElasticsearchParseException("Template must have [template] field configured");
} }
executable = this.scriptService.executable(MustacheScriptEngineService.NAME, templateContext.template(), templateContext.scriptType(), ScriptContext.SEARCH, templateContext.params()); executable = this.scriptService.executable(MustacheScriptEngineService.NAME, templateContext.template(), templateContext.scriptType(), ScriptContext.Standard.SEARCH, templateContext.params());
} }
BytesReference processedQuery = (BytesReference) executable.run(); BytesReference processedQuery = (BytesReference) executable.run();

View File

@ -30,10 +30,7 @@ import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParsingException; import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.*;
import org.elasticsearch.script.ScriptParameterParser;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregation;
import java.io.IOException; import java.io.IOException;
@ -86,7 +83,7 @@ public class ScriptHeuristic extends SignificanceHeuristic {
} }
public void initialize(InternalAggregation.ReduceContext context) { public void initialize(InternalAggregation.ReduceContext context) {
script = context.scriptService().executable(scriptLang, scriptString, scriptType, ScriptContext.AGGS, params); script = context.scriptService().executable(scriptLang, scriptString, scriptType, ScriptContext.Standard.AGGS, params);
script.setNextVar("_subset_freq", subsetDfHolder); script.setNextVar("_subset_freq", subsetDfHolder);
script.setNextVar("_subset_size", subsetSizeHolder); script.setNextVar("_subset_size", subsetSizeHolder);
script.setNextVar("_superset_freq", supersetDfHolder); script.setNextVar("_superset_freq", supersetDfHolder);
@ -171,7 +168,7 @@ public class ScriptHeuristic extends SignificanceHeuristic {
} }
ExecutableScript searchScript; ExecutableScript searchScript;
try { try {
searchScript = scriptService.executable(scriptLang, script, scriptType, ScriptContext.AGGS, params); searchScript = scriptService.executable(scriptLang, script, scriptType, ScriptContext.Standard.AGGS, params);
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchParseException("The script [" + script + "] could not be loaded", e); throw new ElasticsearchParseException("The script [" + script + "] could not be loaded", e);
} }

View File

@ -24,8 +24,8 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.search.aggregations.AggregationStreams; import org.elasticsearch.search.aggregations.AggregationStreams;
import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregation;
import org.elasticsearch.search.aggregations.metrics.InternalMetricsAggregation; import org.elasticsearch.search.aggregations.metrics.InternalMetricsAggregation;
@ -99,7 +99,7 @@ public class InternalScriptedMetric extends InternalMetricsAggregation implement
} }
params.put("_aggs", aggregationObjects); params.put("_aggs", aggregationObjects);
ExecutableScript script = reduceContext.scriptService().executable(firstAggregation.scriptLang, firstAggregation.reduceScript, ExecutableScript script = reduceContext.scriptService().executable(firstAggregation.scriptLang, firstAggregation.reduceScript,
firstAggregation.scriptType, ScriptContext.AGGS, params); firstAggregation.scriptType, ScriptContext.Standard.AGGS, params);
aggregation = script.run(); aggregation = script.run();
} else { } else {
aggregation = aggregationObjects; aggregation = aggregationObjects;

View File

@ -23,9 +23,8 @@ import org.apache.lucene.index.LeafReaderContext;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.*;
import org.elasticsearch.script.ScriptService.ScriptType; import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.AggregatorFactory;
@ -75,11 +74,11 @@ public class ScriptedMetricAggregator extends MetricsAggregator {
} }
ScriptService scriptService = context.searchContext().scriptService(); ScriptService scriptService = context.searchContext().scriptService();
if (initScript != null) { if (initScript != null) {
scriptService.executable(scriptLang, initScript, initScriptType, ScriptContext.AGGS, this.params).run(); scriptService.executable(scriptLang, initScript, initScriptType, ScriptContext.Standard.AGGS, this.params).run();
} }
this.mapScript = scriptService.search(context.searchContext().lookup(), scriptLang, mapScript, mapScriptType, ScriptContext.AGGS, this.params); this.mapScript = scriptService.search(context.searchContext().lookup(), scriptLang, mapScript, mapScriptType, ScriptContext.Standard.AGGS, this.params);
if (combineScript != null) { if (combineScript != null) {
this.combineScript = scriptService.executable(scriptLang, combineScript, combineScriptType, ScriptContext.AGGS, this.params); this.combineScript = scriptService.executable(scriptLang, combineScript, combineScriptType, ScriptContext.Standard.AGGS, this.params);
} else { } else {
this.combineScript = null; this.combineScript = null;
} }

View File

@ -29,11 +29,8 @@ import org.elasticsearch.index.mapper.core.BooleanFieldMapper;
import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.mapper.core.NumberFieldMapper; import org.elasticsearch.index.mapper.core.NumberFieldMapper;
import org.elasticsearch.index.mapper.ip.IpFieldMapper; import org.elasticsearch.index.mapper.ip.IpFieldMapper;
import org.elasticsearch.script.ScriptParameterParser; import org.elasticsearch.script.*;
import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue; import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregation;
import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.aggregations.support.format.ValueFormat;
@ -189,7 +186,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
} }
private SearchScript createScript() { private SearchScript createScript() {
return input.script == null ? null : context.scriptService().search(context.lookup(), input.lang, input.script, input.scriptType, ScriptContext.AGGS, input.params); return input.script == null ? null : context.scriptService().search(context.lookup(), input.lang, input.script, input.scriptType, ScriptContext.Standard.AGGS, input.params);
} }
private static ValueFormat resolveFormat(@Nullable String format, @Nullable ValueType valueType) { private static ValueFormat resolveFormat(@Nullable String format, @Nullable ValueType valueType) {

View File

@ -20,11 +20,8 @@
package org.elasticsearch.search.fetch.script; package org.elasticsearch.search.fetch.script;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ScriptParameterParser; import org.elasticsearch.script.*;
import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue; import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
@ -79,7 +76,7 @@ public class ScriptFieldsParseElement implements SearchParseElement {
script = scriptValue.script(); script = scriptValue.script();
scriptType = scriptValue.scriptType(); scriptType = scriptValue.scriptType();
} }
SearchScript searchScript = context.scriptService().search(context.lookup(), scriptParameterParser.lang(), script, scriptType, ScriptContext.SEARCH, params); SearchScript searchScript = context.scriptService().search(context.lookup(), scriptParameterParser.lang(), script, scriptType, ScriptContext.Standard.SEARCH, params);
context.scriptFields().add(new ScriptFieldsContext.ScriptField(fieldName, searchScript, ignoreException)); context.scriptFields().add(new ScriptFieldsContext.ScriptField(fieldName, searchScript, ignoreException));
} }
} }

View File

@ -118,7 +118,7 @@ public class ScriptSortParser implements SortParser {
if (type == null) { if (type == null) {
throw new SearchParseException(context, "_script sorting requires setting the type of the script"); throw new SearchParseException(context, "_script sorting requires setting the type of the script");
} }
final SearchScript searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptType, ScriptContext.SEARCH, params); final SearchScript searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptType, ScriptContext.Standard.SEARCH, params);
if (STRING_SORT_TYPE.equals(type) && (sortMode == MultiValueMode.SUM || sortMode == MultiValueMode.AVG)) { if (STRING_SORT_TYPE.equals(type) && (sortMode == MultiValueMode.SUM || sortMode == MultiValueMode.AVG)) {
throw new SearchParseException(context, "type [string] doesn't support mode [" + sortMode + "]"); throw new SearchParseException(context, "type [string] doesn't support mode [" + sortMode + "]");

View File

@ -30,8 +30,8 @@ import org.elasticsearch.index.analysis.ShingleTokenFilterFactory;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.script.CompiledScript; import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.mustache.MustacheScriptEngineService; import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.search.suggest.SuggestContextParser; import org.elasticsearch.search.suggest.SuggestContextParser;
import org.elasticsearch.search.suggest.SuggestUtils; import org.elasticsearch.search.suggest.SuggestUtils;
@ -153,7 +153,7 @@ public final class PhraseSuggestParser implements SuggestContextParser {
if (suggestion.getCollateQueryScript() != null) { if (suggestion.getCollateQueryScript() != null) {
throw new ElasticsearchIllegalArgumentException("suggester[phrase][collate] query already set, doesn't support additional [" + fieldName + "]"); throw new ElasticsearchIllegalArgumentException("suggester[phrase][collate] query already set, doesn't support additional [" + fieldName + "]");
} }
CompiledScript compiledScript = suggester.scriptService().compile(MustacheScriptEngineService.NAME, templateNameOrTemplateContent, ScriptType.INLINE, ScriptContext.SEARCH); CompiledScript compiledScript = suggester.scriptService().compile(MustacheScriptEngineService.NAME, templateNameOrTemplateContent, ScriptType.INLINE, ScriptContext.Standard.SEARCH);
if ("query".equals(fieldName)) { if ("query".equals(fieldName)) {
suggestion.setCollateQueryScript(compiledScript); suggestion.setCollateQueryScript(compiledScript);
} else { } else {

View File

@ -0,0 +1,133 @@
/*
* 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;
import com.google.common.collect.ImmutableSet;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.AbstractPlugin;
import org.elasticsearch.script.expression.ExpressionScriptEngineService;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
public class CustomScriptContextTests extends ElasticsearchIntegrationTest {
private static final ImmutableSet<String> LANG_SET = ImmutableSet.of(GroovyScriptEngineService.NAME, MustacheScriptEngineService.NAME, ExpressionScriptEngineService.NAME);
private static final String PLUGIN_NAME = "testplugin";
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.builder().put(super.nodeSettings(nodeOrdinal))
.put("plugin.types", CustomScriptContextPlugin.class.getName())
.put("script." + PLUGIN_NAME + "_custom_globally_disabled_op", "off")
.put("script.engine.expression.inline." + PLUGIN_NAME + "_custom_exp_disabled_op", "off")
.build();
}
@Test
public void testCustomScriptContextsSettings() {
ScriptService scriptService = internalCluster().getInstance(ScriptService.class);
for (String lang : LANG_SET) {
for (ScriptService.ScriptType scriptType : ScriptService.ScriptType.values()) {
try {
scriptService.compile(lang, "test", scriptType, new ScriptContext.Plugin(PLUGIN_NAME, "custom_globally_disabled_op"));
fail("script compilation should have been rejected");
} catch(ScriptException e) {
assertThat(e.getMessage(), containsString("scripts of type [" + scriptType + "], operation [" + PLUGIN_NAME + "_custom_globally_disabled_op] and lang [" + lang + "] are disabled"));
}
}
}
try {
scriptService.compile("expression", "1", ScriptService.ScriptType.INLINE, new ScriptContext.Plugin(PLUGIN_NAME, "custom_exp_disabled_op"));
fail("script compilation should have been rejected");
} catch(ScriptException e) {
assertThat(e.getMessage(), containsString("scripts of type [inline], operation [" + PLUGIN_NAME + "_custom_exp_disabled_op] and lang [expression] are disabled"));
}
CompiledScript compiledScript = scriptService.compile("expression", "1", ScriptService.ScriptType.INLINE, randomFrom(ScriptContext.Standard.values()));
assertThat(compiledScript, notNullValue());
compiledScript = scriptService.compile("mustache", "1", ScriptService.ScriptType.INLINE, new ScriptContext.Plugin(PLUGIN_NAME, "custom_exp_disabled_op"));
assertThat(compiledScript, notNullValue());
for (String lang : LANG_SET) {
compiledScript = scriptService.compile(lang, "1", ScriptService.ScriptType.INLINE, new ScriptContext.Plugin(PLUGIN_NAME, "custom_op"));
assertThat(compiledScript, notNullValue());
}
}
@Test
public void testCompileNonRegisteredPluginContext() {
ScriptService scriptService = internalCluster().getInstance(ScriptService.class);
try {
scriptService.compile(randomFrom(LANG_SET.toArray(new String[LANG_SET.size()])), "test", randomFrom(ScriptService.ScriptType.values()), new ScriptContext.Plugin("test", "unknown"));
fail("script compilation should have been rejected");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), containsString("script context [test_unknown] not supported"));
}
}
@Test
public void testCompileNonRegisteredScriptContext() {
ScriptService scriptService = internalCluster().getInstance(ScriptService.class);
try {
scriptService.compile(randomFrom(LANG_SET.toArray(new String[LANG_SET.size()])), "test", randomFrom(ScriptService.ScriptType.values()), new ScriptContext() {
@Override
public String getKey() {
return "test";
}
});
fail("script compilation should have been rejected");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), containsString("script context [test] not supported"));
}
}
public static class CustomScriptContextPlugin extends AbstractPlugin {
@Override
public String name() {
return "custom_script_context_plugin";
}
@Override
public String description() {
return "Custom script context plugin";
}
@Override
public void processModule(Module module) {
if (module instanceof ScriptModule) {
ScriptModule scriptModule = (ScriptModule) module;
scriptModule.registerScriptContext(new ScriptContext.Plugin(PLUGIN_NAME, "custom_op"));
scriptModule.registerScriptContext(new ScriptContext.Plugin(PLUGIN_NAME, "custom_exp_disabled_op"));
scriptModule.registerScriptContext(new ScriptContext.Plugin(PLUGIN_NAME, "custom_globally_disabled_op"));
}
}
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Injector; import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.ModulesBuilder; import org.elasticsearch.common.inject.ModulesBuilder;
@ -58,7 +59,7 @@ public class NativeScriptTests extends ElasticsearchTestCase {
ScriptService scriptService = injector.getInstance(ScriptService.class); ScriptService scriptService = injector.getInstance(ScriptService.class);
ExecutableScript executable = scriptService.executable(NativeScriptEngineService.NAME, "my", ScriptType.INLINE, ScriptContext.SEARCH, null); ExecutableScript executable = scriptService.executable(NativeScriptEngineService.NAME, "my", ScriptType.INLINE, ScriptContext.Standard.SEARCH, null);
assertThat(executable.run().toString(), equalTo("test")); assertThat(executable.run().toString(), equalTo("test"));
terminate(injector.getInstance(ThreadPool.class)); terminate(injector.getInstance(ThreadPool.class));
} }
@ -70,7 +71,7 @@ public class NativeScriptTests extends ElasticsearchTestCase {
ScriptType scriptType = randomFrom(ScriptType.values()); ScriptType scriptType = randomFrom(ScriptType.values());
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptType, randomFrom(ScriptMode.values())); builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptType, randomFrom(ScriptMode.values()));
} else { } else {
ScriptContext scriptContext = randomFrom(ScriptContext.values()); String scriptContext = randomFrom(ScriptContext.Standard.values()).getKey();
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptContext, randomFrom(ScriptMode.values())); builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptContext, randomFrom(ScriptMode.values()));
} }
Settings settings = builder.build(); Settings settings = builder.build();
@ -79,9 +80,10 @@ public class NativeScriptTests extends ElasticsearchTestCase {
Map<String, NativeScriptFactory> nativeScriptFactoryMap = new HashMap<>(); Map<String, NativeScriptFactory> nativeScriptFactoryMap = new HashMap<>();
nativeScriptFactoryMap.put("my", new MyNativeScriptFactory()); nativeScriptFactoryMap.put("my", new MyNativeScriptFactory());
Set<ScriptEngineService> scriptEngineServices = ImmutableSet.<ScriptEngineService>of(new NativeScriptEngineService(settings, nativeScriptFactoryMap)); Set<ScriptEngineService> scriptEngineServices = ImmutableSet.<ScriptEngineService>of(new NativeScriptEngineService(settings, nativeScriptFactoryMap));
ScriptService scriptService = new ScriptService(settings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(settings)); ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Lists.<ScriptContext.Plugin>newArrayList());
ScriptService scriptService = new ScriptService(settings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(settings), scriptContextRegistry);
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
assertThat(scriptService.compile(NativeScriptEngineService.NAME, "my", ScriptType.INLINE, scriptContext), notNullValue()); assertThat(scriptService.compile(NativeScriptEngineService.NAME, "my", ScriptType.INLINE, scriptContext), notNullValue());
} }
} }

View File

@ -0,0 +1,83 @@
/*
* 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;
import com.google.common.collect.Lists;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.io.IOException;
public class ScriptContextRegistryTests extends ElasticsearchTestCase {
@Test
public void testValidateCustomScriptContextsOperation() throws IOException {
for (final String rejectedContext : ScriptContextRegistry.RESERVED_SCRIPT_CONTEXTS) {
try {
//try to register a prohibited script context
new ScriptContextRegistry(Lists.newArrayList(new ScriptContext.Plugin("test", rejectedContext)));
fail("ScriptContextRegistry initialization should have failed");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), Matchers.containsString("[" + rejectedContext + "] is a reserved name, it cannot be registered as a custom script context"));
}
}
}
@Test
public void testValidateCustomScriptContextsPluginName() throws IOException {
for (final String rejectedContext : ScriptContextRegistry.RESERVED_SCRIPT_CONTEXTS) {
try {
//try to register a prohibited script context
new ScriptContextRegistry(Lists.newArrayList(new ScriptContext.Plugin(rejectedContext, "test")));
fail("ScriptContextRegistry initialization should have failed");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), Matchers.containsString("[" + rejectedContext + "] is a reserved name, it cannot be registered as a custom script context"));
}
}
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testValidateCustomScriptContextsEmptyPluginName() throws IOException {
new ScriptContext.Plugin(randomBoolean() ? null : "", "test");
}
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testValidateCustomScriptContextsEmptyOperation() throws IOException {
new ScriptContext.Plugin("test", randomBoolean() ? null : "");
}
@Test
public void testDuplicatedPluginScriptContexts() throws IOException {
try {
//try to register a prohibited script context
new ScriptContextRegistry(Lists.newArrayList(new ScriptContext.Plugin("testplugin", "test"), new ScriptContext.Plugin("testplugin", "test")));
fail("ScriptContextRegistry initialization should have failed");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), Matchers.containsString("script context [testplugin_test] cannot be registered twice"));
}
}
@Test
public void testNonDuplicatedPluginScriptContexts() throws IOException {
new ScriptContextRegistry(Lists.newArrayList(new ScriptContext.Plugin("testplugin1", "test"), new ScriptContext.Plugin("testplugin2", "test")));
}
}

View File

@ -19,8 +19,7 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.*;
import com.google.common.collect.ImmutableSet;
import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
@ -34,10 +33,7 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.Collections; import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.notNullValue;
@ -49,6 +45,8 @@ public class ScriptModesTests extends ElasticsearchTestCase {
static final String[] ENABLE_VALUES = new String[]{"on", "true", "yes", "1"}; static final String[] ENABLE_VALUES = new String[]{"on", "true", "yes", "1"};
static final String[] DISABLE_VALUES = new String[]{"off", "false", "no", "0"}; static final String[] DISABLE_VALUES = new String[]{"off", "false", "no", "0"};
ScriptContextRegistry scriptContextRegistry;
private ScriptContext[] scriptContexts;
private Map<String, ScriptEngineService> scriptEngines; private Map<String, ScriptEngineService> scriptEngines;
private ScriptModes scriptModes; private ScriptModes scriptModes;
private Set<String> checkedSettings; private Set<String> checkedSettings;
@ -57,6 +55,18 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Before @Before
public void setupScriptEngines() { public void setupScriptEngines() {
//randomly register custom script contexts
int randomInt = randomIntBetween(0, 3);
//prevent duplicates using map
Map<String, ScriptContext.Plugin> contexts = Maps.newHashMap();
for (int i = 0; i < randomInt; i++) {
String plugin = randomAsciiOfLength(randomIntBetween(1, 10));
String operation = randomAsciiOfLength(randomIntBetween(1, 30));
String context = plugin + "-" + operation;
contexts.put(context, new ScriptContext.Plugin(plugin, operation));
}
scriptContextRegistry = new ScriptContextRegistry(contexts.values());
scriptContexts = scriptContextRegistry.scriptContexts().toArray(new ScriptContext[scriptContextRegistry.scriptContexts().size()]);
scriptEngines = buildScriptEnginesByLangMap(ImmutableSet.of( scriptEngines = buildScriptEnginesByLangMap(ImmutableSet.of(
new GroovyScriptEngineService(ImmutableSettings.EMPTY), new GroovyScriptEngineService(ImmutableSettings.EMPTY),
new MustacheScriptEngineService(ImmutableSettings.EMPTY), new MustacheScriptEngineService(ImmutableSettings.EMPTY),
@ -72,7 +82,7 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@After @After
public void assertNativeScriptsAreAlwaysAllowed() { public void assertNativeScriptsAreAlwaysAllowed() {
if (assertScriptModesNonNull) { if (assertScriptModesNonNull) {
assertThat(scriptModes.getScriptMode(NativeScriptEngineService.NAME, randomFrom(ScriptType.values()), randomFrom(ScriptContext.values())), equalTo(ScriptMode.ON)); assertThat(scriptModes.getScriptMode(NativeScriptEngineService.NAME, randomFrom(ScriptType.values()), randomFrom(scriptContexts)), equalTo(ScriptMode.ON));
} }
} }
@ -81,7 +91,7 @@ public class ScriptModesTests extends ElasticsearchTestCase {
if (assertScriptModesNonNull) { if (assertScriptModesNonNull) {
assertThat(scriptModes, notNullValue()); assertThat(scriptModes, notNullValue());
//4 is the number of engines (native excluded), custom is counted twice though as it's associated with two different names //4 is the number of engines (native excluded), custom is counted twice though as it's associated with two different names
int numberOfSettings = 5 * ScriptType.values().length * ScriptContext.values().length; int numberOfSettings = 5 * ScriptType.values().length * scriptContextRegistry.scriptContexts().size();
assertThat(scriptModes.scriptModes.size(), equalTo(numberOfSettings)); assertThat(scriptModes.scriptModes.size(), equalTo(numberOfSettings));
if (assertAllSettingsWereChecked) { if (assertAllSettingsWereChecked) {
assertThat(checkedSettings.size(), equalTo(numberOfSettings)); assertThat(checkedSettings.size(), equalTo(numberOfSettings));
@ -91,7 +101,7 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test @Test
public void testDefaultSettings() { public void testDefaultSettings() {
this.scriptModes = new ScriptModes(scriptEngines, ImmutableSettings.EMPTY); this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, ImmutableSettings.EMPTY);
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE); assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE); assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
} }
@ -99,239 +109,95 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test(expected = ElasticsearchIllegalArgumentException.class) @Test(expected = ElasticsearchIllegalArgumentException.class)
public void testMissingSetting() { public void testMissingSetting() {
assertAllSettingsWereChecked = false; assertAllSettingsWereChecked = false;
this.scriptModes = new ScriptModes(scriptEngines, ImmutableSettings.EMPTY); this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, ImmutableSettings.EMPTY);
scriptModes.getScriptMode("non_existing", randomFrom(ScriptType.values()), randomFrom(ScriptContext.values())); scriptModes.getScriptMode("non_existing", randomFrom(ScriptType.values()), randomFrom(scriptContexts));
} }
@Test @Test
public void testEnableInlineGenericSettings() { public void testScriptTypeGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(ENABLE_VALUES)); int randomInt = randomIntBetween(1, ScriptType.values().length - 1);
this.scriptModes = new ScriptModes(scriptEngines, builder.build()); Set<ScriptType> randomScriptTypesSet = Sets.newHashSet();
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE, ScriptType.INLINE); ScriptMode[] randomScriptModes = new ScriptMode[randomInt];
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED); for (int i = 0; i < randomInt; i++) {
boolean added = false;
while (added == false) {
added = randomScriptTypesSet.add(randomFrom(ScriptType.values()));
}
randomScriptModes[i] = randomFrom(ScriptMode.values());
}
ScriptType[] randomScriptTypes = randomScriptTypesSet.toArray(new ScriptType[randomScriptTypesSet.size()]);
ImmutableSettings.Builder builder = ImmutableSettings.builder();
for (int i = 0; i < randomInt; i++) {
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + randomScriptTypes[i], randomScriptModes[i]);
}
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
for (int i = 0; i < randomInt; i++) {
assertScriptModesAllOps(randomScriptModes[i], ALL_LANGS, randomScriptTypes[i]);
}
if (randomScriptTypesSet.contains(ScriptType.FILE) == false) {
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
}
if (randomScriptTypesSet.contains(ScriptType.INDEXED) == false) {
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
}
if (randomScriptTypesSet.contains(ScriptType.INLINE) == false) {
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INLINE);
}
} }
@Test @Test
public void testDisableInlineGenericSettings() { public void testScriptContextGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(DISABLE_VALUES)); int randomInt = randomIntBetween(1, scriptContexts.length - 1);
this.scriptModes = new ScriptModes(scriptEngines, builder.build()); Set<ScriptContext> randomScriptContextsSet = Sets.newHashSet();
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE); ScriptMode[] randomScriptModes = new ScriptMode[randomInt];
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED); for (int i = 0; i < randomInt; i++) {
assertScriptModesAllOps(ScriptMode.OFF, ALL_LANGS, ScriptType.INLINE); boolean added = false;
} while (added == false) {
added = randomScriptContextsSet.add(randomFrom(scriptContexts));
}
randomScriptModes[i] = randomFrom(ScriptMode.values());
}
ScriptContext[] randomScriptContexts = randomScriptContextsSet.toArray(new ScriptContext[randomScriptContextsSet.size()]);
ImmutableSettings.Builder builder = ImmutableSettings.builder();
for (int i = 0; i < randomInt; i++) {
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + randomScriptContexts[i].getKey(), randomScriptModes[i]);
}
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
@Test for (int i = 0; i < randomInt; i++) {
public void testSandboxInlineGenericSettings() { assertScriptModesAllTypes(randomScriptModes[i], ALL_LANGS, randomScriptContexts[i]);
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(ScriptMode.SANDBOX)); }
//nothing changes if setting set is same as default
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
}
@Test ScriptContext[] complementOf = complementOf(randomScriptContexts);
public void testEnableIndexedGenericSettings() { assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, complementOf);
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.indexed", randomFrom(ENABLE_VALUES)); assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, complementOf);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INLINE);
}
@Test
public void testDisableIndexedGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.indexed", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.OFF, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INLINE);
}
@Test
public void testSandboxIndexedGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.indexed", ScriptMode.SANDBOX);
//nothing changes if setting set is same as default
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
}
@Test
public void testEnableFileGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.file", randomFrom(ENABLE_VALUES));
//nothing changes if setting set is same as default
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
}
@Test
public void testDisableFileGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.file", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.OFF, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
}
@Test
public void testSandboxFileGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.file", ScriptMode.SANDBOX);
//nothing changes if setting set is same as default
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.FILE, ScriptType.INDEXED, ScriptType.INLINE);
}
@Test
public void testMultipleScriptTypeGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.file", ScriptMode.SANDBOX).put("script.inline", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.OFF, ALL_LANGS, ScriptType.INLINE);
}
@Test
public void testEnableMappingGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.mapping", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.ON, ALL_LANGS, ScriptContext.MAPPING);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
}
@Test
public void testDisableMappingGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.mapping", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.MAPPING);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
}
@Test
public void testSandboxMappingGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.mapping", ScriptMode.SANDBOX);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.SANDBOX, ALL_LANGS, ScriptContext.MAPPING);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.SEARCH, ScriptContext.UPDATE);
}
@Test
public void testEnableSearchGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.search", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.ON, ALL_LANGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testDisableSearchGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.search", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testSandboxSearchGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.search", ScriptMode.SANDBOX);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.SANDBOX, ALL_LANGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testEnableAggsGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.aggs", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.ON, ALL_LANGS, ScriptContext.AGGS);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testDisableAggsGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.aggs", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.AGGS);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testSandboxAggsGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.aggs", ScriptMode.SANDBOX);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.SANDBOX, ALL_LANGS, ScriptContext.AGGS);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.UPDATE);
}
@Test
public void testEnableUpdateGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.update", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.ON, ALL_LANGS, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
}
@Test
public void testDisableUpdateGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.update", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
}
@Test
public void testSandboxUpdateGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.update", ScriptMode.SANDBOX);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.SANDBOX, ALL_LANGS, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.MAPPING, ScriptContext.AGGS);
}
@Test
public void testMultipleScriptContextGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.update", ScriptMode.SANDBOX)
.put("script.aggs", randomFrom(DISABLE_VALUES))
.put("script.search", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.SANDBOX, ALL_LANGS, ScriptContext.UPDATE);
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.AGGS);
assertScriptModesAllTypes(ScriptMode.ON, ALL_LANGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, ScriptContext.MAPPING);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, ScriptContext.MAPPING);
} }
@Test @Test
public void testConflictingScriptTypeAndOpGenericSettings() { public void testConflictingScriptTypeAndOpGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.update", randomFrom(DISABLE_VALUES)) ScriptContext scriptContext = randomFrom(scriptContexts);
ImmutableSettings.Builder builder = ImmutableSettings.builder().put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptContext.getKey(), randomFrom(DISABLE_VALUES))
.put("script.indexed", randomFrom(ENABLE_VALUES)).put("script.inline", ScriptMode.SANDBOX); .put("script.indexed", randomFrom(ENABLE_VALUES)).put("script.inline", ScriptMode.SANDBOX);
//operations generic settings have precedence over script type generic settings //operations generic settings have precedence over script type generic settings
this.scriptModes = new ScriptModes(scriptEngines, builder.build()); this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.UPDATE); assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, scriptContext);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE, ScriptType.INDEXED}, ScriptContext.MAPPING, ScriptContext.AGGS, ScriptContext.SEARCH); ScriptContext[] complementOf = complementOf(scriptContext);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.AGGS, ScriptContext.SEARCH); assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE, ScriptType.INDEXED}, complementOf);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INLINE}, complementOf);
} }
@Test @Test
public void testEngineSpecificSettings() { public void testEngineSpecificSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder() ImmutableSettings.Builder builder = ImmutableSettings.builder()
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.MAPPING), randomFrom(DISABLE_VALUES)) .put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.MAPPING), randomFrom(DISABLE_VALUES))
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.UPDATE), randomFrom(DISABLE_VALUES)); .put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.UPDATE), randomFrom(DISABLE_VALUES));
ImmutableSet<String> groovyLangSet = ImmutableSet.of(GroovyScriptEngineService.NAME); ImmutableSet<String> groovyLangSet = ImmutableSet.of(GroovyScriptEngineService.NAME);
Set<String> allButGroovyLangSet = new HashSet<>(ALL_LANGS); Set<String> allButGroovyLangSet = new HashSet<>(ALL_LANGS);
allButGroovyLangSet.remove(GroovyScriptEngineService.NAME); allButGroovyLangSet.remove(GroovyScriptEngineService.NAME);
this.scriptModes = new ScriptModes(scriptEngines, builder.build()); this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModes(ScriptMode.OFF, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.UPDATE); assertScriptModes(ScriptMode.OFF, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.Standard.MAPPING, ScriptContext.Standard.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.AGGS); assertScriptModes(ScriptMode.SANDBOX, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, complementOf(ScriptContext.Standard.MAPPING, ScriptContext.Standard.UPDATE));
assertScriptModesAllOps(ScriptMode.SANDBOX, allButGroovyLangSet, ScriptType.INLINE); assertScriptModesAllOps(ScriptMode.SANDBOX, allButGroovyLangSet, ScriptType.INLINE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED); assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE); assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
@ -340,88 +206,21 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test @Test
public void testInteractionBetweenGenericAndEngineSpecificSettings() { public void testInteractionBetweenGenericAndEngineSpecificSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(DISABLE_VALUES)) ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(DISABLE_VALUES))
.put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.AGGS), randomFrom(ENABLE_VALUES)) .put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.AGGS), randomFrom(ENABLE_VALUES))
.put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.SEARCH), randomFrom(ENABLE_VALUES)); .put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.SEARCH), randomFrom(ENABLE_VALUES));
ImmutableSet<String> mustacheLangSet = ImmutableSet.of(MustacheScriptEngineService.NAME); ImmutableSet<String> mustacheLangSet = ImmutableSet.of(MustacheScriptEngineService.NAME);
Set<String> allButMustacheLangSet = new HashSet<>(ALL_LANGS); Set<String> allButMustacheLangSet = new HashSet<>(ALL_LANGS);
allButMustacheLangSet.remove(MustacheScriptEngineService.NAME); allButMustacheLangSet.remove(MustacheScriptEngineService.NAME);
this.scriptModes = new ScriptModes(scriptEngines, builder.build()); this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModes(ScriptMode.ON, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.SEARCH); assertScriptModes(ScriptMode.ON, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.Standard.AGGS, ScriptContext.Standard.SEARCH);
assertScriptModes(ScriptMode.OFF, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.UPDATE); assertScriptModes(ScriptMode.OFF, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, complementOf(ScriptContext.Standard.AGGS, ScriptContext.Standard.SEARCH));
assertScriptModesAllOps(ScriptMode.OFF, allButMustacheLangSet, ScriptType.INLINE); assertScriptModesAllOps(ScriptMode.OFF, allButMustacheLangSet, ScriptType.INLINE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED); assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE); assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
} }
@Test
public void testDefaultSettingsToString() {
assertAllSettingsWereChecked = false;
this.scriptModes = new ScriptModes(scriptEngines, ImmutableSettings.EMPTY);
assertThat(scriptModes.toString(), equalTo(
"script.engine.custom.file.aggs: on\n" +
"script.engine.custom.file.mapping: on\n" +
"script.engine.custom.file.search: on\n" +
"script.engine.custom.file.update: on\n" +
"script.engine.custom.indexed.aggs: sandbox\n" +
"script.engine.custom.indexed.mapping: sandbox\n" +
"script.engine.custom.indexed.search: sandbox\n" +
"script.engine.custom.indexed.update: sandbox\n" +
"script.engine.custom.inline.aggs: sandbox\n" +
"script.engine.custom.inline.mapping: sandbox\n" +
"script.engine.custom.inline.search: sandbox\n" +
"script.engine.custom.inline.update: sandbox\n" +
"script.engine.expression.file.aggs: on\n" +
"script.engine.expression.file.mapping: on\n" +
"script.engine.expression.file.search: on\n" +
"script.engine.expression.file.update: on\n" +
"script.engine.expression.indexed.aggs: sandbox\n" +
"script.engine.expression.indexed.mapping: sandbox\n" +
"script.engine.expression.indexed.search: sandbox\n" +
"script.engine.expression.indexed.update: sandbox\n" +
"script.engine.expression.inline.aggs: sandbox\n" +
"script.engine.expression.inline.mapping: sandbox\n" +
"script.engine.expression.inline.search: sandbox\n" +
"script.engine.expression.inline.update: sandbox\n" +
"script.engine.groovy.file.aggs: on\n" +
"script.engine.groovy.file.mapping: on\n" +
"script.engine.groovy.file.search: on\n" +
"script.engine.groovy.file.update: on\n" +
"script.engine.groovy.indexed.aggs: sandbox\n" +
"script.engine.groovy.indexed.mapping: sandbox\n" +
"script.engine.groovy.indexed.search: sandbox\n" +
"script.engine.groovy.indexed.update: sandbox\n" +
"script.engine.groovy.inline.aggs: sandbox\n" +
"script.engine.groovy.inline.mapping: sandbox\n" +
"script.engine.groovy.inline.search: sandbox\n" +
"script.engine.groovy.inline.update: sandbox\n" +
"script.engine.mustache.file.aggs: on\n" +
"script.engine.mustache.file.mapping: on\n" +
"script.engine.mustache.file.search: on\n" +
"script.engine.mustache.file.update: on\n" +
"script.engine.mustache.indexed.aggs: sandbox\n" +
"script.engine.mustache.indexed.mapping: sandbox\n" +
"script.engine.mustache.indexed.search: sandbox\n" +
"script.engine.mustache.indexed.update: sandbox\n" +
"script.engine.mustache.inline.aggs: sandbox\n" +
"script.engine.mustache.inline.mapping: sandbox\n" +
"script.engine.mustache.inline.search: sandbox\n" +
"script.engine.mustache.inline.update: sandbox\n" +
"script.engine.test.file.aggs: on\n" +
"script.engine.test.file.mapping: on\n" +
"script.engine.test.file.search: on\n" +
"script.engine.test.file.update: on\n" +
"script.engine.test.indexed.aggs: sandbox\n" +
"script.engine.test.indexed.mapping: sandbox\n" +
"script.engine.test.indexed.search: sandbox\n" +
"script.engine.test.indexed.update: sandbox\n" +
"script.engine.test.inline.aggs: sandbox\n" +
"script.engine.test.inline.mapping: sandbox\n" +
"script.engine.test.inline.search: sandbox\n" +
"script.engine.test.inline.update: sandbox\n"));
}
private void assertScriptModesAllOps(ScriptMode expectedScriptMode, Set<String> langs, ScriptType... scriptTypes) { private void assertScriptModesAllOps(ScriptMode expectedScriptMode, Set<String> langs, ScriptType... scriptTypes) {
assertScriptModes(expectedScriptMode, langs, scriptTypes, ScriptContext.values()); assertScriptModes(expectedScriptMode, langs, scriptTypes, scriptContexts);
} }
private void assertScriptModesAllTypes(ScriptMode expectedScriptMode, Set<String> langs, ScriptContext... scriptContexts) { private void assertScriptModesAllTypes(ScriptMode expectedScriptMode, Set<String> langs, ScriptContext... scriptContexts) {
@ -435,15 +234,26 @@ public class ScriptModesTests extends ElasticsearchTestCase {
for (String lang : langs) { for (String lang : langs) {
for (ScriptType scriptType : scriptTypes) { for (ScriptType scriptType : scriptTypes) {
for (ScriptContext scriptContext : scriptContexts) { for (ScriptContext scriptContext : scriptContexts) {
assertThat(lang + "." + scriptType + "." + scriptContext + " doesn't have the expected value", scriptModes.getScriptMode(lang, scriptType, scriptContext), equalTo(expectedScriptMode)); assertThat(lang + "." + scriptType + "." + scriptContext.getKey() + " doesn't have the expected value", scriptModes.getScriptMode(lang, scriptType, scriptContext), equalTo(expectedScriptMode));
checkedSettings.add(lang + "." + scriptType + "." + scriptContext); checkedSettings.add(lang + "." + scriptType + "." + scriptContext);
} }
} }
} }
} }
private ScriptContext[] complementOf(ScriptContext... scriptContexts) {
Map<String, ScriptContext> copy = Maps.newHashMap();
for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
copy.put(scriptContext.getKey(), scriptContext);
}
for (ScriptContext scriptContext : scriptContexts) {
copy.remove(scriptContext.getKey());
}
return copy.values().toArray(new ScriptContext[copy.size()]);
}
private static String specificEngineOpSettings(String lang, ScriptType scriptType, ScriptContext scriptContext) { private static String specificEngineOpSettings(String lang, ScriptType scriptType, ScriptContext scriptContext) {
return ScriptModes.ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext; return ScriptModes.ENGINE_SETTINGS_PREFIX + "." + lang + "." + scriptType + "." + scriptContext.getKey();
} }
static ImmutableMap<String, ScriptEngineService> buildScriptEnginesByLangMap(Set<ScriptEngineService> scriptEngines) { static ImmutableMap<String, ScriptEngineService> buildScriptEnginesByLangMap(Set<ScriptEngineService> scriptEngines) {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.script; package org.elasticsearch.script;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.Streams;
@ -44,16 +45,16 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
/**
*
*/
public class ScriptServiceTests extends ElasticsearchTestCase { public class ScriptServiceTests extends ElasticsearchTestCase {
private ResourceWatcherService resourceWatcherService; private ResourceWatcherService resourceWatcherService;
private Set<ScriptEngineService> scriptEngineServices; private Set<ScriptEngineService> scriptEngineServices;
private Map<String, ScriptEngineService> scriptEnginesByLangMap; private Map<String, ScriptEngineService> scriptEnginesByLangMap;
private ScriptContextRegistry scriptContextRegistry;
private ScriptContext[] scriptContexts;
private ScriptService scriptService; private ScriptService scriptService;
private Path scriptsFilePath; private Path scriptsFilePath;
private Settings baseSettings; private Settings baseSettings;
@ -76,6 +77,24 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
scriptEngineServices = ImmutableSet.of(new TestEngineService(), new GroovyScriptEngineService(baseSettings), scriptEngineServices = ImmutableSet.of(new TestEngineService(), new GroovyScriptEngineService(baseSettings),
new ExpressionScriptEngineService(baseSettings), new MustacheScriptEngineService(baseSettings)); new ExpressionScriptEngineService(baseSettings), new MustacheScriptEngineService(baseSettings));
scriptEnginesByLangMap = ScriptModesTests.buildScriptEnginesByLangMap(scriptEngineServices); scriptEnginesByLangMap = ScriptModesTests.buildScriptEnginesByLangMap(scriptEngineServices);
//randomly register custom script contexts
int randomInt = randomIntBetween(0, 3);
//prevent duplicates using map
Map<String, ScriptContext.Plugin> contexts = Maps.newHashMap();
for (int i = 0; i < randomInt; i++) {
String plugin;
do {
plugin = randomAsciiOfLength(randomIntBetween(1, 10));
} while (ScriptContextRegistry.RESERVED_SCRIPT_CONTEXTS.contains(plugin));
String operation;
do {
operation = randomAsciiOfLength(randomIntBetween(1, 30));
} while (ScriptContextRegistry.RESERVED_SCRIPT_CONTEXTS.contains(operation));
String context = plugin + "_" + operation;
contexts.put(context, new ScriptContext.Plugin(plugin, operation));
}
scriptContextRegistry = new ScriptContextRegistry(contexts.values());
scriptContexts = scriptContextRegistry.scriptContexts().toArray(new ScriptContext[scriptContextRegistry.scriptContexts().size()]);
logger.info("--> setup script service"); logger.info("--> setup script service");
scriptsFilePath = genericConfigFolder.resolve("scripts"); scriptsFilePath = genericConfigFolder.resolve("scripts");
Files.createDirectories(scriptsFilePath); Files.createDirectories(scriptsFilePath);
@ -84,7 +103,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
private void buildScriptService(Settings additionalSettings) throws IOException { private void buildScriptService(Settings additionalSettings) throws IOException {
Settings finalSettings = ImmutableSettings.builder().put(baseSettings).put(additionalSettings).build(); Settings finalSettings = ImmutableSettings.builder().put(baseSettings).put(additionalSettings).build();
Environment environment = new Environment(finalSettings); Environment environment = new Environment(finalSettings);
scriptService = new ScriptService(finalSettings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(finalSettings)) { scriptService = new ScriptService(finalSettings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(finalSettings), scriptContextRegistry) {
@Override @Override
String getScriptFromIndex(String scriptLang, String id) { String getScriptFromIndex(String scriptLang, String id) {
//mock the script that gets retrieved from an index //mock the script that gets retrieved from an index
@ -114,7 +133,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
resourceWatcherService.notifyNow(); resourceWatcherService.notifyNow();
logger.info("--> verify that file with extension was correctly processed"); logger.info("--> verify that file with extension was correctly processed");
CompiledScript compiledScript = scriptService.compile("test", "test_script", ScriptType.FILE, ScriptContext.SEARCH); CompiledScript compiledScript = scriptService.compile("test", "test_script", ScriptType.FILE, ScriptContext.Standard.SEARCH);
assertThat(compiledScript.compiled(), equalTo((Object) "compiled_test_file")); assertThat(compiledScript.compiled(), equalTo((Object) "compiled_test_file"));
logger.info("--> delete both files"); logger.info("--> delete both files");
@ -124,7 +143,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
logger.info("--> verify that file with extension was correctly removed"); logger.info("--> verify that file with extension was correctly removed");
try { try {
scriptService.compile("test", "test_script", ScriptType.FILE, ScriptContext.SEARCH); scriptService.compile("test", "test_script", ScriptType.FILE, ScriptContext.Standard.SEARCH);
fail("the script test_script should no longer exist"); fail("the script test_script should no longer exist");
} catch (ElasticsearchIllegalArgumentException ex) { } catch (ElasticsearchIllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString("Unable to find on disk script test_script")); assertThat(ex.getMessage(), containsString("Unable to find on disk script test_script"));
@ -135,17 +154,17 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
public void testScriptsSameNameDifferentLanguage() throws IOException { public void testScriptsSameNameDifferentLanguage() throws IOException {
buildScriptService(ImmutableSettings.EMPTY); buildScriptService(ImmutableSettings.EMPTY);
createFileScripts("groovy", "expression"); createFileScripts("groovy", "expression");
CompiledScript groovyScript = scriptService.compile(GroovyScriptEngineService.NAME, "file_script", ScriptType.FILE, randomFrom(ScriptContext.values())); CompiledScript groovyScript = scriptService.compile(GroovyScriptEngineService.NAME, "file_script", ScriptType.FILE, randomFrom(scriptContexts));
assertThat(groovyScript.lang(), equalTo(GroovyScriptEngineService.NAME)); assertThat(groovyScript.lang(), equalTo(GroovyScriptEngineService.NAME));
CompiledScript expressionScript = scriptService.compile(ExpressionScriptEngineService.NAME, "file_script", ScriptType.FILE, randomFrom(ScriptContext.values())); CompiledScript expressionScript = scriptService.compile(ExpressionScriptEngineService.NAME, "file_script", ScriptType.FILE, randomFrom(scriptContexts));
assertThat(expressionScript.lang(), equalTo(ExpressionScriptEngineService.NAME)); assertThat(expressionScript.lang(), equalTo(ExpressionScriptEngineService.NAME));
} }
@Test @Test
public void testInlineScriptCompiledOnceMultipleLangAcronyms() throws IOException { public void testInlineScriptCompiledOnceMultipleLangAcronyms() throws IOException {
buildScriptService(ImmutableSettings.EMPTY); buildScriptService(ImmutableSettings.EMPTY);
CompiledScript compiledScript1 = scriptService.compile("test", "script", ScriptType.INLINE, randomFrom(ScriptContext.values())); CompiledScript compiledScript1 = scriptService.compile("test", "script", ScriptType.INLINE, randomFrom(scriptContexts));
CompiledScript compiledScript2 = scriptService.compile("test2", "script", ScriptType.INLINE, randomFrom(ScriptContext.values())); CompiledScript compiledScript2 = scriptService.compile("test2", "script", ScriptType.INLINE, randomFrom(scriptContexts));
assertThat(compiledScript1, sameInstance(compiledScript2)); assertThat(compiledScript1, sameInstance(compiledScript2));
} }
@ -153,8 +172,8 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
public void testFileScriptCompiledOnceMultipleLangAcronyms() throws IOException { public void testFileScriptCompiledOnceMultipleLangAcronyms() throws IOException {
buildScriptService(ImmutableSettings.EMPTY); buildScriptService(ImmutableSettings.EMPTY);
createFileScripts("test"); createFileScripts("test");
CompiledScript compiledScript1 = scriptService.compile("test", "file_script", ScriptType.FILE, randomFrom(ScriptContext.values())); CompiledScript compiledScript1 = scriptService.compile("test", "file_script", ScriptType.FILE, randomFrom(scriptContexts));
CompiledScript compiledScript2 = scriptService.compile("test2", "file_script", ScriptType.FILE, randomFrom(ScriptContext.values())); CompiledScript compiledScript2 = scriptService.compile("test2", "file_script", ScriptType.FILE, randomFrom(scriptContexts));
assertThat(compiledScript1, sameInstance(compiledScript2)); assertThat(compiledScript1, sameInstance(compiledScript2));
} }
@ -174,7 +193,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
buildScriptService(builder.build()); buildScriptService(builder.build());
createFileScripts("groovy", "expression", "mustache", "test"); createFileScripts("groovy", "expression", "mustache", "test");
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : scriptContexts) {
//groovy is not sandboxed, only file scripts are enabled by default //groovy is not sandboxed, only file scripts are enabled by default
assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INLINE, scriptContext); assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INLINE, scriptContext);
assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INDEXED, scriptContext); assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INDEXED, scriptContext);
@ -206,12 +225,12 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
} while (scriptSourceSettings.containsKey(scriptType)); } while (scriptSourceSettings.containsKey(scriptType));
scriptSourceSettings.put(scriptType, randomFrom(ScriptMode.values())); scriptSourceSettings.put(scriptType, randomFrom(ScriptMode.values()));
} }
int numScriptContextSettings = randomIntBetween(0, ScriptContext.values().length); int numScriptContextSettings = randomIntBetween(0, this.scriptContextRegistry.scriptContexts().size());
Map<ScriptContext, ScriptMode> scriptContextSettings = new HashMap<>(); Map<String, ScriptMode> scriptContextSettings = new HashMap<>();
for (int i = 0; i < numScriptContextSettings; i++) { for (int i = 0; i < numScriptContextSettings; i++) {
ScriptContext scriptContext; String scriptContext;
do { do {
scriptContext = randomFrom(ScriptContext.values()); scriptContext = randomFrom(this.scriptContexts).getKey();
} while (scriptContextSettings.containsKey(scriptContext)); } while (scriptContextSettings.containsKey(scriptContext));
scriptContextSettings.put(scriptContext, randomFrom(ScriptMode.values())); scriptContextSettings.put(scriptContext, randomFrom(ScriptMode.values()));
} }
@ -223,9 +242,9 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
ScriptEngineService[] scriptEngineServices = this.scriptEngineServices.toArray(new ScriptEngineService[this.scriptEngineServices.size()]); ScriptEngineService[] scriptEngineServices = this.scriptEngineServices.toArray(new ScriptEngineService[this.scriptEngineServices.size()]);
ScriptEngineService scriptEngineService = randomFrom(scriptEngineServices); ScriptEngineService scriptEngineService = randomFrom(scriptEngineServices);
ScriptType scriptType = randomFrom(ScriptType.values()); ScriptType scriptType = randomFrom(ScriptType.values());
ScriptContext scriptContext = randomFrom(ScriptContext.values()); ScriptContext scriptContext = randomFrom(this.scriptContexts);
settingKey = scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext; settingKey = scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext.getKey();
} while(engineSettings.containsKey(settingKey)); } while (engineSettings.containsKey(settingKey));
engineSettings.put(settingKey, randomFrom(ScriptMode.values())); engineSettings.put(settingKey, randomFrom(ScriptMode.values()));
} }
//set the selected fine-grained settings //set the selected fine-grained settings
@ -243,7 +262,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
break; break;
} }
} }
for (Map.Entry<ScriptContext, ScriptMode> entry : scriptContextSettings.entrySet()) { for (Map.Entry<String, ScriptMode> entry : scriptContextSettings.entrySet()) {
switch (entry.getValue()) { switch (entry.getValue()) {
case ON: case ON:
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + entry.getKey(), randomFrom(ScriptModesTests.ENABLE_VALUES)); builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + entry.getKey(), randomFrom(ScriptModesTests.ENABLE_VALUES));
@ -283,11 +302,11 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
//make sure file scripts have a different name than inline ones. //make sure file scripts have a different name than inline ones.
//Otherwise they are always considered file ones as they can be found in the static cache. //Otherwise they are always considered file ones as they can be found in the static cache.
String script = scriptType == ScriptType.FILE ? "file_script" : "script"; String script = scriptType == ScriptType.FILE ? "file_script" : "script";
for (ScriptContext scriptContext : ScriptContext.values()) { for (ScriptContext scriptContext : this.scriptContexts) {
//fallback mechanism: 1) engine specific settings 2) op based settings 3) source based settings //fallback mechanism: 1) engine specific settings 2) op based settings 3) source based settings
ScriptMode scriptMode = engineSettings.get(scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext); ScriptMode scriptMode = engineSettings.get(scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext.getKey());
if (scriptMode == null) { if (scriptMode == null) {
scriptMode = scriptContextSettings.get(scriptContext); scriptMode = scriptContextSettings.get(scriptContext.getKey());
} }
if (scriptMode == null) { if (scriptMode == null) {
scriptMode = scriptSourceSettings.get(scriptType); scriptMode = scriptSourceSettings.get(scriptType);
@ -318,6 +337,28 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
} }
} }
@Test
public void testCompileNonRegisteredContext() throws IOException {
buildScriptService(ImmutableSettings.EMPTY);
String pluginName;
String unknownContext;
do {
pluginName = randomAsciiOfLength(randomIntBetween(1, 10));
unknownContext = randomAsciiOfLength(randomIntBetween(1, 30));
} while(scriptContextRegistry.isSupportedContext(new ScriptContext.Plugin(pluginName, unknownContext)));
for (ScriptEngineService scriptEngineService : scriptEngineServices) {
for (String type : scriptEngineService.types()) {
try {
scriptService.compile(type, "test", randomFrom(ScriptType.values()), new ScriptContext.Plugin(pluginName, unknownContext));
fail("script compilation should have been rejected");
} catch(ElasticsearchIllegalArgumentException e) {
assertThat(e.getMessage(), containsString("script context [" + pluginName + "_" + unknownContext + "] not supported"));
}
}
}
}
private void createFileScripts(String... langs) throws IOException { private void createFileScripts(String... langs) throws IOException {
for (String lang : langs) { for (String lang : langs) {
Path scriptPath = scriptsFilePath.resolve("file_script." + lang); Path scriptPath = scriptsFilePath.resolve("file_script." + lang);