From af46b8af7018e7b062d0aaa17c031a0da1529230 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Tue, 6 Oct 2026 22:08:23 +0300 Subject: [PATCH 1/5] SOLR-18518: Port the snapshot commands to picocli Adds the picocli code path next to the commons-cli one for snapshot-create, -delete, -describe, -export and -list, with test twins and the generated reference pages. The page generator now takes a command's hyphenated name from the pages that exist rather than from splitting the file name, so these are not titled "snapshot create". --- .../SOLR-18518-picocli-snapshot.yml | 9 ++ .../java/org/apache/solr/cli/CLIUtils.java | 42 ++++++++ .../apache/solr/cli/SnapshotCreateTool.java | 53 +++++++++- .../apache/solr/cli/SnapshotDeleteTool.java | 53 +++++++++- .../apache/solr/cli/SnapshotDescribeTool.java | 53 +++++++++- .../apache/solr/cli/SnapshotExportTool.java | 98 ++++++++++++++++++- .../org/apache/solr/cli/SnapshotListTool.java | 37 ++++++- .../src/java/org/apache/solr/cli/SolrCLI.java | 7 +- .../cli/SnapshotExportToolPicocliTest.java | 39 ++++++++ .../solr/cli/SnapshotExportToolTest.java | 32 +++++- .../solr/cli/SnapshotToolsPicocliTest.java | 36 +++++++ .../apache/solr/cli/SnapshotToolsTest.java | 97 ++++++++++++++++++ solr/solr-ref-guide/build.gradle | 48 ++++++--- .../deployment-guide/deployment-nav.adoc | 5 + .../deployment-guide/pages/cli/index.adoc | 5 + .../pages/cli/solr-snapshot-create.adoc | 84 ++++++++++++++++ .../pages/cli/solr-snapshot-delete.adoc | 84 ++++++++++++++++ .../pages/cli/solr-snapshot-describe.adoc | 85 ++++++++++++++++ .../pages/cli/solr-snapshot-export.adoc | 94 ++++++++++++++++++ .../pages/cli/solr-snapshot-list.adoc | 81 +++++++++++++++ 20 files changed, 1021 insertions(+), 21 deletions(-) create mode 100644 changelog/unreleased/SOLR-18518-picocli-snapshot.yml create mode 100644 solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java create mode 100644 solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java create mode 100644 solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc diff --git a/changelog/unreleased/SOLR-18518-picocli-snapshot.yml b/changelog/unreleased/SOLR-18518-picocli-snapshot.yml new file mode 100644 index 000000000000..14d0bd70221a --- /dev/null +++ b/changelog/unreleased/SOLR-18518-picocli-snapshot.yml @@ -0,0 +1,9 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc + +title: The `snapshot-create`, `snapshot-delete`, `snapshot-describe`, `snapshot-export` and `snapshot-list` commands are now available in the experimental picocli command line interface. +type: added +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-18518 + url: https://issues.apache.org/jira/browse/SOLR-18518 diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 68d4eedc4700..6fa7fcbd6e81 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -247,6 +247,48 @@ public static String normalizeSolrUrl(CommandLine cli) throws Exception { return solrUrl; } + /** + * The picocli counterpart of {@link #normalizeSolrUrl(CommandLine)}: {@code --solr-url}, else the + * connection named by {@code --solr-connection} or {@code --zk-host} (or the matching property + * when the option is absent), else the default URL with the same warning. + * + * @param connectionOptions the parsed connection group, or null if none of its options was given + */ + static String resolveSolrUrl(ConnectionOptions connectionOptions, String credentials) + throws Exception { + String solrUrl = connectionOptions != null ? connectionOptions.solrUrl : null; + if (solrUrl == null) { + String solrConnection = + (connectionOptions != null && connectionOptions.solrConnection != null) + ? connectionOptions.solrConnection + : EnvUtils.getProperty("solr-connection"); + String zkHost = + (connectionOptions != null && connectionOptions.zkHost != null) + ? connectionOptions.zkHost + : EnvUtils.getProperty("zkHost"); + if (solrConnection != null && !solrConnection.isBlank()) { + solrUrl = + solrUrlFromConnection( + CloudSolrClient.CloudSolrClientConnection.parse(solrConnection), credentials); + } else if (zkHost != null && !zkHost.isBlank()) { + var zkSolrConnection = CloudSolrClient.CloudSolrClientConnection.parse(zkHost); + if (!zkSolrConnection.isZookeeper()) { + throw new IOException( + String.format( + Locale.ROOT, "Expected ZooKeeper connection string, but got: '%s'.", zkHost)); + } + solrUrl = solrUrlFromConnection(zkSolrConnection, credentials); + } else { + solrUrl = getDefaultSolrUrl(); + CLIO.err( + "Neither --solr-connection, --zk-host or --solr-url parameters, nor SOLR_CONNECTION, ZK_HOST env var provided, so assuming solr url is " + + solrUrl + + "."); + } + } + return normalizeSolrUrl(solrUrl); + } + /** * Resolves a base Solr URL from a parsed connection. The HTTP form (e.g. {@code -s * http://host:port}) already names a Solr URL, so it is used directly without spinning up a diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java index 89263c9da2ca..602154370725 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java @@ -24,8 +24,22 @@ import org.apache.solr.client.solrj.response.CollectionAdminResponse; /** Supports snapshot-create command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-create", + description = "Creates a named snapshot of a collection.", + footerHeading = "%nExamples:%n", + footer = { + " # Create a snapshot", + " bin/solr snapshot-create -c mycollection --snapshot-name snap1" + }) public class SnapshotCreateTool extends ToolBase { + /** + * @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") @@ -35,6 +49,11 @@ public class SnapshotCreateTool extends ToolBase { .desc("Name of collection to be snapshot.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SNAPSHOT_NAME_OPTION = Option.builder() .longOpt("snapshot-name") @@ -48,6 +67,31 @@ public class SnapshotCreateTool extends ToolBase { record SnapshotCreateParams( String solrUrl, String credentials, String collectionName, String snapshotName) {} + // --- 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 = "NAME", + description = "Name of collection to be snapshot.") + private String nameOpt; + + @picocli.CommandLine.Option( + names = "--snapshot-name", + required = true, + paramLabel = "NAME", + description = "Name of the snapshot to produce") + private String snapshotNameOpt; + + public SnapshotCreateTool() { + this(new DefaultToolRuntime()); + } + public SnapshotCreateTool(ToolRuntime runtime) { super(runtime); } @@ -112,6 +156,13 @@ public void createSnapshot(SolrClient solrClient, String collectionName, String @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + SnapshotCreateParams params = + new SnapshotCreateParams( + CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), + credentialsOptions.credentials, + nameOpt, + snapshotNameOpt); + createSnapshot(params); + return 0; } } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java index 7ff35e5e95a9..010355b8326a 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java @@ -24,8 +24,22 @@ import org.apache.solr.client.solrj.response.CollectionAdminResponse; /** Supports snapshot-delete command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-delete", + description = "Deletes a named snapshot of a collection.", + footerHeading = "%nExamples:%n", + footer = { + " # Delete a snapshot", + " bin/solr snapshot-delete -c mycollection --snapshot-name snap1" + }) public class SnapshotDeleteTool extends ToolBase { + /** + * @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") @@ -35,6 +49,11 @@ public class SnapshotDeleteTool extends ToolBase { .desc("Name of collection to manage.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SNAPSHOT_NAME_OPTION = Option.builder() .longOpt("snapshot-name") @@ -48,6 +67,31 @@ public class SnapshotDeleteTool extends ToolBase { record SnapshotDeleteParams( String solrUrl, String credentials, String collectionName, String snapshotName) {} + // --- 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 = "NAME", + description = "Name of collection to manage.") + private String nameOpt; + + @picocli.CommandLine.Option( + names = "--snapshot-name", + required = true, + paramLabel = "NAME", + description = "Name of the snapshot to delete") + private String snapshotNameOpt; + + public SnapshotDeleteTool() { + this(new DefaultToolRuntime()); + } + public SnapshotDeleteTool(ToolRuntime runtime) { super(runtime); } @@ -112,6 +156,13 @@ public void deleteSnapshot(SolrClient solrClient, String collectionName, String @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + SnapshotDeleteParams params = + new SnapshotDeleteParams( + CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), + credentialsOptions.credentials, + nameOpt, + snapshotNameOpt); + deleteSnapshot(params); + return 0; } } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java index d945c23d57f6..f8a1d3908d52 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java @@ -34,8 +34,22 @@ import org.apache.solr.core.snapshots.SolrSnapshotManager; /** Supports snapshot-describe command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-describe", + description = "Describes a named snapshot of a collection.", + footerHeading = "%nExamples:%n", + footer = { + " # Describe a snapshot", + " bin/solr snapshot-describe -c mycollection --snapshot-name snap1" + }) public class SnapshotDescribeTool extends ToolBase { + /** + * @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") @@ -45,6 +59,11 @@ public class SnapshotDescribeTool extends ToolBase { .desc("Name of collection to be snapshot.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SNAPSHOT_NAME_OPTION = Option.builder() .longOpt("snapshot-name") @@ -58,6 +77,31 @@ public class SnapshotDescribeTool extends ToolBase { record SnapshotDescribeParams( String solrUrl, String credentials, String collectionName, String snapshotName) {} + // --- 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 = "NAME", + description = "Name of collection to be snapshot.") + private String nameOpt; + + @picocli.CommandLine.Option( + names = "--snapshot-name", + required = true, + paramLabel = "NAME", + description = "Name of the snapshot to describe") + private String snapshotNameOpt; + + public SnapshotDescribeTool() { + this(new DefaultToolRuntime()); + } + public SnapshotDescribeTool(ToolRuntime runtime) { super(runtime); } @@ -151,6 +195,13 @@ private Collection listCollectionSnapshots( @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + SnapshotDescribeParams params = + new SnapshotDescribeParams( + CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), + credentialsOptions.credentials, + nameOpt, + snapshotNameOpt); + describeSnapshot(params); + return 0; } } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java index 8544122e261c..1f69e846a651 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java @@ -28,11 +28,25 @@ import org.apache.solr.common.params.CollectionAdminParams; /** Supports snapshot-export command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-export", + description = "Backs up a collection's current state to a local directory.", + footerHeading = "%nExamples:%n", + footer = { + " # Export a collection's current state as a backup", + " bin/solr snapshot-export -c mycollection --dest-dir /tmp/backups --backup-repo-name local" + }) public class SnapshotExportTool extends ToolBase { private static final DateTimeFormatter BACKUP_NAME_TIMESTAMP = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'", Locale.ROOT).withZone(ZoneOffset.UTC); + /** + * @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") @@ -46,6 +60,11 @@ public class SnapshotExportTool extends ToolBase { * Accepted only so that passing it can be rejected with an explanation. Selecting a named * snapshot to export required the non-incremental backup format, which no longer exists. */ + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option SNAPSHOT_NAME_OPTION = Option.builder() .longOpt("snapshot-name") @@ -54,6 +73,11 @@ public class SnapshotExportTool extends ToolBase { .desc("No longer supported; passing it fails with an error.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option DEST_DIR_OPTION = Option.builder() .longOpt("dest-dir") @@ -63,6 +87,11 @@ public class SnapshotExportTool extends ToolBase { .desc("Path of a temporary directory on local filesystem during snapshot export command.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option BACKUP_REPO_NAME_OPTION = Option.builder() .longOpt("backup-repo-name") @@ -72,6 +101,11 @@ public class SnapshotExportTool extends ToolBase { "Specifies name of the backup repository to be used during snapshot export preparation.") .get(); + /** + * @deprecated Only used by the commons-cli parser; the picocli path declares this as an annotated + * field. + */ + @Deprecated private static final Option ASYNC_ID_OPTION = Option.builder() .longOpt("async-id") @@ -90,6 +124,53 @@ record SnapshotExportParams( String backupRepo, String asyncReqId) {} + // --- 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 = "NAME", + description = "Name of the collection to be backed up.") + private String nameOpt; + + // Accepted only so that passing it can be rejected with an explanation; see callTool(). + @picocli.CommandLine.Option( + names = "--snapshot-name", + paramLabel = "NAME", + description = "No longer supported; passing it fails with an error.") + private String snapshotNameOpt; + + @picocli.CommandLine.Option( + names = "--dest-dir", + required = true, + paramLabel = "DIR", + description = + "Path of a temporary directory on local filesystem during snapshot export command.") + private String destDirOpt; + + @picocli.CommandLine.Option( + names = "--backup-repo-name", + paramLabel = "DIR", + description = + "Specifies name of the backup repository to be used during snapshot export preparation.") + private String backupRepoNameOpt; + + @picocli.CommandLine.Option( + names = "--async-id", + paramLabel = "ID", + description = + "Specifies the async request identifier to be used during snapshot export preparation.") + private String asyncIdOpt; + + public SnapshotExportTool() { + this(new DefaultToolRuntime()); + } + public SnapshotExportTool(ToolRuntime runtime) { super(runtime); } @@ -178,6 +259,21 @@ public void exportSnapshot( @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + if (snapshotNameOpt != null) { + throw new IllegalArgumentException( + "--snapshot-name is no longer supported. Exporting a named snapshot required the " + + "non-incremental backup format, which was removed in Solr 11; this command now " + + "always backs up the collection's current state. Re-run without --snapshot-name."); + } + SnapshotExportParams params = + new SnapshotExportParams( + CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), + credentialsOptions.credentials, + nameOpt, + destDirOpt, + backupRepoNameOpt, + asyncIdOpt); + exportSnapshot(params); + return 0; } } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java index a4d7ea3b56ae..62c3e1341599 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java @@ -26,8 +26,19 @@ import org.apache.solr.core.snapshots.SolrSnapshotManager; /** Supports snapshot-list command in the bin/solr script. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-list", + description = "Lists the snapshots for a collection.", + footerHeading = "%nExamples:%n", + footer = {" # List a collection's snapshots", " bin/solr snapshot-list -c mycollection"}) public class SnapshotListTool extends ToolBase { + /** + * @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") @@ -40,6 +51,24 @@ public class SnapshotListTool extends ToolBase { /** Parameters for the snapshot-list command, independent of the command line parser. */ record SnapshotListParams(String solrUrl, String credentials, String collectionName) {} + // --- 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 = "NAME", + description = "Name of collection to list snapshots for.") + private String nameOpt; + + public SnapshotListTool() { + this(new DefaultToolRuntime()); + } + public SnapshotListTool(ToolRuntime runtime) { super(runtime); } @@ -99,6 +128,12 @@ public void listSnapshots(SolrClient solrClient, String collectionName) { @Override public int callTool() throws Exception { - throw new UnsupportedOperationException("This tool does not yet support PicoCli"); + SnapshotListParams params = + new SnapshotListParams( + CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), + credentialsOptions.credentials, + nameOpt); + listSnapshots(params); + return 0; } } 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..66717907df47 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,12 @@ ZkTool.class, AuthTool.class, CreateTool.class, - DeleteTool.class + DeleteTool.class, + SnapshotCreateTool.class, + SnapshotDeleteTool.class, + SnapshotDescribeTool.class, + SnapshotExportTool.class, + SnapshotListTool.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java new file mode 100644 index 000000000000..280b1cd4b65e --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.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 the {@link SnapshotExportToolTest} tests that go through {@code runTool} using picocli; the + * tests that call the commons-cli parser directly are inherited and run unchanged. + */ +public class SnapshotExportToolPicocliTest extends SnapshotExportToolTest { + + @Override + protected int runTool( + String[] args, CLITestHelper.TestingRuntime runtime, 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); + 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/SnapshotExportToolTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolTest.java index 81c46f302dd7..a3c45246a105 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolTest.java @@ -37,6 +37,13 @@ @LuceneTestCase.SuppressCodecs({"SimpleText"}) public class SnapshotExportToolTest extends SolrCloudTestCase { + /** Runs the tool. Overridden by the picocli variant of this test. */ + protected int runTool( + String[] args, CLITestHelper.TestingRuntime runtime, Class clazz) + throws Exception { + return CLITestHelper.runTool(args, runtime, clazz); + } + private static final String COLLECTION = "snapshot_export_coll"; @BeforeClass @@ -124,6 +131,29 @@ public void testSnapshotNameNoLongerRequired() throws Exception { Arrays.stream(cli.getOptions()).anyMatch(o -> "snapshot-name".equals(o.getLongOpt()))); } + /** Whichever parser runs, the option is refused with an error exit and nothing is written. */ + @Test + public void testSnapshotNameIsRefusedWithAnErrorExit() throws Exception { + Path destDir = newDestDir(); + int exitCode = + runTool( + new String[] { + "snapshot-export", + "-c", + COLLECTION, + "--dest-dir", + destDir.toString(), + "--solr-url", + solrUrl(), + "--snapshot-name", + "snap1" + }, + new CLITestHelper.TestingRuntime(false), + SnapshotExportTool.class); + assertEquals(1, exitCode); + assertEquals(List.of(), backupDirs(destDir)); + } + @Test public void testBackupNameIsDerivedFromCollectionAndTime() { assertEquals( @@ -137,7 +167,7 @@ public void testExportWritesBackupUnderTheDerivedName() throws Exception { Path destDir = newDestDir(); CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); int exitCode = - CLITestHelper.runTool( + runTool( new String[] { "snapshot-export", "-c", diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java new file mode 100644 index 000000000000..ec10dde51027 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java @@ -0,0 +1,36 @@ +/* + * 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 the {@link SnapshotToolsTest} tests through picocli. */ +public class SnapshotToolsPicocliTest extends SnapshotToolsTest { + + @Override + protected int runTool( + String[] args, CLITestHelper.TestingRuntime runtime, 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); + 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/SnapshotToolsTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java new file mode 100644 index 000000000000..b35a80fd0116 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java @@ -0,0 +1,97 @@ +/* + * 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.ArrayList; +import java.util.List; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; +import org.apache.solr.cloud.SolrCloudTestCase; +import org.apache.solr.common.SolrInputDocument; +import org.junit.BeforeClass; +import org.junit.Test; + +/** The snapshot-create, -list, -describe and -delete commands through the command line. */ +public class SnapshotToolsTest extends SolrCloudTestCase { + private static final String COLLECTION = "snapshotToolsColl"; + + /** Runs the tool. Overridden by the picocli variant of this test. */ + protected int runTool( + String[] args, CLITestHelper.TestingRuntime runtime, Class clazz) + throws Exception { + return CLITestHelper.runTool(args, runtime, clazz); + } + + @BeforeClass + public static void setupCluster() throws Exception { + configureCluster(1).addConfig("conf", configset("cloud-minimal")).configure(); + CollectionAdminRequest.createCollection(COLLECTION, "conf", 1, 1) + .process(cluster.getSolrClient()); + cluster.waitForActiveCollection(COLLECTION, 1, 1); + cluster.getSolrClient().add(COLLECTION, new SolrInputDocument("id", "1")); + cluster.getSolrClient().commit(COLLECTION); + } + + private String run(Class tool, String name, String... extra) + throws Exception { + List args = + new ArrayList<>( + List.of( + name, + "-c", + COLLECTION, + "--solr-url", + cluster.getJettySolrRunner(0).getBaseUrl().toString())); + args.addAll(List.of(extra)); + CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); + assertEquals(0, runTool(args.toArray(new String[0]), runtime, tool)); + return runtime.getOutput(); + } + + @Test + public void testSnapshotLifecycle() throws Exception { + run(SnapshotCreateTool.class, "snapshot-create", "--snapshot-name", "snap1"); + + assertTrue(run(SnapshotListTool.class, "snapshot-list").contains("snap1")); + + String described = + run(SnapshotDescribeTool.class, "snapshot-describe", "--snapshot-name", "snap1"); + assertTrue(described, described.contains("Name: snap1")); + + run(SnapshotDeleteTool.class, "snapshot-delete", "--snapshot-name", "snap1"); + + assertFalse(run(SnapshotListTool.class, "snapshot-list").contains("snap1")); + } + + // snapshot-list reports a failed request in its output and still exits 0, so check the output. + @Test + public void testConnectionFallsBackToTheZkHostProperty() throws Exception { + run(SnapshotCreateTool.class, "snapshot-create", "--snapshot-name", "snapViaProperty"); + + System.setProperty("zkHost", cluster.getZkClient().getZkServerAddress()); + try { + CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); + assertEquals( + 0, + runTool( + new String[] {"snapshot-list", "-c", COLLECTION}, runtime, SnapshotListTool.class)); + assertTrue(runtime.getOutput(), runtime.getOutput().contains("snapViaProperty")); + } finally { + System.clearProperty("zkHost"); + } + run(SnapshotDeleteTool.class, "snapshot-delete", "--snapshot-name", "snapViaProperty"); + } +} diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle index 80ee11363863..136b63321d49 100644 --- a/solr/solr-ref-guide/build.gradle +++ b/solr/solr-ref-guide/build.gradle @@ -608,17 +608,34 @@ def DO_NOT_EDIT_NOTICE = """\ // ./gradlew :solr:solr-ref-guide:generateCliDocs """ +// The parent of a generated page: the longest proper prefix, on a dash boundary, that is itself a +// generated page, or null for a top-level command. A command's own name may contain a dash +// ("snapshot-create"), so the file name alone cannot say where the hierarchy ends. +String cliParentStem(String baseName, Set stems) { + for (def stem = baseName; stem.contains('-'); ) { + stem = stem.substring(0, stem.lastIndexOf('-')) + if (stems.contains(stem)) { + return stem + } + } + return null +} + // Convert a generated filename stem (e.g. "solr-zk-ls") to an Antora page title -// e.g. "solr-zk-ls" -> "bin/solr zk ls" -String cliFileNameToTitle(String baseName) { - def parts = baseName.split('-') - return "bin/solr " + parts.drop(1).join(' ') +// e.g. "solr-zk-ls" -> "bin/solr zk ls", but "solr-snapshot-create" -> "bin/solr snapshot-create" +String cliFileNameToTitle(String baseName, Set stems) { + def parent = cliParentStem(baseName, stems) + if (parent == null) { + return "bin/solr " + baseName.substring('solr-'.length()) + } + return cliFileNameToTitle(parent, stems) + " " + baseName.substring(parent.length() + 1) } -// Return the short nav label for a command file (last segment of dash-separated name) -// e.g. "solr-zk-ls" -> "ls" -String cliFileNameToNavLabel(String baseName) { - return baseName.split('-').last() +// Return the short nav label for a command file: its name below its parent page +// e.g. "solr-zk-ls" -> "ls", "solr-snapshot-create" -> "snapshot-create" +String cliFileNameToNavLabel(String baseName, Set stems) { + def parent = cliParentStem(baseName, stems) + return baseName.substring((parent == null ? 'solr'.length() : parent.length()) + 1) } // Strip "[...]" (optional groups) and "(...)" (required groups whose members @@ -707,10 +724,10 @@ String markRequiredOptionsAndArgs(String content) { // Post-process a raw ManPageGenerator AsciiDoc file for Antora compatibility. // The title lives inside the man-section-header block, so we replace the whole // header block with an Antora-compatible page title + attributes. -String postProcessCliManPage(File rawFile, String asfHeader, String doNotEditNotice, String experimentalNotice) { +String postProcessCliManPage(File rawFile, Set stems, String asfHeader, String doNotEditNotice, String experimentalNotice) { def content = rawFile.text def baseName = rawFile.name.replace('.adoc', '') - def newTitle = cliFileNameToTitle(baseName) + def newTitle = cliFileNameToTitle(baseName, stems) // Replace the entire man-section-header block (which contains :doctype:manpage, the page title // "= solr-start(1)", etc.) with our Antora-compatible title and page attributes. @@ -776,9 +793,10 @@ String buildCliCommandTable(File cliDir) { def files = ((cliDir.listFiles() ?: []) as List) .findAll { it.name.startsWith('solr-') && it.name.endsWith('.adoc') } .sort { it.name } + def stems = files.collect { it.name.replace('.adoc', '') } as Set def rows = files.collect { file -> def baseName = file.name.replace('.adoc', '') - "|xref:cli/${file.name}[${cliFileNameToTitle(baseName)}]" + "|xref:cli/${file.name}[${cliFileNameToTitle(baseName, stems)}]" } return "[cols=\"1\",options=\"header\"]\n|===\n|Command\n\n" + rows.join('\n') + "\n|===" } @@ -793,7 +811,7 @@ List buildCliNavEntries(File cliDir) { def stems = files.collect { it.name.replace('.adoc', '') } as Set return files.collect { file -> def baseName = file.name.replace('.adoc', '') - def label = cliFileNameToNavLabel(baseName) + def label = cliFileNameToNavLabel(baseName, stems) // Nest under the deepest ancestor that is itself a generated page. Counting dashes instead // would mis-nest a command whose own name contains one, e.g. a future "snapshot-create" // would be filed under a "solr-snapshot" page that does not exist. @@ -856,8 +874,9 @@ task generateCliDocs { def rawFiles = runManPageGenerator(rawOutputDir) + def rawStems = rawFiles.collect { it.name.replace('.adoc', '') } as Set rawFiles.each { rawFile -> - def processed = postProcessCliManPage(rawFile, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) + def processed = postProcessCliManPage(rawFile, rawStems, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) new File(destDir, rawFile.name).text = processed } @@ -912,8 +931,9 @@ task checkCliDocsUpToDate { def rawFiles = runManPageGenerator(rawOutputDir) def issues = [] + def rawStems = rawFiles.collect { it.name.replace('.adoc', '') } as Set rawFiles.each { rawFile -> - def processed = postProcessCliManPage(rawFile, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) + def processed = postProcessCliManPage(rawFile, rawStems, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) def committed = new File(committedDir, rawFile.name) if (!committed.exists()) { issues << "MISSING committed file (new command): ${rawFile.name}" 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..6883760d36b1 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,11 @@ *** xref:cli/solr-auth.adoc[auth] *** xref:cli/solr-create.adoc[create] *** xref:cli/solr-delete.adoc[delete] +*** xref:cli/solr-snapshot-create.adoc[snapshot-create] +*** xref:cli/solr-snapshot-delete.adoc[snapshot-delete] +*** xref:cli/solr-snapshot-describe.adoc[snapshot-describe] +*** xref:cli/solr-snapshot-export.adoc[snapshot-export] +*** xref:cli/solr-snapshot-list.adoc[snapshot-list] *** 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..5d2c1ed44cf7 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,11 @@ 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-snapshot-create.adoc[bin/solr snapshot-create] +|xref:cli/solr-snapshot-delete.adoc[bin/solr snapshot-delete] +|xref:cli/solr-snapshot-describe.adoc[bin/solr snapshot-describe] +|xref:cli/solr-snapshot-export.adoc[bin/solr snapshot-export] +|xref:cli/solr-snapshot-list.adoc[bin/solr snapshot-list] |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-snapshot-create.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc new file mode 100644 index 000000000000..717dc9b35a78 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc @@ -0,0 +1,84 @@ +// 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 snapshot-create +: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 snapshot-create [-hv] -c=NAME --snapshot-name=NAME [-u=] + [-s= | --solr-url= | -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Creates a named snapshot of a collection. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-c*, *--name*=_NAME_:: + *(required)* Name of collection to be snapshot. + +*-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. + +*--snapshot-name*=_NAME_:: + *(required)* Name of the snapshot to produce + +*--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] + # Create a snapshot + bin/solr snapshot-create -c mycollection --snapshot-name snap1 + +// end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc new file mode 100644 index 000000000000..d11fa9401828 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc @@ -0,0 +1,84 @@ +// 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 snapshot-delete +: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 snapshot-delete [-hv] -c=NAME --snapshot-name=NAME [-u=] + [-s= | --solr-url= | -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Deletes a named snapshot of a collection. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-c*, *--name*=_NAME_:: + *(required)* Name of collection to manage. + +*-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. + +*--snapshot-name*=_NAME_:: + *(required)* Name of the snapshot to delete + +*--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] + # Delete a snapshot + bin/solr snapshot-delete -c mycollection --snapshot-name snap1 + +// end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc new file mode 100644 index 000000000000..32a2295c064a --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc @@ -0,0 +1,85 @@ +// 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 snapshot-describe +: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 snapshot-describe [-hv] -c=NAME --snapshot-name=NAME [-u=] + [-s= | --solr-url= | + -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Describes a named snapshot of a collection. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-c*, *--name*=_NAME_:: + *(required)* Name of collection to be snapshot. + +*-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. + +*--snapshot-name*=_NAME_:: + *(required)* Name of the snapshot to describe + +*--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] + # Describe a snapshot + bin/solr snapshot-describe -c mycollection --snapshot-name snap1 + +// end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc new file mode 100644 index 000000000000..857a810777e0 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc @@ -0,0 +1,94 @@ +// 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 snapshot-export +: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 snapshot-export [-hv] [--async-id=ID] [--backup-repo-name=DIR] -c=NAME + --dest-dir=DIR [--snapshot-name=NAME] [-u=] + [-s= | --solr-url= | -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Backs up a collection's current state to a local directory. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*--async-id*=_ID_:: + Specifies the async request identifier to be used during snapshot export preparation. + +*--backup-repo-name*=_DIR_:: + Specifies name of the backup repository to be used during snapshot export preparation. + +*-c*, *--name*=_NAME_:: + *(required)* Name of the collection to be backed up. + +*--dest-dir*=_DIR_:: + *(required)* Path of a temporary directory on local filesystem during snapshot export command. + +*-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. + +*--snapshot-name*=_NAME_:: + No longer supported; passing it fails with an error. + +*--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] + # Export a collection's current state as a backup + bin/solr snapshot-export -c mycollection --dest-dir /tmp/backups --backup-repo-name local + +// end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc new file mode 100644 index 000000000000..b51d37569384 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.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 snapshot-list +: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 snapshot-list [-hv] -c=NAME [-u=] [-s= | + --solr-url= | -z=] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Lists the snapshots for a collection. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-c*, *--name*=_NAME_:: + *(required)* Name of collection to list snapshots for. + +*-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] + # List a collection's snapshots + bin/solr snapshot-list -c mycollection + +// end::picocli-generated-man-section-footer[] From 9066f0e190b08f0cbc72098c084a8551ad9bf2b2 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 10:25:37 +0300 Subject: [PATCH 2/5] SOLR-18518: Make snapshot a sub-command group, keep snapshot-* as hidden shims The JIRA asks for bin/solr snapshot , with the five flat spellings only as deprecated, hidden shims and an upgrade note. The flat commands became sub-commands of a snapshot group, the old spellings extend them, and the page generator change is no longer needed. --- .../SOLR-18518-picocli-snapshot.yml | 2 +- .../solr/cli/CollectionNameOptions.java | 32 ++++++ .../apache/solr/cli/SnapshotCreateShim.java | 31 ++++++ .../apache/solr/cli/SnapshotCreateTool.java | 22 ++-- .../apache/solr/cli/SnapshotDeleteShim.java | 31 ++++++ .../apache/solr/cli/SnapshotDeleteTool.java | 22 ++-- .../apache/solr/cli/SnapshotDescribeShim.java | 31 ++++++ .../apache/solr/cli/SnapshotDescribeTool.java | 22 ++-- .../apache/solr/cli/SnapshotExportShim.java | 31 ++++++ .../apache/solr/cli/SnapshotExportTool.java | 13 +-- .../org/apache/solr/cli/SnapshotListShim.java | 31 ++++++ .../org/apache/solr/cli/SnapshotListTool.java | 13 +-- .../apache/solr/cli/SnapshotNameOptions.java | 30 ++++++ .../org/apache/solr/cli/SnapshotTool.java | 46 ++++++++ .../src/java/org/apache/solr/cli/SolrCLI.java | 11 +- .../cli/SnapshotExportToolPicocliTest.java | 15 +-- .../solr/cli/SnapshotToolsPicocliTest.java | 102 ++++++++++++++++-- .../apache/solr/cli/SnapshotToolsTest.java | 5 +- solr/solr-ref-guide/build.gradle | 48 +++------ .../deployment-guide/deployment-nav.adoc | 11 +- .../deployment-guide/pages/cli/index.adoc | 11 +- .../pages/cli/solr-snapshot-create.adoc | 10 +- .../pages/cli/solr-snapshot-delete.adoc | 10 +- .../pages/cli/solr-snapshot-describe.adoc | 10 +- .../pages/cli/solr-snapshot-export.adoc | 8 +- .../pages/cli/solr-snapshot-list.adoc | 8 +- .../pages/cli/solr-snapshot.adoc | 74 +++++++++++++ .../pages/major-changes-in-solr-11.adoc | 16 +++ 28 files changed, 535 insertions(+), 161 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/cli/CollectionNameOptions.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotNameOptions.java create mode 100644 solr/core/src/java/org/apache/solr/cli/SnapshotTool.java create mode 100644 solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot.adoc diff --git a/changelog/unreleased/SOLR-18518-picocli-snapshot.yml b/changelog/unreleased/SOLR-18518-picocli-snapshot.yml index 14d0bd70221a..3a2a27ab76b8 100644 --- a/changelog/unreleased/SOLR-18518-picocli-snapshot.yml +++ b/changelog/unreleased/SOLR-18518-picocli-snapshot.yml @@ -1,6 +1,6 @@ # See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc -title: The `snapshot-create`, `snapshot-delete`, `snapshot-describe`, `snapshot-export` and `snapshot-list` commands are now available in the experimental picocli command line interface. +title: The snapshot commands are now available as `bin/solr snapshot create`, `delete`, `describe`, `export` and `list` in the experimental picocli command line interface. The `snapshot-create` and the other `snapshot-*` spellings still work there, hidden and deprecated, and are removed in Solr 12. type: added authors: - name: Serhiy Bzhezytskyy diff --git a/solr/core/src/java/org/apache/solr/cli/CollectionNameOptions.java b/solr/core/src/java/org/apache/solr/cli/CollectionNameOptions.java new file mode 100644 index 000000000000..975ef0708e23 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/CollectionNameOptions.java @@ -0,0 +1,32 @@ +/* + * 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 picocli.CommandLine; + +/** + * Picocli mixin for the {@code -c} / {@code --name} option naming the collection a command acts on. + */ +class CollectionNameOptions { + + @CommandLine.Option( + names = {"-c", "--name"}, + required = true, + paramLabel = "NAME", + description = "Name of the collection.") + String name; +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java new file mode 100644 index 000000000000..cd30149febc8 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The old top-level {@code snapshot-create} spelling of {@code bin/solr snapshot create}, kept so + * that existing scripts keep working. It is hidden from help and the reference guide. + * + * @deprecated Use {@code bin/solr snapshot create}; this spelling is removed in Solr 12. + */ +@Deprecated(since = "11.0") +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-create", + hidden = true, + description = "Deprecated; use 'snapshot create'.") +public class SnapshotCreateShim extends SnapshotCreateTool {} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java index 602154370725..5066f75f3bbd 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java @@ -26,12 +26,12 @@ /** Supports snapshot-create command in the bin/solr script. */ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( - name = "snapshot-create", + name = "create", description = "Creates a named snapshot of a collection.", footerHeading = "%nExamples:%n", footer = { " # Create a snapshot", - " bin/solr snapshot-create -c mycollection --snapshot-name snap1" + " bin/solr snapshot create -c mycollection --snapshot-name snap1" }) public class SnapshotCreateTool extends ToolBase { @@ -74,19 +74,9 @@ record SnapshotCreateParams( @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; - @picocli.CommandLine.Option( - names = {"-c", "--name"}, - required = true, - paramLabel = "NAME", - description = "Name of collection to be snapshot.") - private String nameOpt; + @picocli.CommandLine.Mixin private CollectionNameOptions collection; - @picocli.CommandLine.Option( - names = "--snapshot-name", - required = true, - paramLabel = "NAME", - description = "Name of the snapshot to produce") - private String snapshotNameOpt; + @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot; public SnapshotCreateTool() { this(new DefaultToolRuntime()); @@ -160,8 +150,8 @@ public int callTool() throws Exception { new SnapshotCreateParams( CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), credentialsOptions.credentials, - nameOpt, - snapshotNameOpt); + collection.name, + snapshot.name); createSnapshot(params); return 0; } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java new file mode 100644 index 000000000000..57ac0dc962aa --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The old top-level {@code snapshot-delete} spelling of {@code bin/solr snapshot delete}, kept so + * that existing scripts keep working. It is hidden from help and the reference guide. + * + * @deprecated Use {@code bin/solr snapshot delete}; this spelling is removed in Solr 12. + */ +@Deprecated(since = "11.0") +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-delete", + hidden = true, + description = "Deprecated; use 'snapshot delete'.") +public class SnapshotDeleteShim extends SnapshotDeleteTool {} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java index 010355b8326a..399796c9835e 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java @@ -26,12 +26,12 @@ /** Supports snapshot-delete command in the bin/solr script. */ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( - name = "snapshot-delete", + name = "delete", description = "Deletes a named snapshot of a collection.", footerHeading = "%nExamples:%n", footer = { " # Delete a snapshot", - " bin/solr snapshot-delete -c mycollection --snapshot-name snap1" + " bin/solr snapshot delete -c mycollection --snapshot-name snap1" }) public class SnapshotDeleteTool extends ToolBase { @@ -74,19 +74,9 @@ record SnapshotDeleteParams( @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; - @picocli.CommandLine.Option( - names = {"-c", "--name"}, - required = true, - paramLabel = "NAME", - description = "Name of collection to manage.") - private String nameOpt; + @picocli.CommandLine.Mixin private CollectionNameOptions collection; - @picocli.CommandLine.Option( - names = "--snapshot-name", - required = true, - paramLabel = "NAME", - description = "Name of the snapshot to delete") - private String snapshotNameOpt; + @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot; public SnapshotDeleteTool() { this(new DefaultToolRuntime()); @@ -160,8 +150,8 @@ public int callTool() throws Exception { new SnapshotDeleteParams( CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), credentialsOptions.credentials, - nameOpt, - snapshotNameOpt); + collection.name, + snapshot.name); deleteSnapshot(params); return 0; } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java new file mode 100644 index 000000000000..8d3aa924af7e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The old top-level {@code snapshot-describe} spelling of {@code bin/solr snapshot describe}, kept + * so that existing scripts keep working. It is hidden from help and the reference guide. + * + * @deprecated Use {@code bin/solr snapshot describe}; this spelling is removed in Solr 12. + */ +@Deprecated(since = "11.0") +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-describe", + hidden = true, + description = "Deprecated; use 'snapshot describe'.") +public class SnapshotDescribeShim extends SnapshotDescribeTool {} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java index f8a1d3908d52..9793903ccfc9 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java @@ -36,12 +36,12 @@ /** Supports snapshot-describe command in the bin/solr script. */ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( - name = "snapshot-describe", + name = "describe", description = "Describes a named snapshot of a collection.", footerHeading = "%nExamples:%n", footer = { " # Describe a snapshot", - " bin/solr snapshot-describe -c mycollection --snapshot-name snap1" + " bin/solr snapshot describe -c mycollection --snapshot-name snap1" }) public class SnapshotDescribeTool extends ToolBase { @@ -84,19 +84,9 @@ record SnapshotDescribeParams( @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; - @picocli.CommandLine.Option( - names = {"-c", "--name"}, - required = true, - paramLabel = "NAME", - description = "Name of collection to be snapshot.") - private String nameOpt; + @picocli.CommandLine.Mixin private CollectionNameOptions collection; - @picocli.CommandLine.Option( - names = "--snapshot-name", - required = true, - paramLabel = "NAME", - description = "Name of the snapshot to describe") - private String snapshotNameOpt; + @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot; public SnapshotDescribeTool() { this(new DefaultToolRuntime()); @@ -199,8 +189,8 @@ public int callTool() throws Exception { new SnapshotDescribeParams( CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), credentialsOptions.credentials, - nameOpt, - snapshotNameOpt); + collection.name, + snapshot.name); describeSnapshot(params); return 0; } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java new file mode 100644 index 000000000000..439e6b0884c8 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The old top-level {@code snapshot-export} spelling of {@code bin/solr snapshot export}, kept so + * that existing scripts keep working. It is hidden from help and the reference guide. + * + * @deprecated Use {@code bin/solr snapshot export}; this spelling is removed in Solr 12. + */ +@Deprecated(since = "11.0") +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-export", + hidden = true, + description = "Deprecated; use 'snapshot export'.") +public class SnapshotExportShim extends SnapshotExportTool {} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java index 1f69e846a651..e62393716311 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java @@ -30,12 +30,12 @@ /** Supports snapshot-export command in the bin/solr script. */ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( - name = "snapshot-export", + name = "export", description = "Backs up a collection's current state to a local directory.", footerHeading = "%nExamples:%n", footer = { " # Export a collection's current state as a backup", - " bin/solr snapshot-export -c mycollection --dest-dir /tmp/backups --backup-repo-name local" + " bin/solr snapshot export -c mycollection --dest-dir /tmp/backups --backup-repo-name local" }) public class SnapshotExportTool extends ToolBase { @@ -131,12 +131,7 @@ record SnapshotExportParams( @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; - @picocli.CommandLine.Option( - names = {"-c", "--name"}, - required = true, - paramLabel = "NAME", - description = "Name of the collection to be backed up.") - private String nameOpt; + @picocli.CommandLine.Mixin private CollectionNameOptions collection; // Accepted only so that passing it can be rejected with an explanation; see callTool(). @picocli.CommandLine.Option( @@ -269,7 +264,7 @@ public int callTool() throws Exception { new SnapshotExportParams( CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), credentialsOptions.credentials, - nameOpt, + collection.name, destDirOpt, backupRepoNameOpt, asyncIdOpt); diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java new file mode 100644 index 000000000000..bf4be8386cbd --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The old top-level {@code snapshot-list} spelling of {@code bin/solr snapshot list}, kept so that + * existing scripts keep working. It is hidden from help and the reference guide. + * + * @deprecated Use {@code bin/solr snapshot list}; this spelling is removed in Solr 12. + */ +@Deprecated(since = "11.0") +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + name = "snapshot-list", + hidden = true, + description = "Deprecated; use 'snapshot list'.") +public class SnapshotListShim extends SnapshotListTool {} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java index 62c3e1341599..4649215b53ef 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java @@ -28,10 +28,10 @@ /** Supports snapshot-list command in the bin/solr script. */ @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( - name = "snapshot-list", + name = "list", description = "Lists the snapshots for a collection.", footerHeading = "%nExamples:%n", - footer = {" # List a collection's snapshots", " bin/solr snapshot-list -c mycollection"}) + footer = {" # List a collection's snapshots", " bin/solr snapshot list -c mycollection"}) public class SnapshotListTool extends ToolBase { /** @@ -58,12 +58,7 @@ record SnapshotListParams(String solrUrl, String credentials, String collectionN @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions; - @picocli.CommandLine.Option( - names = {"-c", "--name"}, - required = true, - paramLabel = "NAME", - description = "Name of collection to list snapshots for.") - private String nameOpt; + @picocli.CommandLine.Mixin private CollectionNameOptions collection; public SnapshotListTool() { this(new DefaultToolRuntime()); @@ -132,7 +127,7 @@ public int callTool() throws Exception { new SnapshotListParams( CLIUtils.resolveSolrUrl(connectionOptions, credentialsOptions.credentials), credentialsOptions.credentials, - nameOpt); + collection.name); listSnapshots(params); return 0; } diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotNameOptions.java b/solr/core/src/java/org/apache/solr/cli/SnapshotNameOptions.java new file mode 100644 index 000000000000..c4a95de99767 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotNameOptions.java @@ -0,0 +1,30 @@ +/* + * 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 picocli.CommandLine; + +/** Picocli mixin for the {@code --snapshot-name} option shared by the snapshot sub-commands. */ +class SnapshotNameOptions { + + @CommandLine.Option( + names = "--snapshot-name", + required = true, + paramLabel = "NAME", + description = "Name of the snapshot.") + String name; +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotTool.java new file mode 100644 index 000000000000..6c7aba2e33af --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotTool.java @@ -0,0 +1,46 @@ +/* + * 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.concurrent.Callable; +import picocli.CommandLine; + +/** + * Sub commands for working with collection snapshots, only here to provide a common parent for the + * subcommands and print tool help. + */ +@CommandLine.Command( + name = "snapshot", + description = "Sub commands for working with collection snapshots.", + footer = "\nPass --help or -h after any COMMAND to see command-specific usage information.", + subcommands = { + SnapshotCreateTool.class, + SnapshotDeleteTool.class, + SnapshotDescribeTool.class, + SnapshotExportTool.class, + SnapshotListTool.class + }) +public class SnapshotTool implements Callable { + @CommandLine.Mixin HelpMixin helpMixin; + @CommandLine.Spec CommandLine.Model.CommandSpec spec; + + @Override + public Integer call() { + spec.commandLine().usage(CLIO.getOutStream()); + return 0; + } +} 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 66717907df47..59a2ec7f90aa 100755 --- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java @@ -81,11 +81,12 @@ AuthTool.class, CreateTool.class, DeleteTool.class, - SnapshotCreateTool.class, - SnapshotDeleteTool.class, - SnapshotDescribeTool.class, - SnapshotExportTool.class, - SnapshotListTool.class + SnapshotTool.class, + SnapshotCreateShim.class, + SnapshotDeleteShim.class, + SnapshotDescribeShim.class, + SnapshotExportShim.class, + SnapshotListShim.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java index 280b1cd4b65e..8be7c47e4594 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.java @@ -16,12 +16,10 @@ */ package org.apache.solr.cli; -import java.util.Arrays; -import picocli.CommandLine; - /** - * Runs the {@link SnapshotExportToolTest} tests that go through {@code runTool} using picocli; the - * tests that call the commons-cli parser directly are inherited and run unchanged. + * Runs the {@link SnapshotExportToolTest} tests that go through {@code runTool} using the {@code + * bin/solr snapshot export} sub-command; the tests that call the commons-cli parser directly are + * inherited and run unchanged. */ public class SnapshotExportToolPicocliTest extends SnapshotExportToolTest { @@ -29,11 +27,6 @@ public class SnapshotExportToolPicocliTest extends SnapshotExportToolTest { protected int runTool( String[] args, CLITestHelper.TestingRuntime runtime, 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); - ToolBase tool = clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime); - return new CommandLine(tool) - .setDefaultValueProvider(new CliDefaultValueProvider()) - .execute(toolArgs); + return SnapshotToolsPicocliTest.runAsGroup(args, runtime); } } diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java index ec10dde51027..b8a0b81b1687 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java @@ -16,21 +16,107 @@ */ package org.apache.solr.cli; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; import picocli.CommandLine; -/** Runs the {@link SnapshotToolsTest} tests through picocli. */ +/** + * Runs {@link SnapshotToolsTest} through picocli, using the {@code bin/solr snapshot } + * group, and checks that the old {@code snapshot-*} spellings still work as hidden, deprecated + * shims. + */ public class SnapshotToolsPicocliTest extends SnapshotToolsTest { + /** + * Builds the real root command, giving each tool the test's runtime so its output is captured. + */ + private static CommandLine root(CLITestHelper.TestingRuntime runtime) { + CommandLine.IFactory factory = + new CommandLine.IFactory() { + @Override + public K create(Class cls) throws Exception { + if (ToolBase.class.isAssignableFrom(cls)) { + try { + return cls.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime); + } catch (NoSuchMethodException e) { + // a shim: it has only the default constructor + } + } + return CommandLine.defaultFactory().create(cls); + } + }; + return new CommandLine(new SolrCLI(), factory); + } + + /** Runs a commons-cli style {@code snapshot- ...} command line as {@code snapshot ...}. */ + static int runAsGroup(String[] args, CLITestHelper.TestingRuntime runtime) { + List grouped = + new ArrayList<>(List.of("snapshot", args[0].substring("snapshot-".length()))); + grouped.addAll(List.of(args).subList(1, args.length)); + return root(runtime).execute(grouped.toArray(new String[0])); + } + @Override protected int runTool( String[] args, CLITestHelper.TestingRuntime runtime, 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); - ToolBase tool = clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime); - return new CommandLine(tool) - .setDefaultValueProvider(new CliDefaultValueProvider()) - .execute(toolArgs); + return runAsGroup(args, runtime); + } + + @Test + public void testOldSpellingsStillWork() throws Exception { + CommandLine root = root(new CLITestHelper.TestingRuntime(true)); + String url = cluster.getJettySolrRunner(0).getBaseUrl().toString(); + + assertEquals( + 0, + root.execute( + "snapshot-create", + "-c", + COLLECTION, + "--snapshot-name", + "oldSpelling", + "--solr-url", + url)); + assertTrue(run(SnapshotListTool.class, "snapshot-list").contains("oldSpelling")); + assertEquals( + 0, + root.execute( + "snapshot-delete", + "-c", + COLLECTION, + "--snapshot-name", + "oldSpelling", + "--solr-url", + url)); + assertFalse(run(SnapshotListTool.class, "snapshot-list").contains("oldSpelling")); + } + + @Test + public void testOldSpellingsAreHiddenAndDeprecated() { + CommandLine root = root(new CLITestHelper.TestingRuntime(true)); + for (String sub : List.of("create", "delete", "describe", "export", "list")) { + CommandLine shim = root.getSubcommands().get("snapshot-" + sub); + assertNotNull("snapshot-" + sub, shim); + assertTrue("snapshot-" + sub, shim.getCommandSpec().usageMessage().hidden()); + Deprecated deprecated = shim.getCommand().getClass().getAnnotation(Deprecated.class); + assertNotNull("snapshot-" + sub, deprecated); + assertEquals("11.0", deprecated.since()); + } + assertFalse(root.getUsageMessage().contains("snapshot-create")); + assertTrue(root.getUsageMessage().contains("snapshot")); + } + + @Test + public void testGroupListsItsSubCommands() { + String usage = + root(new CLITestHelper.TestingRuntime(true)) + .getSubcommands() + .get("snapshot") + .getUsageMessage(); + for (String sub : List.of("create", "delete", "describe", "export", "list")) { + assertTrue(sub + " in " + usage, usage.contains(sub)); + } } } diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java index b35a80fd0116..35945fd465d3 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java @@ -26,7 +26,7 @@ /** The snapshot-create, -list, -describe and -delete commands through the command line. */ public class SnapshotToolsTest extends SolrCloudTestCase { - private static final String COLLECTION = "snapshotToolsColl"; + static final String COLLECTION = "snapshotToolsColl"; /** Runs the tool. Overridden by the picocli variant of this test. */ protected int runTool( @@ -45,8 +45,7 @@ public static void setupCluster() throws Exception { cluster.getSolrClient().commit(COLLECTION); } - private String run(Class tool, String name, String... extra) - throws Exception { + String run(Class tool, String name, String... extra) throws Exception { List args = new ArrayList<>( List.of( diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle index 136b63321d49..80ee11363863 100644 --- a/solr/solr-ref-guide/build.gradle +++ b/solr/solr-ref-guide/build.gradle @@ -608,34 +608,17 @@ def DO_NOT_EDIT_NOTICE = """\ // ./gradlew :solr:solr-ref-guide:generateCliDocs """ -// The parent of a generated page: the longest proper prefix, on a dash boundary, that is itself a -// generated page, or null for a top-level command. A command's own name may contain a dash -// ("snapshot-create"), so the file name alone cannot say where the hierarchy ends. -String cliParentStem(String baseName, Set stems) { - for (def stem = baseName; stem.contains('-'); ) { - stem = stem.substring(0, stem.lastIndexOf('-')) - if (stems.contains(stem)) { - return stem - } - } - return null -} - // Convert a generated filename stem (e.g. "solr-zk-ls") to an Antora page title -// e.g. "solr-zk-ls" -> "bin/solr zk ls", but "solr-snapshot-create" -> "bin/solr snapshot-create" -String cliFileNameToTitle(String baseName, Set stems) { - def parent = cliParentStem(baseName, stems) - if (parent == null) { - return "bin/solr " + baseName.substring('solr-'.length()) - } - return cliFileNameToTitle(parent, stems) + " " + baseName.substring(parent.length() + 1) +// e.g. "solr-zk-ls" -> "bin/solr zk ls" +String cliFileNameToTitle(String baseName) { + def parts = baseName.split('-') + return "bin/solr " + parts.drop(1).join(' ') } -// Return the short nav label for a command file: its name below its parent page -// e.g. "solr-zk-ls" -> "ls", "solr-snapshot-create" -> "snapshot-create" -String cliFileNameToNavLabel(String baseName, Set stems) { - def parent = cliParentStem(baseName, stems) - return baseName.substring((parent == null ? 'solr'.length() : parent.length()) + 1) +// Return the short nav label for a command file (last segment of dash-separated name) +// e.g. "solr-zk-ls" -> "ls" +String cliFileNameToNavLabel(String baseName) { + return baseName.split('-').last() } // Strip "[...]" (optional groups) and "(...)" (required groups whose members @@ -724,10 +707,10 @@ String markRequiredOptionsAndArgs(String content) { // Post-process a raw ManPageGenerator AsciiDoc file for Antora compatibility. // The title lives inside the man-section-header block, so we replace the whole // header block with an Antora-compatible page title + attributes. -String postProcessCliManPage(File rawFile, Set stems, String asfHeader, String doNotEditNotice, String experimentalNotice) { +String postProcessCliManPage(File rawFile, String asfHeader, String doNotEditNotice, String experimentalNotice) { def content = rawFile.text def baseName = rawFile.name.replace('.adoc', '') - def newTitle = cliFileNameToTitle(baseName, stems) + def newTitle = cliFileNameToTitle(baseName) // Replace the entire man-section-header block (which contains :doctype:manpage, the page title // "= solr-start(1)", etc.) with our Antora-compatible title and page attributes. @@ -793,10 +776,9 @@ String buildCliCommandTable(File cliDir) { def files = ((cliDir.listFiles() ?: []) as List) .findAll { it.name.startsWith('solr-') && it.name.endsWith('.adoc') } .sort { it.name } - def stems = files.collect { it.name.replace('.adoc', '') } as Set def rows = files.collect { file -> def baseName = file.name.replace('.adoc', '') - "|xref:cli/${file.name}[${cliFileNameToTitle(baseName, stems)}]" + "|xref:cli/${file.name}[${cliFileNameToTitle(baseName)}]" } return "[cols=\"1\",options=\"header\"]\n|===\n|Command\n\n" + rows.join('\n') + "\n|===" } @@ -811,7 +793,7 @@ List buildCliNavEntries(File cliDir) { def stems = files.collect { it.name.replace('.adoc', '') } as Set return files.collect { file -> def baseName = file.name.replace('.adoc', '') - def label = cliFileNameToNavLabel(baseName, stems) + def label = cliFileNameToNavLabel(baseName) // Nest under the deepest ancestor that is itself a generated page. Counting dashes instead // would mis-nest a command whose own name contains one, e.g. a future "snapshot-create" // would be filed under a "solr-snapshot" page that does not exist. @@ -874,9 +856,8 @@ task generateCliDocs { def rawFiles = runManPageGenerator(rawOutputDir) - def rawStems = rawFiles.collect { it.name.replace('.adoc', '') } as Set rawFiles.each { rawFile -> - def processed = postProcessCliManPage(rawFile, rawStems, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) + def processed = postProcessCliManPage(rawFile, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) new File(destDir, rawFile.name).text = processed } @@ -931,9 +912,8 @@ task checkCliDocsUpToDate { def rawFiles = runManPageGenerator(rawOutputDir) def issues = [] - def rawStems = rawFiles.collect { it.name.replace('.adoc', '') } as Set rawFiles.each { rawFile -> - def processed = postProcessCliManPage(rawFile, rawStems, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) + def processed = postProcessCliManPage(rawFile, ASF_LICENSE_HEADER, DO_NOT_EDIT_NOTICE, EXPERIMENTAL_NOTICE) def committed = new File(committedDir, rawFile.name) if (!committed.exists()) { issues << "MISSING committed file (new command): ${rawFile.name}" 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 6883760d36b1..cc31698f01e4 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc @@ -23,11 +23,12 @@ *** xref:cli/solr-auth.adoc[auth] *** xref:cli/solr-create.adoc[create] *** xref:cli/solr-delete.adoc[delete] -*** xref:cli/solr-snapshot-create.adoc[snapshot-create] -*** xref:cli/solr-snapshot-delete.adoc[snapshot-delete] -*** xref:cli/solr-snapshot-describe.adoc[snapshot-describe] -*** xref:cli/solr-snapshot-export.adoc[snapshot-export] -*** xref:cli/solr-snapshot-list.adoc[snapshot-list] +*** xref:cli/solr-snapshot.adoc[snapshot] +**** xref:cli/solr-snapshot-create.adoc[create] +**** xref:cli/solr-snapshot-delete.adoc[delete] +**** xref:cli/solr-snapshot-describe.adoc[describe] +**** xref:cli/solr-snapshot-export.adoc[export] +**** xref:cli/solr-snapshot-list.adoc[list] *** 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 5d2c1ed44cf7..7387af332056 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,11 +62,12 @@ 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-snapshot-create.adoc[bin/solr snapshot-create] -|xref:cli/solr-snapshot-delete.adoc[bin/solr snapshot-delete] -|xref:cli/solr-snapshot-describe.adoc[bin/solr snapshot-describe] -|xref:cli/solr-snapshot-export.adoc[bin/solr snapshot-export] -|xref:cli/solr-snapshot-list.adoc[bin/solr snapshot-list] +|xref:cli/solr-snapshot-create.adoc[bin/solr snapshot create] +|xref:cli/solr-snapshot-delete.adoc[bin/solr snapshot delete] +|xref:cli/solr-snapshot-describe.adoc[bin/solr snapshot describe] +|xref:cli/solr-snapshot-export.adoc[bin/solr snapshot export] +|xref:cli/solr-snapshot-list.adoc[bin/solr snapshot list] +|xref:cli/solr-snapshot.adoc[bin/solr snapshot] |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-snapshot-create.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc index 717dc9b35a78..e4b1b14c008d 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-create.adoc @@ -19,7 +19,7 @@ // To update: modify the @Command/@Option annotations in the Java source, then run: // ./gradlew :solr:solr-ref-guide:generateCliDocs -= bin/solr snapshot-create += bin/solr snapshot create :page-toclevels: 2 [WARNING] @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot-create [-hv] -c=NAME --snapshot-name=NAME [-u=] +bin/solr snapshot create [-hv] -c=NAME --snapshot-name=NAME [-u=] [-s= | --solr-url= | -z=] .... @@ -50,7 +50,7 @@ Creates a named snapshot of a collection. == Options *-c*, *--name*=_NAME_:: - *(required)* Name of collection to be snapshot. + *(required)* Name of the collection. *-h*, *--help*:: Print this help message and exit. @@ -59,7 +59,7 @@ Creates a named snapshot of a collection. Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is defined in solr.in.sh; otherwise, defaults to localhost:9983. *--snapshot-name*=_NAME_:: - *(required)* Name of the snapshot to produce + *(required)* Name of the snapshot. *--solr-url*=__:: Base Solr URL, which can be used to determine the zk-host if that's not known. @@ -79,6 +79,6 @@ Creates a named snapshot of a collection. == Examples [%hardbreaks] # Create a snapshot - bin/solr snapshot-create -c mycollection --snapshot-name snap1 + bin/solr snapshot create -c mycollection --snapshot-name snap1 // end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc index d11fa9401828..eadf9e84b7c4 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-delete.adoc @@ -19,7 +19,7 @@ // To update: modify the @Command/@Option annotations in the Java source, then run: // ./gradlew :solr:solr-ref-guide:generateCliDocs -= bin/solr snapshot-delete += bin/solr snapshot delete :page-toclevels: 2 [WARNING] @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot-delete [-hv] -c=NAME --snapshot-name=NAME [-u=] +bin/solr snapshot delete [-hv] -c=NAME --snapshot-name=NAME [-u=] [-s= | --solr-url= | -z=] .... @@ -50,7 +50,7 @@ Deletes a named snapshot of a collection. == Options *-c*, *--name*=_NAME_:: - *(required)* Name of collection to manage. + *(required)* Name of the collection. *-h*, *--help*:: Print this help message and exit. @@ -59,7 +59,7 @@ Deletes a named snapshot of a collection. Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is defined in solr.in.sh; otherwise, defaults to localhost:9983. *--snapshot-name*=_NAME_:: - *(required)* Name of the snapshot to delete + *(required)* Name of the snapshot. *--solr-url*=__:: Base Solr URL, which can be used to determine the zk-host if that's not known. @@ -79,6 +79,6 @@ Deletes a named snapshot of a collection. == Examples [%hardbreaks] # Delete a snapshot - bin/solr snapshot-delete -c mycollection --snapshot-name snap1 + bin/solr snapshot delete -c mycollection --snapshot-name snap1 // end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc index 32a2295c064a..11e6a1a31ce0 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-describe.adoc @@ -19,7 +19,7 @@ // To update: modify the @Command/@Option annotations in the Java source, then run: // ./gradlew :solr:solr-ref-guide:generateCliDocs -= bin/solr snapshot-describe += bin/solr snapshot describe :page-toclevels: 2 [WARNING] @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot-describe [-hv] -c=NAME --snapshot-name=NAME [-u=] +bin/solr snapshot describe [-hv] -c=NAME --snapshot-name=NAME [-u=] [-s= | --solr-url= | -z=] .... @@ -51,7 +51,7 @@ Describes a named snapshot of a collection. == Options *-c*, *--name*=_NAME_:: - *(required)* Name of collection to be snapshot. + *(required)* Name of the collection. *-h*, *--help*:: Print this help message and exit. @@ -60,7 +60,7 @@ Describes a named snapshot of a collection. Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is defined in solr.in.sh; otherwise, defaults to localhost:9983. *--snapshot-name*=_NAME_:: - *(required)* Name of the snapshot to describe + *(required)* Name of the snapshot. *--solr-url*=__:: Base Solr URL, which can be used to determine the zk-host if that's not known. @@ -80,6 +80,6 @@ Describes a named snapshot of a collection. == Examples [%hardbreaks] # Describe a snapshot - bin/solr snapshot-describe -c mycollection --snapshot-name snap1 + bin/solr snapshot describe -c mycollection --snapshot-name snap1 // end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc index 857a810777e0..311b201fea00 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc @@ -19,7 +19,7 @@ // To update: modify the @Command/@Option annotations in the Java source, then run: // ./gradlew :solr:solr-ref-guide:generateCliDocs -= bin/solr snapshot-export += bin/solr snapshot export :page-toclevels: 2 [WARNING] @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot-export [-hv] [--async-id=ID] [--backup-repo-name=DIR] -c=NAME +bin/solr snapshot export [-hv] [--async-id=ID] [--backup-repo-name=DIR] -c=NAME --dest-dir=DIR [--snapshot-name=NAME] [-u=] [-s= | --solr-url= | -z=] .... @@ -57,7 +57,7 @@ Backs up a collection's current state to a local directory. Specifies name of the backup repository to be used during snapshot export preparation. *-c*, *--name*=_NAME_:: - *(required)* Name of the collection to be backed up. + *(required)* Name of the collection. *--dest-dir*=_DIR_:: *(required)* Path of a temporary directory on local filesystem during snapshot export command. @@ -89,6 +89,6 @@ Backs up a collection's current state to a local directory. == Examples [%hardbreaks] # Export a collection's current state as a backup - bin/solr snapshot-export -c mycollection --dest-dir /tmp/backups --backup-repo-name local + bin/solr snapshot export -c mycollection --dest-dir /tmp/backups --backup-repo-name local // end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc index b51d37569384..d0139e4520aa 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-list.adoc @@ -19,7 +19,7 @@ // To update: modify the @Command/@Option annotations in the Java source, then run: // ./gradlew :solr:solr-ref-guide:generateCliDocs -= bin/solr snapshot-list += bin/solr snapshot list :page-toclevels: 2 [WARNING] @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot-list [-hv] -c=NAME [-u=] [-s= | +bin/solr snapshot list [-hv] -c=NAME [-u=] [-s= | --solr-url= | -z=] .... @@ -50,7 +50,7 @@ Lists the snapshots for a collection. == Options *-c*, *--name*=_NAME_:: - *(required)* Name of collection to list snapshots for. + *(required)* Name of the collection. *-h*, *--help*:: Print this help message and exit. @@ -76,6 +76,6 @@ Lists the snapshots for a collection. == Examples [%hardbreaks] # List a collection's snapshots - bin/solr snapshot-list -c mycollection + bin/solr snapshot list -c mycollection // end::picocli-generated-man-section-footer[] diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot.adoc new file mode 100644 index 000000000000..f0b674d06788 --- /dev/null +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot.adoc @@ -0,0 +1,74 @@ +// 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 snapshot +: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 snapshot [-h] [COMMAND] +.... + +// end::picocli-generated-man-section-synopsis[] + +// tag::picocli-generated-man-section-description[] +== Description + +Sub commands for working with collection snapshots. + +// end::picocli-generated-man-section-description[] + +// tag::picocli-generated-man-section-options[] +== Options + +*-h*, *--help*:: + Print this help message and exit. + +// end::picocli-generated-man-section-options[] + +// tag::picocli-generated-man-section-commands[] +== Commands + +xref:cli/solr-snapshot-create.adoc[*create*]:: + Creates a named snapshot of a collection. + +xref:cli/solr-snapshot-delete.adoc[*delete*]:: + Deletes a named snapshot of a collection. + +xref:cli/solr-snapshot-describe.adoc[*describe*]:: + Describes a named snapshot of a collection. + +xref:cli/solr-snapshot-export.adoc[*export*]:: + Backs up a collection's current state to a local directory. + +xref:cli/solr-snapshot-list.adoc[*list*]:: + Lists the snapshots for a collection. + +// end::picocli-generated-man-section-commands[] diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc index 0372e4b820b8..56885a4abd05 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc @@ -25,6 +25,22 @@ This page highlights the most important changes including new features and chang Before starting an upgrade to this version of Solr, please be sure to review all information about changes from the version you are currently on up to this one, to include the minor version number changes as well. For example, if you are currently using Solr 10.1, you should review changes made in all subsequent 10.x releases in addition to the 11.0-specific changes on this page. +== Deprecated Features + +=== Snapshot commands are now `bin/solr snapshot` sub-commands + +The `snapshot-create`, `snapshot-delete`, `snapshot-describe`, `snapshot-export` and `snapshot-list` commands of the experimental picocli command line interface are replaced by the sub-commands of `bin/solr snapshot`: + +[source,bash] +---- +bin/solr snapshot create -c mycollection --snapshot-name snap1 +bin/solr snapshot list -c mycollection +---- + +The old spellings still work and are hidden from the help and from this guide. +They are deprecated in Solr 11.0 and are removed in Solr 12.0. +The default command line interface is not changed. + == Removed Features === Overseer Roles API From 1ec61b24adfca84e22a6458db60a67cd29b6a0f2 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 15:43:32 +0300 Subject: [PATCH 3/5] SOLR-18518: 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-18518-picocli-snapshot.yml | 9 --------- 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 changelog/unreleased/SOLR-18518-picocli-snapshot.yml diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml index 85cdf1fd90d4..5b6b437950ef 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-18518 + url: https://issues.apache.org/jira/browse/SOLR-18518 diff --git a/changelog/unreleased/SOLR-18518-picocli-snapshot.yml b/changelog/unreleased/SOLR-18518-picocli-snapshot.yml deleted file mode 100644 index 3a2a27ab76b8..000000000000 --- a/changelog/unreleased/SOLR-18518-picocli-snapshot.yml +++ /dev/null @@ -1,9 +0,0 @@ -# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc - -title: The snapshot commands are now available as `bin/solr snapshot create`, `delete`, `describe`, `export` and `list` in the experimental picocli command line interface. The `snapshot-create` and the other `snapshot-*` spellings still work there, hidden and deprecated, and are removed in Solr 12. -type: added -authors: - - name: Serhiy Bzhezytskyy -links: - - name: SOLR-18518 - url: https://issues.apache.org/jira/browse/SOLR-18518 From f7b468e9e656d277227216cc854581dc31550018 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 17:12:17 +0300 Subject: [PATCH 4/5] SOLR-18518: Deprecate the snapshot-* shims in 10.2 and say so when they run The change lands in 10.2, so the shims are deprecated since 10.2 and removed in 11.0, and the upgrade note moves to the 10.2 section of major-changes-in-solr-10.adoc. A shim logs one deprecation notice naming the new command, because it is otherwise silent. --backup-repo-name takes a repository name, which the synopsis now shows. --- .../apache/solr/cli/SnapshotCreateShim.java | 17 ++++++++-- .../apache/solr/cli/SnapshotDeleteShim.java | 17 ++++++++-- .../apache/solr/cli/SnapshotDescribeShim.java | 17 ++++++++-- .../apache/solr/cli/SnapshotExportShim.java | 17 ++++++++-- .../apache/solr/cli/SnapshotExportTool.java | 2 +- .../org/apache/solr/cli/SnapshotListShim.java | 17 ++++++++-- .../solr/cli/SnapshotToolsPicocliTest.java | 33 ++++++++++++------- .../pages/cli/solr-snapshot-export.adoc | 4 +-- .../pages/major-changes-in-solr-10.adoc | 16 +++++++++ .../pages/major-changes-in-solr-11.adoc | 16 --------- 10 files changed, 111 insertions(+), 45 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java index cd30149febc8..22b55a6da091 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java @@ -16,16 +16,27 @@ */ package org.apache.solr.cli; +import org.apache.solr.logging.DeprecationLog; + /** * The old top-level {@code snapshot-create} spelling of {@code bin/solr snapshot create}, kept so * that existing scripts keep working. It is hidden from help and the reference guide. * - * @deprecated Use {@code bin/solr snapshot create}; this spelling is removed in Solr 12. + * @deprecated Use {@code bin/solr snapshot create}; this spelling is removed in Solr 11. */ -@Deprecated(since = "11.0") +@Deprecated(since = "10.2") @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "snapshot-create", hidden = true, description = "Deprecated; use 'snapshot create'.") -public class SnapshotCreateShim extends SnapshotCreateTool {} +public class SnapshotCreateShim extends SnapshotCreateTool { + + @Override + public int callTool() throws Exception { + DeprecationLog.log( + "cli.snapshot-create", + "'bin/solr snapshot-create' is deprecated and will be removed in Solr 11; use 'bin/solr snapshot create'."); + return super.callTool(); + } +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java index 57ac0dc962aa..9834a17d6681 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java @@ -16,16 +16,27 @@ */ package org.apache.solr.cli; +import org.apache.solr.logging.DeprecationLog; + /** * The old top-level {@code snapshot-delete} spelling of {@code bin/solr snapshot delete}, kept so * that existing scripts keep working. It is hidden from help and the reference guide. * - * @deprecated Use {@code bin/solr snapshot delete}; this spelling is removed in Solr 12. + * @deprecated Use {@code bin/solr snapshot delete}; this spelling is removed in Solr 11. */ -@Deprecated(since = "11.0") +@Deprecated(since = "10.2") @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "snapshot-delete", hidden = true, description = "Deprecated; use 'snapshot delete'.") -public class SnapshotDeleteShim extends SnapshotDeleteTool {} +public class SnapshotDeleteShim extends SnapshotDeleteTool { + + @Override + public int callTool() throws Exception { + DeprecationLog.log( + "cli.snapshot-delete", + "'bin/solr snapshot-delete' is deprecated and will be removed in Solr 11; use 'bin/solr snapshot delete'."); + return super.callTool(); + } +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java index 8d3aa924af7e..8426c8754849 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java @@ -16,16 +16,27 @@ */ package org.apache.solr.cli; +import org.apache.solr.logging.DeprecationLog; + /** * The old top-level {@code snapshot-describe} spelling of {@code bin/solr snapshot describe}, kept * so that existing scripts keep working. It is hidden from help and the reference guide. * - * @deprecated Use {@code bin/solr snapshot describe}; this spelling is removed in Solr 12. + * @deprecated Use {@code bin/solr snapshot describe}; this spelling is removed in Solr 11. */ -@Deprecated(since = "11.0") +@Deprecated(since = "10.2") @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "snapshot-describe", hidden = true, description = "Deprecated; use 'snapshot describe'.") -public class SnapshotDescribeShim extends SnapshotDescribeTool {} +public class SnapshotDescribeShim extends SnapshotDescribeTool { + + @Override + public int callTool() throws Exception { + DeprecationLog.log( + "cli.snapshot-describe", + "'bin/solr snapshot-describe' is deprecated and will be removed in Solr 11; use 'bin/solr snapshot describe'."); + return super.callTool(); + } +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java index 439e6b0884c8..4cb77cf05f24 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java @@ -16,16 +16,27 @@ */ package org.apache.solr.cli; +import org.apache.solr.logging.DeprecationLog; + /** * The old top-level {@code snapshot-export} spelling of {@code bin/solr snapshot export}, kept so * that existing scripts keep working. It is hidden from help and the reference guide. * - * @deprecated Use {@code bin/solr snapshot export}; this spelling is removed in Solr 12. + * @deprecated Use {@code bin/solr snapshot export}; this spelling is removed in Solr 11. */ -@Deprecated(since = "11.0") +@Deprecated(since = "10.2") @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "snapshot-export", hidden = true, description = "Deprecated; use 'snapshot export'.") -public class SnapshotExportShim extends SnapshotExportTool {} +public class SnapshotExportShim extends SnapshotExportTool { + + @Override + public int callTool() throws Exception { + DeprecationLog.log( + "cli.snapshot-export", + "'bin/solr snapshot-export' is deprecated and will be removed in Solr 11; use 'bin/solr snapshot export'."); + return super.callTool(); + } +} diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java index e62393716311..06b8017c1ed3 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportTool.java @@ -150,7 +150,7 @@ record SnapshotExportParams( @picocli.CommandLine.Option( names = "--backup-repo-name", - paramLabel = "DIR", + paramLabel = "NAME", description = "Specifies name of the backup repository to be used during snapshot export preparation.") private String backupRepoNameOpt; diff --git a/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java b/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java index bf4be8386cbd..4085ead8ff05 100644 --- a/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java +++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java @@ -16,16 +16,27 @@ */ package org.apache.solr.cli; +import org.apache.solr.logging.DeprecationLog; + /** * The old top-level {@code snapshot-list} spelling of {@code bin/solr snapshot list}, kept so that * existing scripts keep working. It is hidden from help and the reference guide. * - * @deprecated Use {@code bin/solr snapshot list}; this spelling is removed in Solr 12. + * @deprecated Use {@code bin/solr snapshot list}; this spelling is removed in Solr 11. */ -@Deprecated(since = "11.0") +@Deprecated(since = "10.2") @SuppressWarnings("UnnecessarilyFullyQualified") @picocli.CommandLine.Command( name = "snapshot-list", hidden = true, description = "Deprecated; use 'snapshot list'.") -public class SnapshotListShim extends SnapshotListTool {} +public class SnapshotListShim extends SnapshotListTool { + + @Override + public int callTool() throws Exception { + DeprecationLog.log( + "cli.snapshot-list", + "'bin/solr snapshot-list' is deprecated and will be removed in Solr 11; use 'bin/solr snapshot list'."); + return super.callTool(); + } +} diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java index b8a0b81b1687..c6366f618794 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.List; +import org.apache.solr.logging.DeprecationLog; +import org.apache.solr.util.LogListener; import org.junit.Test; import picocli.CommandLine; @@ -69,16 +71,23 @@ public void testOldSpellingsStillWork() throws Exception { CommandLine root = root(new CLITestHelper.TestingRuntime(true)); String url = cluster.getJettySolrRunner(0).getBaseUrl().toString(); - assertEquals( - 0, - root.execute( - "snapshot-create", - "-c", - COLLECTION, - "--snapshot-name", - "oldSpelling", - "--solr-url", - url)); + // the notice is logged once per JVM, and this is the only test that runs the old spellings + try (LogListener deprecation = + LogListener.warn(DeprecationLog.LOG_PREFIX + "cli.snapshot-create")) { + assertEquals( + 0, + root.execute( + "snapshot-create", + "-c", + COLLECTION, + "--snapshot-name", + "oldSpelling", + "--solr-url", + url)); + String notice = deprecation.pollMessage(); + assertNotNull("a deprecation notice is logged", notice); + assertTrue(notice, notice.contains("bin/solr snapshot create")); + } assertTrue(run(SnapshotListTool.class, "snapshot-list").contains("oldSpelling")); assertEquals( 0, @@ -93,6 +102,8 @@ public void testOldSpellingsStillWork() throws Exception { assertFalse(run(SnapshotListTool.class, "snapshot-list").contains("oldSpelling")); } + /** Goes with the shims, which are removed in Solr 11. */ + @Deprecated @Test public void testOldSpellingsAreHiddenAndDeprecated() { CommandLine root = root(new CLITestHelper.TestingRuntime(true)); @@ -102,7 +113,7 @@ public void testOldSpellingsAreHiddenAndDeprecated() { assertTrue("snapshot-" + sub, shim.getCommandSpec().usageMessage().hidden()); Deprecated deprecated = shim.getCommand().getClass().getAnnotation(Deprecated.class); assertNotNull("snapshot-" + sub, deprecated); - assertEquals("11.0", deprecated.since()); + assertEquals("10.2", deprecated.since()); } assertFalse(root.getUsageMessage().contains("snapshot-create")); assertTrue(root.getUsageMessage().contains("snapshot")); diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc index 311b201fea00..606617e3f3c6 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-snapshot-export.adoc @@ -33,7 +33,7 @@ See xref:cli/index.adoc[] to enable this implementation and for its known limita == Synopsis .... -bin/solr snapshot export [-hv] [--async-id=ID] [--backup-repo-name=DIR] -c=NAME +bin/solr snapshot export [-hv] [--async-id=ID] [--backup-repo-name=NAME] -c=NAME --dest-dir=DIR [--snapshot-name=NAME] [-u=] [-s= | --solr-url= | -z=] .... @@ -53,7 +53,7 @@ Backs up a collection's current state to a local directory. *--async-id*=_ID_:: Specifies the async request identifier to be used during snapshot export preparation. -*--backup-repo-name*=_DIR_:: +*--backup-repo-name*=_NAME_:: Specifies name of the backup repository to be used during snapshot export preparation. *-c*, *--name*=_NAME_:: diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc index 3a5a236542a9..d8450340a57b 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc @@ -34,6 +34,22 @@ This compatibility safeguard can be disabled via the environment variable `SOLR_ Solr 10.0 requires at least Java 21, while SolrJ 10.0 requires at least Java 17. +== Solr 10.2 + +=== Snapshot commands are now `bin/solr snapshot` sub-commands + +The `snapshot-create`, `snapshot-delete`, `snapshot-describe`, `snapshot-export` and `snapshot-list` commands of the experimental picocli command line interface are replaced by the sub-commands of `bin/solr snapshot`: + +[source,bash] +---- +bin/solr snapshot create -c mycollection --snapshot-name snap1 +bin/solr snapshot list -c mycollection +---- + +The old spellings still work and are hidden from the help and from this guide. +They are deprecated in Solr 10.2 and are removed in Solr 11.0. +The default command line interface is not changed. + == Solr 10.1 === Misc diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc index 56885a4abd05..0372e4b820b8 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc @@ -25,22 +25,6 @@ This page highlights the most important changes including new features and chang Before starting an upgrade to this version of Solr, please be sure to review all information about changes from the version you are currently on up to this one, to include the minor version number changes as well. For example, if you are currently using Solr 10.1, you should review changes made in all subsequent 10.x releases in addition to the 11.0-specific changes on this page. -== Deprecated Features - -=== Snapshot commands are now `bin/solr snapshot` sub-commands - -The `snapshot-create`, `snapshot-delete`, `snapshot-describe`, `snapshot-export` and `snapshot-list` commands of the experimental picocli command line interface are replaced by the sub-commands of `bin/solr snapshot`: - -[source,bash] ----- -bin/solr snapshot create -c mycollection --snapshot-name snap1 -bin/solr snapshot list -c mycollection ----- - -The old spellings still work and are hidden from the help and from this guide. -They are deprecated in Solr 11.0 and are removed in Solr 12.0. -The default command line interface is not changed. - == Removed Features === Overseer Roles API From a5dfb399f974b8ef3a74d88a611348f41e70d852 Mon Sep 17 00:00:00 2001 From: Serhiy Bzhezytskyy Date: Wed, 7 Oct 2026 18:07:20 +0300 Subject: [PATCH 5/5] SOLR-18518: Read SOLR_CONNECTION as the solr.connection property EnvUtils maps the SOLR_CONNECTION environment variable to solr.connection, so the solr-connection key the shared connection lookup of the snapshot commands read could never be set. The picocli path now reads the right key, which a test covers, and the zkHost test drops its own property cleanup because the base class restores properties. --- .../src/java/org/apache/solr/cli/CLIUtils.java | 2 +- .../apache/solr/cli/SnapshotToolsPicocliTest.java | 14 ++++++++++++++ .../org/apache/solr/cli/SnapshotToolsTest.java | 15 +++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 6fa7fcbd6e81..34743e5976d3 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -261,7 +261,7 @@ static String resolveSolrUrl(ConnectionOptions connectionOptions, String credent String solrConnection = (connectionOptions != null && connectionOptions.solrConnection != null) ? connectionOptions.solrConnection - : EnvUtils.getProperty("solr-connection"); + : EnvUtils.getProperty("solr.connection"); String zkHost = (connectionOptions != null && connectionOptions.zkHost != null) ? connectionOptions.zkHost diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java index c6366f618794..649c5185cf97 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java @@ -66,6 +66,20 @@ protected int runTool( return runAsGroup(args, runtime); } + @Test + public void testConnectionFallsBackToTheSolrConnectionProperty() throws Exception { + // SOLR_CONNECTION reaches the JVM as the solr.connection property + run(SnapshotCreateTool.class, "snapshot-create", "--snapshot-name", "snapViaConnection"); + + System.setProperty("solr.connection", cluster.getZkClient().getZkServerAddress()); + CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); + assertEquals( + 0, + runTool(new String[] {"snapshot-list", "-c", COLLECTION}, runtime, SnapshotListTool.class)); + assertTrue(runtime.getOutput(), runtime.getOutput().contains("snapViaConnection")); + run(SnapshotDeleteTool.class, "snapshot-delete", "--snapshot-name", "snapViaConnection"); + } + @Test public void testOldSpellingsStillWork() throws Exception { CommandLine root = root(new CLITestHelper.TestingRuntime(true)); diff --git a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java index 35945fd465d3..5389dfe1573e 100644 --- a/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java +++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java @@ -81,16 +81,11 @@ public void testConnectionFallsBackToTheZkHostProperty() throws Exception { run(SnapshotCreateTool.class, "snapshot-create", "--snapshot-name", "snapViaProperty"); System.setProperty("zkHost", cluster.getZkClient().getZkServerAddress()); - try { - CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); - assertEquals( - 0, - runTool( - new String[] {"snapshot-list", "-c", COLLECTION}, runtime, SnapshotListTool.class)); - assertTrue(runtime.getOutput(), runtime.getOutput().contains("snapViaProperty")); - } finally { - System.clearProperty("zkHost"); - } + CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); + assertEquals( + 0, + runTool(new String[] {"snapshot-list", "-c", COLLECTION}, runtime, SnapshotListTool.class)); + assertTrue(runtime.getOutput(), runtime.getOutput().contains("snapViaProperty")); run(SnapshotDeleteTool.class, "snapshot-delete", "--snapshot-name", "snapViaProperty"); } }