Skip to content

Commit 2cf160f

Browse files
authored
Remove deprecated code from stored scripts (#78643)
This change removes several pieces of deprecated code from stored scripts. Stored scripts/templates are no longer allowed to be an empty and will throw an exception when used with PutStoredScript. ScriptMetadata will now drop any existing stored scripts that are empty with a deprecation warning in the case they have not been previously removed. The code field is now only allowed as source as part of a PutStoredScript JSON blob.
1 parent a74573b commit 2cf160f

File tree

6 files changed

+58
-263
lines changed

6 files changed

+58
-263
lines changed

docs/reference/migration/migrate_8_0/scripting.asciidoc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,31 @@ scripts. Any use of `getDayOfWeek` expecting a return value of `int` will result
2222
in a compilation error or runtime error and may not allow the upgraded node to
2323
start.
2424
====
25+
26+
.Stored scripts no longer support empty scripts or search templates.
27+
[%collapsible]
28+
====
29+
*Details* +
30+
The {ref}/create-stored-script-api.html[create or update stored script API]'s
31+
`source` parameter cannot be empty.
32+
33+
*Impact* +
34+
Before upgrading, use the {ref}/delete-stored-script-api.html[delete stored
35+
script API] to delete any empty stored scripts or search templates.
36+
In 8.0, {es} will drop any empty stored scripts or empty search templates from
37+
the cluster state. Requests to create a stored script or search template with
38+
an empty `source` will return an error.
39+
====
40+
41+
.The create or update stored script API's `code` parameter has been removed.
42+
[%collapsible]
43+
====
44+
*Details* +
45+
The {ref}/create-stored-script-api.html[create or update stored script API]'s
46+
`code` parameter has been removed. Use the `source` parameter instead.
47+
48+
*Impact* +
49+
Discontinue use of the `code` parameter. Requests that include the parameter
50+
will return an error.
51+
====
2552
// end::notable-breaking-changes[]

server/src/internalClusterTest/java/org/elasticsearch/script/StoredScriptsIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void testBasics() {
5555

5656
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().admin().cluster().preparePutStoredScript()
5757
.setId("id#")
58-
.setContent(new BytesArray("{}"), XContentType.JSON)
58+
.setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"1\"} }"), XContentType.JSON)
5959
.get());
6060
assertEquals("Validation Failed: 1: id cannot contain '#' for stored script;", e.getMessage());
6161
}

server/src/main/java/org/elasticsearch/script/ScriptMetadata.java

Lines changed: 15 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*/
88
package org.elasticsearch.script;
99

