Skip to content

Migrate org.apache.solr.cli tools from V1 to V2 APIs#4154

Open
epugh wants to merge 9 commits intoapache:mainfrom
epugh:migrate-cli-to-v2-apache
Open

Migrate org.apache.solr.cli tools from V1 to V2 APIs#4154
epugh wants to merge 9 commits intoapache:mainfrom
epugh:migrate-cli-to-v2-apache

Conversation

@epugh
Copy link
Contributor

@epugh epugh commented Feb 22, 2026

Replaces legacy V1 admin API calls (CollectionAdminRequest, CoreAdminRequest) in the bin/solr CLI tools with generated V2 OpenAPI SolrJ classes. Also adds a new proper SolrJ V2 endpoint for GET /cluster/nodes that was previously missing, eliminating the need for a GenericV2SolrRequest workaround.

I used Copilot for this, and here is the agent chat: https://github.com/epugh/solr/tasks/3c2f15c6-c0c7-4e39-b6b6-1cd51f8e7aa4

Migrated calls

Class Method Before → After
CLIUtils safeCheckCollectionExists CollectionAdminRequest.ListCollectionsApi.ListCollections
CreateTool createCore CoreAdminRequest.createCoreCoresApi.CreateCore
CreateTool createCollection CollectionAdminRequest.createCollectionCollectionsApi.CreateCollection
DeleteTool deleteCollection CollectionAdminRequest.deleteCollectionCollectionsApi.DeleteCollection
DeleteTool deleteCore CoreAdminRequest.UnloadCoresApi.UnloadCore
StatusTool getCloudStatus CollectionAdminRequest.ClusterStatusClusterApi.ListClusterNodes + CollectionsApi.ListCollections

Verbose output restored via Jackson

The old V1 code serialized NamedList responses with noggit's JSONWriter. V2 response POJOs are Jackson-annotated, so verbose output now uses JacksonContentWriter.DEFAULT_MAPPER.writerWithDefaultPrettyPrinter() — actually cleaner output since the responses are already proper JSON objects:

// CreateTool, DeleteTool — after req.process():
if (isVerbose() && response != null) {
  echo(JacksonContentWriter.DEFAULT_MAPPER
      .writerWithDefaultPrettyPrinter()
      .writeValueAsString(response));
}

New GET /cluster/nodes V2 API (SolrJ support)

StatusTool previously fell back to GenericV2SolrRequest because SolrJ had no typed class for this endpoint. The full API pipeline was wired up:

  • ListClusterNodesResponse — new model with Set<String> nodes
  • ListClusterNodesApi — new endpoint interface at @Path("/cluster/nodes")
  • ListClusterNodes — new Jersey implementation registered in CollectionsHandler
  • ClusterApi.ListClusterNodes — generated SolrJ request class (replaces GenericV2SolrRequest)
// Before
NamedList<Object> resp = solrClient.request(
    new GenericV2SolrRequest(SolrRequest.METHOD.GET, "/cluster/nodes", SolrRequestType.ADMIN));
var liveNodes = (Collection<?>) resp.get("nodes");

// After
var resp = new ClusterApi.ListClusterNodes().process(solrClient);
var liveNodes = resp.nodes; // Set<String>

Not migrated

Snapshot tools (SnapshotCreateTool, SnapshotDeleteTool, etc.) are intentionally excluded as candidates for removal.

In fact, we may just remove them...?

Tests

Existing tests + bats tests all pass.

Copilot AI and others added 5 commits February 21, 2026 19:10
Co-authored-by: epugh <22395+epugh@users.noreply.github.com>
…r/nodes + /collections APIs

Co-authored-by: epugh <22395+epugh@users.noreply.github.com>
… POJOs

Co-authored-by: epugh <22395+epugh@users.noreply.github.com>
Co-authored-by: epugh <22395+epugh@users.noreply.github.com>
Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good; just some minor feedback

@gerlowskija
Copy link
Contributor

Right now, Solr allows users to disable the v2 APIs with a boolean sysprop: disable.v2.api

I'm 100% in favor of using the v2 APIs in more places - we desperately need that dog-fooding on them and this is a great way to do that. But if we're going to do this then we need to rip out that sysprop.

Ideally that'd happen before this PR gets merged, just to make sure we don't forget about it and accidentally release something where the bin/solr tools could be effectively bricked on any Solr deploys that use the sysprop. But I'd also be OK with filing a "Blocker" JIRA ticket, or a promise to tackle it soon, or something similar.

@epugh
Copy link
Contributor Author

epugh commented Feb 26, 2026

Right now, Solr allows users to disable the v2 APIs with a boolean sysprop: disable.v2.api

I'm 100% in favor of using the v2 APIs in more places - we desperately need that dog-fooding on them and this is a great way to do that. But if we're going to do this then we need to rip out that sysprop.

Ideally that'd happen before this PR gets merged, just to make sure we don't forget about it and accidentally release something where the bin/solr tools could be effectively bricked on any Solr deploys that use the sysprop. But I'd also be OK with filing a "Blocker" JIRA ticket, or a promise to tackle it soon, or something similar.

Good find! Yeah, I had forgotten about that setting. We could just commit these to main but I do worry about how much pain we set our selves up for in branch_10x.

For various v2 api's that we want to have flexiblity on, can we keep them as "experimental" for a little longer, so we don't have to worry about back compat, with the idea that internal to solr consumers, like the CLI (or maybe internal APIS) wouldn't care that we are changing them over time...

