Skip to content

Commit 5bf446e

Browse files
authored
Reconcile differences between Stateful and Stateless dot-prefix validation (#114946)
This commit makes the dot prefix deprecation match the existing changes to validation for the SLO and SLA UIs. Relates to #112571
1 parent f99321b commit 5bf446e

File tree

2 files changed

+137
-4
lines changed

2 files changed

+137
-4
lines changed

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,19 @@ public abstract class DotPrefixValidator<RequestType> implements MappedActionFil
5656
*
5757
* .elastic-connectors-* is used by enterprise search
5858
* .ml-* is used by ML
59+
* .slo-observability-* is used by Observability
5960
*/
6061
private static Set<String> IGNORED_INDEX_NAMES = Set.of(
6162
".elastic-connectors-v1",
6263
".elastic-connectors-sync-jobs-v1",
6364
".ml-state",
6465
".ml-anomalies-unrelated"
6566
);
66-
private static Set<Pattern> IGNORED_INDEX_PATTERNS = Set.of(Pattern.compile("\\.ml-state-\\d+"));
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+
);
6772

6873
DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DotPrefixValidator.class);
6974

@@ -99,10 +104,11 @@ void validateIndices(@Nullable Set<String> indices) {
99104
if (Strings.hasLength(index)) {
100105
char c = getFirstChar(index);
101106
if (c == '.') {
102-
if (IGNORED_INDEX_NAMES.contains(index)) {
107+
final String strippedName = stripDateMath(index);
108+
if (IGNORED_INDEX_NAMES.contains(strippedName)) {
103109
return;
104110
}
105-
if (IGNORED_INDEX_PATTERNS.stream().anyMatch(p -> p.matcher(index).matches())) {
111+
if (IGNORED_INDEX_PATTERNS.stream().anyMatch(p -> p.matcher(strippedName).matches())) {
106112
return;
107113
}
108114
deprecationLogger.warn(
@@ -132,7 +138,18 @@ private static char getFirstChar(String index) {
132138
return c;
133139
}
134140

135-
private boolean isInternalRequest() {
141+
private static String stripDateMath(String index) {
142+
char c = index.charAt(0);
143+
if (c == '<') {
144+
assert index.charAt(index.length() - 1) == '>'
145+
: "expected index name with date math to start with < and end with >, how did this pass request validation? " + index;
146+
return index.substring(1, index.length() - 1);
147+
} else {
148+
return index;
149+
}
150+
}
151+
152+
boolean isInternalRequest() {
136153
final String actionOrigin = threadContext.getTransient(ThreadContext.ACTION_ORIGIN_TRANSIENT_NAME);
137154
final boolean isSystemContext = threadContext.isSystemContext();
138155
final boolean isInternalOrigin = Optional.ofNullable(actionOrigin).map(Strings::hasText).orElse(false);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.validation;
11+
12+
import org.elasticsearch.cluster.service.ClusterService;
13+
import org.elasticsearch.common.settings.ClusterSettings;
14+
import org.elasticsearch.common.settings.Setting;
15+
import org.elasticsearch.common.settings.Settings;
16+
import org.elasticsearch.common.util.concurrent.ThreadContext;
17+
import org.elasticsearch.common.util.set.Sets;
18+
import org.elasticsearch.test.ESTestCase;
19+
import org.elasticsearch.threadpool.ThreadPool;
20+
import org.junit.BeforeClass;
21+
22+
import java.util.HashSet;
23+
import java.util.Set;
24+
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.when;
27+
28+
public class DotPrefixValidatorTests extends ESTestCase {
29+
private final OperatorValidator<?> opV = new OperatorValidator<>();
30+
private final NonOperatorValidator<?> nonOpV = new NonOperatorValidator<>();
31+
private static final Set<Setting<?>> settings;
32+
33+
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+
}
41+
42+
@BeforeClass
43+
public static void beforeClass() {
44+
clusterService = mock(ClusterService.class);
45+
clusterSettings = new ClusterSettings(Settings.EMPTY, Sets.newHashSet(DotPrefixValidator.VALIDATE_DOT_PREFIXES));
46+
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
47+
when(clusterService.getSettings()).thenReturn(Settings.EMPTY);
48+
when(clusterService.threadPool()).thenReturn(mock(ThreadPool.class));
49+
}
50+
51+
public void testValidation() {
52+
53+
nonOpV.validateIndices(Set.of("regular"));
54+
opV.validateIndices(Set.of("regular"));
55+
assertFails(Set.of(".regular"));
56+
opV.validateIndices(Set.of(".regular"));
57+
assertFails(Set.of("first", ".second"));
58+
assertFails(Set.of("<.regular-{MM-yy-dd}>"));
59+
60+
// Test ignored names
61+
nonOpV.validateIndices(Set.of(".elastic-connectors-v1"));
62+
nonOpV.validateIndices(Set.of(".elastic-connectors-sync-jobs-v1"));
63+
nonOpV.validateIndices(Set.of(".ml-state"));
64+
nonOpV.validateIndices(Set.of(".ml-anomalies-unrelated"));
65+
66+
// Test ignored patterns
67+
nonOpV.validateIndices(Set.of(".ml-state-21309"));
68+
nonOpV.validateIndices(Set.of(">.ml-state-21309>"));
69+
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2"));
70+
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2.3"));
71+
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2.3-2024-01-01"));
72+
nonOpV.validateIndices(Set.of("<.slo-observability.sli-v3.3.{2024-10-16||/M{yyyy-MM-dd|UTC}}>"));
73+
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2"));
74+
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3"));
75+
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3-2024-01-01"));
76+
nonOpV.validateIndices(Set.of("<.slo-observability.summary-v3.3.{2024-10-16||/M{yyyy-MM-dd|UTC}}>"));
77+
}
78+
79+
private void assertFails(Set<String> indices) {
80+
nonOpV.validateIndices(indices);
81+
assertWarnings(
82+
"Index ["
83+
+ indices.stream().filter(i -> i.startsWith(".") || i.startsWith("<.")).toList().getFirst()
84+
+ "] name begins with a dot (.), which is deprecated, and will not be allowed in a future Elasticsearch version."
85+
);
86+
}
87+
88+
private class NonOperatorValidator<R> extends DotPrefixValidator<R> {
89+
90+
private NonOperatorValidator() {
91+
super(new ThreadContext(Settings.EMPTY), clusterService);
92+
}
93+
94+
@Override
95+
protected Set<String> getIndicesFromRequest(Object request) {
96+
return Set.of();
97+
}
98+
99+
@Override
100+
public String actionName() {
101+
return "";
102+
}
103+
104+
@Override
105+
boolean isInternalRequest() {
106+
return false;
107+
}
108+
}
109+
110+
private class OperatorValidator<R> extends NonOperatorValidator<R> {
111+
@Override
112+
boolean isInternalRequest() {
113+
return true;
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)