From d85a4e0edd972e536b482ccc8d7bbccbb25c5dab Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Tue, 6 Oct 2026 20:45:01 +0300 Subject: [PATCH 1/5] SOLR-18514: Port HealthcheckTool to picocli Adds the picocli code path next to the commons-cli one, with a test twin, the generated reference page and test_healthcheck.bats in the picocli suite. --- .../SOLR-18514-picocli-healthcheck.yml | 9 ++ .../org/apache/solr/cli/HealthcheckTool.java | 93 ++++++++++++++++++- .../src/java/org/apache/solr/cli/SolrCLI.java | 3 +- .../solr/cli/HealthcheckToolPicocliTest.java | 39 ++++++++ .../apache/solr/cli/HealthcheckToolTest.java | 15 ++- solr/packaging/build.gradle | 1 + .../deployment-guide/deployment-nav.adoc | 1 + .../deployment-guide/pages/cli/index.adoc | 1 + .../pages/cli/solr-healthcheck.adoc | 81 ++++++++++++++++ 9 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/SOLR-18514-picocli-healthcheck.yml create mode 100644 solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc diff --git a/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml b/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml new file mode 100644 index 000000000000..a017db3b1e19 --- /dev/null +++ b/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml @@ -0,0 +1,9 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc + +title: The `healthcheck` command is now available in the experimental picocli command line interface. +type: added +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-18514 + url: https://issues.apache.org/jira/browse/SOLR-18514 diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 7311877cc78c..3d0fffa7d4ac 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -20,16 +20,19 @@ import static org.apache.solr.common.params.CommonParams.DISTRIB; import static org.apache.solr.common.params.CommonParams.NAME; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.jetty.HttpJettySolrClient; import org.apache.solr.client.solrj.request.SolrQuery; @@ -40,15 +43,27 @@ import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Slice; +import org.apache.solr.common.util.EnvUtils; import org.noggit.CharArr; import org.noggit.JSONWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Supports healthcheck command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "healthcheck", + description = "Requests health information about a specific collection in SolrCloud.", + footerHeading = "%nExamples:%n", + footer = {" # Check the health of a collection", " bin/solr healthcheck -c gettingstarted"}) public class HealthcheckTool extends ToolBase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option COLLECTION_NAME_OPTION = Option.builder("c") .longOpt("name") @@ -76,6 +91,24 @@ enum ShardState { /** Parameters for the healthcheck command, independent of the command line parser. */ record HealthcheckParams(String collection, String credentials) {} + // --- picocli fields --- + + @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1") + private ConnectionOptions connectionOptions; + + @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; + + @picocli.CommandLine.Option( + names = {"-c", "--name"}, + required = true, + paramLabel = "COLLECTION", + description = "Name of the collection to check.") + private String nameOpt; + + public HealthcheckTool() { + this(new DefaultToolRuntime()); + } + /** Requests health information about a specific collection in SolrCloud. */ public HealthcheckTool(ToolRuntime runtime) { super(runtime); @@ -213,7 +246,65 @@ protected void runCloudTool(CloudSolrClient cloudSolrClient, HealthcheckParams p @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + var solrConnection = resolveSolrConnection(credentialsOptions.credentials); + if (solrConnection == null) { + CLIO.err("Healthcheck tool only works in Solr Cloud mode."); + runtime.exit(1); + } + HealthcheckParams params = new HealthcheckParams(nameOpt, credentialsOptions.credentials); + var builder = + new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials(params.credentials()); + try (var cloudSolrClient = CLIUtils.getCloudSolrClient(solrConnection, builder)) { + echoIfVerbose("Connecting to Solr at " + solrConnection.toString()); + runCloudTool(cloudSolrClient, params); + } + return 0; + } + + /** + * Mirrors {@link CLIUtils#getSolrConnection(CommandLine)}: an explicit {@code --solr-connection} + * or {@code --zk-host} (or the matching property) wins, otherwise a running Solr is asked for its + * ZooKeeper, and null means it is not in SolrCloud mode. + */ + private CloudSolrClient.CloudSolrClientConnection resolveSolrConnection(String credentials) + throws Exception { + String solrConnection = + (connectionOptions != null && connectionOptions.solrConnection != null) + ? connectionOptions.solrConnection + : EnvUtils.getProperty("solr-connection"); + if (solrConnection != null && !solrConnection.isBlank()) { + return CloudSolrClient.CloudSolrClientConnection.parse(solrConnection); + } + String zkHost = + (connectionOptions != null && connectionOptions.zkHost != null) + ? connectionOptions.zkHost + : EnvUtils.getProperty("zkHost"); + if (zkHost != null && !zkHost.isBlank()) { + var zkConnection = CloudSolrClient.CloudSolrClientConnection.parse(zkHost); + if (!zkConnection.isZookeeper()) { + throw new IOException( + String.format( + Locale.ROOT, "Expected ZooKeeper connection string, but got: '%s'.", zkHost)); + } + return zkConnection; + } + String solrUrl = + (connectionOptions != null && connectionOptions.solrUrl != null) + ? CLIUtils.normalizeSolrUrl(connectionOptions.solrUrl) + : CLIUtils.getDefaultSolrUrl(); + try (SolrClient solrClient = CLIUtils.getSolrClient(solrUrl, credentials)) { + Map status = StatusTool.reportStatus(solrClient); + @SuppressWarnings("unchecked") + Map cloud = (Map) status.get("cloud"); + if (cloud == null) { + return null; + } + String zookeeper = (String) cloud.get("ZooKeeper"); + if (zookeeper.endsWith("(embedded)")) { + zookeeper = zookeeper.substring(0, zookeeper.length() - "(embedded)".length()); + } + return CloudSolrClient.CloudSolrClientConnection.parse(zookeeper); + } } } diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java index a80d7f7883e8..57c7e1fa68ce 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -80,7 +80,8 @@ ZkTool.class, AuthTool.class, CreateTool.class, - DeleteTool.class + DeleteTool.class, + HealthcheckTool.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { diff --git a/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java new file mode 100644 index 000000000000..d519ba8b7c03 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java @@ -0,0 +1,39 @@ +/* + * 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.cli; + +import java.util.Arrays; +import picocli.CommandLine; + +/** + * Runs all {@link HealthcheckToolTest} tests through the picocli invocation path. + * + *