@gerlowskija
Copy link
Contributor

I used Copilot for this, and here is the agent chat: ...

FYI that these links are "private" AFAIK - I get a 404 when trying to click through.

can we keep them as "experimental" for a little longer

I started out writing a reply to this that said essentially: "Sure, why not. Solr offers plenty of feature flags, and there's no clear correlation between those and 'experimental'-ness. Lots of non-experimental features have flags. And there are plenty of other 'experimental' features that don't have a feature flag. No reason to tie these things together."

But in writing that and reading it over I find I've unconvinced myself 😦

The problem here is security. I don't know of any security issues specific to the v2 API, but it's a whole new attack surface. While we're signaling that it's not ready for prime time then I think a subset of users are going to want to be able to disable it. Telling them: "here's this thing that's not quite ready yet, pray it doesn't expose you in some way" is not a great message.

This feels like a much larger discussion and I don't want it to hold up this PR. Would you be OK making this "main"-only for now, and I'll file a JIRA ticket or dev@ thread to figure out a path forward here? The dust may settle on that discussion in a few weeks and we decide we're OK with this hitting branch_10x, but keeping it "main" only for the moment helps you stay unblocked while we figure out the larger question...

@epugh
Copy link
Contributor Author

epugh commented Feb 26, 2026

I used Copilot for this, and here is the agent chat: ...

FYI that these links are "private" AFAIK - I get a 404 when trying to click through.

can we keep them as "experimental" for a little longer

I started out writing a reply to this that said essentially: "Sure, why not. Solr offers plenty of feature flags, and there's no clear correlation between those and 'experimental'-ness. Lots of non-experimental features have flags. And there are plenty of other 'experimental' features that don't have a feature flag. No reason to tie these things together."

But in writing that and reading it over I find I've unconvinced myself 😦

The problem here is security. I don't know of any security issues specific to the v2 API, but it's a whole new attack surface. While we're signaling that it's not ready for prime time then I think a subset of users are going to want to be able to disable it. Telling them: "here's this thing that's not quite ready yet, pray it doesn't expose you in some way" is not a great message.

This feels like a much larger discussion and I don't want it to hold up this PR. Would you be OK making this "main"-only for now, and I'll file a JIRA ticket or dev@ thread to figure out a path forward here? The dust may settle on that discussion in a few weeks and we decide we're OK with this hitting branch_10x, but keeping it "main" only for the moment helps you stay unblocked while we figure out the larger question...

I love a suggestion that keeps me unblocked ;-). I think main works, and then we can back port later as we decide we want this.

Interesting, I think of these as experimental not because of security but because we aren't sure we have the right flavour of API. Somethign I've noticed lately is that when I build APIs, that then, later, when I use them "for real" I find a lot of issues with them not being "quite right" yet. Not because of security or performacnce, but maybe because of too much paylard returned or not enough filters or what not. So yeah, please do move forward, and I'll merge this to main only.

Copy link
Contributor

@gerlowskija gerlowskija left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one bug in your code I think; see comments inline.

Also, this PR adds a whole new API (ListClusterNodes), so it probably deserves a changelog entry and some tests that use the generated v2 request.

@SuppressWarnings("unchecked")
List<String> collections = (List<String>) existsCheckResult.get("collections");
exists = collections != null && collections.contains(collection);
var response = new CollectionsApi.ListCollections().process(solrClient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] I may be out of date, but as of one point at least these generated v2 request/response objects didn't throw exception on errors the way everything else in SolrJ does.

Assuming this is still true, users are responsible for inspecting the .responseHeader.status field to make sure that the operation succeeded. Hopefully I'm wrong, but if not you'll need to add that checking here and elsewhere.


// TODO get this as a metric from the metrics API instead, or something else.
var collections = (Map<String, Object>) json._get(List.of("cluster", "collections"), null);
var collectionsResponse = new CollectionsApi.ListCollections().process(solrClient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[-1] The original code here was grabbing the .cluster.collections bit of the CLUSTERSTATUS response, which includes tons of info:

  "cluster":{
    "live_nodes":["localhost:8983_solr"],
    "collections":{
      "foo":{
        "pullReplicas":"0",
        "configName":"foo",
        "nrtReplicas":1,
        "replicationFactor":1,
        "tlogReplicas":"0",
        "router":{
          "name":"compositeId"
        },
        ....

Are you dropping all that info on purpose in your replacement, or was that a mistake?

/**
* V2 API for listing the live nodes in the SolrCloud cluster.
*
* <p>This API (GET /v2/cluster/nodes) is equivalent to the v1 CLUSTERSTATUS action filtered to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] IMO the API comparison is a bit clearer if you put the verbatim v1 request here that you're comparing the v2 API to, rather than trying to describe it.

i.e. /admin/collections?action=CLUSTERSTATUS&liveNodes=true&includeAll=false

@epugh
Copy link
Contributor Author

epugh commented Feb 27, 2026

Found one bug in your code I think; see comments inline.

Also, this PR adds a whole new API (ListClusterNodes), so it probably deserves a changelog entry and some tests that use the generated v2 request.

I am a bit fuzzy on did I ADD a whole new API? Or did I just complete what already existed? Checking
image

Also, I think it can't be retunring just live nodes if its supposed to return all nodes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants