Skip to content

Commit 395482e

Browse files
[8.16] Align dot prefix validation with Serverless (#116266) (#116287)
* Align dot prefix validation with Serverless (#116266) This aligns the deprecation warnings for on-prem dot-prefixed indices to be the same as the Serverless validation. It adds exemptions for the `.entities…` indices, and makes the list a dynamic setting. (cherry picked from commit 72aa17a) * Fix compilation --------- Co-authored-by: Elastic Machine <[email protected]>
1 parent 10934b3 commit 395482e

File tree

4 files changed

+68
-20
lines changed

4 files changed

+68
-20
lines changed

docs/changelog/116266.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 116266
2+
summary: Align dot prefix validation with Serverless
3+
area: Indices APIs
4+
type: bug
5+
issues: []

modules/dot-prefix-validation/src/main/java/org/elasticsearch/validation/DotPrefixValidationPlugin.java

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.elasticsearch.action.support.MappedActionFilter;
1313
import org.elasticsearch.cluster.service.ClusterService;
14+
import org.elasticsearch.common.settings.Setting;
1415
import org.elasticsearch.common.util.concurrent.ThreadContext;
1516
import org.elasticsearch.plugins.ActionPlugin;
1617
import org.elasticsearch.plugins.Plugin;
@@ -45,4 +46,9 @@ public Collection<?> createComponents(PluginServices services) {
4546
public Collection<MappedActionFilter> getMappedActionFilters() {
4647
return actionFilters.get();
4748
}
49+
50+
@Override
51+
public List<Setting<?>> getSettings() {
52+
return List.of(DotPrefixValidator.VALIDATE_DOT_PREFIXES, DotPrefixValidator.IGNORED_INDEX_PATTERNS_SETTING);
53+
}
4854
}

modules/dot-prefix-validation/src/main/java/org/elasticsearch/validation/DotPrefixValidator.java

+34-6
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
import org.elasticsearch.core.Nullable;
2424
import org.elasticsearch.tasks.Task;
2525

26+
import java.util.List;
2627
import java.util.Optional;
2728
import java.util.Set;
29+
import java.util.function.Function;
2830
import java.util.regex.Pattern;
31+
import java.util.regex.PatternSyntaxException;
32+
import java.util.stream.Collectors;
2933

3034
/**
3135
* DotPrefixValidator provides an abstract class implementing a mapped action filter.
@@ -39,7 +43,7 @@
3943
* method, which subclasses must implement.
4044
*
4145
* Some built-in index names and patterns are also elided from the check, as defined in
42-
* {@link #IGNORED_INDEX_NAMES} and {@link #IGNORED_INDEX_PATTERNS}.
46+
* {@link #IGNORED_INDEX_NAMES} and {@link #IGNORED_INDEX_PATTERNS_SETTING}.
4347
*/
4448
public abstract class DotPrefixValidator<RequestType> implements MappedActionFilter {
4549
public static final Setting<Boolean> VALIDATE_DOT_PREFIXES = Setting.boolSetting(
@@ -64,20 +68,44 @@ public abstract class DotPrefixValidator<RequestType> implements MappedActionFil
6468
".ml-state",
6569
".ml-anomalies-unrelated"
6670
);
67-
private static Set<Pattern> IGNORED_INDEX_PATTERNS = Set.of(
68-
Pattern.compile("\\.ml-state-\\d+"),
69-
Pattern.compile("\\.slo-observability\\.sli-v\\d+.*"),
70-
Pattern.compile("\\.slo-observability\\.summary-v\\d+.*")
71+
public static Setting<List<String>> IGNORED_INDEX_PATTERNS_SETTING = Setting.listSetting(
72+
"cluster.indices.validate_ignored_dot_patterns",
73+
List.of(
74+
"\\.ml-state-\\d+",
75+
"\\.slo-observability\\.sli-v\\d+.*",
76+
"\\.slo-observability\\.summary-v\\d+.*",
77+
"\\.entities\\.v\\d+\\.latest\\..*"
78+
),
79+
Function.identity(),
80+
(patternList) -> patternList.forEach(pattern -> {
81+
try {
82+
Pattern.compile(pattern);
83+
} catch (PatternSyntaxException e) {
84+
throw new IllegalArgumentException("invalid dot validation exception pattern: [" + pattern + "]", e);
85+
}
86+
}),
87+
Setting.Property.NodeScope,
88+
Setting.Property.Dynamic
7189
);
7290

7391
DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DotPrefixValidator.class);
7492

7593
private final ThreadContext threadContext;
7694
private final boolean isEnabled;
95+
private volatile Set<Pattern> ignoredIndexPatterns;
7796

7897
public DotPrefixValidator(ThreadContext threadContext, ClusterService clusterService) {
7998
this.threadContext = threadContext;
8099
this.isEnabled = VALIDATE_DOT_PREFIXES.get(clusterService.getSettings());
100+
this.ignoredIndexPatterns = IGNORED_INDEX_PATTERNS_SETTING.get(clusterService.getSettings())
101+
.stream()
102+
.map(Pattern::compile)
103+
.collect(Collectors.toSet());
104+
clusterService.getClusterSettings().addSettingsUpdateConsumer(IGNORED_INDEX_PATTERNS_SETTING, this::updateIgnoredIndexPatterns);
105+
}
106+
107+
private void updateIgnoredIndexPatterns(List<String> patterns) {
108+
this.ignoredIndexPatterns = patterns.stream().map(Pattern::compile).collect(Collectors.toSet());
81109
}
82110

83111
protected abstract Set<String> getIndicesFromRequest(RequestType request);
@@ -108,7 +136,7 @@ void validateIndices(@Nullable Set<String> indices) {
108136
if (IGNORED_INDEX_NAMES.contains(strippedName)) {
109137
return;
110138
}
111-
if (IGNORED_INDEX_PATTERNS.stream().anyMatch(p -> p.matcher(strippedName).matches())) {
139+
if (this.ignoredIndexPatterns.stream().anyMatch(p -> p.matcher(strippedName).matches())) {
112140
return;
113141
}
114142
deprecationLogger.warn(

modules/dot-prefix-validation/src/test/java/org/elasticsearch/validation/DotPrefixValidatorTests.java

+23-14
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010
package org.elasticsearch.validation;
1111

1212
import org.elasticsearch.cluster.service.ClusterService;
13+
import org.elasticsearch.common.Strings;
1314
import org.elasticsearch.common.settings.ClusterSettings;
14-
import org.elasticsearch.common.settings.Setting;
1515
import org.elasticsearch.common.settings.Settings;
1616
import org.elasticsearch.common.util.concurrent.ThreadContext;
1717
import org.elasticsearch.common.util.set.Sets;
1818
import org.elasticsearch.test.ESTestCase;
1919
import org.elasticsearch.threadpool.ThreadPool;
2020
import org.junit.BeforeClass;
2121

22-
import java.util.HashSet;
22+
import java.util.ArrayList;
23+
import java.util.List;
2324
import java.util.Set;
2425

2526
import static org.mockito.Mockito.mock;
@@ -28,23 +29,24 @@
2829
public class DotPrefixValidatorTests extends ESTestCase {
2930
private final OperatorValidator<?> opV = new OperatorValidator<>();
3031
private final NonOperatorValidator<?> nonOpV = new NonOperatorValidator<>();
31-
private static final Set<Setting<?>> settings;
3232

3333
private static ClusterService clusterService;
34-
private static ClusterSettings clusterSettings;
35-
36-
static {
37-
Set<Setting<?>> cSettings = new HashSet<>(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
38-
cSettings.add(DotPrefixValidator.VALIDATE_DOT_PREFIXES);
39-
settings = cSettings;
40-
}
4134

4235
@BeforeClass
4336
public static void beforeClass() {
37+
List<String> allowed = new ArrayList<>(DotPrefixValidator.IGNORED_INDEX_PATTERNS_SETTING.getDefault(Settings.EMPTY));
38+
// Add a new allowed pattern for testing
39+
allowed.add("\\.potato\\d+");
40+
Settings settings = Settings.builder()
41+
.put(DotPrefixValidator.IGNORED_INDEX_PATTERNS_SETTING.getKey(), Strings.collectionToCommaDelimitedString(allowed))
42+
.build();
4443
clusterService = mock(ClusterService.class);
45-
clusterSettings = new ClusterSettings(Settings.EMPTY, Sets.newHashSet(DotPrefixValidator.VALIDATE_DOT_PREFIXES));
44+
ClusterSettings clusterSettings = new ClusterSettings(
45+
settings,
46+
Sets.newHashSet(DotPrefixValidator.VALIDATE_DOT_PREFIXES, DotPrefixValidator.IGNORED_INDEX_PATTERNS_SETTING)
47+
);
4648
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
47-
when(clusterService.getSettings()).thenReturn(Settings.EMPTY);
49+
when(clusterService.getSettings()).thenReturn(settings);
4850
when(clusterService.threadPool()).thenReturn(mock(ThreadPool.class));
4951
}
5052

@@ -74,6 +76,13 @@ public void testValidation() {
7476
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3"));
7577
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3-2024-01-01"));
7678
nonOpV.validateIndices(Set.of("<.slo-observability.summary-v3.3.{2024-10-16||/M{yyyy-MM-dd|UTC}}>"));
79+
nonOpV.validateIndices(Set.of(".entities.v1.latest.builtin_services_from_ecs_data"));
80+
nonOpV.validateIndices(Set.of(".entities.v92.latest.eggplant.potato"));
81+
nonOpV.validateIndices(Set.of("<.entities.v12.latest.eggplant-{M{yyyy-MM-dd|UTC}}>"));
82+
83+
// Test pattern added to the settings
84+
nonOpV.validateIndices(Set.of(".potato5"));
85+
nonOpV.validateIndices(Set.of("<.potato5>"));
7786
}
7887

7988
private void assertFails(Set<String> indices) {
@@ -85,7 +94,7 @@ private void assertFails(Set<String> indices) {
8594
);
8695
}
8796

88-
private class NonOperatorValidator<R> extends DotPrefixValidator<R> {
97+
private static class NonOperatorValidator<R> extends DotPrefixValidator<R> {
8998

9099
private NonOperatorValidator() {
91100
super(new ThreadContext(Settings.EMPTY), clusterService);
@@ -107,7 +116,7 @@ boolean isInternalRequest() {
107116
}
108117
}
109118

110-
private class OperatorValidator<R> extends NonOperatorValidator<R> {
119+
private static class OperatorValidator<R> extends NonOperatorValidator<R> {
111120
@Override
112121
boolean isInternalRequest() {
113122
return true;

0 commit comments

Comments
 (0)