Skip to content

Commit 99c7c13

Browse files
PeteGillinElasticsmalyshev
authored andcommitted
Remove ecs option on user_agent processor (#116077)
This removes the `ecs` option on the `user_agent` ingest processor, which is deprecated (way back in 6.7) and ignored. It will no longer be possible to create instances with this option, and the option will be removed from instances persisted in the cluster state on startup. The mechanism to do this upgrade on startup is designed to be reusable for other upgrades either to ingest processors or more generally to any custom metadata. It is inspired by the existing mechanism to upgrade index templates.
1 parent 384128a commit 99c7c13

File tree

13 files changed

+466
-38
lines changed

13 files changed

+466
-38
lines changed

docs/changelog/116077.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pr: 116077
2+
summary: Remove `ecs` option on `user_agent` processor
3+
area: Ingest Node
4+
type: breaking
5+
issues: []
6+
breaking:
7+
title: Remove `ecs` option on `user_agent` processor
8+
area: Ingest
9+
details: >-
10+
The `user_agent` ingest processor no longer accepts the `ecs` option. (It was previously deprecated and ignored.)
11+
impact: >-
12+
Users should stop using the `ecs` option when creating instances of the `user_agent` ingest processor.
13+
The option will be removed from existing processors stored in the cluster state on upgrade.
14+
notable: false

modules/ingest-user-agent/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
module org.elasticsearch.ingest.useragent {
1111
requires org.elasticsearch.server;
1212
requires org.elasticsearch.xcontent;
13+
requires org.elasticsearch.base;
1314
}

modules/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/IngestUserAgentPlugin.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
package org.elasticsearch.ingest.useragent;
1111

12+
import org.elasticsearch.cluster.metadata.Metadata;
1213
import org.elasticsearch.common.settings.Setting;
14+
import org.elasticsearch.ingest.IngestMetadata;
1315
import org.elasticsearch.ingest.Processor;
1416
import org.elasticsearch.plugins.IngestPlugin;
1517
import org.elasticsearch.plugins.Plugin;
@@ -23,6 +25,7 @@
2325
import java.util.HashMap;
2426
import java.util.List;
2527
import java.util.Map;
28+
import java.util.function.UnaryOperator;
2629
import java.util.stream.Stream;
2730

2831
public class IngestUserAgentPlugin extends Plugin implements IngestPlugin {
@@ -97,4 +100,15 @@ static Map<String, UserAgentParser> createUserAgentParsers(Path userAgentConfigD
97100
public List<Setting<?>> getSettings() {
98101
return List.of(CACHE_SIZE_SETTING);
99102
}
103+
104+
@Override
105+
public Map<String, UnaryOperator<Metadata.Custom>> getCustomMetadataUpgraders() {
106+
return Map.of(
107+
IngestMetadata.TYPE,
108+
ingestMetadata -> ((IngestMetadata) ingestMetadata).maybeUpgradeProcessors(
109+
UserAgentProcessor.TYPE,
110+
UserAgentProcessor::maybeUpgradeConfig
111+
)
112+
);
113+
}
100114
}

modules/ingest-user-agent/src/main/java/org/elasticsearch/ingest/useragent/UserAgentProcessor.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99

1010
package org.elasticsearch.ingest.useragent;
1111

12-
import org.elasticsearch.common.logging.DeprecationCategory;
13-
import org.elasticsearch.common.logging.DeprecationLogger;
1412
import org.elasticsearch.common.util.Maps;
13+
import org.elasticsearch.core.UpdateForV10;
1514
import org.elasticsearch.ingest.AbstractProcessor;
1615
import org.elasticsearch.ingest.IngestDocument;
1716
import org.elasticsearch.ingest.Processor;
@@ -32,8 +31,6 @@
3231

3332
public class UserAgentProcessor extends AbstractProcessor {
3433

35-
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(UserAgentProcessor.class);
36-
3734
public static final String TYPE = "user_agent";
3835

3936
private final String field;
@@ -198,21 +195,13 @@ public UserAgentProcessor create(
198195
String processorTag,
199196
String description,
200197
Map<String, Object> config
201-
) throws Exception {
198+
) {
202199
String field = readStringProperty(TYPE, processorTag, config, "field");
203200
String targetField = readStringProperty(TYPE, processorTag, config, "target_field", "user_agent");
204201
String regexFilename = readStringProperty(TYPE, processorTag, config, "regex_file", IngestUserAgentPlugin.DEFAULT_PARSER_NAME);
205202
List<String> propertyNames = readOptionalList(TYPE, processorTag, config, "properties");
206203
boolean extractDeviceType = readBooleanProperty(TYPE, processorTag, config, "extract_device_type", false);
207204
boolean ignoreMissing = readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
208-
Object ecsValue = config.remove("ecs");
209-
if (ecsValue != null) {
210-
deprecationLogger.warn(
211-
DeprecationCategory.SETTINGS,
212-
"ingest_useragent_ecs_settings",
213-
"setting [ecs] is deprecated as ECS format is the default and only option"
214-
);
215-
}
216205

217206
UserAgentParser parser = userAgentParsers.get(regexFilename);
218207
if (parser == null) {
@@ -272,4 +261,14 @@ public static Property parseProperty(String propertyName) {
272261
}
273262
}
274263
}
264+
265+
@UpdateForV10(owner = UpdateForV10.Owner.DATA_MANAGEMENT)
266+
// This can be removed in V10. It's not possible to create an instance with the ecs property in V9, and all instances created by V8 or
267+
// earlier will have been fixed when upgraded to V9.
268+
static boolean maybeUpgradeConfig(Map<String, Object> config) {
269+
// Instances created using ES 8.x (or earlier) may have the 'ecs' config entry.
270+
// This was ignored in 8.x and is unsupported in 9.0.
271+
// In 9.x, we should remove it from any existing processors on startup.
272+
return config.remove("ecs") != null;
273+
}
275274
}

modules/ingest-user-agent/src/test/java/org/elasticsearch/ingest/useragent/UserAgentProcessorTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,18 @@ public void testExtractDeviceTypeDisabled() {
331331
device.put("name", "Other");
332332
assertThat(target.get("device"), is(device));
333333
}
334+
335+
public void testMaybeUpgradeConfig_removesEcsIfPresent() {
336+
Map<String, Object> config = new HashMap<>(Map.of("field", "user-agent", "ecs", "whatever"));
337+
boolean changed = UserAgentProcessor.maybeUpgradeConfig(config);
338+
assertThat(changed, is(true));
339+
assertThat(config, is(Map.of("field", "user-agent")));
340+
}
341+
342+
public void testMaybeUpgradeConfig_doesNothingIfEcsAbsent() {
343+
Map<String, Object> config = new HashMap<>(Map.of("field", "user-agent"));
344+
boolean changed = UserAgentProcessor.maybeUpgradeConfig(config);
345+
assertThat(changed, is(false));
346+
assertThat(config, is(Map.of("field", "user-agent")));
347+
}
334348
}

server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,18 +300,31 @@ static Metadata upgradeMetadata(Metadata metadata, IndexMetadataVerifier indexMe
300300
upgradedMetadata.put(newMetadata, false);
301301
}
302302
// upgrade current templates
303-
if (applyPluginUpgraders(
303+
if (applyPluginTemplateUpgraders(
304304
metadata.getTemplates(),
305305
metadataUpgrader.indexTemplateMetadataUpgraders,
306306
upgradedMetadata::removeTemplate,
307307
(s, indexTemplateMetadata) -> upgradedMetadata.put(indexTemplateMetadata)
308308
)) {
309309
changed = true;
310310
}
311+
// upgrade custom metadata
312+
for (Map.Entry<String, UnaryOperator<Metadata.Custom>> entry : metadataUpgrader.customMetadataUpgraders.entrySet()) {
313+
String type = entry.getKey();
314+
Function<Metadata.Custom, Metadata.Custom> upgrader = entry.getValue();
315+
Metadata.Custom original = metadata.custom(type);
316+
if (original != null) {
317+
Metadata.Custom upgraded = upgrader.apply(original);
318+
if (upgraded.equals(original) == false) {
319+
upgradedMetadata.putCustom(type, upgraded);
320+
changed = true;
321+
}
322+
}
323+
}
311324
return changed ? upgradedMetadata.build() : metadata;
312325
}
313326

314-
private static boolean applyPluginUpgraders(
327+
private static boolean applyPluginTemplateUpgraders(
315328
Map<String, IndexTemplateMetadata> existingData,
316329
UnaryOperator<Map<String, IndexTemplateMetadata>> upgrader,
317330
Consumer<String> removeData,

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,39 @@ public boolean equals(Object o) {
169169
public int hashCode() {
170170
return pipelines.hashCode();
171171
}
172+
173+
/**
174+
* Returns a copy of this object with processor upgrades applied, if necessary. Otherwise, returns this object.
175+
*
176+
* <p>The given upgrader is applied to the config map for any processor of the given type.
177+
*/
178+
public IngestMetadata maybeUpgradeProcessors(String processorType, ProcessorConfigUpgrader processorConfigUpgrader) {
179+
Map<String, PipelineConfiguration> newPipelines = null; // as an optimization, we will lazily copy the map only if needed
180+
for (Map.Entry<String, PipelineConfiguration> entry : pipelines.entrySet()) {
181+
String pipelineId = entry.getKey();
182+
PipelineConfiguration originalPipeline = entry.getValue();
183+
PipelineConfiguration upgradedPipeline = originalPipeline.maybeUpgradeProcessors(processorType, processorConfigUpgrader);
184+
if (upgradedPipeline.equals(originalPipeline) == false) {
185+
if (newPipelines == null) {
186+
newPipelines = new HashMap<>(pipelines);
187+
}
188+
newPipelines.put(pipelineId, upgradedPipeline);
189+
}
190+
}
191+
return newPipelines != null ? new IngestMetadata(newPipelines) : this;
192+
}
193+
194+
/**
195+
* Functional interface for upgrading processor configs. An implementation of this will be associated with a specific processor type.
196+
*/
197+
public interface ProcessorConfigUpgrader {
198+
199+
/**
200+
* Upgrades the config for an individual processor of the appropriate type, if necessary.
201+
*
202+
* @param processorConfig The config to upgrade, which will be mutated if required
203+
* @return Whether an upgrade was required
204+
*/
205+
boolean maybeUpgrade(Map<String, Object> processorConfig);
206+
}
172207
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.xcontent.XContentType;
2525

2626
import java.io.IOException;
27+
import java.io.UncheckedIOException;
2728
import java.util.Map;
2829
import java.util.Objects;
2930

@@ -156,4 +157,36 @@ public int hashCode() {
156157
result = 31 * result + getConfigAsMap().hashCode();
157158
return result;
158159
}
160+
161+
/**
162+
* Returns a copy of this object with processor upgrades applied, if necessary. Otherwise, returns this object.
163+
*
164+
* <p>The given upgrader is applied to the config map for any processor of the given type.
165+
*/
166+
PipelineConfiguration maybeUpgradeProcessors(String type, IngestMetadata.ProcessorConfigUpgrader upgrader) {
167+
Map<String, Object> mutableConfigMap = getConfigAsMap();
168+
boolean changed = false;
169+
// This should be a List of Maps, where the keys are processor types and the values are config maps.
170+
// But we'll skip upgrading rather than fail if not.
171+
if (mutableConfigMap.get(Pipeline.PROCESSORS_KEY) instanceof Iterable<?> processors) {
172+
for (Object processor : processors) {
173+
if (processor instanceof Map<?, ?> processorMap && processorMap.get(type) instanceof Map<?, ?> targetProcessor) {
174+
@SuppressWarnings("unchecked") // All XContent maps will be <String, Object>
175+
Map<String, Object> processorConfigMap = (Map<String, Object>) targetProcessor;
176+
if (upgrader.maybeUpgrade(processorConfigMap)) {
177+
changed = true;
178+
}
179+
}
180+
}
181+
}
182+
if (changed) {
183+
try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) {
184+
return new PipelineConfiguration(id, BytesReference.bytes(builder.map(mutableConfigMap)), xContentType);
185+
} catch (IOException e) {
186+
throw new UncheckedIOException(e);
187+
}
188+
} else {
189+
return this;
190+
}
191+
}
159192
}

server/src/main/java/org/elasticsearch/node/NodeConstruction.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.elasticsearch.cluster.metadata.DataStreamGlobalRetentionSettings;
4848
import org.elasticsearch.cluster.metadata.IndexMetadataVerifier;
4949
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
50+
import org.elasticsearch.cluster.metadata.Metadata;
5051
import org.elasticsearch.cluster.metadata.MetadataCreateDataStreamService;
5152
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
5253
import org.elasticsearch.cluster.metadata.MetadataDataStreamsService;
@@ -232,6 +233,7 @@
232233
import java.util.Set;
233234
import java.util.concurrent.TimeUnit;
234235
import java.util.function.Function;
236+
import java.util.function.UnaryOperator;
235237
import java.util.stream.Collectors;
236238
import java.util.stream.Stream;
237239

@@ -970,7 +972,9 @@ private void construct(
970972
);
971973

972974
var indexTemplateMetadataUpgraders = pluginsService.map(Plugin::getIndexTemplateMetadataUpgrader).toList();
973-
modules.bindToInstance(MetadataUpgrader.class, new MetadataUpgrader(indexTemplateMetadataUpgraders));
975+
List<Map<String, UnaryOperator<Metadata.Custom>>> customMetadataUpgraders = pluginsService.map(Plugin::getCustomMetadataUpgraders)
976+
.toList();
977+
modules.bindToInstance(MetadataUpgrader.class, new MetadataUpgrader(indexTemplateMetadataUpgraders, customMetadataUpgraders));
974978

975979
final IndexMetadataVerifier indexMetadataVerifier = new IndexMetadataVerifier(
976980
settings,
@@ -1463,6 +1467,7 @@ private CircuitBreakerService createCircuitBreakerService(
14631467

14641468
/**
14651469
* Wrap a group of reloadable plugins into a single reloadable plugin interface
1470+
*
14661471
* @param reloadablePlugins A list of reloadable plugins
14671472
* @return A single ReloadablePlugin that, upon reload, reloads the plugins it wraps
14681473
*/

server/src/main/java/org/elasticsearch/plugins/MetadataUpgrader.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,56 @@
1414

1515
import java.util.Collection;
1616
import java.util.HashMap;
17+
import java.util.List;
1718
import java.util.Map;
1819
import java.util.function.UnaryOperator;
1920

21+
import static java.util.stream.Collectors.collectingAndThen;
22+
import static java.util.stream.Collectors.groupingBy;
23+
import static java.util.stream.Collectors.mapping;
24+
import static java.util.stream.Collectors.toList;
25+
2026
/**
2127
* Upgrades {@link Metadata} on startup on behalf of installed {@link Plugin}s
2228
*/
2329
public class MetadataUpgrader {
2430
public final UnaryOperator<Map<String, IndexTemplateMetadata>> indexTemplateMetadataUpgraders;
31+
public final Map<String, UnaryOperator<Metadata.Custom>> customMetadataUpgraders;
2532

26-
public MetadataUpgrader(Collection<UnaryOperator<Map<String, IndexTemplateMetadata>>> indexTemplateMetadataUpgraders) {
33+
public MetadataUpgrader(
34+
Collection<UnaryOperator<Map<String, IndexTemplateMetadata>>> indexTemplateMetadataUpgraders,
35+
Collection<Map<String, UnaryOperator<Metadata.Custom>>> customMetadataUpgraders
36+
) {
2737
this.indexTemplateMetadataUpgraders = templates -> {
2838
Map<String, IndexTemplateMetadata> upgradedTemplates = new HashMap<>(templates);
2939
for (UnaryOperator<Map<String, IndexTemplateMetadata>> upgrader : indexTemplateMetadataUpgraders) {
3040
upgradedTemplates = upgrader.apply(upgradedTemplates);
3141
}
3242
return upgradedTemplates;
3343
};
44+
this.customMetadataUpgraders = customMetadataUpgraders.stream()
45+
// Flatten the stream of maps into a stream of entries
46+
.flatMap(map -> map.entrySet().stream())
47+
.collect(
48+
groupingBy(
49+
// Group by the type of custom metadata to be upgraded (the entry key)
50+
Map.Entry::getKey,
51+
// For each type, extract the operators (the entry values), collect to a list, and make an operator which combines them
52+
collectingAndThen(mapping(Map.Entry::getValue, toList()), CombiningCustomUpgrader::new)
53+
)
54+
);
55+
}
56+
57+
private record CombiningCustomUpgrader(List<UnaryOperator<Metadata.Custom>> upgraders) implements UnaryOperator<Metadata.Custom> {
58+
59+
@Override
60+
public Metadata.Custom apply(Metadata.Custom custom) {
61+
Metadata.Custom upgraded = custom;
62+
for (UnaryOperator<Metadata.Custom> upgrader : upgraders) {
63+
upgraded = upgrader.apply(upgraded);
64+
}
65+
return upgraded;
66+
}
3467
}
68+
3569
}

0 commit comments

Comments
 (0)