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)
| `search` |Search api, Percolator api and Suggester api (e.g filters, script_fields)
| `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,
regardless of the script source, for any engine. Scripts can still be
executed from sandboxed languages as part of `aggregations` and `search`
operations though, as the above defaults still get applied.
executed from sandboxed languages as part of `aggregations`, `search`
and plugins execution though, as the above defaults still get applied.
[source,yaml]
-----------------------------------
@ -325,14 +330,17 @@ script.engine.groovy.file.aggs: on
script.engine.groovy.file.mapping: on
script.engine.groovy.file.search: on
script.engine.groovy.file.update: on
script.engine.groovy.file.plugin: on
script.engine.groovy.indexed.aggs: on
script.engine.groovy.indexed.mapping: off
script.engine.groovy.indexed.search: on
script.engine.groovy.indexed.update: off
script.engine.groovy.indexed.plugin: off
script.engine.groovy.inline.aggs: on
script.engine.groovy.inline.mapping: off
script.engine.groovy.inline.search: 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("_source", upsertDoc);
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.run();
// we need to unwrap the ctx...
@ -193,7 +193,7 @@ public class UpdateHelper extends AbstractComponent {
ctx.put("_source", sourceAndContent.v2());
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.run();
// 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.RootObjectMapper;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptService.ScriptType;
import java.io.IOException;
import java.util.ArrayList;
@ -79,7 +80,6 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
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) {
try {
// 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);
ctx.put("_source", sourceAsMap);
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) {
this.script = script;
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

View File

@ -26,8 +26,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import java.io.IOException;
@ -77,7 +77,7 @@ public class TemplateQueryParser implements QueryParser {
public Query parse(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser();
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();

View File

@ -87,7 +87,7 @@ public class ScriptScoreFunctionParser implements ScoreFunctionParser {
SearchScript searchScript;
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);
} catch (Exception 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;
import java.util.Locale;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Strings;
/**
* Operation/api that uses a script as part of its execution.
* Note that the suggest api is considered part of search for simplicity, as well as the percolate api.
* Context of an operation that uses scripts as part of its execution.
*/
public enum ScriptContext {
MAPPING,
UPDATE,
SEARCH,
AGGS;
public interface ScriptContext {
@Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
/**
* @return the name of the operation
*/
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;
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.
//native scripts are always on as they are static by definition.
Map<String, ScriptEngineService> filteredEngines = Maps.newHashMap(scriptEngines);
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();
//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
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
addGlobalScriptTypeModes(scriptEngines.keySet(), ScriptType.INLINE, ScriptMode.SANDBOX, scriptModesMap);
addGlobalScriptTypeModes(scriptEngines.keySet(), scriptContextRegistry, ScriptType.INLINE, ScriptMode.SANDBOX, scriptModesMap);
processSourceBasedGlobalSettings(settings, scriptEngines, scriptModesMap);
processOperationBasedGlobalSettings(settings, scriptEngines, scriptModesMap);
processEngineSpecificSettings(settings, scriptEngines, scriptModesMap);
processSourceBasedGlobalSettings(settings, scriptEngines, scriptContextRegistry, scriptModesMap);
processOperationBasedGlobalSettings(settings, scriptEngines, scriptContextRegistry, scriptModesMap);
processEngineSpecificSettings(settings, scriptEngines, scriptContextRegistry, 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)
for (ScriptType scriptType : ScriptType.values()) {
String scriptTypeSetting = settings.get(SCRIPT_SETTINGS_PREFIX + scriptType);
if (Strings.hasLength(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)
//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);
if (scriptMode != null) {
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);
for (Map.Entry<String, Settings> langSettings : langGroupedSettings.entrySet()) {
//read engine specific settings that refer to a non existing script lang will be ignored
ScriptEngineService scriptEngineService = scriptEngines.get(langSettings.getKey());
if (scriptEngineService != null) {
String enginePrefix = ScriptModes.ENGINE_SETTINGS_PREFIX + "." + langSettings.getKey() + ".";
for (ScriptType scriptType : ScriptType.values()) {
String scriptTypePrefix = scriptType + ".";
for (ScriptContext scriptContext : ScriptContext.values()) {
for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
ScriptMode scriptMode = getScriptContextMode(langSettings.getValue(), scriptTypePrefix, scriptContext);
if (scriptMode != null) {
addScriptMode(scriptEngineService, scriptType, scriptContext, scriptMode, scriptModes);
@ -109,16 +108,16 @@ public class ScriptModes {
}
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)) {
return ScriptMode.parse(settingValue);
}
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 (ScriptContext scriptContext : ScriptContext.values()) {
for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
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) {
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 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
*/
public ScriptMode getScriptMode(String lang, ScriptType scriptType, ScriptContext scriptContext) {
@ -158,9 +157,9 @@ public class ScriptModes {
if (NativeScriptEngineService.NAME.equals(lang)) {
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) {
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;
}

View File

@ -46,6 +46,8 @@ public class ScriptModule extends AbstractModule {
private final Map<String, Class<? extends NativeScriptFactory>> scripts = Maps.newHashMap();
private final List<ScriptContext.Plugin> customScriptContexts = Lists.newArrayList();
public ScriptModule(Settings settings) {
this.settings = settings;
}
@ -58,6 +60,14 @@ public class ScriptModule extends AbstractModule {
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
protected void configure() {
MapBinder<String, NativeScriptFactory> scriptsBinder
@ -105,6 +115,7 @@ public class ScriptModule extends AbstractModule {
multibinder.addBinding().to(scriptEngine).asEagerSingleton();
}
bind(ScriptContextRegistry.class).toInstance(new ScriptContextRegistry(customScriptContexts));
bind(ScriptService.class).asEagerSingleton();
}
}

View File

@ -103,6 +103,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
private final FileWatcher fileWatcher;
private final ScriptModes scriptModes;
private final ScriptContextRegistry scriptContextRegistry;
private Client client = null;
@ -113,7 +114,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
@Inject
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);
if (Strings.hasLength(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING))) {
@ -122,6 +123,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
}
this.scriptEngines = scriptEngines;
this.scriptContextRegistry = scriptContextRegistry;
int cacheMaxSize = settings.getAsInt(SCRIPT_CACHE_SIZE_SETTING, 100);
TimeValue cacheExpire = settings.getAsTime(SCRIPT_CACHE_EXPIRE_SETTING, null);
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.scriptEnginesByExt = enginesByExtBuilder.build();
this.scriptModes = new ScriptModes(this.scriptEnginesByLang, settings);
this.scriptModes = new ScriptModes(this.scriptEnginesByLang, scriptContextRegistry, settings);
// add file watcher for static scripts
scriptsDirectory = env.configFile().resolve("scripts");
@ -212,6 +214,18 @@ public class ScriptService extends AbstractComponent implements Closeable {
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.
*/
@ -226,7 +240,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
ScriptEngineService scriptEngineService = getScriptEngineServiceForLang(lang);
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);
}
@ -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
*/
@ -394,6 +420,18 @@ public class ScriptService extends AbstractComponent implements Closeable {
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
*/
@ -403,7 +441,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
}
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)) {
return true;
}
@ -413,6 +451,9 @@ public class ScriptService extends AbstractComponent implements Closeable {
private boolean canExecuteScript(String lang, ScriptEngineService scriptEngineService, ScriptType scriptType, ScriptContext scriptContext) {
assert lang != null;
if (scriptContextRegistry.isSupportedContext(scriptContext) == false) {
throw new ElasticsearchIllegalArgumentException("script context [" + scriptContext.getKey() + "] not supported");
}
ScriptMode mode = scriptModes.getScriptMode(lang, scriptType, scriptContext);
switch (mode) {
case ON:

View File

@ -71,8 +71,9 @@ import org.elasticsearch.indices.IndicesWarmer.TerminationHandle;
import org.elasticsearch.indices.IndicesWarmer.WarmerContext;
import org.elasticsearch.indices.cache.query.IndicesQueryCache;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContextRegistry;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.search.dfs.CachedDfSource;
import org.elasticsearch.search.dfs.DfsPhase;
@ -652,7 +653,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
final ExecutableScript executable;
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 {
if (!hasLength(request.templateSource())) {
return;
@ -692,7 +693,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
if (!hasLength(templateContext.template())) {
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();

View File

@ -30,10 +30,7 @@ import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptParameterParser;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.*;
import org.elasticsearch.search.aggregations.InternalAggregation;
import java.io.IOException;
@ -86,7 +83,7 @@ public class ScriptHeuristic extends SignificanceHeuristic {
}
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_size", subsetSizeHolder);
script.setNextVar("_superset_freq", supersetDfHolder);
@ -171,7 +168,7 @@ public class ScriptHeuristic extends SignificanceHeuristic {
}
ExecutableScript searchScript;
try {
searchScript = scriptService.executable(scriptLang, script, scriptType, ScriptContext.AGGS, params);
searchScript = scriptService.executable(scriptLang, script, scriptType, ScriptContext.Standard.AGGS, params);
} catch (Exception 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.xcontent.XContentBuilder;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.search.aggregations.AggregationStreams;
import org.elasticsearch.search.aggregations.InternalAggregation;
import org.elasticsearch.search.aggregations.metrics.InternalMetricsAggregation;
@ -99,7 +99,7 @@ public class InternalScriptedMetric extends InternalMetricsAggregation implement
}
params.put("_aggs", aggregationObjects);
ExecutableScript script = reduceContext.scriptService().executable(firstAggregation.scriptLang, firstAggregation.reduceScript,
firstAggregation.scriptType, ScriptContext.AGGS, params);
firstAggregation.scriptType, ScriptContext.Standard.AGGS, params);
aggregation = script.run();
} else {
aggregation = aggregationObjects;

View File

@ -23,9 +23,8 @@ import org.apache.lucene.index.LeafReaderContext;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.*;
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.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactory;
@ -75,11 +74,11 @@ public class ScriptedMetricAggregator extends MetricsAggregator {
}
ScriptService scriptService = context.searchContext().scriptService();
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) {
this.combineScript = scriptService.executable(scriptLang, combineScript, combineScriptType, ScriptContext.AGGS, this.params);
this.combineScript = scriptService.executable(scriptLang, combineScript, combineScriptType, ScriptContext.Standard.AGGS, this.params);
} else {
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.NumberFieldMapper;
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.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.InternalAggregation;
import org.elasticsearch.search.aggregations.support.format.ValueFormat;
@ -189,7 +186,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
}
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) {

View File

@ -20,11 +20,8 @@
package org.elasticsearch.search.fetch.script;
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.ScriptService;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.internal.SearchContext;
@ -79,7 +76,7 @@ public class ScriptFieldsParseElement implements SearchParseElement {
script = scriptValue.script();
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));
}
}

View File

@ -118,7 +118,7 @@ public class ScriptSortParser implements SortParser {
if (type == null) {
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)) {
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.MapperService;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.search.suggest.SuggestContextParser;
import org.elasticsearch.search.suggest.SuggestUtils;
@ -153,7 +153,7 @@ public final class PhraseSuggestParser implements SuggestContextParser {
if (suggestion.getCollateQueryScript() != null) {
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)) {
suggestion.setCollateQueryScript(compiledScript);
} 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;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.ModulesBuilder;
@ -58,7 +59,7 @@ public class NativeScriptTests extends ElasticsearchTestCase {
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"));
terminate(injector.getInstance(ThreadPool.class));
}
@ -70,7 +71,7 @@ public class NativeScriptTests extends ElasticsearchTestCase {
ScriptType scriptType = randomFrom(ScriptType.values());
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptType, randomFrom(ScriptMode.values()));
} else {
ScriptContext scriptContext = randomFrom(ScriptContext.values());
String scriptContext = randomFrom(ScriptContext.Standard.values()).getKey();
builder.put(ScriptModes.SCRIPT_SETTINGS_PREFIX + scriptContext, randomFrom(ScriptMode.values()));
}
Settings settings = builder.build();
@ -79,9 +80,10 @@ public class NativeScriptTests extends ElasticsearchTestCase {
Map<String, NativeScriptFactory> nativeScriptFactoryMap = new HashMap<>();
nativeScriptFactoryMap.put("my", new MyNativeScriptFactory());
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());
}
}

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;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.*;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.ImmutableSettings;
@ -34,10 +33,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static org.hamcrest.CoreMatchers.equalTo;
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[] DISABLE_VALUES = new String[]{"off", "false", "no", "0"};
ScriptContextRegistry scriptContextRegistry;
private ScriptContext[] scriptContexts;
private Map<String, ScriptEngineService> scriptEngines;
private ScriptModes scriptModes;
private Set<String> checkedSettings;
@ -57,6 +55,18 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Before
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(
new GroovyScriptEngineService(ImmutableSettings.EMPTY),
new MustacheScriptEngineService(ImmutableSettings.EMPTY),
@ -72,7 +82,7 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@After
public void assertNativeScriptsAreAlwaysAllowed() {
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) {
assertThat(scriptModes, notNullValue());
//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));
if (assertAllSettingsWereChecked) {
assertThat(checkedSettings.size(), equalTo(numberOfSettings));
@ -91,7 +101,7 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test
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.SANDBOX, ALL_LANGS, ScriptType.INDEXED, ScriptType.INLINE);
}
@ -99,239 +109,95 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test(expected = ElasticsearchIllegalArgumentException.class)
public void testMissingSetting() {
assertAllSettingsWereChecked = false;
this.scriptModes = new ScriptModes(scriptEngines, ImmutableSettings.EMPTY);
scriptModes.getScriptMode("non_existing", randomFrom(ScriptType.values()), randomFrom(ScriptContext.values()));
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, ImmutableSettings.EMPTY);
scriptModes.getScriptMode("non_existing", randomFrom(ScriptType.values()), randomFrom(scriptContexts));
}
@Test
public void testEnableInlineGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(ENABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE, ScriptType.INLINE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
public void testScriptTypeGenericSettings() {
int randomInt = randomIntBetween(1, ScriptType.values().length - 1);
Set<ScriptType> randomScriptTypesSet = Sets.newHashSet();
ScriptMode[] randomScriptModes = new ScriptMode[randomInt];
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
public void testDisableInlineGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.inline", randomFrom(DISABLE_VALUES));
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.OFF, ALL_LANGS, ScriptType.INLINE);
}
public void testScriptContextGenericSettings() {
int randomInt = randomIntBetween(1, scriptContexts.length - 1);
Set<ScriptContext> randomScriptContextsSet = Sets.newHashSet();
ScriptMode[] randomScriptModes = new ScriptMode[randomInt];
for (int i = 0; i < randomInt; i++) {
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
public void testSandboxInlineGenericSettings() {
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);
}
for (int i = 0; i < randomInt; i++) {
assertScriptModesAllTypes(randomScriptModes[i], ALL_LANGS, randomScriptContexts[i]);
}
@Test
public void testEnableIndexedGenericSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder().put("script.indexed", randomFrom(ENABLE_VALUES));
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);
ScriptContext[] complementOf = complementOf(randomScriptContexts);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE}, complementOf);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INDEXED, ScriptType.INLINE}, complementOf);
}
@Test
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);
//operations generic settings have precedence over script type generic settings
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE, ScriptType.INDEXED}, ScriptContext.MAPPING, ScriptContext.AGGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.AGGS, ScriptContext.SEARCH);
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModesAllTypes(ScriptMode.OFF, ALL_LANGS, scriptContext);
ScriptContext[] complementOf = complementOf(scriptContext);
assertScriptModes(ScriptMode.ON, ALL_LANGS, new ScriptType[]{ScriptType.FILE, ScriptType.INDEXED}, complementOf);
assertScriptModes(ScriptMode.SANDBOX, ALL_LANGS, new ScriptType[]{ScriptType.INLINE}, complementOf);
}
@Test
public void testEngineSpecificSettings() {
ImmutableSettings.Builder builder = ImmutableSettings.builder()
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.MAPPING), randomFrom(DISABLE_VALUES))
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.UPDATE), randomFrom(DISABLE_VALUES));
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.MAPPING), randomFrom(DISABLE_VALUES))
.put(specificEngineOpSettings(GroovyScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.UPDATE), randomFrom(DISABLE_VALUES));
ImmutableSet<String> groovyLangSet = ImmutableSet.of(GroovyScriptEngineService.NAME);
Set<String> allButGroovyLangSet = new HashSet<>(ALL_LANGS);
allButGroovyLangSet.remove(GroovyScriptEngineService.NAME);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModes(ScriptMode.OFF, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.SEARCH, ScriptContext.AGGS);
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModes(ScriptMode.OFF, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.Standard.MAPPING, ScriptContext.Standard.UPDATE);
assertScriptModes(ScriptMode.SANDBOX, groovyLangSet, new ScriptType[]{ScriptType.INLINE}, complementOf(ScriptContext.Standard.MAPPING, ScriptContext.Standard.UPDATE));
assertScriptModesAllOps(ScriptMode.SANDBOX, allButGroovyLangSet, ScriptType.INLINE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
assertScriptModesAllOps(ScriptMode.ON, ALL_LANGS, ScriptType.FILE);
@ -340,88 +206,21 @@ public class ScriptModesTests extends ElasticsearchTestCase {
@Test
public void testInteractionBetweenGenericAndEngineSpecificSettings() {
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.SEARCH), randomFrom(ENABLE_VALUES));
.put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.AGGS), randomFrom(ENABLE_VALUES))
.put(specificEngineOpSettings(MustacheScriptEngineService.NAME, ScriptType.INLINE, ScriptContext.Standard.SEARCH), randomFrom(ENABLE_VALUES));
ImmutableSet<String> mustacheLangSet = ImmutableSet.of(MustacheScriptEngineService.NAME);
Set<String> allButMustacheLangSet = new HashSet<>(ALL_LANGS);
allButMustacheLangSet.remove(MustacheScriptEngineService.NAME);
this.scriptModes = new ScriptModes(scriptEngines, builder.build());
assertScriptModes(ScriptMode.ON, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.AGGS, ScriptContext.SEARCH);
assertScriptModes(ScriptMode.OFF, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.MAPPING, ScriptContext.UPDATE);
this.scriptModes = new ScriptModes(scriptEngines, scriptContextRegistry, builder.build());
assertScriptModes(ScriptMode.ON, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, ScriptContext.Standard.AGGS, ScriptContext.Standard.SEARCH);
assertScriptModes(ScriptMode.OFF, mustacheLangSet, new ScriptType[]{ScriptType.INLINE}, complementOf(ScriptContext.Standard.AGGS, ScriptContext.Standard.SEARCH));
assertScriptModesAllOps(ScriptMode.OFF, allButMustacheLangSet, ScriptType.INLINE);
assertScriptModesAllOps(ScriptMode.SANDBOX, ALL_LANGS, ScriptType.INDEXED);
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) {
assertScriptModes(expectedScriptMode, langs, scriptTypes, ScriptContext.values());
assertScriptModes(expectedScriptMode, langs, scriptTypes, 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 (ScriptType scriptType : scriptTypes) {
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);
}
}
}
}
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) {
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) {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.script;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.Streams;
@ -44,16 +45,16 @@ import java.util.Map;
import java.util.Set;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.*;
/**
*
*/
public class ScriptServiceTests extends ElasticsearchTestCase {
private ResourceWatcherService resourceWatcherService;
private Set<ScriptEngineService> scriptEngineServices;
private Map<String, ScriptEngineService> scriptEnginesByLangMap;
private ScriptContextRegistry scriptContextRegistry;
private ScriptContext[] scriptContexts;
private ScriptService scriptService;
private Path scriptsFilePath;
private Settings baseSettings;
@ -76,6 +77,24 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
scriptEngineServices = ImmutableSet.of(new TestEngineService(), new GroovyScriptEngineService(baseSettings),
new ExpressionScriptEngineService(baseSettings), new MustacheScriptEngineService(baseSettings));
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");
scriptsFilePath = genericConfigFolder.resolve("scripts");
Files.createDirectories(scriptsFilePath);
@ -84,7 +103,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
private void buildScriptService(Settings additionalSettings) throws IOException {
Settings finalSettings = ImmutableSettings.builder().put(baseSettings).put(additionalSettings).build();
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
String getScriptFromIndex(String scriptLang, String id) {
//mock the script that gets retrieved from an index
@ -114,7 +133,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
resourceWatcherService.notifyNow();
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"));
logger.info("--> delete both files");
@ -124,7 +143,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
logger.info("--> verify that file with extension was correctly removed");
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");
} catch (ElasticsearchIllegalArgumentException ex) {
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 {
buildScriptService(ImmutableSettings.EMPTY);
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));
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));
}
@Test
public void testInlineScriptCompiledOnceMultipleLangAcronyms() throws IOException {
buildScriptService(ImmutableSettings.EMPTY);
CompiledScript compiledScript1 = scriptService.compile("test", "script", ScriptType.INLINE, randomFrom(ScriptContext.values()));
CompiledScript compiledScript2 = scriptService.compile("test2", "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(scriptContexts));
assertThat(compiledScript1, sameInstance(compiledScript2));
}
@ -153,8 +172,8 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
public void testFileScriptCompiledOnceMultipleLangAcronyms() throws IOException {
buildScriptService(ImmutableSettings.EMPTY);
createFileScripts("test");
CompiledScript compiledScript1 = scriptService.compile("test", "file_script", ScriptType.FILE, randomFrom(ScriptContext.values()));
CompiledScript compiledScript2 = scriptService.compile("test2", "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(scriptContexts));
assertThat(compiledScript1, sameInstance(compiledScript2));
}
@ -174,7 +193,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
buildScriptService(builder.build());
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
assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INLINE, scriptContext);
assertCompileRejected(GroovyScriptEngineService.NAME, "script", ScriptType.INDEXED, scriptContext);
@ -206,12 +225,12 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
} while (scriptSourceSettings.containsKey(scriptType));
scriptSourceSettings.put(scriptType, randomFrom(ScriptMode.values()));
}
int numScriptContextSettings = randomIntBetween(0, ScriptContext.values().length);
Map<ScriptContext, ScriptMode> scriptContextSettings = new HashMap<>();
int numScriptContextSettings = randomIntBetween(0, this.scriptContextRegistry.scriptContexts().size());
Map<String, ScriptMode> scriptContextSettings = new HashMap<>();
for (int i = 0; i < numScriptContextSettings; i++) {
ScriptContext scriptContext;
String scriptContext;
do {
scriptContext = randomFrom(ScriptContext.values());
scriptContext = randomFrom(this.scriptContexts).getKey();
} while (scriptContextSettings.containsKey(scriptContext));
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 scriptEngineService = randomFrom(scriptEngineServices);
ScriptType scriptType = randomFrom(ScriptType.values());
ScriptContext scriptContext = randomFrom(ScriptContext.values());
settingKey = scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext;
} while(engineSettings.containsKey(settingKey));
ScriptContext scriptContext = randomFrom(this.scriptContexts);
settingKey = scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext.getKey();
} while (engineSettings.containsKey(settingKey));
engineSettings.put(settingKey, randomFrom(ScriptMode.values()));
}
//set the selected fine-grained settings
@ -243,7 +262,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
break;
}
}
for (Map.Entry<ScriptContext, ScriptMode> entry : scriptContextSettings.entrySet()) {
for (Map.Entry<String, ScriptMode> entry : scriptContextSettings.entrySet()) {
switch (entry.getValue()) {
case ON:
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.
//Otherwise they are always considered file ones as they can be found in the static cache.
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
ScriptMode scriptMode = engineSettings.get(scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext);
ScriptMode scriptMode = engineSettings.get(scriptEngineService.types()[0] + "." + scriptType + "." + scriptContext.getKey());
if (scriptMode == null) {
scriptMode = scriptContextSettings.get(scriptContext);
scriptMode = scriptContextSettings.get(scriptContext.getKey());
}
if (scriptMode == null) {
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 {
for (String lang : langs) {
Path scriptPath = scriptsFilePath.resolve("file_script." + lang);