Skip to content

Commit 1690e78

Browse files
authored
Validation for data stream creation
1 parent 2f619ad commit 1690e78

File tree

4 files changed

+81
-4
lines changed

4 files changed

+81
-4
lines changed

server/src/main/java/org/elasticsearch/action/admin/indices/datastream/CreateDataStreamAction.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.cluster.metadata.DataStream;
3636
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
3737
import org.elasticsearch.cluster.metadata.MetaData;
38+
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
3839
import org.elasticsearch.cluster.service.ClusterService;
3940
import org.elasticsearch.common.Priority;
4041
import org.elasticsearch.common.Strings;
@@ -164,9 +165,11 @@ static ClusterState createDataStream(ClusterState currentState, Request request)
164165
throw new IllegalArgumentException("data_stream [" + request.name + "] already exists");
165166
}
166167

168+
MetaDataCreateIndexService.validateIndexOrAliasName(request.name,
169+
(s1, s2) -> new IllegalArgumentException("data_stream [" + s1 + "] " + s2));
170+
167171
MetaData.Builder builder = MetaData.builder(currentState.metaData()).put(
168172
new DataStream(request.name, request.timestampFieldName, Collections.emptyList()));
169-
170173
logger.info("adding data stream [{}]", request.name);
171174
return ClusterState.builder(currentState).metaData(builder).build();
172175
}

server/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ public Builder generateClusterUuidIfNeeded() {
13531353

13541354
public MetaData build() {
13551355
// TODO: We should move these datastructures to IndexNameExpressionResolver, this will give the following benefits:
1356-
// 1) The datastructures will only be rebuilded when needed. Now during serializing we rebuild these datastructures
1356+
// 1) The datastructures will be rebuilt only when needed. Now during serializing we rebuild these datastructures
13571357
// while these datastructures aren't even used.
13581358
// 2) The aliasAndIndexLookup can be updated instead of rebuilding it all the time.
13591359

@@ -1391,20 +1391,21 @@ public MetaData build() {
13911391
// iterate again and constructs a helpful message
13921392
ArrayList<String> duplicates = new ArrayList<>();
13931393
for (ObjectCursor<IndexMetaData> cursor : indices.values()) {
1394-
for (String alias: duplicateAliasesIndices) {
1394+
for (String alias : duplicateAliasesIndices) {
13951395
if (cursor.value.getAliases().containsKey(alias)) {
13961396
duplicates.add(alias + " (alias of " + cursor.value.getIndex() + ")");
13971397
}
13981398
}
13991399
}
14001400
assert duplicates.size() > 0;
14011401
throw new IllegalStateException("index and alias names need to be unique, but the following duplicates were found ["
1402-
+ Strings.collectionToCommaDelimitedString(duplicates)+ "]");
1402+
+ Strings.collectionToCommaDelimitedString(duplicates) + "]");
14031403

14041404
}
14051405

14061406
SortedMap<String, AliasOrIndex> aliasAndIndexLookup = Collections.unmodifiableSortedMap(buildAliasAndIndexLookup());
14071407

1408+
validateDataStreams(aliasAndIndexLookup);
14081409

14091410
// build all concrete indices arrays:
14101411
// TODO: I think we can remove these arrays. it isn't worth the effort, for operations on all indices.
@@ -1447,6 +1448,24 @@ private SortedMap<String, AliasOrIndex> buildAliasAndIndexLookup() {
14471448
return aliasAndIndexLookup;
14481449
}
14491450

1451+
private void validateDataStreams(SortedMap<String, AliasOrIndex> aliasAndIndexLookup) {
1452+
DataStreamMetadata dsMetadata = (DataStreamMetadata) customs.get(DataStreamMetadata.TYPE);
1453+
if (dsMetadata != null) {
1454+
for (DataStream ds : dsMetadata.dataStreams().values()) {
1455+
if (aliasAndIndexLookup.containsKey(ds.getName())) {
1456+
throw new IllegalStateException("data stream [" + ds.getName() + "] conflicts with existing index or alias");
1457+
}
1458+
1459+
SortedMap<?, ?> map = aliasAndIndexLookup.subMap(ds.getName() + "-", ds.getName() + "."); // '.' is the char after '-'
1460+
if (map.size() != 0) {
1461+
throw new IllegalStateException("data stream [" + ds.getName() +
1462+
"] could create backing indices that conflict with " + map.size() + " existing index(s) or alias(s)" +
1463+
" including '" + map.firstKey() + "'");
1464+
}
1465+
}
1466+
}
1467+
}
1468+
14501469
public static void toXContent(MetaData metaData, XContentBuilder builder, ToXContent.Params params) throws IOException {
14511470
XContentContext context = XContentContext.valueOf(params.param(CONTEXT_MODE_PARAM, CONTEXT_MODE_API));
14521471

server/src/test/java/org/elasticsearch/action/admin/indices/datastream/CreateDataStreamRequestTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,13 @@ public void testCreateDuplicateDataStream() {
8181
() -> CreateDataStreamAction.TransportAction.createDataStream(cs, req));
8282
assertThat(e.getMessage(), containsString("data_stream [" + dataStreamName + "] already exists"));
8383
}
84+
85+
public void testCreateDataStreamWithInvalidName() {
86+
final String dataStreamName = "_My-da#ta- ,stream-";
87+
ClusterState cs = ClusterState.builder(new ClusterName("_name")).build();
88+
CreateDataStreamAction.Request req = new CreateDataStreamAction.Request(dataStreamName);
89+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
90+
() -> CreateDataStreamAction.TransportAction.createDataStream(cs, req));
91+
assertThat(e.getMessage(), containsString("must not contain the following characters"));
92+
}
8493
}

