diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml index 85cdf1fd90d4..bf2d57cb1f34 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-18510 + url: https://issues.apache.org/jira/browse/SOLR-18510 diff --git a/solr/core/src/java/org/apache/solr/cli/AssertTool.java b/solr/core/src/java/org/apache/solr/cli/AssertTool.java index 5ba193f9494d..c3027b82ed76 100644 --- a/solr/core/src/java/org/apache/solr/cli/AssertTool.java +++ b/solr/core/src/java/org/apache/solr/cli/AssertTool.java @@ -39,21 +39,55 @@ * Supports assert command in the bin/solr script. Asserts various conditions and exists with error * code if there are failures, else continues with no output. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "assert", + description = + "Asserts various conditions and exits with an error code if there are failures, else" + + " continues with no output.", + footerHeading = "%nExamples:%n", + footer = { + " # Assert Solr is running before continuing", + " bin/solr assert --started http://localhost:8983 --timeout 5000", + "", + " # Assert we are not running as root", + " bin/solr assert --not-root" + }) public class AssertTool extends ToolBase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private String message = null; private boolean useExitCode = false; private Long timeoutMs = 1000L; + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_NOT_ROOT_OPTION = Option.builder().desc("Asserts that we are NOT the root user.").longOpt("not-root").get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_ROOT_OPTION = Option.builder().desc("Asserts that we are the root user.").longOpt("root").get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final OptionGroup ROOT_OPTION = new OptionGroup().addOption(IS_NOT_ROOT_OPTION).addOption(IS_ROOT_OPTION); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_NOT_RUNNING_ON_OPTION = Option.builder() .desc("Asserts that Solr is NOT running on a certain URL. Default timeout is 1000ms.") @@ -62,6 +96,11 @@ public class AssertTool extends ToolBase { .argName("url") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_RUNNING_ON_OPTION = Option.builder() .desc("Asserts that Solr is running on a certain URL. Default timeout is 1000ms.") @@ -70,9 +109,19 @@ public class AssertTool extends ToolBase { .argName("url") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final OptionGroup RUNNING_OPTION = new OptionGroup().addOption(IS_NOT_RUNNING_ON_OPTION).addOption(IS_RUNNING_ON_OPTION); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SAME_USER_OPTION = Option.builder() .desc("Asserts that we run as same user that owns .") @@ -81,6 +130,11 @@ public class AssertTool extends ToolBase { .argName("directory") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option DIRECTORY_EXISTS_OPTION = Option.builder() .desc("Asserts that directory exists.") @@ -89,6 +143,11 @@ public class AssertTool extends ToolBase { .argName("directory") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option DIRECTORY_NOT_EXISTS_OPTION = Option.builder() .desc("Asserts that directory does NOT exist.") @@ -97,9 +156,19 @@ public class AssertTool extends ToolBase { .argName("directory") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final OptionGroup DIRECTORY_OPTION = new OptionGroup().addOption(DIRECTORY_EXISTS_OPTION).addOption(DIRECTORY_NOT_EXISTS_OPTION); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_CLOUD_OPTION = Option.builder() .desc( @@ -109,6 +178,11 @@ public class AssertTool extends ToolBase { .argName("url") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option IS_NOT_CLOUD_OPTION = Option.builder() .desc( @@ -118,9 +192,19 @@ public class AssertTool extends ToolBase { .argName("url") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final OptionGroup CLOUD_OPTION = new OptionGroup().addOption(IS_CLOUD_OPTION).addOption(IS_NOT_CLOUD_OPTION); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option MESSAGE_OPTION = Option.builder() .desc("Exception message to be used in place of the default error message.") @@ -129,6 +213,11 @@ public class AssertTool extends ToolBase { .argName("message") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option TIMEOUT_OPTION = Option.builder() .desc("Timeout in ms for commands supporting a timeout.") @@ -138,6 +227,11 @@ public class AssertTool extends ToolBase { .argName("ms") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option EXIT_CODE_OPTION = Option.builder() .desc("Return an exit code instead of printing error message on assert fail.") @@ -188,6 +282,109 @@ record AssertParams( String credentials, List assertions) {} + // --- picocli fields --- + + static class RootOptions { + @picocli.CommandLine.Option( + names = "--root", + description = "Asserts that we are the root user.") + boolean isRoot; + + @picocli.CommandLine.Option( + names = "--not-root", + description = "Asserts that we are NOT the root user.") + boolean isNotRoot; + } + + static class RunningOptions { + @picocli.CommandLine.Option( + names = "--started", + paramLabel = "url", + description = "Asserts that Solr is running on a certain URL. Default timeout is 1000ms.") + String startedUrl; + + @picocli.CommandLine.Option( + names = "--not-started", + paramLabel = "url", + description = + "Asserts that Solr is NOT running on a certain URL. Default timeout is 1000ms.") + String notStartedUrl; + } + + static class DirectoryOptions { + @picocli.CommandLine.Option( + names = "--exists", + paramLabel = "directory", + description = "Asserts that directory exists.") + String existsDir; + + @picocli.CommandLine.Option( + names = "--not-exists", + paramLabel = "directory", + description = "Asserts that directory does NOT exist.") + String notExistsDir; + } + + static class CloudOptions { + @picocli.CommandLine.Option( + names = "--cloud", + paramLabel = "url", + description = + "Asserts that Solr is running in cloud mode. Also fails if Solr not running. URL" + + " should be for root Solr path.") + String cloudUrl; + + @picocli.CommandLine.Option( + names = "--not-cloud", + paramLabel = "url", + description = + "Asserts that Solr is not running in cloud mode. Also fails if Solr not running. " + + " URL should be for root Solr path.") + String notCloudUrl; + } + + @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1") + private RootOptions rootOptions; + + @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1") + private RunningOptions runningOptions; + + @picocli.CommandLine.Option( + names = "--same-user", + paramLabel = "directory", + description = "Asserts that we run as same user that owns .") + private String sameUserOpt; + + @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1") + private DirectoryOptions directoryOptions; + + @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1") + private CloudOptions cloudOptions; + + @picocli.CommandLine.Option( + names = "--message", + paramLabel = "message", + description = "Exception message to be used in place of the default error message.") + private String messageOpt; + + @picocli.CommandLine.Option( + names = "--timeout", + paramLabel = "ms", + defaultValue = "1000", + description = "Timeout in ms for commands supporting a timeout.") + private long timeoutOpt; + + @picocli.CommandLine.Option( + names = "--exitcode", + description = "Return an exit code instead of printing error message on assert fail.") + private boolean exitCodeOpt; + + @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; + + public AssertTool() { + this(new DefaultToolRuntime()); + } + public AssertTool(ToolRuntime runtime) { super(runtime); } @@ -496,7 +693,57 @@ private static boolean runningSolrIsCloud(String url, String credentials) throws @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + List assertions = new ArrayList<>(); + if (rootOptions != null && rootOptions.isRoot) { + assertions.add(new Assertion.RootUser()); + } + if (rootOptions != null && rootOptions.isNotRoot) { + assertions.add(new Assertion.NotRootUser()); + } + if (directoryOptions != null && directoryOptions.existsDir != null) { + assertions.add(new Assertion.DirExists(directoryOptions.existsDir)); + } + if (directoryOptions != null && directoryOptions.notExistsDir != null) { + assertions.add(new Assertion.DirNotExists(directoryOptions.notExistsDir)); + } + if (sameUserOpt != null) { + assertions.add(new Assertion.SameUser(sameUserOpt)); + } + if (runningOptions != null && runningOptions.startedUrl != null) { + assertions.add(new Assertion.SolrRunning(runningOptions.startedUrl)); + } + if (runningOptions != null && runningOptions.notStartedUrl != null) { + assertions.add(new Assertion.SolrNotRunning(runningOptions.notStartedUrl)); + } + if (cloudOptions != null && cloudOptions.cloudUrl != null) { + assertions.add(new Assertion.CloudMode(cloudOptions.cloudUrl)); + } + if (cloudOptions != null && cloudOptions.notCloudUrl != null) { + assertions.add(new Assertion.NotCloudMode(cloudOptions.notCloudUrl)); + } + + try { + return runAssert( + new AssertParams( + messageOpt, + timeoutOpt, + exitCodeOpt, + credentialsOptions.credentials, + List.copyOf(assertions))); + } catch (Exception exc) { + // Mirrors the commons-cli path's runTool() override: an assertion failure or other error + // with a message becomes exit code 100, not the ToolBase default of 1. + String excMsg = exc.getMessage(); + if (excMsg == null) { + throw exc; + } + if (isVerbose()) { + CLIO.err("\nERROR: " + exc + "\n"); + } else { + CLIO.err("\nERROR: " + excMsg + "\n"); + } + return 100; + } } public static class AssertionFailureException extends Exception { 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..62d3475c9788 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, + AssertTool.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { diff --git a/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.java new file mode 100644 index 000000000000..ba1618234552 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.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 AssertToolTest} tests through the picocli invocation path. + * + *

All {@code @Test} methods are inherited; only the invocation strategy is overridden. + */ +public class AssertToolPicocliTest extends AssertToolTest { + + @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/AssertToolTest.java b/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java index 9f3aa0e77a57..77f6a273108b 100644 --- a/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java @@ -28,6 +28,11 @@ public class AssertToolTest 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).addConfig("conf", configset("cloud-minimal")).configure(); @@ -42,7 +47,7 @@ public void raisesExitCode100OnError() throws Exception { System.getSecurityManager() != null); final String[] args = new String[] {"assert", "--exitcode", "--exists", "/tmp"}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals("Expected AssertTool to raise an error", 100, numAssertionsFailed); } @@ -52,7 +57,7 @@ public void checksForTheExistenceOfDirectoryThatExists() throws Exception { Path tempDir = Files.createTempDirectory("myTempDir"); final String[] args = new String[] {"assert", "--exitcode", "--exists", tempDir.toString()}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to pass assertion that directory exists", 0, numAssertionsFailed); @@ -66,7 +71,7 @@ public void checksForTheExistenceOfDirectoryThatDoesntExist() throws Exception { final String[] args = new String[] {"assert", "--exitcode", "--exists", tempDir.toString() + "/foo/bar/baz"}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to fail assertion that directory exists", 1, numAssertionsFailed); @@ -77,7 +82,7 @@ public void checksForTheNonExistenceOfDirectoryThatExists() throws Exception { Path tempDir = Files.createTempDirectory("myTempDir"); final String[] args = new String[] {"assert", "--exitcode", "--not-exists", tempDir.toString()}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to fail assertion that directory doesnt exist", @@ -91,7 +96,7 @@ public void checksForTheNonExistenceDirectoryThatDoesntExist() throws Exception final String[] args = new String[] {"assert", "--exitcode", "--not-exists", tempDir.toString() + "/foo/bar/baz"}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to fail assertion that directory doesnt exist", @@ -104,7 +109,7 @@ public void checksForThePresenceOfSolrOnCorrectUrl() throws Exception { final String baseUrl = getRealSolrBaseUrl(); final String[] args = new String[] {"assert", "--exitcode", "--started", baseUrl}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to pass assertion when Solr is running on provided URL", @@ -117,7 +122,7 @@ public void checksForThePresenceOfSolrOnIncorrectUrl() throws Exception { final String[] args = new String[] {"assert", "--exitcode", "--started", "http://www.google.com"}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to fail assertion when Solr isn't running on provided URL", @@ -130,7 +135,7 @@ public void checksForTheAbsenceOfSolrOnCorrectUrl() throws Exception { final String baseUrl = getRealSolrBaseUrl(); final String[] args = new String[] {"assert", "--exitcode", "--not-started", baseUrl}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to fail assertion when Solr is running on provided URL", @@ -143,7 +148,7 @@ public void checksForTheAbsenceOfSolrOnIncorrectUrl() throws Exception { final String[] args = new String[] {"assert", "--exitcode", "--not-started", "http://www.google.com"}; - final int numAssertionsFailed = CLITestHelper.runTool(args, AssertTool.class); + final int numAssertionsFailed = runTool(args, AssertTool.class); assertEquals( "Expected AssertTool to pass assertion when Solr isn't running on provided URL", diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle index 9672174b2a43..ccdffbeeaf4d 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_auth.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..53a667155efa 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc @@ -20,6 +20,7 @@ * xref:solr-control-script-reference.adoc[] ** xref:cli/index.adoc[Experimental picocli CLI] // CLI-DOCS-START +*** xref:cli/solr-assert.adoc[assert] *** xref:cli/solr-auth.adoc[auth] *** xref:cli/solr-create.adoc[create] *** xref:cli/solr-delete.adoc[delete] 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..1690291943b8 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 @@ -59,6 +59,7 @@ Any command not listed here is unavailable when the picocli implementation is en |=== |Command +|xref:cli/solr-assert.adoc[bin/solr assert] |xref:cli/solr-auth.adoc[bin/solr auth] |xref:cli/solr-create.adoc[bin/solr create] |xref:cli/solr-delete.adoc[bin/solr delete] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc new file mode 100644 index 000000000000..39c13bf82063 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc @@ -0,0 +1,111 @@ +// 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 assert +: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 assert [-hv] [--exitcode] [--message=message] [--same-user=directory] + [--timeout=ms] [-u=] [--root | --not-root] + [--started=url | --not-started=url] [--exists=directory | + --not-exists=directory] [--cloud=url | --not-cloud=url] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Asserts various conditions and exits with an error code if there are failures, else continues with no output. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*--cloud*=_url_:: + Asserts that Solr is running in cloud mode. Also fails if Solr not running. URL should be for root Solr path. + +*--exists*=_directory_:: + Asserts that directory exists. + +*--exitcode*:: + Return an exit code instead of printing error message on assert fail. + +*-h*, *--help*:: + Print this help message and exit. + +*--message*=_message_:: + Exception message to be used in place of the default error message. + +*--not-cloud*=_url_:: + Asserts that Solr is not running in cloud mode. Also fails if Solr not running. URL should be for root Solr path. + +*--not-exists*=_directory_:: + Asserts that directory does NOT exist. + +*--not-root*:: + Asserts that we are NOT the root user. + +*--not-started*=_url_:: + Asserts that Solr is NOT running on a certain URL. Default timeout is 1000ms. + +*--root*:: + Asserts that we are the root user. + +*--same-user*=_directory_:: + Asserts that we run as same user that owns . + +*--started*=_url_:: + Asserts that Solr is running on a certain URL. Default timeout is 1000ms. + +*--timeout*=_ms_:: + Timeout in ms for commands supporting a timeout. + +*-u*, *--credentials*=__:: + Credentials in the format username:password. Example: --credentials solr:SolrRocks + +*-v*, *--verbose*:: + Enable verbose mode. + +// end::picocli-generated-man-section-options[] + +// tag::picocli-generated-man-section-footer[] +== Examples +[%hardbreaks] + # Assert Solr is running before continuing + bin/solr assert --started http://localhost:8983 --timeout 5000 + +[%hardbreaks] + # Assert we are not running as root + bin/solr assert --not-root + +// end::picocli-generated-man-section-footer[]