diff --git a/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java b/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java index 4ccf26a33..2faf7f08c 100644 --- a/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java +++ b/src/main/java/org/opensearch/securityanalytics/SecurityAnalyticsPlugin.java @@ -121,7 +121,7 @@ public Collection createComponents(Client client, Supplier repositoriesServiceSupplier) { detectorIndices = new DetectorIndices(client.admin(), clusterService, threadPool); ruleTopicIndices = new RuleTopicIndices(client, clusterService); - mapperService = new MapperService(client.admin().indices()); + mapperService = new MapperService(client.admin().indices(), clusterService); ruleIndices = new RuleIndices(client, clusterService, threadPool); return List.of(detectorIndices, ruleTopicIndices, ruleIndices, mapperService); } diff --git a/src/main/java/org/opensearch/securityanalytics/mapper/MapperService.java b/src/main/java/org/opensearch/securityanalytics/mapper/MapperService.java index 52ce060d2..c24556605 100644 --- a/src/main/java/org/opensearch/securityanalytics/mapper/MapperService.java +++ b/src/main/java/org/opensearch/securityanalytics/mapper/MapperService.java @@ -12,13 +12,17 @@ import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchStatusException; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; import org.opensearch.action.admin.indices.mapping.get.GetMappingsRequest; import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; import org.opensearch.action.support.GroupedActionListener; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.IndicesAdminClient; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.xcontent.XContentType; import org.opensearch.rest.RestStatus; @@ -31,6 +35,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.opensearch.securityanalytics.action.GetMappingsViewResponse; +import org.opensearch.securityanalytics.util.IndexUtils; import org.opensearch.securityanalytics.util.SecurityAnalyticsException; @@ -40,13 +45,15 @@ public class MapperService { private static final Logger log = LogManager.getLogger(MapperService.class); + private ClusterService clusterService; IndicesAdminClient indicesClient; public MapperService() {} - public MapperService(IndicesAdminClient indicesClient) { + public MapperService(IndicesAdminClient indicesClient, ClusterService clusterService) { this.indicesClient = indicesClient; + this.clusterService = clusterService; } void setIndicesAdminClient(IndicesAdminClient client) { @@ -93,7 +100,8 @@ public void onResponse(Collection response) { public void onFailure(Exception e) { actionListener.onFailure( new SecurityAnalyticsException( - "Failed applying mappings to index", RestStatus.INTERNAL_SERVER_ERROR, e) + "Failed applying mappings to index", RestStatus.INTERNAL_SERVER_ERROR, e + ) ); } }, numOfIndices); @@ -280,7 +288,34 @@ public void getMappingsViewAction( String mapperTopic, ActionListener actionListener ) { - GetMappingsRequest getMappingsRequest = new GetMappingsRequest().indices(indexName); + try { + // We are returning mappings view for only 1 index: writeIndex or latest from the pattern + resolveConcreteIndex(indexName, new ActionListener<>() { + @Override + public void onResponse(String concreteIndex) { + doGetMappingsView(mapperTopic, actionListener, concreteIndex); + } + + @Override + public void onFailure(Exception e) { + actionListener.onFailure(e); + } + }); + + + } catch (IOException e) { + throw SecurityAnalyticsException.wrap(e); + } + } + + /** + * Constructs Mappings View of index + * @param mapperTopic Mapper Topic describing set of alias mappings + * @param actionListener Action Listener + * @param concreteIndex Concrete Index name for which we're computing Mappings View + */ + private void doGetMappingsView(String mapperTopic, ActionListener actionListener, String concreteIndex) { + GetMappingsRequest getMappingsRequest = new GetMappingsRequest().indices(concreteIndex); indicesClient.getMappings(getMappingsRequest, new ActionListener<>() { @Override public void onResponse(GetMappingsResponse getMappingsResponse) { @@ -333,4 +368,45 @@ public void onFailure(Exception e) { } }); } + + /** + * Given index name, resolves it to single concrete index, depending on what initial indexName is. + * In case of Datastream or Alias, WriteIndex would be returned. In case of index pattern, newest index by creation date would be returned. + * @param indexName Datastream, Alias, index patter or concrete index + * @param actionListener Action Listener + * @throws IOException + */ + private void resolveConcreteIndex(String indexName, ActionListener actionListener) throws IOException { + + indicesClient.getIndex((new GetIndexRequest()).indices(indexName), new ActionListener<>() { + @Override + public void onResponse(GetIndexResponse getIndexResponse) { + String[] indices = getIndexResponse.indices(); + if (indices.length == 0) { + actionListener.onFailure( + SecurityAnalyticsException.wrap( + new IllegalArgumentException("Invalid index name: [" + indexName + "]") + ) + ); + } else if (indices.length == 1) { + actionListener.onResponse(indices[0]); + } else if (indices.length > 1) { + String writeIndex = IndexUtils.getWriteIndex(indexName, MapperService.this.clusterService.state()); + if (writeIndex != null) { + actionListener.onResponse(writeIndex); + } else { + actionListener.onResponse( + IndexUtils.getNewestIndexByCreationDate(indices, MapperService.this.clusterService.state()) + ); + } + } + } + + @Override + public void onFailure(Exception e) { + actionListener.onFailure(e); + } + }); + + } } \ No newline at end of file diff --git a/src/main/java/org/opensearch/securityanalytics/transport/TransportGetMappingsViewAction.java b/src/main/java/org/opensearch/securityanalytics/transport/TransportGetMappingsViewAction.java index 2ec636d4e..319bc5bca 100644 --- a/src/main/java/org/opensearch/securityanalytics/transport/TransportGetMappingsViewAction.java +++ b/src/main/java/org/opensearch/securityanalytics/transport/TransportGetMappingsViewAction.java @@ -47,17 +47,6 @@ public TransportGetMappingsViewAction( @Override protected void doExecute(Task task, GetMappingsViewRequest request, ActionListener actionListener) { this.threadPool.getThreadContext().stashContext(); - IndexMetadata index = clusterService.state().metadata().index(request.getIndexName()); - if (index == null) { - actionListener.onFailure( - SecurityAnalyticsException.wrap( - new OpenSearchStatusException( - "Could not find index [" + request.getIndexName() + "]", RestStatus.NOT_FOUND - ) - ) - ); - return; - } - mapperService.getMappingsViewAction(request.getIndexName(), request.getRuleTopic(), actionListener); + this.mapperService.getMappingsViewAction(request.getIndexName(), request.getRuleTopic(), actionListener); } } \ No newline at end of file diff --git a/src/main/java/org/opensearch/securityanalytics/util/IndexUtils.java b/src/main/java/org/opensearch/securityanalytics/util/IndexUtils.java index 1bd1ff00f..aa1455470 100644 --- a/src/main/java/org/opensearch/securityanalytics/util/IndexUtils.java +++ b/src/main/java/org/opensearch/securityanalytics/util/IndexUtils.java @@ -4,11 +4,13 @@ */ package org.opensearch.securityanalytics.util; +import java.util.SortedMap; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.IndicesAdminClient; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexAbstraction; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.xcontent.LoggingDeprecationHandler; import org.opensearch.common.xcontent.NamedXContentRegistry; @@ -105,4 +107,39 @@ public static void updateIndexMapping( } } } + + public static boolean isDataStream(String name, ClusterState clusterState) { + return clusterState.getMetadata().dataStreams().containsKey(name); + } + public static boolean isAlias(String indexName, ClusterState clusterState) { + return clusterState.getMetadata().hasAlias(indexName); + } + public static String getWriteIndex(String indexName, ClusterState clusterState) { + if(isAlias(indexName, clusterState) || isDataStream(indexName, clusterState)) { + IndexMetadata metadata = clusterState.getMetadata() + .getIndicesLookup() + .get(indexName).getWriteIndex(); + if (metadata != null) { + return metadata.getIndex().getName(); + } + } + return null; + } + + public static String getNewestIndexByCreationDate(String[] concreteIndices, ClusterState clusterState) { + final SortedMap lookup = clusterState.getMetadata().getIndicesLookup(); + long maxCreationDate = Long.MIN_VALUE; + String newestIndex = null; + for (String indexName : concreteIndices) { + IndexAbstraction index = lookup.get(indexName); + IndexMetadata indexMetadata = clusterState.getMetadata().index(indexName); + if(index != null && index.getType() == IndexAbstraction.Type.CONCRETE_INDEX) { + if (indexMetadata.getCreationDate() > maxCreationDate) { + maxCreationDate = indexMetadata.getCreationDate(); + newestIndex = indexName; + } + } + } + return newestIndex; + } } \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java b/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java index 61192251c..f62dfb920 100644 --- a/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java +++ b/src/test/java/org/opensearch/securityanalytics/SecurityAnalyticsRestTestCase.java @@ -1342,4 +1342,9 @@ private Map getIndexSettingsAPI(String index) throws IOException Map respMap = asMap(resp); return respMap; } + + protected void doRollover(String datastreamName) throws IOException { + Response response = makeRequest(client(), "POST", datastreamName + "/_rollover", Collections.emptyMap(), null); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + } } \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/mapper/MapperRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/mapper/MapperRestApiIT.java index 431b89564..3e9e4f021 100644 --- a/src/test/java/org/opensearch/securityanalytics/mapper/MapperRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/mapper/MapperRestApiIT.java @@ -4,7 +4,11 @@ */ package org.opensearch.securityanalytics.mapper; +import java.util.Collections; +import java.util.Optional; import org.apache.http.HttpStatus; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.opensearch.action.search.SearchResponse; import org.opensearch.client.Request; @@ -289,6 +293,197 @@ public void testGetMappingsViewSuccess() throws IOException { assertEquals(2, unmappedFieldAliases.size()); } + public void testGetMappingsView_index_pattern_two_indices_Success() throws IOException { + + String testIndexName1 = "get_mappings_view_index11"; + String testIndexName2 = "get_mappings_view_index22"; + String indexPattern = "get_mappings_view_index*"; + createSampleIndex(testIndexName1); + createSampleIndex(testIndexName2); + indexDoc(testIndexName2, "987654", "{ \"extra_field\": 12345 }"); + + // Execute CreateMappingsAction to add alias mapping for index + Request request = new Request("GET", SecurityAnalyticsPlugin.MAPPINGS_VIEW_BASE_URI); + // both req params and req body are supported + request.addParameter("index_name", indexPattern); + request.addParameter("rule_topic", "netflow"); + Response response = client().performRequest(request); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Map respMap = responseAsMap(response); + // Verify alias mappings + Map props = (Map) respMap.get("properties"); + assertEquals(4, props.size()); + assertTrue(props.containsKey("source.ip")); + assertTrue(props.containsKey("destination.ip")); + assertTrue(props.containsKey("source.port")); + assertTrue(props.containsKey("destination.port")); + // Verify unmapped index fields + List unmappedIndexFields = (List) respMap.get("unmapped_index_fields"); + assertEquals(7, unmappedIndexFields.size()); + // Verify that we got Mappings View of concrete index testIndexName2 because it is newest of all under this alias + Optional extraField = unmappedIndexFields.stream().filter(e -> e.equals("extra_field")).findFirst(); + assertTrue(extraField.isPresent()); + // Verify unmapped field aliases + List unmappedFieldAliases = (List) respMap.get("unmapped_field_aliases"); + assertEquals(2, unmappedFieldAliases.size()); + } + + public void testGetMappingsView_alias_without_writeindex_Success() throws IOException { + + String testIndexName1 = "get_mappings_view_index11"; + String testIndexName2 = "get_mappings_view_index22"; + String indexAlias = "index_alias"; + createSampleIndex(testIndexName1, Settings.EMPTY, "\"" + indexAlias + "\":{}"); + createSampleIndex(testIndexName2, Settings.EMPTY, "\"" + indexAlias + "\":{}"); + indexDoc(testIndexName2, "987654", "{ \"extra_field\": 12345 }"); + + // Execute CreateMappingsAction to add alias mapping for index + Request request = new Request("GET", SecurityAnalyticsPlugin.MAPPINGS_VIEW_BASE_URI); + // both req params and req body are supported + request.addParameter("index_name", indexAlias); + request.addParameter("rule_topic", "netflow"); + Response response = client().performRequest(request); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Map respMap = responseAsMap(response); + // Verify alias mappings + Map props = (Map) respMap.get("properties"); + assertEquals(4, props.size()); + assertTrue(props.containsKey("source.ip")); + assertTrue(props.containsKey("destination.ip")); + assertTrue(props.containsKey("source.port")); + assertTrue(props.containsKey("destination.port")); + // Verify unmapped index fields + List unmappedIndexFields = (List) respMap.get("unmapped_index_fields"); + assertEquals(7, unmappedIndexFields.size()); + // Verify that we got Mappings View of concrete index testIndexName2 because it is newest of all under this alias + Optional extraField = unmappedIndexFields.stream().filter(e -> e.equals("extra_field")).findFirst(); + assertTrue(extraField.isPresent()); + // Verify unmapped field aliases + List unmappedFieldAliases = (List) respMap.get("unmapped_field_aliases"); + assertEquals(2, unmappedFieldAliases.size()); + } + + public void testGetMappingsView_alias_with_writeindex_Success() throws IOException { + + String testIndexName1 = "get_mappings_view_index11"; + String testIndexName2 = "get_mappings_view_index22"; + String indexAlias = "index_alias"; + + createSampleIndex(testIndexName2, Settings.EMPTY, "\"" + indexAlias + "\":{}"); + createSampleIndex(testIndexName1, Settings.EMPTY, "\"" + indexAlias + "\":{ \"is_write_index\":true }"); + + // Add extra field by inserting doc to index #1 to differentiate two easier + indexDoc(testIndexName1, "987654", "{ \"extra_field\": 12345 }"); + + // Execute CreateMappingsAction to add alias mapping for index + Request request = new Request("GET", SecurityAnalyticsPlugin.MAPPINGS_VIEW_BASE_URI); + // both req params and req body are supported + request.addParameter("index_name", indexAlias); + request.addParameter("rule_topic", "netflow"); + Response response = client().performRequest(request); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Map respMap = responseAsMap(response); + // Verify alias mappings + Map props = (Map) respMap.get("properties"); + assertEquals(4, props.size()); + assertTrue(props.containsKey("source.ip")); + assertTrue(props.containsKey("destination.ip")); + assertTrue(props.containsKey("source.port")); + assertTrue(props.containsKey("destination.port")); + // Verify unmapped index fields + List unmappedIndexFields = (List) respMap.get("unmapped_index_fields"); + assertEquals(7, unmappedIndexFields.size()); + // Verify that we got Mappings View of concrete index testIndexName2 because it is newest of all under this alias + Optional extraField = unmappedIndexFields.stream().filter(e -> e.equals("extra_field")).findFirst(); + assertTrue(extraField.isPresent()); + // Verify unmapped field aliases + List unmappedFieldAliases = (List) respMap.get("unmapped_field_aliases"); + assertEquals(2, unmappedFieldAliases.size()); + } + + public void testGetMappingsView_datastream_one_backing_index_Success() throws IOException { + + String datastreamName = "my_data_stream"; + createSampleDatastream(datastreamName); + // Execute GetMappingsViewAction to add alias mapping for index + Request request = new Request("GET", SecurityAnalyticsPlugin.MAPPINGS_VIEW_BASE_URI); + // both req params and req body are supported + request.addParameter("index_name", datastreamName); + request.addParameter("rule_topic", "netflow"); + Response response = client().performRequest(request); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Map respMap = responseAsMap(response); + // Verify alias mappings + Map props = (Map) respMap.get("properties"); + assertEquals(4, props.size()); + assertTrue(props.containsKey("source.ip")); + assertTrue(props.containsKey("destination.ip")); + assertTrue(props.containsKey("source.port")); + assertTrue(props.containsKey("destination.port")); + // Verify unmapped index fields + List unmappedIndexFields = (List) respMap.get("unmapped_index_fields"); + assertEquals(7, unmappedIndexFields.size()); + // Verify unmapped field aliases + List unmappedFieldAliases = (List) respMap.get("unmapped_field_aliases"); + assertEquals(2, unmappedFieldAliases.size()); + + deleteDatastream(datastreamName); + } + + public void testGetMappingsView_datastream_two_backing_index_Success() throws IOException { + + String datastreamName = "my_data_stream"; + createSampleDatastream(datastreamName); + + // Modify index template to change mappings and then rollover + String indexMapping = + " \"properties\": {" + + " \"@timestamp\": {" + + " \"type\": \"date\"" + + " }," + + " \"netflow.source_ipv4_address\": {" + + " \"type\": \"ip\"" + + " }" + + "}"; + + String indexTemplateRequest = "{\n" + + " \"index_patterns\": [\"" + datastreamName + "*\"],\n" + + " \"data_stream\": { },\n" + + " \"template\": {\n" + + " \"mappings\" : {" + indexMapping + "}\n" + + " }," + + " \"priority\": 500\n" + + "}"; + + + Response response = makeRequest(client(), "PUT", "_index_template/" + datastreamName + "-template", Collections.emptyMap(), + new StringEntity(indexTemplateRequest), new BasicHeader("Content-Type", "application/json")); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + doRollover(datastreamName); + + // Execute GetMappingsViewAction to add alias mapping for index + Request request = new Request("GET", SecurityAnalyticsPlugin.MAPPINGS_VIEW_BASE_URI); + // both req params and req body are supported + request.addParameter("index_name", datastreamName); + request.addParameter("rule_topic", "netflow"); + response = client().performRequest(request); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Map respMap = responseAsMap(response); + // Verify alias mappings + Map props = (Map) respMap.get("properties"); + assertEquals(1, props.size()); + assertTrue(props.containsKey("source.ip")); + // Verify unmapped index fields + List unmappedIndexFields = (List) respMap.get("unmapped_index_fields"); + assertEquals(1, unmappedIndexFields.size()); + // Verify unmapped field aliases + List unmappedFieldAliases = (List) respMap.get("unmapped_field_aliases"); + assertEquals(5, unmappedFieldAliases.size()); + + deleteDatastream(datastreamName); + } + public void testCreateMappings_withIndexPattern_success() throws IOException { String indexName1 = "test_index_1"; String indexName2 = "test_index_2"; @@ -440,6 +635,10 @@ public void testCreateMappings_withIndexPattern_oneNoMappings_failure() throws I } private void createSampleIndex(String indexName) throws IOException { + createSampleIndex(indexName, Settings.EMPTY, null); + } + + private void createSampleIndex(String indexName, Settings settings, String aliases) throws IOException { String indexMapping = " \"properties\": {" + " \"netflow.source_ipv4_address\": {" + @@ -491,7 +690,7 @@ private void createSampleIndex(String indexName) throws IOException { "}" + " }"; - createIndex(indexName, Settings.EMPTY, indexMapping); + createIndex(indexName, settings, indexMapping, aliases); // Insert sample doc String sampleDoc = "{" + @@ -511,6 +710,100 @@ private void createSampleIndex(String indexName) throws IOException { assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); } + private void createSampleDatastream(String datastreamName) throws IOException { + String indexMapping = + " \"properties\": {" + + " \"@timestamp\": {" + + " \"type\": \"date\"" + + " }," + + " \"netflow.source_ipv4_address\": {" + + " \"type\": \"ip\"" + + " }," + + " \"netflow.destination_transport_port\": {" + + " \"type\": \"integer\"" + + " }," + + " \"netflow.destination_ipv4_address\": {" + + " \"type\": \"ip\"" + + " }," + + " \"netflow.source_transport_port\": {" + + " \"type\": \"integer\"" + + " }," + + " \"netflow.event.stop\": {" + + " \"type\": \"integer\"" + + " }," + + " \"dns.event.stop\": {" + + " \"type\": \"integer\"" + + " }," + + " \"ipx.event.stop\": {" + + " \"type\": \"integer\"" + + " }," + + " \"plain1\": {" + + " \"type\": \"integer\"" + + " }," + + " \"user\":{" + + " \"type\":\"nested\"," + + " \"properties\":{" + + " \"first\":{" + + " \"type\":\"text\"," + + " \"fields\":{" + + " \"keyword\":{" + + " \"type\":\"keyword\"," + + " \"ignore_above\":256" + + "}" + + "}" + + "}," + + " \"last\":{" + + "\"type\":\"text\"," + + "\"fields\":{" + + " \"keyword\":{" + + " \"type\":\"keyword\"," + + " \"ignore_above\":256" + + "}" + + "}" + + "}" + + "}" + + "}" + + " }"; + + + // Create index template + String indexTemplateRequest = "{\n" + + " \"index_patterns\": [\"" + datastreamName + "*\"],\n" + + " \"data_stream\": { },\n" + + " \"template\": {\n" + + " \"mappings\" : {" + indexMapping + "}\n" + + " }," + + " \"priority\": 500\n" + + "}"; + + + Response response = makeRequest(client(), "PUT", "_index_template/" + datastreamName + "-template", Collections.emptyMap(), + new StringEntity(indexTemplateRequest), new BasicHeader("Content-Type", "application/json")); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + // Insert sample doc + String sampleDoc = "{" + + " \"@timestamp\":\"2023-05-06T16:21:15.000Z\"," + + " \"netflow.source_ipv4_address\":\"10.50.221.10\"," + + " \"netflow.destination_transport_port\":1234," + + " \"netflow.destination_ipv4_address\":\"10.53.111.14\"," + + " \"netflow.source_transport_port\":4444" + + "}"; + + // Index doc + Request indexRequest = new Request("POST", datastreamName + "/_doc?refresh=wait_for"); + indexRequest.setJsonEntity(sampleDoc); + response = client().performRequest(indexRequest); + assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); + // Refresh everything + response = client().performRequest(new Request("POST", "_refresh")); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + } + + private void deleteDatastream(String datastreamName) throws IOException { + Request indexRequest = new Request("DELETE", "_data_stream/" + datastreamName); + Response response = client().performRequest(indexRequest); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + } private final String DNS_SAMPLE = "dns-sample.json"; private final String CLOUDTRAIL_SAMPLE = "cloudtrail-sample.json"; diff --git a/src/test/java/org/opensearch/securityanalytics/mapper/MapperUtilsTests.java b/src/test/java/org/opensearch/securityanalytics/mapper/MapperUtilsTests.java index bf093314a..3410d3eaf 100644 --- a/src/test/java/org/opensearch/securityanalytics/mapper/MapperUtilsTests.java +++ b/src/test/java/org/opensearch/securityanalytics/mapper/MapperUtilsTests.java @@ -46,7 +46,7 @@ public void testValidateIndexMappingsEmptyMappings() throws IOException { mappings.put("my_index", mappingMetadata); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> MapperUtils.validateIndexMappings("my_index", mappingMetadata, MapperTopicStore.aliasMappings("test123"))); - assertTrue(e.getMessage().contains(String.format(Locale.getDefault(), "Mappings for index [%s] are empty", "my_index"))); + assertTrue(e.getMessage().contains("Mappings for index [my_index] are empty")); } public void testValidateIndexMappingsNoMissing() throws IOException {