All {@code @Test} methods are inherited; only the invocation strategy is overridden. + */ +public class HealthcheckToolPicocliTest extends HealthcheckToolTest { + + @Override + protected int runTool(String[] args, Class clazz) throws Exception { + // args[0] is the tool name used by commons-cli dispatch; strip it for picocli. + String[] toolArgs = Arrays.copyOfRange(args, 1, args.length); + ToolRuntime runtime = new CLITestHelper.TestingRuntime(false); + ToolBase tool = clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime); + return new CommandLine(tool) + .setDefaultValueProvider(new CliDefaultValueProvider()) + .execute(toolArgs); + } +} diff --git a/solr/core/src/test/org/apache/solr/cli/HealthcheckToolTest.java b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolTest.java index 68d7fba3c5d5..0cd3d8b583ca 100644 --- a/solr/core/src/test/org/apache/solr/cli/HealthcheckToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolTest.java @@ -27,6 +27,11 @@ public class HealthcheckToolTest extends SolrCloudTestCase { + /** Runs the tool. Overridden by the picocli variant of this test. */ + protected int runTool(String[] args, Class clazz) throws Exception { + return CLITestHelper.runTool(args, clazz); + } + @BeforeClass public static void setupCluster() throws Exception { configureCluster(1) @@ -44,7 +49,7 @@ public void testHealthcheckWithZkHostParameter() throws Exception { String[] args = new String[] {"healthcheck", "-c", "bob", "-z", cluster.getZkClient().getZkServerAddress()}; - assertEquals(0, CLITestHelper.runTool(args, HealthcheckTool.class)); + assertEquals(0, runTool(args, HealthcheckTool.class)); } @Test @@ -56,7 +61,7 @@ public void testHealthcheckWithSolrUrlParameter() throws Exception { ZkStateReader.from(cluster.getSolrClient()).getBaseUrlForNodeName(firstLiveNode); String[] args = new String[] {"healthcheck", "-c", "bob", "--solr-url", solrUrl}; - assertEquals(0, CLITestHelper.runTool(args, HealthcheckTool.class)); + assertEquals(0, runTool(args, HealthcheckTool.class)); } @Test @@ -70,7 +75,7 @@ public void testHealthcheckWithSolrRootUrlParameter() throws Exception { solrUrl = solrUrl.substring(0, solrUrl.indexOf("/solr")); String[] args = new String[] {"healthcheck", "-c", "bob", "--solr-url", solrUrl}; - assertEquals(0, CLITestHelper.runTool(args, HealthcheckTool.class)); + assertEquals(0, runTool(args, HealthcheckTool.class)); } @Test @@ -81,7 +86,7 @@ public void testHealthcheckWithSolrConnectionParameter() throws Exception { for (String connectionString : connStrings) { String[] args = new String[] {"healthcheck", "-c", "bob", "--solr-connection", connectionString}; - assertEquals(0, CLITestHelper.runTool(args, HealthcheckTool.class)); + assertEquals(0, runTool(args, HealthcheckTool.class)); } } @@ -89,6 +94,6 @@ public void testHealthcheckWithSolrConnectionParameter() throws Exception { public void testHealthcheckWithZookeeperParameter() throws Exception { String zkHost = cluster.getZkServer().getZkAddress(); String[] args = new String[] {"healthcheck", "-c", "bob", "--zk-host", zkHost}; - assertEquals(0, CLITestHelper.runTool(args, HealthcheckTool.class)); + assertEquals(0, runTool(args, HealthcheckTool.class)); } } diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle index 9672174b2a43..08ee17af507c 100644 --- a/solr/packaging/build.gradle +++ b/solr/packaging/build.gradle @@ -363,6 +363,7 @@ task integrationTestsPicocli(type: BatsTask) { 'test/test_create.bats', 'test/test_delete_collection.bats', 'test/test_zk.bats', + 'test/test_healthcheck.bats', ] } diff --git a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc index 309b2bd959f6..0f7e67731206 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc @@ -23,6 +23,7 @@ *** xref:cli/solr-auth.adoc[auth] *** xref:cli/solr-create.adoc[create] *** xref:cli/solr-delete.adoc[delete] +*** xref:cli/solr-healthcheck.adoc[healthcheck] *** xref:cli/solr-start.adoc[start] *** xref:cli/solr-status.adoc[status] *** xref:cli/solr-stop.adoc[stop] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc index f00109598068..c671e1715c45 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc @@ -62,6 +62,7 @@ Any command not listed here is unavailable when the picocli implementation is en |xref:cli/solr-auth.adoc[bin/solr auth] |xref:cli/solr-create.adoc[bin/solr create] |xref:cli/solr-delete.adoc[bin/solr delete] +|xref:cli/solr-healthcheck.adoc[bin/solr healthcheck] |xref:cli/solr-start.adoc[bin/solr start] |xref:cli/solr-status.adoc[bin/solr status] |xref:cli/solr-stop.adoc[bin/solr stop] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc new file mode 100644 index 000000000000..bae65dc3fdae --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc @@ -0,0 +1,81 @@ +// 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. +// +// DO NOT EDIT -- this page is auto-generated from picocli annotations. +// To update: modify the @Command/@Option annotations in the Java source, then run: +// ./gradlew :solr:solr-ref-guide:generateCliDocs + += bin/solr healthcheck +:page-toclevels: 2 + +[WARNING] +==== +This page documents the *experimental* picocli implementation of `bin/solr`, which is not enabled by default. +For what `bin/solr` does by default, see xref:solr-control-script-reference.adoc[]. +See xref:cli/index.adoc[] to enable this implementation and for its known limitations. +==== + +// tag::picocli-generated-man-section-synopsis[] +== Synopsis + +.... +bin/solr healthcheck [-hv] -c=COLLECTION [-u=] [-s= | + --solr-url= | -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Requests health information about a specific collection in SolrCloud. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-c*, *--name*=_COLLECTION_:: + *(required)* Name of the collection to check. + +*-h*, *--help*:: + Print this help message and exit. + +*-s*, *--solr-connection*=__:: + Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is defined in solr.in.sh; otherwise, defaults to localhost:9983. + +*--solr-url*=__:: + Base Solr URL, which can be used to determine the zk-host if that's not known. + +*-u*, *--credentials*=__:: + Credentials in the format username:password. Example: --credentials solr:SolrRocks + +*-v*, *--verbose*:: + Enable verbose mode. + +*-z*, *--zk-host*=__:: + Zookeeper connection string; unnecessary if ZK_HOST is defined in solr.in.sh; otherwise, defaults to localhost:9983. + +// end::picocli-generated-man-section-options[] + +// tag::picocli-generated-man-section-footer[] +== Examples +[%hardbreaks] + # Check the health of a collection + bin/solr healthcheck -c gettingstarted + +// end::picocli-generated-man-section-footer[] From 729a008d2528549dbef28dbfbb3cd04fad8edd19 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 11:24:06 +0300 Subject: [PATCH 2/5] SOLR-18514: Describe healthcheck as the reference guide does The generated page now says what the command checks (document counts, leaders, active replicas) and that it needs SolrCloud, matching the healthcheck page of the reference guide instead of a generic sentence. --- solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java | 5 ++++- .../modules/deployment-guide/pages/cli/solr-healthcheck.adoc | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 3d0fffa7d4ac..75174cc3b6a3 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -53,7 +53,10 @@ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "healthcheck", - description = "Requests health information about a specific collection in SolrCloud.", + description = + "Verifies that a collection is functioning: queries every replica directly, compares" + + " document counts and checks that each shard has a leader and every replica is" + + " ACTIVE. Requires SolrCloud.", footerHeading = "%nExamples:%n", footer = {" # Check the health of a collection", " bin/solr healthcheck -c gettingstarted"}) public class HealthcheckTool extends ToolBase { diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc index bae65dc3fdae..15365fde2fce 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-healthcheck.adoc @@ -42,7 +42,7 @@ bin/solr healthcheck [-hv] -c=COLLECTION [-u=] [-s= // tag::picocli-generated-man-section-description[] == Description -Requests health information about a specific collection in SolrCloud. +Verifies that a collection is functioning: queries every replica directly, compares document counts and checks that each shard has a leader and every replica is ACTIVE. Requires SolrCloud. // end::picocli-generated-man-section-description[] From cce2c846dd137fa62ec83e324b909d0f8db99254 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 15:43:17 +0300 Subject: [PATCH 3/5] SOLR-18514: Add the change to the shared picocli changelog entry No picocli feature has been released yet, so a new command is a detail of the SOLR-17697 entry. Its author and JIRA are added there instead of a separate entry. --- .../unreleased/SOLR-17697-picocli-experimental-cli.yml | 3 +++ changelog/unreleased/SOLR-18514-picocli-healthcheck.yml | 9 --------- 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 changelog/unreleased/SOLR-18514-picocli-healthcheck.yml diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml index 85cdf1fd90d4..2f0b478ba1d8 100644 --- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml +++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml @@ -7,6 +7,9 @@ type: added authors: - name: Jan Høydahl url: https://home.apache.org/phonebook.html?uid=janhoy + - name: Serhiy Bzhezytskyy links: - name: SOLR-17697 url: https://issues.apache.org/jira/browse/SOLR-17697 + - name: SOLR-18514 + url: https://issues.apache.org/jira/browse/SOLR-18514 diff --git a/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml b/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml deleted file mode 100644 index a017db3b1e19..000000000000 --- a/changelog/unreleased/SOLR-18514-picocli-healthcheck.yml +++ /dev/null @@ -1,9 +0,0 @@ -# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc - -title: The `healthcheck` command is now available in the experimental picocli command line interface. -type: added -authors: - - name: Serhiy Bzhezytskyy -links: - - name: SOLR-18514 - url: https://issues.apache.org/jira/browse/SOLR-18514 From dcb08ea6a7c380ab010e848088fb0eec9e695041 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 15:49:24 +0300 Subject: [PATCH 4/5] SOLR-18514: Return 1 from the picocli path instead of exiting After the Cloud-mode error the picocli path called runtime.exit(1) and relied on code after it never running; returning 1 does the same and keeps the method straight-line. The commons-cli path is unchanged. --- solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 75174cc3b6a3..c25ef35c9c11 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -252,7 +252,7 @@ public int callTool() throws Exception { var solrConnection = resolveSolrConnection(credentialsOptions.credentials); if (solrConnection == null) { CLIO.err("Healthcheck tool only works in Solr Cloud mode."); - runtime.exit(1); + return 1; } HealthcheckParams params = new HealthcheckParams(nameOpt, credentialsOptions.credentials); var builder = From f228552379dde840fe030197bba0069963396e41 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 17:56:19 +0300 Subject: [PATCH 5/5] SOLR-18514: Read SOLR_CONNECTION as the solr.connection property EnvUtils maps the SOLR_CONNECTION environment variable to solr.connection, so the solr-connection key the healthcheck connection lookup read could never be set. The picocli path now reads the right key, which a test covers. --- .../src/java/org/apache/solr/cli/HealthcheckTool.java | 2 +- .../org/apache/solr/cli/HealthcheckToolPicocliTest.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index c25ef35c9c11..005be05cec77 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -274,7 +274,7 @@ private CloudSolrClient.CloudSolrClientConnection resolveSolrConnection(String c String solrConnection = (connectionOptions != null && connectionOptions.solrConnection != null) ? connectionOptions.solrConnection - : EnvUtils.getProperty("solr-connection"); + : EnvUtils.getProperty("solr.connection"); if (solrConnection != null && !solrConnection.isBlank()) { return CloudSolrClient.CloudSolrClientConnection.parse(solrConnection); } diff --git a/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java index d519ba8b7c03..bcef8b7e705b 100644 --- a/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java +++ b/solr/core/src/test/org/apache/solr/cli/HealthcheckToolPicocliTest.java @@ -17,6 +17,7 @@ package org.apache.solr.cli; import java.util.Arrays; +import org.junit.Test; import picocli.CommandLine; /** @@ -36,4 +37,12 @@ protected int runTool(String[] args, Class clazz) throws Exc .setDefaultValueProvider(new CliDefaultValueProvider()) .execute(toolArgs); } + + @Test + public void testHealthcheckWithSolrConnectionProperty() throws Exception { + // SOLR_CONNECTION reaches the JVM as the solr.connection property + System.setProperty("solr.connection", getHttpSolrConnection().toString()); + String[] args = new String[] {"healthcheck", "-c", "bob"}; + assertEquals(0, runTool(args, HealthcheckTool.class)); + } }