Scripting: Use type aware script contexts (elastic/x-pack-elasticsearch#1538)

This creates two different script contexts for watcher, one which may be
used for SearchScript, and another for ExecutableScript.
This is the xpack side of elastic/elasticsearch#24868.

Original commit: elastic/x-pack-elasticsearch@9ae3d45fed
This commit is contained in:
Ryan Ernst 2017-05-24 14:29:25 -07:00 committed by GitHub
parent d7e528f7f7
commit 9a7c28786a
15 changed files with 39 additions and 47 deletions

View File

@ -112,6 +112,7 @@ import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -335,7 +336,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
@Override
public List<ScriptContext> getContexts() {
return Collections.singletonList(Watcher.SCRIPT_CONTEXT);
return Arrays.asList(Watcher.SCRIPT_SEARCH_CONTEXT, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
}
@Override

View File

@ -5,19 +5,15 @@
*/
package org.elasticsearch.xpack.common.text;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.template.CompiledTemplate;
import org.elasticsearch.xpack.watcher.Watcher;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -56,7 +52,7 @@ public class TextTemplateEngine extends AbstractComponent {
options.put(Script.CONTENT_TYPE_OPTION, mediaType);
}
Script script = new Script(textTemplate.getType(), "mustache", template, options, mergedModel);
CompiledTemplate compiledTemplate = service.compileTemplate(script, Watcher.SCRIPT_CONTEXT);
CompiledTemplate compiledTemplate = service.compileTemplate(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
return compiledTemplate.run(model);
}

View File

@ -56,8 +56,6 @@ import org.elasticsearch.index.shard.IndexSearcherWrapper;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
@ -276,7 +274,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
params.put("_user", userModel);
// Always enforce mustache script lang:
script = new Script(script.getType(), "mustache", script.getIdOrCode(), script.getOptions(), params);
CompiledTemplate compiledTemplate = scriptService.compileTemplate(script, ScriptContext.SEARCH);
CompiledTemplate compiledTemplate = scriptService.compileTemplate(script, ScriptContext.EXECUTABLE);
return compiledTemplate.run(script.getParams());
} else {
return querySource;

View File

@ -40,6 +40,7 @@ import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.threadpool.ExecutorBuilder;
import org.elasticsearch.threadpool.FixedExecutorBuilder;
import org.elasticsearch.threadpool.ThreadPool;
@ -181,7 +182,11 @@ public class Watcher implements ActionPlugin {
public static final Setting<TimeValue> MAX_STOP_TIMEOUT_SETTING =
Setting.timeSetting("xpack.watcher.stop.timeout", TimeValue.timeValueSeconds(30), Setting.Property.NodeScope);
public static final ScriptContext SCRIPT_CONTEXT = new ScriptContext("xpack");
public static final ScriptContext<SearchScript, SearchScript.Compiled> SCRIPT_SEARCH_CONTEXT =
new ScriptContext<>("xpack", SearchScript.class, SearchScript.Compiled.class);
// TODO: remove this context when each xpack script use case has their own contexts
public static final ScriptContext<ExecutableScript, ExecutableScript.Compiled> SCRIPT_EXECUTABLE_CONTEXT
= new ScriptContext<>("xpack_executable", ExecutableScript.class, ExecutableScript.Compiled.class);
private static final Logger logger = Loggers.getLogger(Watcher.class);
private WatcherIndexingListener listener;

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.condition;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
@ -17,7 +16,6 @@ import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Variables;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalState;
@ -32,7 +30,7 @@ public final class ScriptCondition extends Condition {
private final ScriptService scriptService;
private final Script script;
private final CompiledScript compiledScript;
private final ExecutableScript.Compiled compiledScript;
public ScriptCondition(Script script) {
super(TYPE);
@ -45,7 +43,7 @@ public final class ScriptCondition extends Condition {
super(TYPE);
this.scriptService = scriptService;
this.script = script;
compiledScript = scriptService.compile(script, Watcher.SCRIPT_CONTEXT);
compiledScript = scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
}
public Script getScript() {
@ -72,7 +70,7 @@ public final class ScriptCondition extends Condition {
if (script.getParams() != null && !script.getParams().isEmpty()) {
parameters.putAll(script.getParams());
}
ExecutableScript executable = scriptService.executable(compiledScript, parameters);
ExecutableScript executable = compiledScript.newInstance(parameters);
Object value = executable.run();
if (value instanceof Boolean) {
return (Boolean) value ? MET : UNMET;

View File

@ -13,7 +13,6 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -24,7 +23,6 @@ import org.elasticsearch.xpack.watcher.support.Variables;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/**
@ -54,7 +52,7 @@ public class WatcherSearchTemplateService extends AbstractComponent {
}
// Templates are always of lang mustache:
Script template = new Script(source.getType(), "mustache", source.getIdOrCode(), source.getOptions(), watcherContextParams);
CompiledTemplate compiledTemplate = scriptService.compileTemplate(template, Watcher.SCRIPT_CONTEXT);
CompiledTemplate compiledTemplate = scriptService.compileTemplate(template, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
return compiledTemplate.run(template.getParams());
}

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.transform.script;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
@ -18,7 +17,6 @@ import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -34,7 +32,7 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
this.scriptService = scriptService;
Script script = transform.getScript();
// try to compile so we catch syntax errors early
scriptService.compile(script, Watcher.SCRIPT_CONTEXT);
scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
}
@Override
@ -54,9 +52,10 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
model.putAll(script.getParams());
}
model.putAll(createCtxModel(ctx, payload));
CompiledScript compiledScript = scriptService.compile(script, Watcher.SCRIPT_CONTEXT);
ExecutableScript executable = scriptService.executable(compiledScript, model);
ExecutableScript.Compiled compiledScript = scriptService.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
ExecutableScript executable = compiledScript.newInstance(model);
Object value = executable.run();
// TODO: deprecate one of these styles (returning a map or returning an opaque value below)
if (value instanceof Map) {
return new ScriptTransform.Result(new Payload.Simple((Map<String, Object>) value));
}

View File

@ -5,14 +5,11 @@
*/
package org.elasticsearch.xpack.common.text;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
@ -60,7 +57,7 @@ public class TextTemplateTests extends ESTestCase {
CompiledTemplate compiledTemplate = templateParams -> "rendered_text";
when(service.compileTemplate(new Script(type, lang, templateText,
type == ScriptType.INLINE ? Collections.singletonMap("content_type", "text/plain") : null,
merged), Watcher.SCRIPT_CONTEXT)).thenReturn(compiledTemplate);
merged), Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(compiledTemplate);
TextTemplate template = templateBuilder(type, templateText, params);
assertThat(engine.render(template, model), is("rendered_text"));
@ -75,7 +72,7 @@ public class TextTemplateTests extends ESTestCase {
CompiledTemplate compiledTemplate = templateParams -> "rendered_text";
when(service.compileTemplate(new Script(type, lang, templateText,
type == ScriptType.INLINE ? Collections.singletonMap("content_type", "text/plain") : null,
model), Watcher.SCRIPT_CONTEXT)).thenReturn(compiledTemplate);
model), Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(compiledTemplate);
TextTemplate template = templateBuilder(type, templateText, params);
assertThat(engine.render(template, model), is("rendered_text"));
@ -87,7 +84,7 @@ public class TextTemplateTests extends ESTestCase {
CompiledTemplate compiledTemplate = templateParams -> "rendered_text";
when(service.compileTemplate(new Script(ScriptType.INLINE, lang, templateText,
Collections.singletonMap("content_type", "text/plain"), model), Watcher.SCRIPT_CONTEXT))
Collections.singletonMap("content_type", "text/plain"), model), Watcher.SCRIPT_EXECUTABLE_CONTEXT))
.thenReturn(compiledTemplate);
TextTemplate template = new TextTemplate(templateText);

View File

@ -458,7 +458,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
};
CompiledTemplate compiledScript = mock(CompiledTemplate.class);
when(scriptService.compileTemplate(any(Script.class), eq(ScriptContext.SEARCH))).thenReturn(compiledScript);
when(scriptService.compileTemplate(any(Script.class), eq(ScriptContext.EXECUTABLE))).thenReturn(compiledScript);
XContentBuilder builder = jsonBuilder();
String query = new TermQueryBuilder("field", "{{_user.username}}").toXContent(builder, ToXContent.EMPTY_PARAMS).string();
@ -469,7 +469,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
securityIndexSearcherWrapper.evaluateTemplate(querySource);
ArgumentCaptor<Script> argument = ArgumentCaptor.forClass(Script.class);
verify(scriptService).compileTemplate(argument.capture(), eq(ScriptContext.SEARCH));
verify(scriptService).compileTemplate(argument.capture(), eq(ScriptContext.EXECUTABLE));
Script usedScript = argument.getValue();
assertThat(usedScript.getIdOrCode(), equalTo(script.getIdOrCode()));
assertThat(usedScript.getType(), equalTo(script.getType()));

View File

@ -84,7 +84,7 @@ public class ScriptConditionTests extends ESTestCase {
ScriptEngine engine = new MockScriptEngine(MockScriptEngine.NAME, scripts);
scriptService = new ScriptService(Settings.EMPTY, Collections.singletonMap(engine.getType(), engine),
Collections.singletonMap(Watcher.SCRIPT_CONTEXT.name, Watcher.SCRIPT_CONTEXT));
Collections.singletonMap(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT));
ClusterState.Builder clusterState = new ClusterState.Builder(new ClusterName("_name"));
clusterState.metaData(MetaData.builder().putCustom(ScriptMetaData.TYPE, new ScriptMetaData.Builder(null).build()));

View File

@ -237,7 +237,7 @@ public final class WatcherTestUtils {
.put("path.home", createTempDir())
.build();
Map<String, ScriptContext> contexts = new HashMap<>(ScriptContext.BUILTINS);
contexts.put(Watcher.SCRIPT_CONTEXT.name, Watcher.SCRIPT_CONTEXT);
contexts.put(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
return new ScriptService(settings, Collections.emptyMap(), Collections.emptyMap());
}

View File

@ -173,7 +173,7 @@ public class SearchInputTests extends ESIntegTestCase {
@Override
public List<ScriptContext> getContexts() {
return Collections.singletonList(Watcher.SCRIPT_CONTEXT);
return Collections.singletonList(Watcher.SCRIPT_EXECUTABLE_CONTEXT);
}
}
}

View File

@ -301,7 +301,7 @@ public class SearchTransformTests extends ESIntegTestCase {
@Override
public List<ScriptContext> getContexts() {
return Collections.singletonList(Watcher.SCRIPT_CONTEXT);
return Collections.singletonList(Watcher.SCRIPT_EXECUTABLE_CONTEXT);
}
}
}

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.transform.script;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptException;
@ -61,8 +60,8 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, "_lang", "_script", params);
CompiledScript compiledScript = mock(CompiledScript.class);
when(service.compile(script, Watcher.SCRIPT_CONTEXT)).thenReturn(compiledScript);
ExecutableScript.Compiled compiledScript = mock(ExecutableScript.Compiled.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(compiledScript);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -75,7 +74,7 @@ public class ScriptTransformTests extends ESTestCase {
ExecutableScript executable = mock(ExecutableScript.class);
when(executable.run()).thenReturn(transformed);
when(service.executable(compiledScript, model)).thenReturn(executable);
when(compiledScript.newInstance(model)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -89,8 +88,8 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, "_lang", "_script", params);
CompiledScript compiledScript = mock(CompiledScript.class);
when(service.compile(script, Watcher.SCRIPT_CONTEXT)).thenReturn(compiledScript);
ExecutableScript.Compiled compiledScript = mock(ExecutableScript.Compiled.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(compiledScript);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -101,7 +100,7 @@ public class ScriptTransformTests extends ESTestCase {
ExecutableScript executable = mock(ExecutableScript.class);
when(executable.run()).thenThrow(new RuntimeException("_error"));
when(service.executable(compiledScript, model)).thenReturn(executable);
when(compiledScript.newInstance(model)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -115,8 +114,8 @@ public class ScriptTransformTests extends ESTestCase {
ScriptType type = randomFrom(ScriptType.values());
Map<String, Object> params = Collections.emptyMap();
Script script = new Script(type, "_lang", "_script", params);
CompiledScript compiledScript = mock(CompiledScript.class);
when(service.compile(script, Watcher.SCRIPT_CONTEXT)).thenReturn(compiledScript);
ExecutableScript.Compiled compiledScript = mock(ExecutableScript.Compiled.class);
when(service.compile(script, Watcher.SCRIPT_EXECUTABLE_CONTEXT)).thenReturn(compiledScript);
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -128,7 +127,7 @@ public class ScriptTransformTests extends ESTestCase {
ExecutableScript executable = mock(ExecutableScript.class);
Object value = randomFrom("value", 1, new String[] { "value" }, Arrays.asList("value"), singleton("value"));
when(executable.run()).thenReturn(value);
when(service.executable(compiledScript, model)).thenReturn(executable);
when(compiledScript.newInstance(model)).thenReturn(executable);
Transform.Result result = transform.execute(ctx, payload);
assertThat(result, notNullValue());
@ -170,7 +169,7 @@ public class ScriptTransformTests extends ESTestCase {
String errorMessage = "expected error message";
ScriptException scriptException = new ScriptException(errorMessage, new RuntimeException("foo"),
Collections.emptyList(), "whatever", "whatever");
when(scriptService.compile(anyObject(), eq(Watcher.SCRIPT_CONTEXT))).thenThrow(scriptException);
when(scriptService.compile(anyObject(), eq(Watcher.SCRIPT_EXECUTABLE_CONTEXT))).thenThrow(scriptException);
ScriptTransformFactory transformFactory = new ScriptTransformFactory(Settings.builder().build(), scriptService);

View File

@ -39,7 +39,8 @@ public class WatcherTemplateIT extends ESTestCase {
public void init() throws Exception {
MustacheScriptEngine engine = new MustacheScriptEngine();
Map<String, ScriptEngine> engines = Collections.singletonMap(engine.getType(), engine);
Map<String, ScriptContext> contexts = Collections.singletonMap(Watcher.SCRIPT_CONTEXT.name, Watcher.SCRIPT_CONTEXT);
Map<String, ScriptContext<?, ?>> contexts =
Collections.singletonMap(Watcher.SCRIPT_EXECUTABLE_CONTEXT.name, Watcher.SCRIPT_EXECUTABLE_CONTEXT);
ScriptService scriptService = new ScriptService(Settings.EMPTY, engines, contexts);
textTemplateEngine = new TextTemplateEngine(Settings.EMPTY, scriptService);
}