server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataTests.java

+46
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
import java.io.IOException;
4646
import java.util.Arrays;
47+
import java.util.Collections;
4748
import java.util.HashMap;
4849
import java.util.HashSet;
4950
import java.util.List;
@@ -941,6 +942,51 @@ public void testBuilderRejectsNullInCustoms() {
941942
assertThat(expectThrows(NullPointerException.class, () -> builder.customs(map)).getMessage(), containsString(key));
942943
}
943944

945+
public void testBuilderRejectsDataStreamThatConflictsWithIndex() {
946+
final String dataStreamName = "my-data-stream";
947+
MetaData.Builder b = MetaData.builder()
948+
.put(IndexMetaData.builder(dataStreamName)
949+
.settings(settings(Version.CURRENT))
950+
.numberOfShards(1)
951+
.numberOfReplicas(1)
952+
.build(), false)
953+
.put(new DataStream(dataStreamName, "ts", Collections.emptyList()));
954+
955+
IllegalStateException e = expectThrows(IllegalStateException.class, b::build);
956+
assertThat(e.getMessage(), containsString("data stream [" + dataStreamName + "] conflicts with existing index or alias"));
957+
}
958+
959+
public void testBuilderRejectsDataStreamThatConflictsWithAlias() {
960+
final String dataStreamName = "my-data-stream";
961+
MetaData.Builder b = MetaData.builder()
962+
.put(IndexMetaData.builder(dataStreamName + "z")
963+
.settings(settings(Version.CURRENT))
964+
.numberOfShards(1)
965+
.numberOfReplicas(1)
966+
.putAlias(AliasMetaData.builder(dataStreamName).build())
967+
.build(), false)
968+
.put(new DataStream(dataStreamName, "ts", Collections.emptyList()));
969+
970+
IllegalStateException e = expectThrows(IllegalStateException.class, b::build);
971+
assertThat(e.getMessage(), containsString("data stream [" + dataStreamName + "] conflicts with existing index or alias"));
972+
}
973+
974+
public void testBuilderRejectsDataStreamWithConflictingBackingIndices() {
975+
final String dataStreamName = "my-data-stream";
976+
final String conflictingIndex = dataStreamName + "-00001";
977+
MetaData.Builder b = MetaData.builder()
978+
.put(IndexMetaData.builder(conflictingIndex)
979+
.settings(settings(Version.CURRENT))
980+
.numberOfShards(1)
981+
.numberOfReplicas(1)
982+
.build(), false)
983+
.put(new DataStream(dataStreamName, "ts", Collections.emptyList()));
984+
985+
IllegalStateException e = expectThrows(IllegalStateException.class, b::build);
986+
assertThat(e.getMessage(), containsString("data stream [" + dataStreamName +
987+
"] could create backing indices that conflict with 1 existing index(s) or alias(s) including '" + conflictingIndex + "'"));
988+
}
989+
944990
public void testSerialization() throws IOException {
945991
final MetaData orig = randomMetaData();
946992
final BytesStreamOutput out = new BytesStreamOutput();

0 commit comments

Comments
 (0)