Skip to content

Commit 64e8659

Browse files
authored
#104411 Add warning headers for ingest pipelines containing special characters (#114837)
* Add logs and headers For pipeline creation when name is invalid * Fix YAML tests and add YAML test for warnings * Update docs/changelog/114837.yaml * Changelog entry * Changelog entry * Update docs/changelog/114837.yaml * Changelog entry
1 parent 80c163e commit 64e8659

File tree

7 files changed

+95
-12
lines changed

7 files changed

+95
-12
lines changed

docs/changelog/114837.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 114837
2+
summary: Add warning headers for ingest pipelines containing special characters
3+
area: Ingest Node
4+
type: bug
5+
issues: [ 104411 ]

qa/smoke-test-ingest-with-all-dependencies/src/yamlRestTest/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
"Test rolling up json object arrays":
215215
- do:
216216
ingest.put_pipeline:
217-
id: "_id"
217+
id: "pipeline-id"
218218
body: >
219219
{
220220
"processors": [
@@ -237,7 +237,7 @@
237237
index:
238238
index: test
239239
id: "1"
240-
pipeline: "_id"
240+
pipeline: "pipeline-id"
241241
body: {
242242
values_flat : [],
243243
values: [

qa/smoke-test-ingest-with-all-dependencies/src/yamlRestTest/resources/rest-api-spec/test/ingest/20_combine_processors.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"Test with date processor":
33
- do:
44
ingest.put_pipeline:
5-
id: "_id"
5+
id: "pipeline-id"
66
body: >
77
{
88
"processors": [
@@ -44,7 +44,7 @@
4444
index:
4545
index: test
4646
id: "1"
47-
pipeline: "_id"
47+
pipeline: "pipeline-id"
4848
body: {
4949
log: "89.160.20.128 - - [08/Sep/2014:02:54:42 +0000] \"GET /presentations/logstash-scale11x/images/ahhh___rage_face_by_samusmmx-d5g5zap.png HTTP/1.1\" 200 175208 \"http://mobile.rivals.com/board_posts.asp?SID=880&mid=198829575&fid=2208&tid=198829575&Team=&TeamId=&SiteId=\" \"Mozilla/5.0 (Linux; Android 4.2.2; VS980 4G Build/JDQ39B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.135 Mobile Safari/537.36\""
5050
}
@@ -71,7 +71,7 @@
7171
"Test with date processor and ECS-v1":
7272
- do:
7373
ingest.put_pipeline:
74-
id: "_id"
74+
id: "pipeline-id"
7575
body: >
7676
{
7777
"processors": [
@@ -102,7 +102,7 @@
102102
index:
103103
index: test
104104
id: "1"
105-
pipeline: "_id"
105+
pipeline: "pipeline-id"
106106
body: {
107107
log: "89.160.20.128 - - [08/Sep/2014:02:54:42 +0000] \"GET /presentations/logstash-scale11x/images/ahhh___rage_face_by_samusmmx-d5g5zap.png HTTP/1.1\" 200 175208 \"http://mobile.rivals.com/board_posts.asp?SID=880&mid=198829575&fid=2208&tid=198829575&Team=&TeamId=&SiteId=\" \"Mozilla/5.0 (Linux; Android 4.2.2; VS980 4G Build/JDQ39B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.135 Mobile Safari/537.36\""
108108
}
@@ -128,7 +128,7 @@
128128
"Test mutate":
129129
- do:
130130
ingest.put_pipeline:
131-
id: "_id"
131+
id: "pipeline-id"
132132
body: >
133133
{
134134
"processors": [
@@ -188,7 +188,7 @@
188188
index:
189189
index: test
190190
id: "1"
191-
pipeline: "_id"
191+
pipeline: "pipeline-id"
192192
body: {
193193
"age" : 33,
194194
"eyeColor" : "brown",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"Test invalid name warnings":
3+
- requires:
4+
cluster_features: [ "ingest.pipeline_name_special_chars_warning" ]
5+
test_runner_features: [ "warnings" ]
6+
reason: verifying deprecation warnings from 9.0 onwards for invalid pipeline names
7+
8+
- do:
9+
cluster.health:
10+
wait_for_status: green
11+
12+
- do:
13+
ingest.put_pipeline:
14+
id: "Invalid*-pipeline:id"
15+
body: >
16+
{
17+
"description": "_description",
18+
"processors": [
19+
{
20+
"set" : {
21+
"field" : "field1",
22+
"value": "_value"
23+
}
24+
}]
25+
}
26+
warnings:
27+
- "Invalid pipeline id: Invalid*-pipeline:id"
28+
- match: { acknowledged: true }

server/src/main/java/org/elasticsearch/common/Strings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) {
285285
static final Set<Character> INVALID_CHARS = Set.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',');
286286

287287
public static final String INVALID_FILENAME_CHARS = INVALID_CHARS.stream()
288+
.sorted()
288289
.map(c -> "'" + c + "'")
289290
.collect(Collectors.joining(",", "[", "]"));
290291

server/src/main/java/org/elasticsearch/ingest/IngestService.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@
3939
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
4040
import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
4141
import org.elasticsearch.cluster.metadata.Metadata;
42+
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
4243
import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService;
4344
import org.elasticsearch.cluster.node.DiscoveryNode;
4445
import org.elasticsearch.cluster.service.ClusterService;
4546
import org.elasticsearch.cluster.service.MasterServiceTaskQueue;
4647
import org.elasticsearch.common.Priority;
4748
import org.elasticsearch.common.TriConsumer;
4849
import org.elasticsearch.common.bytes.BytesReference;
50+
import org.elasticsearch.common.logging.DeprecationCategory;
51+
import org.elasticsearch.common.logging.DeprecationLogger;
4952
import org.elasticsearch.common.regex.Regex;
5053
import org.elasticsearch.common.settings.Settings;
5154
import org.elasticsearch.common.util.CollectionUtils;
@@ -55,7 +58,9 @@
5558
import org.elasticsearch.core.Releasable;
5659
import org.elasticsearch.core.TimeValue;
5760
import org.elasticsearch.core.Tuple;
61+
import org.elasticsearch.core.UpdateForV10;
5862
import org.elasticsearch.env.Environment;
63+
import org.elasticsearch.features.NodeFeature;
5964
import org.elasticsearch.gateway.GatewayService;
6065
import org.elasticsearch.grok.MatcherWatchdog;
6166
import org.elasticsearch.index.IndexSettings;
@@ -97,6 +102,7 @@
97102
import java.util.stream.Collectors;
98103

99104
import static org.elasticsearch.core.Strings.format;
105+
import static org.elasticsearch.core.UpdateForV10.Owner.DATA_MANAGEMENT;
100106

101107
/**
102108
* Holder class for several ingest related services.
@@ -107,7 +113,10 @@ public class IngestService implements ClusterStateApplier, ReportingService<Inge
107113

108114
public static final String INGEST_ORIGIN = "ingest";
109115

116+
public static final NodeFeature PIPELINE_NAME_VALIDATION_WARNINGS = new NodeFeature("ingest.pipeline_name_special_chars_warning");
117+
110118
private static final Logger logger = LogManager.getLogger(IngestService.class);
119+
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(IngestService.class);
111120

112121
private final MasterServiceTaskQueue<PipelineClusterStateUpdateTask> taskQueue;
113122
private final ClusterService clusterService;
@@ -652,12 +661,24 @@ public IngestMetadata execute(IngestMetadata currentIngestMetadata, Collection<I
652661
}
653662
}
654663

664+
@UpdateForV10(owner = DATA_MANAGEMENT) // Change deprecation log for special characters in name to a failure
655665
void validatePipeline(Map<DiscoveryNode, IngestInfo> ingestInfos, String pipelineId, Map<String, Object> pipelineConfig)
656666
throws Exception {
657667
if (ingestInfos.isEmpty()) {
658668
throw new IllegalStateException("Ingest info is empty");
659669
}
660670

671+
try {
672+
MetadataCreateIndexService.validateIndexOrAliasName(
673+
pipelineId,
674+
(pipelineName, error) -> new IllegalArgumentException(
675+
"Pipeline name [" + pipelineName + "] will be disallowed in a future version for the following reason: " + error
676+
)
677+
);
678+
} catch (IllegalArgumentException e) {
679+
deprecationLogger.critical(DeprecationCategory.API, "pipeline_name_special_chars", e.getMessage());
680+
}
681+
661682
Pipeline pipeline = Pipeline.create(pipelineId, pipelineConfig, processorFactories, scriptService);
662683
List<Exception> exceptions = new ArrayList<>();
663684
for (Processor processor : pipeline.flattenAllProcessors()) {

server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.action.ingest.DeletePipelineRequest;
2525
import org.elasticsearch.action.ingest.PutPipelineRequest;
2626
import org.elasticsearch.action.support.ActionTestUtils;
27+
import org.elasticsearch.action.support.master.AcknowledgedRequest;
2728
import org.elasticsearch.action.support.master.AcknowledgedResponse;
2829
import org.elasticsearch.action.update.UpdateRequest;
2930
import org.elasticsearch.client.internal.Client;
@@ -48,6 +49,7 @@
4849
import org.elasticsearch.common.util.concurrent.EsExecutors;
4950
import org.elasticsearch.common.xcontent.XContentHelper;
5051
import org.elasticsearch.core.Strings;
52+
import org.elasticsearch.core.TimeValue;
5153
import org.elasticsearch.core.Tuple;
5254
import org.elasticsearch.index.IndexSettings;
5355
import org.elasticsearch.index.IndexVersion;
@@ -424,7 +426,7 @@ public void testDelete() {
424426

425427
public void testValidateNoIngestInfo() throws Exception {
426428
IngestService ingestService = createWithProcessors();
427-
PutPipelineRequest putRequest = putJsonPipelineRequest("_id", """
429+
PutPipelineRequest putRequest = putJsonPipelineRequest("pipeline-id", """
428430
{"processors": [{"set" : {"field": "_field", "value": "_value"}}]}""");
429431

430432
var pipelineConfig = XContentHelper.convertToMap(putRequest.getSource(), false, putRequest.getXContentType()).v2();
@@ -965,7 +967,7 @@ public void testGetPipelines() {
965967

966968
public void testValidateProcessorTypeOnAllNodes() throws Exception {
967969
IngestService ingestService = createWithProcessors();
968-
PutPipelineRequest putRequest = putJsonPipelineRequest("_id", """
970+
PutPipelineRequest putRequest = putJsonPipelineRequest("pipeline-id", """
969971
{
970972
"processors": [
971973
{
@@ -1009,7 +1011,7 @@ public void testValidateConfigurationExceptions() {
10091011
// ordinary validation issues happen at processor construction time
10101012
throw newConfigurationException("fail_validation", tag, "no_property_name", "validation failure reason");
10111013
}));
1012-
PutPipelineRequest putRequest = putJsonPipelineRequest("_id", """
1014+
PutPipelineRequest putRequest = putJsonPipelineRequest("pipeline-id", """
10131015
{
10141016
"processors": [
10151017
{
@@ -1043,7 +1045,7 @@ public void extraValidation() throws Exception {
10431045
}
10441046
};
10451047
}));
1046-
PutPipelineRequest putRequest = putJsonPipelineRequest("_id", """
1048+
PutPipelineRequest putRequest = putJsonPipelineRequest("pipeline-id", """
10471049
{
10481050
"processors": [
10491051
{
@@ -1067,6 +1069,32 @@ public void extraValidation() throws Exception {
10671069
assertEquals("fail_extra_validation", e.getMetadata("es.processor_type").get(0));
10681070
}
10691071

1072+
public void testValidatePipelineName() throws Exception {
1073+
IngestService ingestService = createWithProcessors();
1074+
for (Character badChar : List.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',')) {
1075+
PutPipelineRequest putRequest = new PutPipelineRequest(
1076+
TimeValue.timeValueSeconds(10),
1077+
AcknowledgedRequest.DEFAULT_ACK_TIMEOUT,
1078+
"_id",
1079+
new BytesArray("""
1080+
{"description":"test processor","processors":[{"set":{"field":"_field","value":"_value"}}]}"""),
1081+
XContentType.JSON
1082+
);
1083+
var pipelineConfig = XContentHelper.convertToMap(putRequest.getSource(), false, putRequest.getXContentType()).v2();
1084+
DiscoveryNode node1 = DiscoveryNodeUtils.create("_node_id1", buildNewFakeTransportAddress(), Map.of(), Set.of());
1085+
Map<DiscoveryNode, IngestInfo> ingestInfos = new HashMap<>();
1086+
ingestInfos.put(node1, new IngestInfo(List.of(new ProcessorInfo("set"))));
1087+
final String name = randomAlphaOfLength(5) + badChar + randomAlphaOfLength(5);
1088+
ingestService.validatePipeline(ingestInfos, name, pipelineConfig);
1089+
assertCriticalWarnings(
1090+
"Pipeline name ["
1091+
+ name
1092+
+ "] will be disallowed in a future version for the following reason: must not contain the following characters"
1093+
+ " [' ','\"','*',',','/','<','>','?','\\','|']"
1094+
);
1095+
}
1096+
}
1097+
10701098
public void testExecuteIndexPipelineExistsButFailedParsing() {
10711099
IngestService ingestService = createWithProcessors(
10721100
Map.of("mock", (factories, tag, description, config) -> new AbstractProcessor("mock", "description") {

0 commit comments

Comments
 (0)