Skip to content

Commit

Permalink
Reconcile differences between Stateful and Stateless dot-prefix valid…
Browse files Browse the repository at this point in the history
…ation (elastic#114946)

This commit makes the dot prefix deprecation match the existing changes to validation for the SLO
and SLA UIs.

Relates to elastic#112571
  • Loading branch information
dakrone authored Oct 17, 2024
1 parent f99321b commit 5bf446e
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,19 @@ public abstract class DotPrefixValidator<RequestType> implements MappedActionFil
*
* .elastic-connectors-* is used by enterprise search
* .ml-* is used by ML
* .slo-observability-* is used by Observability
*/
private static Set<String> IGNORED_INDEX_NAMES = Set.of(
".elastic-connectors-v1",
".elastic-connectors-sync-jobs-v1",
".ml-state",
".ml-anomalies-unrelated"
);
private static Set<Pattern> IGNORED_INDEX_PATTERNS = Set.of(Pattern.compile("\\.ml-state-\\d+"));
private static Set<Pattern> IGNORED_INDEX_PATTERNS = Set.of(
Pattern.compile("\\.ml-state-\\d+"),
Pattern.compile("\\.slo-observability\\.sli-v\\d+.*"),
Pattern.compile("\\.slo-observability\\.summary-v\\d+.*")
);

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

Expand Down Expand Up @@ -99,10 +104,11 @@ void validateIndices(@Nullable Set<String> indices) {
if (Strings.hasLength(index)) {
char c = getFirstChar(index);
if (c == '.') {
if (IGNORED_INDEX_NAMES.contains(index)) {
final String strippedName = stripDateMath(index);
if (IGNORED_INDEX_NAMES.contains(strippedName)) {
return;
}
if (IGNORED_INDEX_PATTERNS.stream().anyMatch(p -> p.matcher(index).matches())) {
if (IGNORED_INDEX_PATTERNS.stream().anyMatch(p -> p.matcher(strippedName).matches())) {
return;
}
deprecationLogger.warn(
Expand Down Expand Up @@ -132,7 +138,18 @@ private static char getFirstChar(String index) {
return c;
}

private boolean isInternalRequest() {
private static String stripDateMath(String index) {
char c = index.charAt(0);
if (c == '<') {
assert index.charAt(index.length() - 1) == '>'
: "expected index name with date math to start with < and end with >, how did this pass request validation? " + index;
return index.substring(1, index.length() - 1);
} else {
return index;
}
}

boolean isInternalRequest() {
final String actionOrigin = threadContext.getTransient(ThreadContext.ACTION_ORIGIN_TRANSIENT_NAME);
final boolean isSystemContext = threadContext.isSystemContext();
final boolean isInternalOrigin = Optional.ofNullable(actionOrigin).map(Strings::hasText).orElse(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.validation;

import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.junit.BeforeClass;

import java.util.HashSet;
import java.util.Set;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class DotPrefixValidatorTests extends ESTestCase {
private final OperatorValidator<?> opV = new OperatorValidator<>();
private final NonOperatorValidator<?> nonOpV = new NonOperatorValidator<>();
private static final Set<Setting<?>> settings;

private static ClusterService clusterService;
private static ClusterSettings clusterSettings;

static {
Set<Setting<?>> cSettings = new HashSet<>(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
cSettings.add(DotPrefixValidator.VALIDATE_DOT_PREFIXES);
settings = cSettings;
}

@BeforeClass
public static void beforeClass() {
clusterService = mock(ClusterService.class);
clusterSettings = new ClusterSettings(Settings.EMPTY, Sets.newHashSet(DotPrefixValidator.VALIDATE_DOT_PREFIXES));
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
when(clusterService.getSettings()).thenReturn(Settings.EMPTY);
when(clusterService.threadPool()).thenReturn(mock(ThreadPool.class));
}

public void testValidation() {

nonOpV.validateIndices(Set.of("regular"));
opV.validateIndices(Set.of("regular"));
assertFails(Set.of(".regular"));
opV.validateIndices(Set.of(".regular"));
assertFails(Set.of("first", ".second"));
assertFails(Set.of("<.regular-{MM-yy-dd}>"));

// Test ignored names
nonOpV.validateIndices(Set.of(".elastic-connectors-v1"));
nonOpV.validateIndices(Set.of(".elastic-connectors-sync-jobs-v1"));
nonOpV.validateIndices(Set.of(".ml-state"));
nonOpV.validateIndices(Set.of(".ml-anomalies-unrelated"));

// Test ignored patterns
nonOpV.validateIndices(Set.of(".ml-state-21309"));
nonOpV.validateIndices(Set.of(">.ml-state-21309>"));
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2"));
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2.3"));
nonOpV.validateIndices(Set.of(".slo-observability.sli-v2.3-2024-01-01"));
nonOpV.validateIndices(Set.of("<.slo-observability.sli-v3.3.{2024-10-16||/M{yyyy-MM-dd|UTC}}>"));
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2"));
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3"));
nonOpV.validateIndices(Set.of(".slo-observability.summary-v2.3-2024-01-01"));
nonOpV.validateIndices(Set.of("<.slo-observability.summary-v3.3.{2024-10-16||/M{yyyy-MM-dd|UTC}}>"));
}

private void assertFails(Set<String> indices) {
nonOpV.validateIndices(indices);
assertWarnings(
"Index ["
+ indices.stream().filter(i -> i.startsWith(".") || i.startsWith("<.")).toList().getFirst()
+ "] name begins with a dot (.), which is deprecated, and will not be allowed in a future Elasticsearch version."
);
}

private class NonOperatorValidator<R> extends DotPrefixValidator<R> {

private NonOperatorValidator() {
super(new ThreadContext(Settings.EMPTY), clusterService);
}

@Override
protected Set<String> getIndicesFromRequest(Object request) {
return Set.of();
}

@Override
public String actionName() {
return "";
}

@Override
boolean isInternalRequest() {
return false;
}
}

private class OperatorValidator<R> extends NonOperatorValidator<R> {
@Override
boolean isInternalRequest() {
return true;
}
}
}

0 comments on commit 5bf446e

Please sign in to comment.