10+
import org.apache.logging.log4j.LogManager;
11+
import org.apache.logging.log4j.Logger;
1012
import org.elasticsearch.ResourceNotFoundException;
1113
import org.elasticsearch.Version;
1214
import org.elasticsearch.cluster.ClusterState;
@@ -18,8 +20,6 @@
1820
import org.elasticsearch.common.io.stream.StreamInput;
1921
import org.elasticsearch.common.io.stream.StreamOutput;
2022
import org.elasticsearch.common.io.stream.Writeable;
21-
import org.elasticsearch.common.logging.DeprecationCategory;
22-
import org.elasticsearch.common.logging.DeprecationLogger;
2323
import org.elasticsearch.common.xcontent.ToXContentFragment;
2424
import org.elasticsearch.common.xcontent.XContentBuilder;
2525
import org.elasticsearch.common.xcontent.XContentParser;
@@ -38,9 +38,9 @@
3838
public final class ScriptMetadata implements Metadata.Custom, Writeable, ToXContentFragment {
3939

4040
/**
41-
* Standard deprecation logger for used to deprecate allowance of empty templates.
41+
* Standard logger used to warn about dropped scripts.
4242
*/
43-
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(ScriptMetadata.class);
43+
private static final Logger logger = LogManager.getLogger(ScriptMetadata.class);
4444

4545
/**
4646
* A builder used to modify the currently stored scripts data held within
@@ -162,16 +162,10 @@ static ScriptMetadata deleteStoredScript(ScriptMetadata previous, String id) {
162162
* ...
163163
* }
164164
* }
165-
*
166-
* When loading from a source prior to 6.0, if multiple scripts
167-
* using the old namespace id format of [lang#id] are found to have the
168-
* same id but different languages an error will occur.
169165
*/
170166
public static ScriptMetadata fromXContent(XContentParser parser) throws IOException {
171167
Map<String, StoredScriptSource> scripts = new HashMap<>();
172168
String id = null;
173-
StoredScriptSource source;
174-
StoredScriptSource exists;
175169

176170
Token token = parser.currentToken();
177171

@@ -189,84 +183,28 @@ public static ScriptMetadata fromXContent(XContentParser parser) throws IOExcept
189183
switch (token) {
190184
case FIELD_NAME:
191185
id = parser.currentName();
192-
break;
193-
case VALUE_STRING:
194-
if (id == null) {
195-
throw new ParsingException(parser.getTokenLocation(),
196-
"unexpected token [" + token + "], expected [<id>, <code>, {]");
197-
}
198-
199-
int split = id.indexOf('#');
200-
String lang;
201-
202-
if (split == -1) {
203-
throw new IllegalArgumentException("illegal stored script id [" + id + "], does not contain lang");
204-
} else {
205-
lang = id.substring(0, split);
206-
id = id.substring(split + 1);
207-
source = new StoredScriptSource(lang, parser.text(), Collections.emptyMap());
208-
209-
if (source.getSource().isEmpty()) {
210-
if (source.getLang().equals(Script.DEFAULT_TEMPLATE_LANG)) {
211-
deprecationLogger.critical(
212-
DeprecationCategory.TEMPLATES,
213-
"empty_templates",
214-
"empty templates should no longer be used"
215-
);
216-
} else {
217-
deprecationLogger.critical(
218-
DeprecationCategory.TEMPLATES,
219-
"empty_scripts",
220-
"empty scripts should no longer be used"
221-
);
222-
}
223-
}
224-
}
225-
226-
exists = scripts.get(id);
227-
228-
if (exists == null) {
229-
scripts.put(id, source);
230-
} else if (exists.getLang().equals(lang) == false) {
231-
throw new IllegalArgumentException("illegal stored script, id [" + id + "] used for multiple scripts with " +
232-
"different languages [" + exists.getLang() + "] and [" + lang + "]; scripts using the old namespace " +
233-
"of [lang#id] as a stored script id will have to be updated to use only the new namespace of [id]");
234-
}
235-
236-
id = null;
237-
238186
break;
239187
case START_OBJECT:
240188
if (id == null) {
241189
throw new ParsingException(parser.getTokenLocation(),
242190
"unexpected token [" + token + "], expected [<id>, <code>, {]");
243191
}
244192

245-
exists = scripts.get(id);
246-
source = StoredScriptSource.fromXContent(parser, true);
247-
248-
if (exists == null) {
249-
// due to a bug (https://github.com/elastic/elasticsearch/issues/47593)
250-
// scripts may have been retained during upgrade that include the old-style
251-
// id of lang#id; these scripts are unreachable after 7.0, so they are dropped
252-
if (id.contains("#") == false) {
253-
scripts.put(id, source);
193+
StoredScriptSource source = StoredScriptSource.fromXContent(parser, true);
194+
// as of 8.0 we drop scripts/templates with an empty source
195+
// this check should be removed for the next upgradable version after 8.0
196+
// since there is a guarantee no more empty scripts will exist
197+
if (source.getSource().isEmpty()) {
198+
if (Script.DEFAULT_TEMPLATE_LANG.equals(source.getLang())) {
199+
logger.warn("empty template [" + id + "] found and dropped");
200+
} else {
201+
logger.warn("empty script [" + id + "] found and dropped");
254202
}
255-
} else if (exists.getLang().equals(source.getLang()) == false) {
256-
throw new IllegalArgumentException(
257-
"illegal stored script, id ["
258-
+ id
259-
+ "] used for multiple scripts with different languages ["
260-
+ exists.getLang()
261-
+ "] and ["
262-
+ source.getLang()
263-
+ "]; scripts using the old namespace of [lang#id] as a stored script id will have to be updated "
264-
+ "to use only the new namespace of [id]"
265-
);
203+
} else {
204+
scripts.put(id, source);
266205
}
267206

268207
id = null;
269-
270208
break;
271209
default:
272210
throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + "], expected [<id>, <code>, {]");
@@ -318,8 +256,6 @@ public void writeTo(StreamOutput out) throws IOException {
318256
}
319257
}
320258

321-
322-
323259
/**
324260
* This will write XContent from {@link ScriptMetadata}. The following format will be written:
325261
*

server/src/main/java/org/elasticsearch/script/StoredScriptSource.java

Lines changed: 9 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,18 @@
1111
import org.elasticsearch.cluster.AbstractDiffable;
1212
import org.elasticsearch.cluster.ClusterState;
1313
import org.elasticsearch.cluster.Diff;
14-
import org.elasticsearch.common.xcontent.ParseField;
1514
import org.elasticsearch.common.ParsingException;
1615
import org.elasticsearch.common.Strings;
1716
import org.elasticsearch.common.bytes.BytesReference;
1817
import org.elasticsearch.common.io.stream.StreamInput;
1918
import org.elasticsearch.common.io.stream.StreamOutput;
2019
import org.elasticsearch.common.io.stream.Writeable;
21-
import org.elasticsearch.common.logging.DeprecationCategory;
2220
import org.elasticsearch.common.logging.DeprecationLogger;
2321
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
2422
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
2523
import org.elasticsearch.common.xcontent.ObjectParser;
2624
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
25+
import org.elasticsearch.common.xcontent.ParseField;
2726
import org.elasticsearch.common.xcontent.ToXContentObject;
2827
import org.elasticsearch.common.xcontent.XContentBuilder;
2928
import org.elasticsearch.common.xcontent.XContentFactory;
@@ -63,7 +62,7 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
6362
/**
6463
* Standard {@link ParseField} for source on the inner level.
6564
*/
66-
public static final ParseField SOURCE_PARSE_FIELD = new ParseField("source", "code");
65+
public static final ParseField SOURCE_PARSE_FIELD = new ParseField("source");
6766

6867
/**
6968
* Standard {@link ParseField} for options on the inner level.
@@ -121,8 +120,7 @@ private void setOptions(Map<String, String> options) {
121120
* Validates the parameters and creates an {@link StoredScriptSource}.
122121
*
123122
* @param ignoreEmpty Specify as {@code true} to ignoreEmpty the empty source check.
124-
* This allow empty templates to be loaded for backwards compatibility.
125-
* This allow empty templates to be loaded for backwards compatibility.
123+
* This allow empty templates to be dropped for backwards compatibility.
126124
*/
127125
private StoredScriptSource build(boolean ignoreEmpty) {
128126
if (lang == null) {
@@ -132,29 +130,13 @@ private StoredScriptSource build(boolean ignoreEmpty) {
132130
}
133131

134132
if (source == null) {
135-
if (ignoreEmpty || Script.DEFAULT_TEMPLATE_LANG.equals(lang)) {
136-
if (Script.DEFAULT_TEMPLATE_LANG.equals(lang)) {
137-
deprecationLogger.critical(DeprecationCategory.TEMPLATES, "empty_templates",
138-
"empty templates should no longer be used");
139-
} else {
140-
deprecationLogger.critical(DeprecationCategory.TEMPLATES, "empty_scripts",
141-
"empty scripts should no longer be used");
142-
}
133+
if (ignoreEmpty) {
134+
source = "";
143135
} else {
144136
throw new IllegalArgumentException("must specify source for stored script");
145137
}
146-
} else if (source.isEmpty()) {
147-
if (ignoreEmpty || Script.DEFAULT_TEMPLATE_LANG.equals(lang)) {
148-
if (Script.DEFAULT_TEMPLATE_LANG.equals(lang)) {
149-
deprecationLogger.critical(DeprecationCategory.TEMPLATES, "empty_templates",
150-
"empty templates should no longer be used");
151-
} else {
152-
deprecationLogger.critical(DeprecationCategory.TEMPLATES, "empty_scripts",
153-
"empty scripts should no longer be used");
154-
}
155-
} else {
156-
throw new IllegalArgumentException("source cannot be empty");
157-
}
138+
} else if (source.isEmpty() && ignoreEmpty == false) {
139+
throw new IllegalArgumentException("source cannot be empty");
158140
}
159141

160142
if (options.size() > 1 || options.size() == 1 && options.get(Script.CONTENT_TYPE_OPTION) == null) {
@@ -175,21 +157,9 @@ private StoredScriptSource build(boolean ignoreEmpty) {
175157
}
176158

177159
/**
178-
* This will parse XContent into a {@link StoredScriptSource}. The following formats can be parsed:
179-
*
180-
* The simple script format with no compiler options or user-defined params:
181-
*
182-
* Example:
183-
* {@code
184-
* {"script": "return Math.log(doc.popularity) * 100;"}
185-
* }
160+
* This will parse XContent into a {@link StoredScriptSource}.
186161
*
187-
* The above format requires the lang to be specified using the deprecated stored script namespace
188-
* (as a url parameter during a put request). See {@link ScriptMetadata} for more information about
189-
* the stored script namespaces.
190-
*
191-
* The complex script format using the new stored script namespace
192-
* where lang and source are required but options is optional:
162+
* Examples of legal JSON:
193163
*
194164
* {@code
195165
* {
@@ -215,22 +185,6 @@ private StoredScriptSource build(boolean ignoreEmpty) {
215185
* }
216186
* }
217187
*
218-
* The use of "source" may also be substituted with "code" for backcompat with 5.3 to 5.5 format. For example:
219-
*
220-
* {@code
221-
* {
222-
* "script" : {
223-
* "lang" : "<lang>",
224-
* "code" : "<source>",
225-
* "options" : {
226-
* "option0" : "<option0>",
227-
* "option1" : "<option1>",
228-
* ...
229-
* }
230-
* }
231-
* }
232-
* }
233-
*
234188
* Note that the "source" parameter can also handle template parsing including from
235189
* a complex JSON object.
236190
*
@@ -249,12 +203,6 @@ public static StoredScriptSource parse(BytesReference content, XContentType xCon
249203

250204
token = parser.nextToken();
251205

252-
if (token == Token.END_OBJECT) {
253-
deprecationLogger.critical(DeprecationCategory.TEMPLATES, "empty_templates", "empty templates should no longer be used");
254-
255-
return new StoredScriptSource(Script.DEFAULT_TEMPLATE_LANG, "", Collections.emptyMap());
256-
}
257-
258206
if (token != Token.FIELD_NAME) {
259207
throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + ", expected [" +
260208
SCRIPT_PARSE_FIELD.getPreferredName() + "]");

0 commit comments

Comments
 (0)