Disallow lang to be used with Stored Scripts (#25610)
Requests that execute a stored script will no longer be allowed to specify the lang of the script. This information is stored in the cluster state making only an id necessary to execute against. Putting a stored script will still require a lang.
This commit is contained in:
parent
8d7cbc43b5
commit
d2b4f7ac5a
|
@ -283,7 +283,7 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||||
request = new UpdateRequest("posts", "doc", "1").fetchSource(true);
|
request = new UpdateRequest("posts", "doc", "1").fetchSource(true);
|
||||||
//tag::update-request-with-stored-script
|
//tag::update-request-with-stored-script
|
||||||
Script stored =
|
Script stored =
|
||||||
new Script(ScriptType.STORED, "painless", "increment-field", parameters); // <1>
|
new Script(ScriptType.STORED, null, "increment-field", parameters); // <1>
|
||||||
request.script(stored); // <2>
|
request.script(stored); // <2>
|
||||||
//end::update-request-with-stored-script
|
//end::update-request-with-stored-script
|
||||||
updateResponse = client.update(request);
|
updateResponse = client.update(request);
|
||||||
|
|
|
@ -339,7 +339,7 @@ public class QueryDSLDocumentationTests extends ESTestCase {
|
||||||
parameters.put("param1", 5);
|
parameters.put("param1", 5);
|
||||||
scriptQuery(new Script(
|
scriptQuery(new Script(
|
||||||
ScriptType.STORED, // <1>
|
ScriptType.STORED, // <1>
|
||||||
"painless", // <2>
|
null, // <2>
|
||||||
"myscript", // <3>
|
"myscript", // <3>
|
||||||
singletonMap("param1", 5))); // <4>
|
singletonMap("param1", 5))); // <4>
|
||||||
// end::script_file
|
// end::script_file
|
||||||
|
|
|
@ -19,15 +19,12 @@
|
||||||
|
|
||||||
package org.elasticsearch.script;
|
package org.elasticsearch.script;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
|
||||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
|
@ -66,8 +63,7 @@ import java.util.Objects;
|
||||||
* <li> {@link ScriptType#STORED}
|
* <li> {@link ScriptType#STORED}
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li> {@link Script#lang} - the language will be specified when storing the script, so this should
|
* <li> {@link Script#lang} - the language will be specified when storing the script, so this should
|
||||||
* be {@code null}; however, this can be specified to look up a stored
|
* be {@code null}
|
||||||
* script as part of the deprecated API
|
|
||||||
* <li> {@link Script#idOrCode} - specifies the id of the stored script to be looked up, must not be {@code null}
|
* <li> {@link Script#idOrCode} - specifies the id of the stored script to be looked up, must not be {@code null}
|
||||||
* <li> {@link Script#options} - compiler options will be specified when a stored script is stored,
|
* <li> {@link Script#options} - compiler options will be specified when a stored script is stored,
|
||||||
* so they have no meaning here and must be {@code null}
|
* so they have no meaning here and must be {@code null}
|
||||||
|
@ -78,16 +74,6 @@ import java.util.Objects;
|
||||||
*/
|
*/
|
||||||
public final class Script implements ToXContentObject, Writeable {
|
public final class Script implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard logger necessary for allocation of the deprecation logger.
|
|
||||||
*/
|
|
||||||
private static final Logger LOGGER = ESLoggerFactory.getLogger(ScriptMetaData.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecation logger necessary for namespace changes related to stored scripts.
|
|
||||||
*/
|
|
||||||
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(LOGGER);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the of the default scripting language.
|
* The name of the of the default scripting language.
|
||||||
*/
|
*/
|
||||||
|
@ -233,7 +219,7 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
if (idOrCode == null) {
|
if (idOrCode == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"must specify <id> for an [" + ScriptType.INLINE.getParseField().getPreferredName() + "] script");
|
"must specify <id> for an inline script");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.size() > 1 || options.size() == 1 && options.get(CONTENT_TYPE_OPTION) == null) {
|
if (options.size() > 1 || options.size() == 1 && options.get(CONTENT_TYPE_OPTION) == null) {
|
||||||
|
@ -242,26 +228,21 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
throw new IllegalArgumentException("illegal compiler options [" + options + "] specified");
|
throw new IllegalArgumentException("illegal compiler options [" + options + "] specified");
|
||||||
}
|
}
|
||||||
} else if (type == ScriptType.STORED) {
|
} else if (type == ScriptType.STORED) {
|
||||||
// Only issue this deprecation warning if we aren't using a template. Templates during
|
if (lang != null) {
|
||||||
// this deprecation phase must always specify the default template language or they would
|
throw new IllegalArgumentException(
|
||||||
// possibly pick up a script in a different language as defined by the user under the new
|
"illegally specified <lang> for a stored script");
|
||||||
// namespace unintentionally.
|
|
||||||
if (lang != null && lang.equals(DEFAULT_TEMPLATE_LANG) == false) {
|
|
||||||
DEPRECATION_LOGGER.deprecated("specifying the field [" + LANG_PARSE_FIELD.getPreferredName() + "] " +
|
|
||||||
"for executing " + ScriptType.STORED + " scripts is deprecated; use only the field " +
|
|
||||||
"[" + ScriptType.STORED.getParseField().getPreferredName() + "] to specify an <id>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idOrCode == null) {
|
if (idOrCode == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"must specify <code> for an [" + ScriptType.STORED.getParseField().getPreferredName() + "] script");
|
"must specify <code> for a stored script");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
options = null;
|
options = null;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("field [" + OPTIONS_PARSE_FIELD.getPreferredName() + "] " +
|
throw new IllegalArgumentException("field [" + OPTIONS_PARSE_FIELD.getPreferredName() + "] " +
|
||||||
"cannot be specified using a [" + ScriptType.STORED.getParseField().getPreferredName() + "] script");
|
"cannot be specified using a stored script");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,11 +404,14 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
this.lang = Objects.requireNonNull(lang);
|
this.lang = Objects.requireNonNull(lang);
|
||||||
this.options = Collections.unmodifiableMap(Objects.requireNonNull(options));
|
this.options = Collections.unmodifiableMap(Objects.requireNonNull(options));
|
||||||
} else if (type == ScriptType.STORED) {
|
} else if (type == ScriptType.STORED) {
|
||||||
this.lang = lang;
|
if (lang != null) {
|
||||||
|
throw new IllegalArgumentException("lang cannot be specified for stored scripts");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lang = null;
|
||||||
|
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("options cannot be specified for stored scripts");
|
||||||
"options must be null for [" + ScriptType.STORED.getParseField().getPreferredName() + "] scripts");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options = null;
|
this.options = null;
|
||||||
|
@ -455,7 +439,8 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
// same order as the constructor.
|
// same order as the constructor.
|
||||||
} else if (in.getVersion().onOrAfter(Version.V_5_1_1)) {
|
} else if (in.getVersion().onOrAfter(Version.V_5_1_1)) {
|
||||||
this.type = ScriptType.readFrom(in);
|
this.type = ScriptType.readFrom(in);
|
||||||
this.lang = in.readString();
|
String lang = in.readString();
|
||||||
|
this.lang = this.type == ScriptType.STORED ? null : lang;
|
||||||
|
|
||||||
this.idOrCode = in.readString();
|
this.idOrCode = in.readString();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -482,7 +467,7 @@ public final class Script implements ToXContentObject, Writeable {
|
||||||
String lang = in.readOptionalString();
|
String lang = in.readOptionalString();
|
||||||
|
|
||||||
if (lang == null) {
|
if (lang == null) {
|
||||||
this.lang = DEFAULT_SCRIPT_LANG;
|
this.lang = this.type == ScriptType.STORED ? null : DEFAULT_SCRIPT_LANG;
|
||||||
} else {
|
} else {
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,28 +231,11 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
|
||||||
|
|
||||||
String id = idOrCode;
|
String id = idOrCode;
|
||||||
|
|
||||||
// lang may be null when looking up a stored script, so we must get the
|
|
||||||
// source to retrieve the lang before checking if the context is supported
|
|
||||||
if (type == ScriptType.STORED) {
|
if (type == ScriptType.STORED) {
|
||||||
// search template requests can possibly pass in the entire path instead
|
// * lang and options will both be null when looking up a stored script,
|
||||||
// of just an id for looking up a stored script, so we parse the path and
|
// so we must get the source to retrieve the them before checking if the
|
||||||
// check for appropriate errors
|
// context is supported
|
||||||
String[] path = id.split("/");
|
// * a stored script must be pulled from the cluster state every time in case
|
||||||
|
|
||||||
if (path.length == 3) {
|
|
||||||
if (lang != null && lang.equals(path[1]) == false) {
|
|
||||||
throw new IllegalStateException("conflicting script languages, found [" + path[1] + "] but expected [" + lang + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
id = path[2];
|
|
||||||
|
|
||||||
deprecationLogger.deprecated("use of </lang/id> [" + idOrCode + "] for looking up" +
|
|
||||||
" stored scripts/templates has been deprecated, use only <id> [" + id + "] instead");
|
|
||||||
} else if (path.length != 1) {
|
|
||||||
throw new IllegalArgumentException("illegal stored script format [" + id + "] use only <id>");
|
|
||||||
}
|
|
||||||
|
|
||||||
// a stored script must be pulled from the cluster state every time in case
|
|
||||||
// the script has been updated since the last compilation
|
// the script has been updated since the last compilation
|
||||||
StoredScriptSource source = getScriptFromClusterState(id, lang);
|
StoredScriptSource source = getScriptFromClusterState(id, lang);
|
||||||
lang = source.getLang();
|
lang = source.getLang();
|
||||||
|
@ -262,7 +245,7 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
|
||||||
|
|
||||||
// TODO: fix this through some API or something, that's wrong
|
// TODO: fix this through some API or something, that's wrong
|
||||||
// special exception to prevent expressions from compiling as update or mapping scripts
|
// special exception to prevent expressions from compiling as update or mapping scripts
|
||||||
boolean expression = "expression".equals(script.getLang());
|
boolean expression = "expression".equals(lang);
|
||||||
boolean notSupported = context.name.equals(ExecutableScript.UPDATE_CONTEXT.name);
|
boolean notSupported = context.name.equals(ExecutableScript.UPDATE_CONTEXT.name);
|
||||||
if (expression && notSupported) {
|
if (expression && notSupported) {
|
||||||
throw new UnsupportedOperationException("scripts of type [" + script.getType() + "]," +
|
throw new UnsupportedOperationException("scripts of type [" + script.getType() + "]," +
|
||||||
|
@ -303,7 +286,6 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
|
||||||
try {
|
try {
|
||||||
// Either an un-cached inline script or indexed script
|
// Either an un-cached inline script or indexed script
|
||||||
// If the script type is inline the name will be the same as the code for identification in exceptions
|
// If the script type is inline the name will be the same as the code for identification in exceptions
|
||||||
|
|
||||||
// but give the script engine the chance to be better, give it separate name + source code
|
// but give the script engine the chance to be better, give it separate name + source code
|
||||||
// for the inline case, then its anonymous: null.
|
// for the inline case, then its anonymous: null.
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
|
@ -379,22 +361,16 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredScriptSource getScriptFromClusterState(String id, String lang) {
|
StoredScriptSource getScriptFromClusterState(String id, String lang) {
|
||||||
if (lang != null && isLangSupported(lang) == false) {
|
|
||||||
throw new IllegalArgumentException("unable to get stored script with unsupported lang [" + lang + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptMetaData scriptMetadata = clusterState.metaData().custom(ScriptMetaData.TYPE);
|
ScriptMetaData scriptMetadata = clusterState.metaData().custom(ScriptMetaData.TYPE);
|
||||||
|
|
||||||
if (scriptMetadata == null) {
|
if (scriptMetadata == null) {
|
||||||
throw new ResourceNotFoundException("unable to find script [" + id + "]" +
|
throw new ResourceNotFoundException("unable to find script [" + id + "] in cluster state");
|
||||||
(lang == null ? "" : " using lang [" + lang + "]") + " in cluster state");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredScriptSource source = scriptMetadata.getStoredScript(id, lang);
|
StoredScriptSource source = scriptMetadata.getStoredScript(id, lang);
|
||||||
|
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
throw new ResourceNotFoundException("unable to find script [" + id + "]" +
|
throw new ResourceNotFoundException("unable to find script [" + id + "] in cluster state");
|
||||||
(lang == null ? "" : " using lang [" + lang + "]") + " in cluster state");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.script;
|
package org.elasticsearch.script;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.elasticsearch.ResourceNotFoundException;
|
import org.elasticsearch.ResourceNotFoundException;
|
||||||
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
|
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
|
@ -40,6 +33,12 @@ import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.sameInstance;
|
import static org.hamcrest.Matchers.sameInstance;
|
||||||
|
@ -77,8 +76,11 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
scriptService = new ScriptService(finalSettings, engines, contexts) {
|
scriptService = new ScriptService(finalSettings, engines, contexts) {
|
||||||
@Override
|
@Override
|
||||||
StoredScriptSource getScriptFromClusterState(String id, String lang) {
|
StoredScriptSource getScriptFromClusterState(String id, String lang) {
|
||||||
|
if (lang != null) {
|
||||||
|
throw new IllegalArgumentException("expected null lang in test");
|
||||||
|
}
|
||||||
//mock the script that gets retrieved from an index
|
//mock the script that gets retrieved from an index
|
||||||
return new StoredScriptSource(lang, "1+1", Collections.emptyMap());
|
return new StoredScriptSource("test", "1+1", Collections.emptyMap());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -128,7 +130,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
buildScriptService(Settings.EMPTY);
|
buildScriptService(Settings.EMPTY);
|
||||||
|
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||||
assertCompileAccepted("painless", "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileAccepted(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowAllScriptContextSettings() throws IOException {
|
public void testAllowAllScriptContextSettings() throws IOException {
|
||||||
|
@ -146,7 +148,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||||
assertCompileRejected("painless", "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowSomeScriptContextSettings() throws IOException {
|
public void testAllowSomeScriptContextSettings() throws IOException {
|
||||||
|
@ -165,7 +167,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||||
assertCompileRejected("painless", "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowNoScriptContextSettings() throws IOException {
|
public void testAllowNoScriptContextSettings() throws IOException {
|
||||||
|
@ -183,7 +185,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
|
|
||||||
String type = scriptEngine.getType();
|
String type = scriptEngine.getType();
|
||||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () ->
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () ->
|
||||||
scriptService.compile(new Script(randomFrom(ScriptType.values()), type, "test", Collections.emptyMap()), ExecutableScript.INGEST_CONTEXT));
|
scriptService.compile(new Script(ScriptType.INLINE, type, "test", Collections.emptyMap()), ExecutableScript.INGEST_CONTEXT));
|
||||||
assertThat(e.getMessage(), containsString("script context [" + ExecutableScript.INGEST_CONTEXT.name + "] not supported"));
|
assertThat(e.getMessage(), containsString("script context [" + ExecutableScript.INGEST_CONTEXT.name + "] not supported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +218,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIndexedScriptCountedInCompilationStats() throws IOException {
|
public void testIndexedScriptCountedInCompilationStats() throws IOException {
|
||||||
buildScriptService(Settings.EMPTY);
|
buildScriptService(Settings.EMPTY);
|
||||||
scriptService.compile(new Script(ScriptType.STORED, "test", "script", Collections.emptyMap()), randomFrom(contexts.values()));
|
scriptService.compile(new Script(ScriptType.STORED, null, "script", Collections.emptyMap()), randomFrom(contexts.values()));
|
||||||
assertEquals(1L, scriptService.stats().getCompilations());
|
assertEquals(1L, scriptService.stats().getCompilations());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -791,13 +791,13 @@ public class ScriptedMetricIT extends ESIntegTestCase {
|
||||||
scriptedMetric("scripted")
|
scriptedMetric("scripted")
|
||||||
.params(params)
|
.params(params)
|
||||||
.initScript(
|
.initScript(
|
||||||
new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "initScript_stored", Collections.emptyMap()))
|
new Script(ScriptType.STORED, null, "initScript_stored", Collections.emptyMap()))
|
||||||
.mapScript(
|
.mapScript(
|
||||||
new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "mapScript_stored", Collections.emptyMap()))
|
new Script(ScriptType.STORED, null, "mapScript_stored", Collections.emptyMap()))
|
||||||
.combineScript(
|
.combineScript(
|
||||||
new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "combineScript_stored", Collections.emptyMap()))
|
new Script(ScriptType.STORED, null, "combineScript_stored", Collections.emptyMap()))
|
||||||
.reduceScript(
|
.reduceScript(
|
||||||
new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "reduceScript_stored", Collections.emptyMap())))
|
new Script(ScriptType.STORED, null, "reduceScript_stored", Collections.emptyMap())))
|
||||||
.get();
|
.get();
|
||||||
assertSearchResponse(response);
|
assertSearchResponse(response);
|
||||||
assertThat(response.getHits().getTotalHits(), equalTo(numDocs));
|
assertThat(response.getHits().getTotalHits(), equalTo(numDocs));
|
||||||
|
|
|
@ -496,7 +496,7 @@ public class BucketScriptIT extends ESIntegTestCase {
|
||||||
.subAggregation(sum("field4Sum").field(FIELD_4_NAME))
|
.subAggregation(sum("field4Sum").field(FIELD_4_NAME))
|
||||||
.subAggregation(
|
.subAggregation(
|
||||||
bucketScript("seriesArithmetic",
|
bucketScript("seriesArithmetic",
|
||||||
new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "my_script", Collections.emptyMap()),
|
new Script(ScriptType.STORED, null, "my_script", Collections.emptyMap()),
|
||||||
"field2Sum", "field3Sum", "field4Sum"))).execute().actionGet();
|
"field2Sum", "field3Sum", "field4Sum"))).execute().actionGet();
|
||||||
|
|
||||||
assertSearchResponse(response);
|
assertSearchResponse(response);
|
||||||
|
|
|
@ -438,7 +438,7 @@ public class BucketSelectorIT extends ESIntegTestCase {
|
||||||
.setContent(new BytesArray("{ \"script\": \"Double.isNaN(_value0) ? false : (_value0 + _value1 > 100)\" }"),
|
.setContent(new BytesArray("{ \"script\": \"Double.isNaN(_value0) ? false : (_value0 + _value1 > 100)\" }"),
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
|
|
||||||
Script script = new Script(ScriptType.STORED, CustomScriptPlugin.NAME, "my_script", Collections.emptyMap());
|
Script script = new Script(ScriptType.STORED, null, "my_script", Collections.emptyMap());
|
||||||
|
|
||||||
SearchResponse response = client()
|
SearchResponse response = client()
|
||||||
.prepareSearch("idx")
|
.prepareSearch("idx")
|
||||||
|
|
|
@ -26,3 +26,11 @@ The `_index` variable has been removed. If you used it for advanced scoring, con
|
||||||
|
|
||||||
All of the existing scripting security settings have been removed. Instead
|
All of the existing scripting security settings have been removed. Instead
|
||||||
they are replaced with `script.allowed_types` and `script.allowed_contexts`.
|
they are replaced with `script.allowed_types` and `script.allowed_contexts`.
|
||||||
|
|
||||||
|
==== `lang` can no longer be specified when using a stored script as part of a request
|
||||||
|
|
||||||
|
The `lang` variable can no longer be specified as part of a request that uses a stored
|
||||||
|
script otherwise an error will occur. Note that a request using a stored script is
|
||||||
|
different from a request that puts a stored script. The language of the script has
|
||||||
|
already been stored as part of the cluster state and an `id` is sufficient to access
|
||||||
|
all of the information necessary to execute a stored script.
|
|
@ -115,28 +115,6 @@ minute will be compiled. You can change this setting dynamically by setting
|
||||||
Scripts may be stored in and retrieved from the cluster state using the
|
Scripts may be stored in and retrieved from the cluster state using the
|
||||||
`_scripts` end-point.
|
`_scripts` end-point.
|
||||||
|
|
||||||
==== Deprecated Namespace
|
|
||||||
|
|
||||||
The namespace for stored scripts using both `lang` and `id` as a unique
|
|
||||||
identifier has been deprecated. The new namespace for stored scripts will
|
|
||||||
only use `id`. Stored scripts with the same `id`, but different `lang`'s
|
|
||||||
will no longer be allowed in 6.0. To comply with the new namespace for
|
|
||||||
stored scripts, existing stored scripts should be deleted and put again.
|
|
||||||
Any scripts that share an `id` but have different `lang`s will need to
|
|
||||||
be re-named. For example, take the following:
|
|
||||||
|
|
||||||
"id": "example", "lang": "painless"
|
|
||||||
"id": "example", "lang": "expressions"
|
|
||||||
|
|
||||||
The above scripts will conflict under the new namespace since the id's are
|
|
||||||
the same. At least one will have to be re-named to comply with the new
|
|
||||||
namespace of only `id`.
|
|
||||||
|
|
||||||
As a final caveat, stored search templates and stored scripts share
|
|
||||||
the same namespace, so if a search template has the same `id` as a
|
|
||||||
stored script, one of the two will have to be re-named as well using
|
|
||||||
delete and put requests.
|
|
||||||
|
|
||||||
==== Request Examples
|
==== Request Examples
|
||||||
|
|
||||||
The following are examples of using a stored script that lives at
|
The following are examples of using a stored script that lives at
|
||||||
|
|
|
@ -136,7 +136,7 @@ public final class ScriptProcessor extends AbstractProcessor {
|
||||||
script = new Script(INLINE, lang, source, (Map<String, Object>)params);
|
script = new Script(INLINE, lang, source, (Map<String, Object>)params);
|
||||||
scriptPropertyUsed = "source";
|
scriptPropertyUsed = "source";
|
||||||
} else if (Strings.hasLength(id)) {
|
} else if (Strings.hasLength(id)) {
|
||||||
script = new Script(STORED, lang, id, (Map<String, Object>)params);
|
script = new Script(STORED, null, id, (Map<String, Object>)params);
|
||||||
scriptPropertyUsed = "id";
|
scriptPropertyUsed = "id";
|
||||||
} else {
|
} else {
|
||||||
throw newConfigurationException(TYPE, processorTag, null, "Could not initialize script");
|
throw newConfigurationException(TYPE, processorTag, null, "Could not initialize script");
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class ScriptProcessorFactoryTests extends ESTestCase {
|
||||||
String randomType = randomFrom("id", "source");
|
String randomType = randomFrom("id", "source");
|
||||||
configMap.put(randomType, "foo");
|
configMap.put(randomType, "foo");
|
||||||
ScriptProcessor processor = factory.create(null, randomAlphaOfLength(10), configMap);
|
ScriptProcessor processor = factory.create(null, randomAlphaOfLength(10), configMap);
|
||||||
assertThat(processor.getScript().getLang(), equalTo(Script.DEFAULT_SCRIPT_LANG));
|
assertThat(processor.getScript().getLang(), equalTo(randomType.equals("id") ? null : Script.DEFAULT_SCRIPT_LANG));
|
||||||
assertThat(processor.getScript().getType().toString(), equalTo(ingestScriptParamToType.get(randomType)));
|
assertThat(processor.getScript().getType().toString(), equalTo(ingestScriptParamToType.get(randomType)));
|
||||||
assertThat(processor.getScript().getParams(), equalTo(Collections.emptyMap()));
|
assertThat(processor.getScript().getParams(), equalTo(Collections.emptyMap()));
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ public class ScriptProcessorFactoryTests extends ESTestCase {
|
||||||
configMap.put(randomType, "foo");
|
configMap.put(randomType, "foo");
|
||||||
configMap.put("params", randomParams);
|
configMap.put("params", randomParams);
|
||||||
ScriptProcessor processor = factory.create(null, randomAlphaOfLength(10), configMap);
|
ScriptProcessor processor = factory.create(null, randomAlphaOfLength(10), configMap);
|
||||||
assertThat(processor.getScript().getLang(), equalTo(Script.DEFAULT_SCRIPT_LANG));
|
assertThat(processor.getScript().getLang(), equalTo(randomType.equals("id") ? null : Script.DEFAULT_SCRIPT_LANG));
|
||||||
assertThat(processor.getScript().getType().toString(), equalTo(ingestScriptParamToType.get(randomType)));
|
assertThat(processor.getScript().getType().toString(), equalTo(ingestScriptParamToType.get(randomType)));
|
||||||
assertThat(processor.getScript().getParams(), equalTo(randomParams));
|
assertThat(processor.getScript().getParams(), equalTo(randomParams));
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class StoredExpressionTests extends ESIntegTestCase {
|
||||||
client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON).get();
|
client().prepareIndex("test", "scriptTest", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON).get();
|
||||||
try {
|
try {
|
||||||
client().prepareUpdate("test", "scriptTest", "1")
|
client().prepareUpdate("test", "scriptTest", "1")
|
||||||
.setScript(new Script(ScriptType.STORED, ExpressionScriptEngine.NAME, "script1", Collections.emptyMap())).get();
|
.setScript(new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())).get();
|
||||||
fail("update script should have been rejected");
|
fail("update script should have been rejected");
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
assertThat(e.getMessage(), containsString("failed to execute script"));
|
assertThat(e.getMessage(), containsString("failed to execute script"));
|
||||||
|
@ -67,7 +67,7 @@ public class StoredExpressionTests extends ESIntegTestCase {
|
||||||
try {
|
try {
|
||||||
client().prepareSearch()
|
client().prepareSearch()
|
||||||
.setSource(
|
.setSource(
|
||||||
new SearchSourceBuilder().scriptField("test1", new Script(ScriptType.STORED, "expression", "script1", Collections.emptyMap())))
|
new SearchSourceBuilder().scriptField("test1", new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())))
|
||||||
.setIndices("test").setTypes("scriptTest").get();
|
.setIndices("test").setTypes("scriptTest").get();
|
||||||
fail("search script should have been rejected");
|
fail("search script should have been rejected");
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
@ -77,7 +77,7 @@ public class StoredExpressionTests extends ESIntegTestCase {
|
||||||
client().prepareSearch("test")
|
client().prepareSearch("test")
|
||||||
.setSource(
|
.setSource(
|
||||||
new SearchSourceBuilder().aggregation(AggregationBuilders.terms("test").script(
|
new SearchSourceBuilder().aggregation(AggregationBuilders.terms("test").script(
|
||||||
new Script(ScriptType.STORED, "expression", "script1", Collections.emptyMap())))).get();
|
new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())))).get();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertThat(e.toString(), containsString("cannot execute scripts using [aggs] context"));
|
assertThat(e.toString(), containsString("cannot execute scripts using [aggs] context"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
import org.elasticsearch.script.ScriptType;
|
||||||
import org.elasticsearch.script.TemplateScript;
|
import org.elasticsearch.script.TemplateScript;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
@ -95,7 +96,8 @@ public class TransportSearchTemplateAction extends HandledTransportAction<Search
|
||||||
|
|
||||||
static SearchRequest convert(SearchTemplateRequest searchTemplateRequest, SearchTemplateResponse response, ScriptService scriptService,
|
static SearchRequest convert(SearchTemplateRequest searchTemplateRequest, SearchTemplateResponse response, ScriptService scriptService,
|
||||||
NamedXContentRegistry xContentRegistry) throws IOException {
|
NamedXContentRegistry xContentRegistry) throws IOException {
|
||||||
Script script = new Script(searchTemplateRequest.getScriptType(), TEMPLATE_LANG, searchTemplateRequest.getScript(),
|
Script script = new Script(searchTemplateRequest.getScriptType(),
|
||||||
|
searchTemplateRequest.getScriptType() == ScriptType.STORED ? null : TEMPLATE_LANG, searchTemplateRequest.getScript(),
|
||||||
searchTemplateRequest.getScriptParams() == null ? Collections.emptyMap() : searchTemplateRequest.getScriptParams());
|
searchTemplateRequest.getScriptParams() == null ? Collections.emptyMap() : searchTemplateRequest.getScriptParams());
|
||||||
TemplateScript compiledScript = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(script.getParams());
|
TemplateScript compiledScript = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(script.getParams());
|
||||||
String source = compiledScript.execute();
|
String source = compiledScript.execute();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.script.mustache;
|
package org.elasticsearch.script.mustache;
|
||||||
|
|
||||||
|
import org.elasticsearch.ResourceNotFoundException;
|
||||||
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
|
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
|
||||||
import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
|
@ -201,12 +202,6 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
|
||||||
getResponse = client().admin().cluster()
|
getResponse = client().admin().cluster()
|
||||||
.prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get();
|
.prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get();
|
||||||
assertNull(getResponse.getSource());
|
assertNull(getResponse.getSource());
|
||||||
|
|
||||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
|
|
||||||
.setRequest(new SearchRequest("test").types("type"))
|
|
||||||
.setScript("/template_index/mustache/1000").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
|
|
||||||
.get());
|
|
||||||
assertThat(e.getMessage(), containsString("illegal stored script format [/template_index/mustache/1000] use only <id>"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexedTemplate() throws Exception {
|
public void testIndexedTemplate() throws Exception {
|
||||||
|
@ -266,16 +261,9 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
|
||||||
.get();
|
.get();
|
||||||
assertHitCount(searchResponse.getResponse(), 4);
|
assertHitCount(searchResponse.getResponse(), 4);
|
||||||
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
|
expectThrows(ResourceNotFoundException.class, () -> new SearchTemplateRequestBuilder(client())
|
||||||
.setRequest(new SearchRequest().indices("test").types("type"))
|
.setRequest(new SearchRequest().indices("test").types("type"))
|
||||||
.setScript("/template_index/mustache/1000")
|
.setScript("1000")
|
||||||
.setScriptType(ScriptType.STORED)
|
|
||||||
.setScriptParams(templateParams)
|
|
||||||
.get());
|
|
||||||
|
|
||||||
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
|
|
||||||
.setRequest(new SearchRequest().indices("test").types("type"))
|
|
||||||
.setScript("/myindex/mustache/1")
|
|
||||||
.setScriptType(ScriptType.STORED)
|
.setScriptType(ScriptType.STORED)
|
||||||
.setScriptParams(templateParams)
|
.setScriptParams(templateParams)
|
||||||
.get());
|
.get());
|
||||||
|
@ -283,11 +271,9 @@ public class SearchTemplateIT extends ESSingleNodeTestCase {
|
||||||
templateParams.put("fieldParam", "bar");
|
templateParams.put("fieldParam", "bar");
|
||||||
searchResponse = new SearchTemplateRequestBuilder(client())
|
searchResponse = new SearchTemplateRequestBuilder(client())
|
||||||
.setRequest(new SearchRequest("test").types("type"))
|
.setRequest(new SearchRequest("test").types("type"))
|
||||||
.setScript("/mustache/2").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
|
.setScript("2").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
|
||||||
.get();
|
.get();
|
||||||
assertHitCount(searchResponse.getResponse(), 1);
|
assertHitCount(searchResponse.getResponse(), 1);
|
||||||
assertWarnings("use of </lang/id> [/mustache/2] for looking up" +
|
|
||||||
" stored scripts/templates has been deprecated, use only <id> [2] instead");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relates to #10397
|
// Relates to #10397
|
||||||
|
|
|
@ -223,6 +223,8 @@ public class RoundTripTests extends ESTestCase {
|
||||||
String idOrCode = randomSimpleString(random());
|
String idOrCode = randomSimpleString(random());
|
||||||
Map<String, Object> params = Collections.emptyMap();
|
Map<String, Object> params = Collections.emptyMap();
|
||||||
|
|
||||||
return new Script(type, lang, idOrCode, params);
|
type = ScriptType.STORED;
|
||||||
|
|
||||||
|
return new Script(type, type == ScriptType.STORED ? null : lang, idOrCode, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue