Skip to content

Commit

Permalink
compile ScriptProcessor inline scripts when creating ingest pipelines (
Browse files Browse the repository at this point in the history
…#21858)

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

Fixes #21842.
  • Loading branch information
talevy committed Dec 15, 2016
1 parent f0c0f57 commit eaf82a6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 5 deletions.
Expand Up @@ -28,21 +28,21 @@
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 {

Expand All @@ -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);
Expand Down Expand Up @@ -111,16 +123,27 @@ public ScriptProcessor create(Map<String, Processor.Factory> registry, String pr
}

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);
}
}
Expand Down
Expand Up @@ -21,6 +21,7 @@

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;
Expand All @@ -31,7 +32,9 @@

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 {

Expand Down Expand Up @@ -98,4 +101,22 @@ public void testFactoryValidationAtLeastOneScriptingType() throws Exception {

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"));
}
}
Expand Up @@ -24,12 +24,10 @@

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;
Expand Down
Expand Up @@ -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" }

0 comments on commit eaf82a6

Please sign in to comment.