This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new ef23d0f8ad2 SOLR-18518: Port the snapshot commands to picocli (#5044)
ef23d0f8ad2 is described below
commit ef23d0f8ad23168a46da5b257498a76d07a8868c
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Thu Oct 8 19:21:36 2026 +0300
SOLR-18518: Port the snapshot commands to picocli (#5044)
Co-authored-by: Jan Høydahl <[email protected]>
---
.../SOLR-17697-picocli-experimental-cli.yml | 2 +
.../src/java/org/apache/solr/cli/CLIUtils.java | 42 ++++++
.../org/apache/solr/cli/CollectionNameOptions.java | 32 +++++
.../org/apache/solr/cli/SnapshotCreateShim.java | 42 ++++++
.../org/apache/solr/cli/SnapshotCreateTool.java | 43 +++++-
.../org/apache/solr/cli/SnapshotDeleteShim.java | 42 ++++++
.../org/apache/solr/cli/SnapshotDeleteTool.java | 43 +++++-
.../org/apache/solr/cli/SnapshotDescribeShim.java | 42 ++++++
.../org/apache/solr/cli/SnapshotDescribeTool.java | 43 +++++-
.../org/apache/solr/cli/SnapshotExportShim.java | 42 ++++++
.../org/apache/solr/cli/SnapshotExportTool.java | 93 ++++++++++++-
.../java/org/apache/solr/cli/SnapshotListShim.java | 42 ++++++
.../java/org/apache/solr/cli/SnapshotListTool.java | 32 ++++-
.../org/apache/solr/cli/SnapshotNameOptions.java | 30 +++++
.../src/java/org/apache/solr/cli/SnapshotTool.java | 46 +++++++
.../core/src/java/org/apache/solr/cli/SolrCLI.java | 8 +-
.../solr/cli/SnapshotExportToolPicocliTest.java | 32 +++++
.../apache/solr/cli/SnapshotExportToolTest.java | 32 ++++-
.../apache/solr/cli/SnapshotToolsPicocliTest.java | 147 +++++++++++++++++++++
.../org/apache/solr/cli/SnapshotToolsTest.java | 91 +++++++++++++
.../modules/deployment-guide/deployment-nav.adoc | 6 +
.../modules/deployment-guide/pages/cli/index.adoc | 6 +
.../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 ++++++++++++
.../deployment-guide/pages/cli/solr-snapshot.adoc | 74 +++++++++++
.../pages/major-changes-in-solr-10.adoc | 16 +++
29 files changed, 1449 insertions(+), 7 deletions(-)
diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
index 58cd2b4142a..5037bd6b2df 100644
--- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
+++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
@@ -27,3 +27,5 @@ links:
url: https://issues.apache.org/jira/browse/SOLR-18516
- name: SOLR-18519
url: https://issues.apache.org/jira/browse/SOLR-18519
+ - 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 f60d03c3574..3c5d2a98ad6 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 final class CLIUtils {
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/CollectionNameOptions.java
b/solr/core/src/java/org/apache/solr/cli/CollectionNameOptions.java
new file mode 100644
index 00000000000..975ef0708e2
--- /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 00000000000..22b55a6da09
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateShim.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 11.
+ */
+@Deprecated(since = "10.2")
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "snapshot-create",
+ hidden = true,
+ description = "Deprecated; use 'snapshot create'.")
+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/SnapshotCreateTool.java
b/solr/core/src/java/org/apache/solr/cli/SnapshotCreateTool.java
index 89263c9da2c..5066f75f3bb 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.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
/** Supports snapshot-create command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ 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"
+ })
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,21 @@ 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.Mixin private CollectionNameOptions collection;
+
+ @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot;
+
+ public SnapshotCreateTool() {
+ this(new DefaultToolRuntime());
+ }
+
public SnapshotCreateTool(ToolRuntime runtime) {
super(runtime);
}
@@ -112,6 +146,13 @@ public class SnapshotCreateTool extends ToolBase {
@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,
+ 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 00000000000..9834a17d668
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteShim.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 11.
+ */
+@Deprecated(since = "10.2")
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "snapshot-delete",
+ hidden = true,
+ description = "Deprecated; use 'snapshot delete'.")
+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/SnapshotDeleteTool.java
b/solr/core/src/java/org/apache/solr/cli/SnapshotDeleteTool.java
index 7ff35e5e95a..399796c9835 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.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
/** Supports snapshot-delete command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ 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"
+ })
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,21 @@ 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.Mixin private CollectionNameOptions collection;
+
+ @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot;
+
+ public SnapshotDeleteTool() {
+ this(new DefaultToolRuntime());
+ }
+
public SnapshotDeleteTool(ToolRuntime runtime) {
super(runtime);
}
@@ -112,6 +146,13 @@ public class SnapshotDeleteTool extends ToolBase {
@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,
+ 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 00000000000..8426c875484
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeShim.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 11.
+ */
+@Deprecated(since = "10.2")
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "snapshot-describe",
+ hidden = true,
+ description = "Deprecated; use 'snapshot describe'.")
+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/SnapshotDescribeTool.java
b/solr/core/src/java/org/apache/solr/cli/SnapshotDescribeTool.java
index d945c23d57f..9793903ccfc 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.CollectionSnapshotMetaData;
import org.apache.solr.core.snapshots.SolrSnapshotManager;
/** Supports snapshot-describe command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ 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"
+ })
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,21 @@ 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.Mixin private CollectionNameOptions collection;
+
+ @picocli.CommandLine.Mixin private SnapshotNameOptions snapshot;
+
+ public SnapshotDescribeTool() {
+ this(new DefaultToolRuntime());
+ }
+
public SnapshotDescribeTool(ToolRuntime runtime) {
super(runtime);
}
@@ -151,6 +185,13 @@ public class SnapshotDescribeTool extends ToolBase {
@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,
+ 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 00000000000..4cb77cf05f2
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cli/SnapshotExportShim.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 11.
+ */
+@Deprecated(since = "10.2")
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "snapshot-export",
+ hidden = true,
+ description = "Deprecated; use 'snapshot export'.")
+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 8544122e261..06b8017c1ed 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.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.common.params.CollectionAdminParams;
/** Supports snapshot-export command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ 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"
+ })
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,48 @@ public class SnapshotExportTool extends ToolBase {
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.Mixin private CollectionNameOptions collection;
+
+ // 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 = "NAME",
+ 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 +254,21 @@ public class SnapshotExportTool extends ToolBase {
@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,
+ collection.name,
+ destDirOpt,
+ backupRepoNameOpt,
+ asyncIdOpt);
+ exportSnapshot(params);
+ return 0;
}
}
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 00000000000..4085ead8ff0
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cli/SnapshotListShim.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 11.
+ */
+@Deprecated(since = "10.2")
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "snapshot-list",
+ hidden = true,
+ description = "Deprecated; use 'snapshot list'.")
+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/java/org/apache/solr/cli/SnapshotListTool.java
b/solr/core/src/java/org/apache/solr/cli/SnapshotListTool.java
index a4d7ea3b56a..4649215b53e 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.common.util.NamedList;
import org.apache.solr.core.snapshots.SolrSnapshotManager;
/** Supports snapshot-list command in the bin/solr script. */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+ name = "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,19 @@ 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.Mixin private CollectionNameOptions collection;
+
+ public SnapshotListTool() {
+ this(new DefaultToolRuntime());
+ }
+
public SnapshotListTool(ToolRuntime runtime) {
super(runtime);
}
@@ -99,6 +123,12 @@ public class SnapshotListTool extends ToolBase {
@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,
+ 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 00000000000..c4a95de9976
--- /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 00000000000..6c7aba2e33a
--- /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.
+ */
[email protected](
+ 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<Integer> {
+ @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 4f2c27d6681..f1936cf8d68 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -89,7 +89,13 @@ import org.slf4j.LoggerFactory;
HealthcheckTool.class,
PostTool.class,
PostLogsTool.class,
- StreamTool.class
+ StreamTool.class,
+ SnapshotTool.class,
+ SnapshotCreateShim.class,
+ SnapshotDeleteShim.class,
+ SnapshotDescribeShim.class,
+ SnapshotExportShim.class,
+ SnapshotListShim.class
})
public class SolrCLI implements CLIO, java.util.concurrent.Callable<Integer> {
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 00000000000..8be7c47e459
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/SnapshotExportToolPicocliTest.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;
+
+/**
+ * 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 {
+
+ @Override
+ protected int runTool(
+ String[] args, CLITestHelper.TestingRuntime runtime, Class<? extends
ToolBase> clazz)
+ throws Exception {
+ return SnapshotToolsPicocliTest.runAsGroup(args, runtime);
+ }
+}
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 81c46f302dd..a3c45246a10 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 @@ import org.junit.Test;
@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<? extends
ToolBase> clazz)
+ throws Exception {
+ return CLITestHelper.runTool(args, runtime, clazz);
+ }
+
private static final String COLLECTION = "snapshot_export_coll";
@BeforeClass
@@ -124,6 +131,29 @@ public class SnapshotExportToolTest extends
SolrCloudTestCase {
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 class SnapshotExportToolTest extends
SolrCloudTestCase {
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 00000000000..649c5185cf9
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsPicocliTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.logging.DeprecationLog;
+import org.apache.solr.util.LogListener;
+import org.junit.Test;
+import picocli.CommandLine;
+
+/**
+ * Runs {@link SnapshotToolsTest} through picocli, using the {@code bin/solr
snapshot <sub-command>}
+ * 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> K create(Class<K> 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-<x> ...} command line as {@code
snapshot <x> ...}. */
+ static int runAsGroup(String[] args, CLITestHelper.TestingRuntime runtime) {
+ List<String> 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<? extends
ToolBase> clazz)
+ throws Exception {
+ 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));
+ String url = cluster.getJettySolrRunner(0).getBaseUrl().toString();
+
+ // 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,
+ root.execute(
+ "snapshot-delete",
+ "-c",
+ COLLECTION,
+ "--snapshot-name",
+ "oldSpelling",
+ "--solr-url",
+ url));
+ 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));
+ 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("10.2", 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
new file mode 100644
index 00000000000..5389dfe1573
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/SnapshotToolsTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 {
+ 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<? extends
ToolBase> 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);
+ }
+
+ String run(Class<? extends ToolBase> tool, String name, String... extra)
throws Exception {
+ List<String> 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());
+ 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");
+ }
+}
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 074440d2a99..034346e499d 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
@@ -31,6 +31,12 @@
*** xref:cli/solr-healthcheck.adoc[healthcheck]
*** xref:cli/solr-post.adoc[post]
*** xref:cli/solr-postlogs.adoc[postlogs]
+*** 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 e7e950ea5e1..4dbe2b5dea4 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
@@ -70,6 +70,12 @@ Any command not listed here is unavailable when the picocli
implementation is en
|xref:cli/solr-healthcheck.adoc[bin/solr healthcheck]
|xref:cli/solr-post.adoc[bin/solr post]
|xref:cli/solr-postlogs.adoc[bin/solr postlogs]
+|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
new file mode 100644
index 00000000000..e4b1b14c008
--- /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=<credentials>]
+ [-s=<solrConnection> | --solr-url=<solrUrl> |
-z=<zkHost>]
+....
+
+// 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 the collection.
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ 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.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ 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 00000000000..eadf9e84b7c
--- /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=<credentials>]
+ [-s=<solrConnection> | --solr-url=<solrUrl> |
-z=<zkHost>]
+....
+
+// 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 the collection.
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ 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.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ 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 00000000000..11e6a1a31ce
--- /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=<credentials>]
+ [-s=<solrConnection> | --solr-url=<solrUrl> |
+ -z=<zkHost>]
+....
+
+// 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 the collection.
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ 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.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ 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 00000000000..606617e3f3c
--- /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=NAME]
-c=NAME
+ --dest-dir=DIR [--snapshot-name=NAME]
[-u=<credentials>]
+ [-s=<solrConnection> | --solr-url=<solrUrl> |
-z=<zkHost>]
+....
+
+// 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*=_NAME_::
+ Specifies name of the backup repository to be used during snapshot export
preparation.
+
+*-c*, *--name*=_NAME_::
+ *(required)* Name of the collection.
+
+*--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*=_<solrConnection>_::
+ 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*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ 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 00000000000..d0139e4520a
--- /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=<credentials>] [-s=<solrConnection> |
+ --solr-url=<solrUrl> | -z=<zkHost>]
+....
+
+// 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 the collection.
+
+*-h*, *--help*::
+ Print this help message and exit.
+
+*-s*, *--solr-connection*=_<solrConnection>_::
+ Zookeeper or HTTP(s) connection string; unnecessary if SOLR_CONNECTION is
defined in solr.in.sh; otherwise, defaults to localhost:9983.
+
+*--solr-url*=_<solrUrl>_::
+ Base Solr URL, which can be used to determine the zk-host if that's not
known.
+
+*-u*, *--credentials*=_<credentials>_::
+ Credentials in the format username:password. Example: --credentials
solr:SolrRocks
+
+*-v*, *--verbose*::
+ Enable verbose mode.
+
+*-z*, *--zk-host*=_<zkHost>_::
+ 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[]
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 00000000000..f0b674d0678
--- /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-10.adoc
b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
index 3a5a236542a..d8450340a57 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