diff --git a/solr/core/src/java/org/apache/solr/cli/ConnectionOptions.java b/solr/core/src/java/org/apache/solr/cli/ConnectionOptions.java new file mode 100644 index 000000000000..b34d81f4c6a2 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cli/ConnectionOptions.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; + +/** + * Picocli ArgGroup for mutually-exclusive Solr URL / ZooKeeper connection options. + * + *
Use as the type of an {@code @ArgGroup(exclusive = true, multiplicity = "0..1")} field to
+ * ensure the user provides at most one of {@code --solr-url} or {@code --zk-host}.
+ */
+class ConnectionOptions {
+ @picocli.CommandLine.Option(
+ names = {"-s", "--solr-url"},
+ description =
+ "Base Solr URL, which can be used to determine the zk-host if that's not known.")
+ String solrUrl;
+
+ @picocli.CommandLine.Option(
+ names = {"-z", "--zk-host"},
+ description =
+ "Zookeeper connection string; unnecessary if ZK_HOST is defined in solr.in.sh; otherwise, defaults to "
+ + CommonCLIOptions.DefaultValues.ZK_HOST
+ + ".")
+ String zkHost;
+}
diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
index 1f81609f0b78..ed1995d94345 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
@@ -21,6 +21,7 @@
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.cli.CommandLine;
@@ -38,9 +39,15 @@
import org.apache.solr.client.solrj.response.SystemInfoResponse;
import org.apache.solr.cloud.ZkConfigSetService;
import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.core.ConfigSetService;
/** Supports create command in the bin/solr script. */
+@picocli.CommandLine.Command(
+ name = "create",
+ mixinStandardHelpOptions = true,
+ description =
+ "Creates a core or collection depending on whether Solr is running in standalone (core) or SolrCloud mode (collection).")
public class CreateTool extends ToolBase {
private static final Option COLLECTION_NAME_OPTION =
@@ -90,6 +97,60 @@ public class CreateTool extends ToolBase {
.desc("Configuration name; default is the collection name.")
.get();
+ /** Options bean shared between commons-cli and picocli paths. */
+ record CreateParams(
+ String name,
+ String confDir,
+ String confName,
+ String solrUrl,
+ String credentials,
+ int shards,
+ int replicationFactor) {}
+
+ // --- 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,
+ description = "Name of collection or core to create.")
+ private String name;
+
+ @picocli.CommandLine.Option(
+ names = {"-sh", "--shards"},
+ description = "Number of shards; default is 1.",
+ defaultValue = "1")
+ private int shards;
+
+ @picocli.CommandLine.Option(
+ names = {"-rf", "--replication-factor"},
+ description =
+ "Number of copies of each document across the collection (replicas per shard); default is 1.",
+ defaultValue = "1")
+ private int replicationFactor;
+
+ @picocli.CommandLine.Option(
+ names = {"-d", "--conf-dir"},
+ description =
+ "Configuration directory to copy when creating the new collection; default is "
+ + DefaultValues.DEFAULT_CONFIG_SET
+ + ".",
+ defaultValue = DefaultValues.DEFAULT_CONFIG_SET)
+ private String confDir;
+
+ @picocli.CommandLine.Option(
+ names = {"-n", "--conf-name"},
+ description = "Configuration name; default is the collection name.")
+ private String confName;
+
+ public CreateTool() {
+ this(new DefaultToolRuntime());
+ }
+
public CreateTool(ToolRuntime runtime) {
super(runtime);
}
@@ -123,45 +184,45 @@ public Options getOptions() {
@Override
public void runImpl(CommandLine cli) throws Exception {
try (var solrClient = CLIUtils.getSolrClient(cli)) {
+ CreateParams params =
+ new CreateParams(
+ cli.getOptionValue(COLLECTION_NAME_OPTION),
+ cli.getOptionValue(CONF_DIR_OPTION, DefaultValues.DEFAULT_CONFIG_SET),
+ cli.getOptionValue(CONF_NAME_OPTION),
+ cli.getOptionValue(CommonCLIOptions.SOLR_URL_OPTION, CLIUtils.getDefaultSolrUrl()),
+ cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION),
+ cli.getParsedOptionValue(SHARDS_OPTION, 1),
+ cli.getParsedOptionValue(REPLICATION_FACTOR_OPTION, 1));
if (CLIUtils.isCloudMode(solrClient)) {
- createCollection(cli);
+ createCollection(CLIUtils.getZkHost(cli), params);
} else {
- createCore(cli, solrClient);
+ createCore(params, solrClient);
}
}
}
- protected void createCore(CommandLine cli, SolrClient solrClient) throws Exception {
- String coreName = cli.getOptionValue(COLLECTION_NAME_OPTION);
- String solrUrl =
- cli.getOptionValue(CommonCLIOptions.SOLR_URL_OPTION, CLIUtils.getDefaultSolrUrl());
-
- final String solrInstallDir = System.getProperty("solr.install.dir");
- final String confDirName =
- cli.getOptionValue(CONF_DIR_OPTION, DefaultValues.DEFAULT_CONFIG_SET);
-
+ private void createCore(CreateParams params, SolrClient solrClient) throws Exception {
// we allow them to pass a directory instead of a configset name
- Path configsetDir = Path.of(confDirName);
- Path solrInstallDirPath = Path.of(solrInstallDir);
+ Path configsetDir = Path.of(params.confDir);
+ Path solrInstallDirPath = Path.of(System.getProperty("solr.install.dir"));
if (!Files.isDirectory(configsetDir)) {
ensureConfDirExists(solrInstallDirPath, configsetDir);
}
- printDefaultConfigsetWarningIfNecessary(cli);
+ printDefaultConfigsetWarning(params);
SystemInfoResponse sysResponse = (new SystemInfoRequest()).process(solrClient);
// usually same as solr home, but not always
String coreRootDirectory = sysResponse.getCoreRoot();
- if (CLIUtils.safeCheckCoreExists(
- solrUrl, coreName, cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION))) {
+ if (CLIUtils.safeCheckCoreExists(params.solrUrl, params.name, params.credentials)) {
throw new IllegalArgumentException(
"\nCore '"
- + coreName
+ + params.name
+ "' already exists!\nChecked core existence using Core API command");
}
- Path coreInstanceDir = Path.of(coreRootDirectory, coreName);
+ Path coreInstanceDir = Path.of(coreRootDirectory, params.name);
Path confDir = getFullConfDir(solrInstallDirPath, configsetDir).resolve("conf");
if (!Files.isDirectory(coreInstanceDir)) {
Files.createDirectories(coreInstanceDir);
@@ -177,14 +238,14 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti
+ coreInstanceDir.toAbsolutePath());
}
- echoIfVerbose("\nCreating new core '" + coreName + "' using V2 Cores API");
+ echoIfVerbose("\nCreating new core '" + params.name + "' using V2 Cores API");
try {
var req = new CoresApi.CreateCore();
- req.setName(coreName);
- req.setInstanceDir(coreName);
+ req.setName(params.name);
+ req.setInstanceDir(params.name);
req.process(solrClient);
- echo(String.format(Locale.ROOT, "\nCreated new core '%s'", coreName));
+ echo(String.format(Locale.ROOT, "\nCreated new core '%s'", params.name));
} catch (Exception e) {
/* create-core failed, cleanup the copied configset before propagating the error. */
@@ -193,32 +254,26 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti
}
}
- protected void createCollection(CommandLine cli) throws Exception {
+ private void createCollection(String zkHost, CreateParams params) throws Exception {
var builder =
new HttpJettySolrClient.Builder()
.withIdleTimeout(30, TimeUnit.SECONDS)
.withConnectionTimeout(15, TimeUnit.SECONDS)
.withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
- .withOptionalBasicAuthCredentials(
- cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION));
- String zkHost = CLIUtils.getZkHost(cli);
+ .withOptionalBasicAuthCredentials(params.credentials);
echoIfVerbose("Connecting to ZooKeeper at " + zkHost);
try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) {
- createCollection(cloudSolrClient, cli);
+ createCollection(cloudSolrClient, params);
}
}
- protected void createCollection(CloudSolrClient cloudSolrClient, CommandLine cli)
+ private void createCollection(CloudSolrClient cloudSolrClient, CreateParams params)
throws Exception {
- String collectionName = cli.getOptionValue(COLLECTION_NAME_OPTION);
- final String solrInstallDir = System.getProperty("solr.install.dir");
- String confName = cli.getOptionValue(CONF_NAME_OPTION);
- String confDir = cli.getOptionValue(CONF_DIR_OPTION, DefaultValues.DEFAULT_CONFIG_SET);
- Path solrInstallDirPath = Path.of(solrInstallDir);
- Path confDirPath = Path.of(confDir);
+ Path solrInstallDirPath = Path.of(System.getProperty("solr.install.dir"));
+ Path confDirPath = Path.of(params.confDir);
ensureConfDirExists(solrInstallDirPath, confDirPath);
- printDefaultConfigsetWarningIfNecessary(cli);
+ printDefaultConfigsetWarning(params);
Set Use as {@code @CommandLine.Mixin CredentialsOptions credentialsOptions} in a command class.
+ */
+public class CredentialsOptions {
+ @picocli.CommandLine.Option(
+ names = {"-u", "--credentials"},
+ description =
+ "Credentials in the format username:password. Example: --credentials solr:SolrRocks")
+ public String credentials;
+}
diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
index 07f92894f311..25a49e4e82b3 100644
--- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
@@ -19,6 +19,7 @@
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Locale;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -32,10 +33,16 @@
import org.apache.solr.client.solrj.request.CollectionsApi;
import org.apache.solr.client.solrj.request.CoresApi;
import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.EnvUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Supports delete command in the bin/solr script. */
+@picocli.CommandLine.Command(
+ name = "delete",
+ mixinStandardHelpOptions = true,
+ description =
+ "Deletes a collection or core depending on whether Solr is running in SolrCloud or standalone mode.")
public class DeleteTool extends ToolBase {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -52,7 +59,7 @@ public class DeleteTool extends ToolBase {
Option.builder()
.longOpt("delete-config")
.desc(
- "Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is true.")
+ "Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is false.")
.get();
private static final Option FORCE_OPTION =
@@ -62,6 +69,38 @@ public class DeleteTool extends ToolBase {
"Skip safety checks when deleting the configuration directory used by a collection.")
.get();
+ /** Options bean shared between commons-cli and picocli paths. */
+ record DeleteParams(String name, String credentials, boolean deleteConfig, boolean force) {}
+
+ // --- 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,
+ description = "Name of the core / collection to delete.")
+ private String name;
+
+ @picocli.CommandLine.Option(
+ names = {"--delete-config"},
+ description =
+ "Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is false.")
+ private boolean deleteConfig;
+
+ @picocli.CommandLine.Option(
+ names = {"-f", "--force"},
+ description =
+ "Skip safety checks when deleting the configuration directory used by a collection.")
+ private boolean force;
+
+ public DeleteTool() {
+ this(new DefaultToolRuntime());
+ }
+
public DeleteTool(ToolRuntime runtime) {
super(runtime);
}
@@ -93,31 +132,34 @@ public Options getOptions() {
@Override
public void runImpl(CommandLine cli) throws Exception {
try (var solrClient = CLIUtils.getSolrClient(cli)) {
+ DeleteParams params =
+ new DeleteParams(
+ cli.getOptionValue(COLLECTION_NAME_OPTION),
+ cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION),
+ cli.hasOption(DELETE_CONFIG_OPTION),
+ cli.hasOption(FORCE_OPTION));
if (CLIUtils.isCloudMode(solrClient)) {
- deleteCollection(cli);
+ deleteCollection(CLIUtils.getZkHost(cli), params);
} else {
- deleteCore(cli, solrClient);
+ deleteCore(params, solrClient);
}
}
}
- protected void deleteCollection(CommandLine cli) throws Exception {
+ private void deleteCollection(String zkHost, DeleteParams params) throws Exception {
var builder =
new HttpJettySolrClient.Builder()
.withIdleTimeout(30, TimeUnit.SECONDS)
.withConnectionTimeout(15, TimeUnit.SECONDS)
.withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
- .withOptionalBasicAuthCredentials(
- cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION));
-
- String zkHost = CLIUtils.getZkHost(cli);
+ .withOptionalBasicAuthCredentials(params.credentials);
+ echoIfVerbose("Connecting to ZooKeeper at " + zkHost);
try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) {
- echoIfVerbose("Connecting to ZooKeeper at " + zkHost);
- deleteCollection(cloudSolrClient, cli);
+ deleteCollection(cloudSolrClient, params);
}
}
- protected void deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli)
+ private void deleteCollection(CloudSolrClient cloudSolrClient, DeleteParams params)
throws Exception {
Set