compile ScriptProcessor inline scripts when creating ingest pipelines (#21858)

Inline scripts defined in Ingest Pipelines are now compiled at creation time to preemptively catch errors on initialization of the pipeline.

Fixes #21842.
This commit is contained in:
Tal Levy 2016-12-14 17:26:51 -08:00 committed by GitHub
parent f0c0f571bf
commit eaf82a6e7e
4 changed files with 70 additions and 5 deletions

View File

@ -28,21 +28,21 @@ import org.elasticsearch.ingest.Processor;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import static java.util.Collections.emptyMap;
import static org.elasticsearch.common.Strings.hasLength;
import static org.elasticsearch.ingest.ConfigurationUtils.newConfigurationException;
import static org.elasticsearch.ingest.ConfigurationUtils.readOptionalMap;
import static org.elasticsearch.ingest.ConfigurationUtils.readOptionalStringProperty;
import static org.elasticsearch.ingest.ConfigurationUtils.readStringProperty;
import static org.elasticsearch.script.ScriptType.FILE;
import static org.elasticsearch.script.ScriptType.INLINE;
import static org.elasticsearch.script.ScriptType.STORED;
/**
* Processor that adds new fields with their corresponding values. If the field is already present, its value
* will be replaced with the provided one.
* Processor that evaluates a script with an ingest document in its context.
*/
public final class ScriptProcessor extends AbstractProcessor {
@ -51,12 +51,24 @@ public final class ScriptProcessor extends AbstractProcessor {
private final Script script;
private final ScriptService scriptService;
/**
* Processor that evaluates a script with an ingest document in its context
*
* @param tag The processor's tag.
* @param script The {@link Script} to execute.
* @param scriptService The {@link ScriptService} used to execute the script.
*/
ScriptProcessor(String tag, Script script, ScriptService scriptService) {
super(tag);
this.script = script;
this.scriptService = scriptService;
}
/**
* Executes the script with the Ingest document in context.
*
* @param document The Ingest document passed into the script context under the "ctx" object.
*/
@Override
public void execute(IngestDocument document) {
ExecutableScript executableScript = scriptService.executable(script, ScriptContext.Standard.INGEST);
@ -111,16 +123,27 @@ public final class ScriptProcessor extends AbstractProcessor {
}
final Script script;
String scriptPropertyUsed;
if (Strings.hasLength(file)) {
script = new Script(FILE, lang, file, (Map<String, Object>)params);
scriptPropertyUsed = "file";
} else if (Strings.hasLength(inline)) {
script = new Script(INLINE, lang, inline, (Map<String, Object>)params);
scriptPropertyUsed = "inline";
} else if (Strings.hasLength(id)) {
script = new Script(STORED, lang, id, (Map<String, Object>)params);
scriptPropertyUsed = "id";
} else {
throw newConfigurationException(TYPE, processorTag, null, "Could not initialize script");
}
// verify script is able to be compiled before successfully creating processor.
try {
scriptService.compile(script, ScriptContext.Standard.INGEST, script.getOptions());
} catch (ScriptException e) {
throw newConfigurationException(TYPE, processorTag, scriptPropertyUsed, e);
}
return new ScriptProcessor(processorTag, script, scriptService);
}
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.ingest.common;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
@ -31,7 +32,9 @@ import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ScriptProcessorFactoryTests extends ESTestCase {
@ -98,4 +101,22 @@ public class ScriptProcessorFactoryTests extends ESTestCase {
assertThat(exception.getMessage(), is("Need [file], [id], or [inline] parameter to refer to scripts"));
}
public void testFactoryInvalidateWithInvalidCompiledScript() throws Exception {
String randomType = randomFrom("inline", "file", "id");
ScriptService mockedScriptService = mock(ScriptService.class);
ScriptException thrownException = new ScriptException("compile-time exception", new RuntimeException(),
Collections.emptyList(), "script", "mockscript");
when(mockedScriptService.compile(any(), any(), any())).thenThrow(thrownException);
factory = new ScriptProcessor.Factory(mockedScriptService);
Map<String, Object> configMap = new HashMap<>();
configMap.put("lang", "mockscript");
configMap.put(randomType, "my_script");
ElasticsearchException exception = expectThrows(ElasticsearchException.class,
() -> factory.create(null, randomAsciiOfLength(10), configMap));
assertThat(exception.getMessage(), is("compile-time exception"));
}
}

View File

@ -24,12 +24,10 @@ import java.util.Map;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.mockito.stubbing.Answer;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.core.Is.is;

View File

@ -115,3 +115,26 @@
- match: { _source.bytes_in: 1234 }
- match: { _source.bytes_out: 4321 }
- match: { _source.bytes_total: 5555 }
---
"Test script processor with syntax error in inline script":
- do:
catch: request
ingest.put_pipeline:
id: "my_pipeline"
body: >
{
"description": "_description",
"processors": [
{
"script" : {
"inline": "invalid painless, hear me roar!"
}
}
]
}
- match: { error.header.processor_type: "script" }
- match: { error.header.property_name: "inline" }
- match: { error.type: "script_exception" }
- match: { error.reason: "compile error" }