This is an automated email from the ASF dual-hosted git repository.
gerlowskija pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new 762ab9bb311 SOLR-11029 Create a v2 API equivalent for DELETENODE API
762ab9bb311 is described below
commit 762ab9bb3111d82fc60e15906a0555dbad22d5f9
Author: bszabo97 <[email protected]>
AuthorDate: Wed Feb 15 19:13:52 2023 +0200
SOLR-11029 Create a v2 API equivalent for DELETENODE API
The "DELETENODE" functionality is now exposed in the v2 API at:
`POST /api/cluster/nodes/nodeName/clear {...}`
Co-authored-by: Jason Gerlowski <[email protected]>
---
solr/CHANGES.txt | 3 +
.../solr/handler/admin/CollectionsHandler.java | 16 ++-
.../solr/handler/admin/api/DeleteNodeAPI.java | 130 +++++++++++++++++++++
.../solr/handler/admin/api/DeleteNodeAPITest.java | 73 ++++++++++++
.../pages/cluster-node-management.adoc | 9 +-
5 files changed, 228 insertions(+), 3 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index cbd52a1adb7..8caea375f31 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -119,6 +119,9 @@ Improvements
* SOLR-16665: The base docker image has been upgraded from Ubuntu 20 (Focal)
to Ubuntu 22 (Jammy). (Houston Putman)
+* SOLR-11029: A v2 equivalent of the `/admin/collections?action=DELETENODE`
command is now available at
+ `POST /api/cluster/nodes/nodeName/clear`. (Bence Szabo via Jason Gerlowski)
+
Optimizations
---------------------
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index a4a7679daac..75d94fe63d9 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -215,6 +215,7 @@ import
org.apache.solr.handler.admin.api.BalanceShardUniqueAPI;
import org.apache.solr.handler.admin.api.CollectionStatusAPI;
import org.apache.solr.handler.admin.api.CreateShardAPI;
import org.apache.solr.handler.admin.api.DeleteCollectionAPI;
+import org.apache.solr.handler.admin.api.DeleteNodeAPI;
import org.apache.solr.handler.admin.api.DeleteReplicaAPI;
import org.apache.solr.handler.admin.api.DeleteReplicaPropertyAPI;
import org.apache.solr.handler.admin.api.DeleteShardAPI;
@@ -1898,7 +1899,15 @@ public class CollectionsHandler extends
RequestHandlerBase implements Permission
"shard",
FOLLOW_ALIASES);
}),
- DELETENODE_OP(DELETENODE, (req, rsp, h) ->
copy(req.getParams().required(), null, "node")),
+ DELETENODE_OP(
+ DELETENODE,
+ (req, rsp, h) -> {
+ final DeleteNodeAPI deleteNodeAPI = new
DeleteNodeAPI(h.coreContainer, req, rsp);
+ final SolrJerseyResponse deleteNodeResponse =
+ DeleteNodeAPI.invokeUsingV1Inputs(deleteNodeAPI,
req.getParams());
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp,
deleteNodeResponse);
+ return null;
+ }),
MOCK_COLL_TASK_OP(
MOCK_COLL_TASK,
(req, rsp, h) -> {
@@ -2225,7 +2234,10 @@ public class CollectionsHandler extends
RequestHandlerBase implements Permission
@Override
public Collection<Class<? extends JerseyResource>> getJerseyResources() {
return List.of(
- AddReplicaPropertyAPI.class, DeleteReplicaPropertyAPI.class,
ReplaceNodeAPI.class);
+ AddReplicaPropertyAPI.class,
+ DeleteReplicaPropertyAPI.class,
+ ReplaceNodeAPI.class,
+ DeleteNodeAPI.class);
}
@Override
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNodeAPI.java
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNodeAPI.java
new file mode 100644
index 00000000000..868e2b93273
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNodeAPI.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.handler.admin.api;
+
+import static
org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
+import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION;
+import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static
org.apache.solr.handler.admin.CollectionsHandler.DEFAULT_COLLECTION_OP_TIMEOUT;
+import static
org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.params.RequiredSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.admin.CollectionsHandler;
+import org.apache.solr.jersey.JacksonReflectMapWriter;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.jersey.SolrJerseyResponse;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+
+/**
+ * V2 API for deleting all replicas of all collections in one node. Please
note that the node itself
+ * will remain as a live node after this operation.
+ *
+ * <p>This API is analogous to the V1 /admin/collections?action=DELETENODE
+ */
+@Path("cluster/nodes/{nodeName}/clear/")
+public class DeleteNodeAPI extends AdminAPIBase {
+
+ @Inject
+ public DeleteNodeAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
+ }
+
+ @POST
+ @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SolrJerseyResponse deleteNode(
+ @Parameter(
+ description =
+ "The name of the node to be cleared. Usually of the form
'host:1234_solr'.",
+ required = true)
+ @PathParam("nodeName")
+ String nodeName,
+ @RequestBody(description = "Contains user provided parameters", required
= true)
+ DeleteNodeRequestBody requestBody)
+ throws Exception {
+ final SolrJerseyResponse response =
instantiateJerseyResponse(SolrJerseyResponse.class);
+ final CoreContainer coreContainer =
fetchAndValidateZooKeeperAwareCoreContainer();
+ final ZkNodeProps remoteMessage = createRemoteMessage(nodeName,
requestBody);
+ final SolrResponse remoteResponse =
+ CollectionsHandler.submitCollectionApiCommand(
+ coreContainer,
+ coreContainer.getDistributedCollectionCommandRunner(),
+ remoteMessage,
+ CollectionParams.CollectionAction.DELETENODE,
+ DEFAULT_COLLECTION_OP_TIMEOUT);
+ if (remoteResponse.getException() != null) {
+ throw remoteResponse.getException();
+ }
+ disableResponseCaching();
+ return response;
+ }
+
+ public static SolrJerseyResponse invokeUsingV1Inputs(DeleteNodeAPI
apiInstance, SolrParams params)
+ throws Exception {
+ final RequiredSolrParams requiredParams = params.required();
+ final DeleteNodeRequestBody requestBody = new
DeleteNodeRequestBody(params.get(ASYNC));
+ return apiInstance.deleteNode(requiredParams.get(NODE), requestBody);
+ }
+
+ public static ZkNodeProps createRemoteMessage(
+ String nodeName, DeleteNodeRequestBody requestBody) {
+ Map<String, Object> remoteMessage = new HashMap<>();
+ remoteMessage.put(NODE, nodeName);
+ if (requestBody != null) {
+ if (requestBody.async != null) {
+ remoteMessage.put(ASYNC, requestBody.async);
+ }
+ }
+ remoteMessage.put(QUEUE_OPERATION,
CollectionParams.CollectionAction.DELETENODE.toLower());
+
+ return new ZkNodeProps(remoteMessage);
+ }
+
+ public static class DeleteNodeRequestBody implements JacksonReflectMapWriter
{
+
+ public DeleteNodeRequestBody() {}
+
+ public DeleteNodeRequestBody(String async) {
+ this.async = async;
+ }
+
+ @Schema(description = "Request ID to track this action which will be
processed asynchronously.")
+ @JsonProperty("async")
+ public String async;
+ }
+}
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/api/DeleteNodeAPITest.java
b/solr/core/src/test/org/apache/solr/handler/admin/api/DeleteNodeAPITest.java
new file mode 100644
index 00000000000..f306881bf41
--- /dev/null
+++
b/solr/core/src/test/org/apache/solr/handler/admin/api/DeleteNodeAPITest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.handler.admin.api;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.Map;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/** Unit tests for {@link DeleteNodeAPI} */
+public class DeleteNodeAPITest extends SolrTestCaseJ4 {
+
+ @BeforeClass
+ public static void ensureWorkingMockito() {
+ assumeWorkingMockito();
+ }
+
+ @Test
+ public void testV1InvocationThrowsErrorsIfRequiredParametersMissing() {
+ final var api = mock(DeleteNodeAPI.class);
+ final SolrException e =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ DeleteNodeAPI.invokeUsingV1Inputs(api, new
ModifiableSolrParams());
+ });
+ assertEquals("Missing required parameter: node", e.getMessage());
+ }
+
+ @Test
+ public void testValidOverseerMessageIsCreated() {
+ DeleteNodeAPI.DeleteNodeRequestBody requestBody =
+ new DeleteNodeAPI.DeleteNodeRequestBody("async");
+ final ZkNodeProps createdMessage =
+ DeleteNodeAPI.createRemoteMessage("nodeNameToDelete", requestBody);
+ final Map<String, Object> createdMessageProps =
createdMessage.getProperties();
+ assertEquals(3, createdMessageProps.size());
+ assertEquals("nodeNameToDelete", createdMessageProps.get("node"));
+ assertEquals("async", createdMessageProps.get("async"));
+ assertEquals("deletenode", createdMessageProps.get("operation"));
+ }
+
+ @Test
+ public void testRequestBodyCanBeOmitted() throws Exception {
+ final ZkNodeProps createdMessage =
DeleteNodeAPI.createRemoteMessage("nodeNameToDelete", null);
+ final Map<String, Object> createdMessageProps =
createdMessage.getProperties();
+ assertEquals(2, createdMessageProps.size());
+ assertEquals("nodeNameToDelete", createdMessageProps.get("node"));
+ assertEquals("deletenode", createdMessageProps.get("operation"));
+ assertFalse(
+ "Expected message to not contain value for async: " +
createdMessageProps.get("async"),
+ createdMessageProps.containsKey("async"));
+ }
+}
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc
index c0abf5cffb7..6fb17351171 100644
---
a/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc
+++
b/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc
@@ -590,7 +590,14 @@
http://localhost:8983/solr/admin/collections?action=DELETENODE&node=nodeName
====
[.tab-label]*V2 API*
-We do not currently have a V2 equivalent.
+[source,bash]
+----
+curl -X POST
"http://localhost:8983/api/cluster/nodes/localhost:7574_solr/clear/" -H
'Content-Type: application/json' -d '
+ {
+ "async": "someAsyncId"
+ }
+'
+----
====
--