From e3acafd48b00d88b0ada9c5584bef8ffdb098ec0 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Fri, 19 Aug 2022 15:32:36 -0700 Subject: [PATCH] [Feature/extensions] Provide Extension API to OpenSearch (#4100) * Provide Extension API to OpenSearch Signed-off-by: Daniel Widdis * Add tests Signed-off-by: Daniel Widdis * Add javadocs Signed-off-by: Daniel Widdis * Add parsing of API requests Signed-off-by: Daniel Widdis * Tweak API parsing for more flexible user formatting Signed-off-by: Daniel Widdis * Remove forbidden toUpperCase usage Signed-off-by: Daniel Widdis * Fix failing test as we now register four handlers Signed-off-by: Daniel Widdis * Rename Api to RestApi Signed-off-by: Daniel Widdis * Fix errors in previous merge conflict resolution Signed-off-by: Daniel Widdis * Rename RestApi to RestActions Signed-off-by: Daniel Widdis * Fix tests to fail on the correct wrong thing Signed-off-by: Daniel Widdis * More verbose variable name Signed-off-by: Daniel Widdis * Fix license header Signed-off-by: Daniel Widdis * Fixing NodeGatewayStartedShards bwc (de)serialization issues (#4258) Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko * Fixing NodeGatewayStartedShards bwc (de)serialization issues (#4258) Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko * Fix tests to account for TotalHits uncertainty Signed-off-by: Daniel Widdis Signed-off-by: Daniel Widdis Signed-off-by: Andriy Redko Co-authored-by: Andriy Redko --- .../licenses/commons-logging-1.2.jar.sha1 | 1 - .../extensions/ExtensionsOrchestrator.java | 145 +++++++++++++----- .../RegisterRestActionsRequest.java | 72 +++++++++ .../RegisterRestActionsResponse.java | 41 +++++ ...ransportNodesListGatewayStartedShards.java | 4 +- .../ExtensionsOrchestratorTests.java | 135 +++++++++++----- .../AbstractStringFieldDataTestCase.java | 9 +- 7 files changed, 327 insertions(+), 80 deletions(-) delete mode 100644 modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 create mode 100644 server/src/main/java/org/opensearch/extensions/RegisterRestActionsRequest.java create mode 100644 server/src/main/java/org/opensearch/extensions/RegisterRestActionsResponse.java diff --git a/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 b/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 deleted file mode 100644 index f40f0242448e8..0000000000000 --- a/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/server/src/main/java/org/opensearch/extensions/ExtensionsOrchestrator.java b/server/src/main/java/org/opensearch/extensions/ExtensionsOrchestrator.java index ad2ccbaa39ceb..870f34bb4fdf8 100644 --- a/server/src/main/java/org/opensearch/extensions/ExtensionsOrchestrator.java +++ b/server/src/main/java/org/opensearch/extensions/ExtensionsOrchestrator.java @@ -17,12 +17,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -48,6 +46,7 @@ import org.opensearch.indices.cluster.IndicesClusterStateService; import org.opensearch.node.ReportingService; import org.opensearch.plugins.PluginInfo; +import org.opensearch.rest.RestRequest; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponse; @@ -56,8 +55,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import java.util.HashMap; -import java.util.List; /** * The main class for Plugin Extensibility @@ -71,6 +68,7 @@ public class ExtensionsOrchestrator implements ReportingService extensionsList; List extensionsInitializedList; + Map extensionIdMap; + Map> extensionRestActionsMap; TransportService transportService; ClusterService clusterService; ExtensionNamedWriteableRegistry namedWriteableRegistry; + /** + * Instantiate a new ExtensionsOrchestrator object to handle requests and responses from extensions. + * + * @param settings Settings from the node the orchestrator is running on. + * @param extensionsPath Path to a directory containing extensions. + * @throws IOException If the extensions discovery file is not properly retrieved. + */ public ExtensionsOrchestrator(Settings settings, Path extensionsPath) throws IOException { logger.info("ExtensionsOrchestrator initialized"); this.extensionsPath = extensionsPath; this.transportService = null; this.extensionsList = new ArrayList(); this.extensionsInitializedList = new ArrayList(); + this.extensionIdMap = new HashMap(); + this.extensionRestActionsMap = new HashMap>(); this.clusterService = null; this.namedWriteableRegistry = null; @@ -122,6 +132,11 @@ public ExtensionsOrchestrator(Settings settings, Path extensionsPath) throws IOE } + /** + * Sets the transport service and registers request handlers. + * + * @param transportService The transport service to set. + */ public void setTransportService(TransportService transportService) { this.transportService = transportService; registerRequestHandler(); @@ -136,6 +151,14 @@ public void setNamedWriteableRegistry() { } private void registerRequestHandler() { + transportService.registerRequestHandler( + REQUEST_EXTENSION_REGISTER_REST_ACTIONS, + ThreadPool.Names.GENERIC, + false, + false, + RegisterRestActionsRequest::new, + ((request, channel, task) -> channel.sendResponse(handleRegisterRestActionsRequest(request))) + ); transportService.registerRequestHandler( REQUEST_EXTENSION_CLUSTER_STATE, ThreadPool.Names.GENERIC, @@ -185,30 +208,30 @@ private void extensionsDiscovery() throws IOException { } for (Extension extension : extensions) { try { - extensionsList.add( - new DiscoveryExtension( + DiscoveryExtension discoveryExtension = new DiscoveryExtension( + extension.getName(), + extension.getUniqueId(), + // placeholder for ephemeral id, will change with POC discovery + extension.getUniqueId(), + extension.getHostName(), + extension.getHostAddress(), + new TransportAddress(InetAddress.getByName(extension.getHostAddress()), Integer.parseInt(extension.getPort())), + new HashMap(), + Version.fromString(extension.getOpensearchVersion()), + new PluginInfo( extension.getName(), - extension.getUniqueId(), - // placeholder for ephemeral id, will change with POC discovery - extension.getUniqueId(), - extension.getHostName(), - extension.getHostAddress(), - new TransportAddress(InetAddress.getByName(extension.getHostAddress()), Integer.parseInt(extension.getPort())), - new HashMap(), + extension.getDescription(), + extension.getVersion(), Version.fromString(extension.getOpensearchVersion()), - new PluginInfo( - extension.getName(), - extension.getDescription(), - extension.getVersion(), - Version.fromString(extension.getOpensearchVersion()), - extension.getJavaVersion(), - extension.getClassName(), - new ArrayList(), - Boolean.parseBoolean(extension.hasNativeController()) - ) + extension.getJavaVersion(), + extension.getClassName(), + new ArrayList(), + Boolean.parseBoolean(extension.hasNativeController()) ) ); - logger.info("Loaded extension: " + extension); + extensionsList.add(discoveryExtension); + extensionIdMap.put(extension.getUniqueId(), discoveryExtension); + logger.info("Loaded extension: " + extension + " with id " + extension.getUniqueId()); } catch (IllegalArgumentException e) { logger.error(e.toString()); } @@ -242,6 +265,7 @@ public void handleResponse(InitializeExtensionsResponse response) { for (DiscoveryExtension extension : extensionsList) { if (extension.getName().equals(response.getName())) { extensionsInitializedList.add(extension); + logger.info("Initialized extension: " + extension.getName()); break; } } @@ -250,7 +274,7 @@ public void handleResponse(InitializeExtensionsResponse response) { @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage("Extension request failed"), exp); + logger.debug(new ParameterizedMessage("Extension initialization failed"), exp); inProgressLatch.countDown(); } @@ -274,23 +298,62 @@ public String executor() { } } - TransportResponse handleExtensionRequest(ExtensionRequest extensionRequest) throws Exception { - // Read enum - if (extensionRequest.getRequestType() == RequestType.REQUEST_EXTENSION_CLUSTER_STATE) { - ClusterStateResponse clusterStateResponse = new ClusterStateResponse( - clusterService.getClusterName(), - clusterService.state(), - false + /** + * Handles a {@link RegisterRestActionsRequest}. + * + * @param restActionsRequest The request to handle. + * @return A {@link RegisterRestActionsResponse} indicating success. + * @throws Exception if the request is not handled properly. + */ + TransportResponse handleRegisterRestActionsRequest(RegisterRestActionsRequest restActionsRequest) throws Exception { + DiscoveryExtension extension = extensionIdMap.get(restActionsRequest.getNodeId()); + if (extension == null) { + throw new IllegalArgumentException( + "REST Actions Request unique id " + restActionsRequest.getNodeId() + " does not match a discovered extension." ); - return clusterStateResponse; - } else if (extensionRequest.getRequestType() == RequestType.REQUEST_EXTENSION_LOCAL_NODE) { - LocalNodeResponse localNodeResponse = new LocalNodeResponse(clusterService); - return localNodeResponse; - } else if (extensionRequest.getRequestType() == RequestType.REQUEST_EXTENSION_CLUSTER_SETTINGS) { - ClusterSettingsResponse clusterSettingsResponse = new ClusterSettingsResponse(clusterService); - return clusterSettingsResponse; } - throw new Exception("Handler not present for the provided request"); + for (String restAction : restActionsRequest.getRestActions()) { + RestRequest.Method method; + String uri; + try { + int delim = restAction.indexOf(' '); + method = RestRequest.Method.valueOf(restAction.substring(0, delim)); + uri = restAction.substring(delim).trim(); + } catch (IndexOutOfBoundsException | IllegalArgumentException e) { + throw new IllegalArgumentException(restAction + " does not begin with a valid REST method"); + } + logger.info("Registering: " + method + " /_extensions/_" + extension.getName() + uri); + // TODO turn the restAction string into an Action to send to RestController.registerHandler + } + extensionRestActionsMap.put(restActionsRequest.getNodeId(), restActionsRequest.getRestActions()); + return new RegisterRestActionsResponse( + "Registered node " + + restActionsRequest.getNodeId() + + ", extension " + + extension.getName() + + " to handle REST Actions " + + restActionsRequest.getRestActions() + ); + } + + /** + * Handles an {@link ExtensionRequest}. + * + * @param extensionRequest The request to handle, of a type defined in the {@link RequestType} enum. + * @return an Response matching the request. + * @throws Exception if the request is not handled properly. + */ + TransportResponse handleExtensionRequest(ExtensionRequest extensionRequest) throws Exception { + switch (extensionRequest.getRequestType()) { + case REQUEST_EXTENSION_CLUSTER_STATE: + return new ClusterStateResponse(clusterService.getClusterName(), clusterService.state(), false); + case REQUEST_EXTENSION_LOCAL_NODE: + return new LocalNodeResponse(clusterService); + case REQUEST_EXTENSION_CLUSTER_SETTINGS: + return new ClusterSettingsResponse(clusterService); + default: + throw new Exception("Handler not present for the provided request"); + } } public void onIndexModule(IndexModule indexModule) throws UnknownHostException { diff --git a/server/src/main/java/org/opensearch/extensions/RegisterRestActionsRequest.java b/server/src/main/java/org/opensearch/extensions/RegisterRestActionsRequest.java new file mode 100644 index 0000000000000..b0ec3bb8aa67b --- /dev/null +++ b/server/src/main/java/org/opensearch/extensions/RegisterRestActionsRequest.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.extensions; + +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.transport.TransportRequest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Request to register extension REST actions + * + * @opensearch.internal + */ +public class RegisterRestActionsRequest extends TransportRequest { + private String nodeId; + private List restActions; + + public RegisterRestActionsRequest(String nodeId, List restActions) { + this.nodeId = nodeId; + this.restActions = new ArrayList<>(restActions); + } + + public RegisterRestActionsRequest(StreamInput in) throws IOException { + super(in); + nodeId = in.readString(); + restActions = in.readStringList(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(nodeId); + out.writeStringCollection(restActions); + } + + public String getNodeId() { + return nodeId; + } + + public List getRestActions() { + return new ArrayList<>(restActions); + } + + @Override + public String toString() { + return "RestActionsRequest{nodeId=" + nodeId + ", restActions=" + restActions + "}"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + RegisterRestActionsRequest that = (RegisterRestActionsRequest) obj; + return Objects.equals(nodeId, that.nodeId) && Objects.equals(restActions, that.restActions); + } + + @Override + public int hashCode() { + return Objects.hash(nodeId, restActions); + } +} diff --git a/server/src/main/java/org/opensearch/extensions/RegisterRestActionsResponse.java b/server/src/main/java/org/opensearch/extensions/RegisterRestActionsResponse.java new file mode 100644 index 0000000000000..a322552532df0 --- /dev/null +++ b/server/src/main/java/org/opensearch/extensions/RegisterRestActionsResponse.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.extensions; + +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.transport.TransportResponse; + +import java.io.IOException; + +/** + * Response to register REST Actions request. + * + * @opensearch.internal + */ +public class RegisterRestActionsResponse extends TransportResponse { + private String response; + + public RegisterRestActionsResponse(String response) { + this.response = response; + } + + public RegisterRestActionsResponse(StreamInput in) throws IOException { + response = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(response); + } + + public String getResponse() { + return response; + } +} diff --git a/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java b/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java index 953b4def9d653..c43f539243d7a 100644 --- a/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java +++ b/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java @@ -373,7 +373,7 @@ public NodeGatewayStartedShards(StreamInput in) throws IOException { } else { storeException = null; } - if (in.getVersion().onOrAfter(Version.V_3_0_0) && in.readBoolean()) { + if (in.getVersion().onOrAfter(Version.V_2_3_0) && in.readBoolean()) { replicationCheckpoint = new ReplicationCheckpoint(in); } else { replicationCheckpoint = null; @@ -430,7 +430,7 @@ public void writeTo(StreamOutput out) throws IOException { } else { out.writeBoolean(false); } - if (out.getVersion().onOrAfter(Version.V_3_0_0)) { + if (out.getVersion().onOrAfter(Version.V_2_3_0)) { if (replicationCheckpoint != null) { out.writeBoolean(true); replicationCheckpoint.writeTo(out); diff --git a/server/src/test/java/org/opensearch/extensions/ExtensionsOrchestratorTests.java b/server/src/test/java/org/opensearch/extensions/ExtensionsOrchestratorTests.java index fb3a135b2aa34..9a6fd56f1cf04 100644 --- a/server/src/test/java/org/opensearch/extensions/ExtensionsOrchestratorTests.java +++ b/server/src/test/java/org/opensearch/extensions/ExtensionsOrchestratorTests.java @@ -76,6 +76,7 @@ import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.Transport; +import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportService; import org.opensearch.transport.nio.MockNioTransport; @@ -85,12 +86,38 @@ public class ExtensionsOrchestratorTests extends OpenSearchTestCase { private ClusterService clusterService; private MockNioTransport transport; private Path extensionDir; - private List extensionsYmlLines; private final ThreadPool threadPool = new TestThreadPool(ExtensionsOrchestratorTests.class.getSimpleName()); private final Settings settings = Settings.builder() .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build(); + private final List extensionsYmlLines = Arrays.asList( + "extensions:", + " - name: firstExtension", + " uniqueId: uniqueid1", + " hostName: 'myIndependentPluginHost1'", + " hostAddress: '127.0.0.0'", + " port: '9300'", + " version: '0.0.7'", + " description: Fake description 1", + " opensearchVersion: '3.0.0'", + " javaVersion: '14'", + " className: fakeClass1", + " customFolderName: fakeFolder1", + " hasNativeController: false", + " - name: secondExtension", + " uniqueId: 'uniqueid2'", + " hostName: 'myIndependentPluginHost2'", + " hostAddress: '127.0.0.1'", + " port: '9301'", + " version: '3.14.16'", + " description: Fake description 2", + " opensearchVersion: '2.0.0'", + " javaVersion: '17'", + " className: fakeClass2", + " customFolderName: fakeFolder2", + " hasNativeController: true" + ); @Before public void setup() throws Exception { @@ -121,36 +148,11 @@ public void setup() throws Exception { Collections.emptySet() ); clusterService = createClusterService(threadPool); + extensionDir = createTempDir(); - extensionsYmlLines = Arrays.asList( - "extensions:", - " - name: firstExtension", - " uniqueId: uniqueid1", - " hostName: 'myIndependentPluginHost1'", - " hostAddress: '127.0.0.0'", - " port: '9300'", - " version: '0.0.7'", - " description: Fake description 1", - " opensearchVersion: '3.0.0'", - " javaVersion: '14'", - " className: fakeClass1", - " customFolderName: fakeFolder1", - " hasNativeController: false", - " - name: secondExtension", - " uniqueId: 'uniqueid2'", - " hostName: 'myIndependentPluginHost2'", - " hostAddress: '127.0.0.1'", - " port: '9301'", - " version: '3.14.16'", - " description: Fake description 2", - " opensearchVersion: '2.0.0'", - " javaVersion: '17'", - " className: fakeClass2", - " customFolderName: fakeFolder2", - " hasNativeController: true" - ); } + @Override @After public void tearDown() throws Exception { super.tearDown(); @@ -159,6 +161,8 @@ public void tearDown() throws Exception { } public void testExtensionsDiscovery() throws Exception { + Path extensionDir = createTempDir(); + Files.write(extensionDir.resolve("extensions.yml"), extensionsYmlLines, StandardCharsets.UTF_8); ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); @@ -243,8 +247,10 @@ public void testNoExtensionsFile() throws Exception { } public void testEmptyExtensionsFile() throws Exception { - List extensionsYmlLines = Arrays.asList(); - Files.write(extensionDir.resolve("extensions.yml"), extensionsYmlLines, StandardCharsets.UTF_8); + Path extensionDir = createTempDir(); + + List emptyExtensionsYmlLines = Arrays.asList(); + Files.write(extensionDir.resolve("extensions.yml"), emptyExtensionsYmlLines, StandardCharsets.UTF_8); Settings settings = Settings.builder().build(); @@ -252,6 +258,8 @@ public void testEmptyExtensionsFile() throws Exception { } public void testExtensionsInitialize() throws Exception { + Path extensionDir = createTempDir(); + Files.write(extensionDir.resolve("extensions.yml"), extensionsYmlLines, StandardCharsets.UTF_8); ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); @@ -289,6 +297,63 @@ public void testExtensionsInitialize() throws Exception { } } + public void testHandleRegisterActionsRequest() throws Exception { + + Path extensionDir = createTempDir(); + + Files.write(extensionDir.resolve("extensions.yml"), extensionsYmlLines, StandardCharsets.UTF_8); + + ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); + + extensionsOrchestrator.setTransportService(transportService); + String nodeIdStr = "uniqueid1"; + List actionsList = List.of("GET /foo", "PUT /bar", "POST /baz"); + RegisterRestActionsRequest registerActionsRequest = new RegisterRestActionsRequest(nodeIdStr, actionsList); + TransportResponse response = extensionsOrchestrator.handleRegisterRestActionsRequest(registerActionsRequest); + assertEquals(RegisterRestActionsResponse.class, response.getClass()); + assertTrue(((RegisterRestActionsResponse) response).getResponse().contains(nodeIdStr)); + assertTrue(((RegisterRestActionsResponse) response).getResponse().contains(actionsList.toString())); + } + + public void testHandleRegisterActionsRequestWithInvalidId() throws Exception { + + Path extensionDir = createTempDir(); + + ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); + + extensionsOrchestrator.setTransportService(transportService); + String nodeIdStr = "notAValidUniqueId"; + List actionsList = List.of("GET /foo", "PUT /bar", "POST /baz"); + RegisterRestActionsRequest registerActionsRequest = new RegisterRestActionsRequest(nodeIdStr, actionsList); + expectThrows(IllegalArgumentException.class, () -> extensionsOrchestrator.handleRegisterRestActionsRequest(registerActionsRequest)); + } + + public void testHandleRegisterActionsRequestWithInvalidMethod() throws Exception { + + Path extensionDir = createTempDir(); + + ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); + + extensionsOrchestrator.setTransportService(transportService); + String nodeIdStr = "uniqueid1"; + List actionsList = List.of("FOO /foo", "PUT /bar", "POST /baz"); + RegisterRestActionsRequest registerActionsRequest = new RegisterRestActionsRequest(nodeIdStr, actionsList); + expectThrows(IllegalArgumentException.class, () -> extensionsOrchestrator.handleRegisterRestActionsRequest(registerActionsRequest)); + } + + public void testHandleRegisterActionsRequestWithInvalidUri() throws Exception { + + Path extensionDir = createTempDir(); + + ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); + + extensionsOrchestrator.setTransportService(transportService); + String nodeIdStr = "uniqueid1"; + List actionsList = List.of("GET", "PUT /bar", "POST /baz"); + RegisterRestActionsRequest registerActionsRequest = new RegisterRestActionsRequest(nodeIdStr, actionsList); + expectThrows(IllegalArgumentException.class, () -> extensionsOrchestrator.handleRegisterRestActionsRequest(registerActionsRequest)); + } + public void testHandleExtensionRequest() throws Exception { ExtensionsOrchestrator extensionsOrchestrator = new ExtensionsOrchestrator(settings, extensionDir); @@ -296,19 +361,19 @@ public void testHandleExtensionRequest() throws Exception { extensionsOrchestrator.setTransportService(transportService); extensionsOrchestrator.setClusterService(clusterService); ExtensionRequest clusterStateRequest = new ExtensionRequest(ExtensionsOrchestrator.RequestType.REQUEST_EXTENSION_CLUSTER_STATE); - assertEquals(extensionsOrchestrator.handleExtensionRequest(clusterStateRequest).getClass(), ClusterStateResponse.class); + assertEquals(ClusterStateResponse.class, extensionsOrchestrator.handleExtensionRequest(clusterStateRequest).getClass()); ExtensionRequest clusterSettingRequest = new ExtensionRequest( ExtensionsOrchestrator.RequestType.REQUEST_EXTENSION_CLUSTER_SETTINGS ); - assertEquals(extensionsOrchestrator.handleExtensionRequest(clusterSettingRequest).getClass(), ClusterSettingsResponse.class); + assertEquals(ClusterSettingsResponse.class, extensionsOrchestrator.handleExtensionRequest(clusterSettingRequest).getClass()); ExtensionRequest localNodeRequest = new ExtensionRequest(ExtensionsOrchestrator.RequestType.REQUEST_EXTENSION_LOCAL_NODE); - assertEquals(extensionsOrchestrator.handleExtensionRequest(localNodeRequest).getClass(), LocalNodeResponse.class); + assertEquals(LocalNodeResponse.class, extensionsOrchestrator.handleExtensionRequest(localNodeRequest).getClass()); ExtensionRequest exceptionRequest = new ExtensionRequest(ExtensionsOrchestrator.RequestType.GET_SETTINGS); Exception exception = expectThrows(Exception.class, () -> extensionsOrchestrator.handleExtensionRequest(exceptionRequest)); - assertEquals(exception.getMessage(), "Handler not present for the provided request"); + assertEquals("Handler not present for the provided request", exception.getMessage()); } public void testRegisterHandler() throws Exception { @@ -328,7 +393,7 @@ public void testRegisterHandler() throws Exception { ); extensionsOrchestrator.setTransportService(mockTransportService); - verify(mockTransportService, times(3)).registerRequestHandler(anyString(), anyString(), anyBoolean(), anyBoolean(), any(), any()); + verify(mockTransportService, times(4)).registerRequestHandler(anyString(), anyString(), anyBoolean(), anyBoolean(), any(), any()); } diff --git a/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java b/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java index 763ee59a385a2..76496491b3ed4 100644 --- a/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java +++ b/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java @@ -52,6 +52,7 @@ import org.apache.lucene.search.SortField; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopFieldDocs; +import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.join.QueryBitSetProducer; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ToParentBlockJoinQuery; @@ -340,7 +341,13 @@ public void testSortMissing(boolean first, boolean reverse) throws IOException { randomBoolean() ? numDocs : randomIntBetween(10, numDocs), new Sort(sortField) ); - assertEquals(numDocs, topDocs.totalHits.value); + // As of Lucene 9.0.0, totalHits may be a lower bound + if (topDocs.totalHits.relation == TotalHits.Relation.EQUAL_TO) { + assertEquals(numDocs, topDocs.totalHits.value); + } else { + assertTrue(1000 <= topDocs.totalHits.value); + assertTrue(numDocs >= topDocs.totalHits.value); + } BytesRef previousValue = first ? null : reverse ? UnicodeUtil.BIG_TERM : new BytesRef(); for (int i = 0; i < topDocs.scoreDocs.length; ++i) { final String docValue = searcher.doc(topDocs.scoreDocs[i].doc).get("value");