Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog/unreleased/SOLR-16390-list-cluster-nodes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: Migrated ListClusterNodes API from homegrown @EndPoint to JAX-RS
type: other
authors:
- name: Prithvi S
nick: iprithv
links:
- name: SOLR-16390
url: https://issues.apache.org/jira/browse/SOLR-16390
1 change: 1 addition & 0 deletions dev-docs/v2-api-conventions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Following these guidelines has given us the following (non-exhaustive) list of v
* `/api/backups/specificBackupName`
* `/api/backups/specificBackupName/versions`
* `/api/backups/specificBackupName/versions/specificVersion`
* `/api/cluster/nodes`
* `/api/cluster/nodes/specificNodeName/roles`
* `/api/cluster/nodes/specificNodeName/roles/specificRoleName`
* `/api/cluster/properties`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;

/**
* Response for the v2 "list cluster nodes" API. This is a bit unusual that it's wrapping a non
* JAX-RS V2 API defined in org.apache.solr.handler.ClusterAPI.getNodes(). The calls are made using
* just the defaults. TODO: Update this when we migrate ClusterAPI to JAX-RS.
*/
/** Response body for {@code GET /api/cluster/nodes}. */
public class ListClusterNodesResponse extends SolrJerseyResponse {

@Schema(description = "The live nodes in the cluster.")
Expand Down
5 changes: 0 additions & 5 deletions solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,6 @@ public void getCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws
collectionsHandler.handleRequestBody(wrapParams(req, v1Params), rsp);
}

@EndPoint(method = GET, path = "/cluster/nodes", permission = COLL_READ_PERM)
public void getNodes(SolrQueryRequest req, SolrQueryResponse rsp) {
rsp.add("nodes", getCoreContainer().getZkController().getClusterState().getLiveNodes());
}

@EndPoint(method = GET, path = "/cluster", permission = COLL_READ_PERM)
public void getClusterStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
final Map<String, Object> v1Params =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
import org.apache.solr.handler.admin.api.ForceLeader;
import org.apache.solr.handler.admin.api.InstallShardData;
import org.apache.solr.handler.admin.api.ListAliases;
import org.apache.solr.handler.admin.api.ListClusterNodes;
import org.apache.solr.handler.admin.api.ListCollectionBackups;
import org.apache.solr.handler.admin.api.ListCollectionSnapshots;
import org.apache.solr.handler.admin.api.ListCollections;
Expand Down Expand Up @@ -1363,6 +1364,7 @@ public Collection<Class<? extends JerseyResource>> getJerseyResources() {
DeleteShard.class,
ForceLeader.class,
InstallShardData.class,
ListClusterNodes.class,
ListCollections.class,
ListCollectionBackups.class,
ReloadCollectionAPI.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.security.PermissionNameProvider.Name.COLL_READ_PERM;

import jakarta.inject.Inject;
import java.util.Set;
import org.apache.solr.client.api.endpoint.ListClusterNodesApi;
import org.apache.solr.client.api.model.ListClusterNodesResponse;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.jersey.PermissionName;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;

/**
* V2 API for listing live nodes in the SolrCloud cluster.
*
* <p>This API (GET /api/cluster/nodes) has no dedicated v1 equivalent; {@code
* /admin/collections?action=CLUSTERSTATUS} with {@code liveNodes=true} is the closest v1 form.
*/
public class ListClusterNodes extends AdminAPIBase implements ListClusterNodesApi {

@Inject
public ListClusterNodes(
CoreContainer coreContainer, SolrQueryRequest req, SolrQueryResponse rsp) {
super(coreContainer, req, rsp);
}

@Override
@PermissionName(COLL_READ_PERM)
public ListClusterNodesResponse listClusterNodes() {
final ListClusterNodesResponse response =
instantiateJerseyResponse(ListClusterNodesResponse.class);
validateZooKeeperAwareCoreContainer(coreContainer);
response.nodes = Set.copyOf(coreContainer.getZkController().getClusterState().getLiveNodes());
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 org.apache.solr.SolrTestCase;
import org.apache.solr.client.solrj.RemoteSolrException;
import org.apache.solr.client.solrj.request.ClusterApi;
import org.apache.solr.util.SolrJettyTestRule;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

/** Standalone coverage for {@code GET /api/cluster/nodes}. */
public class ListClusterNodesStandaloneTest extends SolrTestCase {

@ClassRule public static final SolrJettyTestRule solrTestRule = new SolrJettyTestRule();

@BeforeClass
public static void setupSolr() throws Exception {
solrTestRule.startSolr(createTempDir());
}

@Test
public void testRequiresSolrCloud() {
final RemoteSolrException ex =
expectThrows(
RemoteSolrException.class,
() -> new ClusterApi.ListClusterNodes().process(solrTestRule.getAdminClient()));
assertEquals(400, ex.code());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 java.util.Set;
import java.util.stream.Collectors;
import org.apache.solr.client.api.model.ListClusterNodesResponse;
import org.apache.solr.client.solrj.request.ClusterApi;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.embedded.JettySolrRunner;
import org.junit.BeforeClass;
import org.junit.Test;

/** HTTP tests for {@code GET /api/cluster/nodes} via the generated SolrJ ClusterApi client. */
public class ListClusterNodesTest extends SolrCloudTestCase {

@BeforeClass
public static void setupCluster() throws Exception {
configureCluster(2).addConfig("conf", configset("cloud-minimal")).configure();
}

@Test
public void testListLiveNodes() throws Exception {
ListClusterNodesResponse rsp =
new ClusterApi.ListClusterNodes().process(cluster.getSolrClient());

assertNotNull(rsp);
assertNull(rsp.error);
assertNotNull(rsp.nodes);

Set<String> expected =
cluster.getJettySolrRunners().stream()
.map(JettySolrRunner::getNodeName)
.collect(Collectors.toSet());
assertEquals(expected, rsp.nodes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Following are some v2 API URL paths and path prefixes, along with some of the op
|`/api/cores` |Create a core.
|`/api/cores/_core-name_` |Reload, rename, delete, and unload a core.
|`/api/node` |Perform overseer operation, rejoin leader election.
|`/api/cluster` |Add role, remove role, set cluster property.
|`/api/cluster` |List live nodes, add role, remove role, set cluster property.
|===

== Introspect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,41 @@ http://localhost:8983/solr/admin/collections?action=CLUSTERSTATUS
}
----

[[list-cluster-nodes]]
== List Cluster Nodes

Fetch the live node names in the SolrCloud cluster.

This is a v2-only API. The closest v1 equivalent is xref:#clusterstatus[CLUSTERSTATUS] with `liveNodes=true`.

[tabs#list-cluster-nodes-request]
======
V2 API::
+
====
[source,bash]
----
curl -X GET http://localhost:8983/api/cluster/nodes
----
====
======

=== List Cluster Nodes Response

The response includes a `nodes` array of live node names. SolrJ provides `ClusterApi.ListClusterNodes` for this request.

[source,json]
----
{
"responseHeader":{
"status":0,
"QTime":3},
"nodes":[
"127.0.1.1:8983_solr",
"127.0.1.1:7574_solr"]
}
----

[[clusterprop]]
== CLUSTERPROP: Cluster Properties

Expand Down
Loading