You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ep...@apache.org on 2023/07/25 14:54:43 UTC

[solr] branch main updated: SOLR-16892: make CreateTool Cloud/Standalone Aware (#1785)

This is an automated email from the ASF dual-hosted git repository.

epugh 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 cb5b64aecf5 SOLR-16892: make CreateTool Cloud/Standalone Aware (#1785)
cb5b64aecf5 is described below

commit cb5b64aecf5d882ec7a2599be5d51c2eaf625d35
Author: Eric Pugh <ep...@opensourceconnections.com>
AuthorDate: Tue Jul 25 10:54:36 2023 -0400

    SOLR-16892: make CreateTool Cloud/Standalone Aware (#1785)
    
    bin/solr create_core and bin/solr create_collection are deprecated in favour of just using bin/solr create.   This aligns the create command with how the bin/solr delete command works.  Also some tech debt resolved.
---
 dev-tools/scripts/cloud.sh                         |   2 +-
 solr/CHANGES.txt                                   |   2 +
 .../core/src/java/org/apache/solr/cli/ApiTool.java |   6 +-
 .../src/java/org/apache/solr/cli/AssertTool.java   |  14 +-
 .../src/java/org/apache/solr/cli/AuthTool.java     |   2 +-
 .../org/apache/solr/cli/ConfigSetDownloadTool.java |   1 +
 .../org/apache/solr/cli/ConfigSetUploadTool.java   |   1 +
 .../src/java/org/apache/solr/cli/ConfigTool.java   |   6 +-
 .../org/apache/solr/cli/CreateCollectionTool.java  |   8 +-
 .../java/org/apache/solr/cli/CreateCoreTool.java   |   7 +
 .../src/java/org/apache/solr/cli/CreateTool.java   | 323 ++++++++++++++++++++-
 .../src/java/org/apache/solr/cli/DeleteTool.java   |  11 +-
 .../src/java/org/apache/solr/cli/ExportTool.java   |   1 +
 .../java/org/apache/solr/cli/HealthcheckTool.java  |   4 +-
 .../src/java/org/apache/solr/cli/PackageTool.java  |  35 +--
 .../src/java/org/apache/solr/cli/PostTool.java     |   1 +
 .../java/org/apache/solr/cli/RunExampleTool.java   |  15 +-
 .../core/src/java/org/apache/solr/cli/SolrCLI.java |   8 +
 .../src/java/org/apache/solr/cli/StatusTool.java   |   6 +-
 .../src/java/org/apache/solr/cli/ZkCpTool.java     |   1 +
 .../src/java/org/apache/solr/cli/ZkLsTool.java     |   1 +
 .../src/java/org/apache/solr/cli/ZkMkrootTool.java |   1 +
 .../src/java/org/apache/solr/cli/ZkMvTool.java     |   1 +
 .../src/java/org/apache/solr/cli/ZkRmTool.java     |   1 +
 solr/packaging/test/test_assert.bats               |  53 ++++
 solr/packaging/test/test_create_collection.bats    |  22 +-
 solr/packaging/test/test_delete_collection.bats    |   8 +-
 27 files changed, 448 insertions(+), 93 deletions(-)

diff --git a/dev-tools/scripts/cloud.sh b/dev-tools/scripts/cloud.sh
index 38f9d6c84ef..3b5e3bfa13c 100755
--- a/dev-tools/scripts/cloud.sh
+++ b/dev-tools/scripts/cloud.sh
@@ -341,7 +341,7 @@ start(){
     -Dsolr.solrxml.location=zookeeper -Dsolr.log.dir=$CLUSTER_WD_FULL/n${i} $JVM_ARGS")
     FINAL_COMMAND="${SOLR}/bin/solr ${argsArray[@]}"
     echo ${FINAL_COMMAND}
-    ${SOLR}/bin/solr "${argsArray[@]}"
+    ${SOLR}/bin/solr start "${argsArray[@]}"
   done
 
   touch ${CLUSTER_WD}  # make this the most recently updated dir for ls -t
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index bde8089eee1..9ebecf145ed 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -64,6 +64,8 @@ Improvements
 * SOLR-16490: `/admin/cores?action=backupcore` now has a v2 equivalent, available at
   `GET /api/cores/coreName/backups` (Sanjay Dutt via Jason Gerlowski)
 
+* SOLR-16892: bin/solr create command supports both standalone and cloud modes, and deprecate create_core and create_collection commands.   (Eric Pugh)
+
 Optimizations
 ---------------------
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
index c4ecce4a639..6122000211b 100644
--- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
@@ -29,8 +29,12 @@ import org.apache.solr.common.util.NamedList;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
+/**
+ * Supports api command in the bin/solr script.
+ *
+ * <p>Used to send an arbitrary HTTP request to a Solr API endpoint.
+ */
 public class ApiTool extends ToolBase {
-  /** Used to send an arbitrary HTTP request to a Solr API endpoint. */
   public ApiTool() {
     this(CLIO.getOutStream());
   }
diff --git a/solr/core/src/java/org/apache/solr/cli/AssertTool.java b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
index 94fc3bd9890..014b19d123a 100644
--- a/solr/core/src/java/org/apache/solr/cli/AssertTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
@@ -28,17 +28,15 @@ import java.util.concurrent.TimeUnit;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.HealthCheckRequest;
-import org.apache.solr.client.solrj.response.CollectionAdminResponse;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.NamedList;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports assert command in the bin/solr script. */
 /** Asserts various conditions and exists with error code if fails, else continues with no output */
 public class AssertTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -373,15 +371,7 @@ public class AssertTool extends ToolBase {
 
   private static boolean runningSolrIsCloud(String url) throws Exception {
     try (final SolrClient client = new Http2SolrClient.Builder(url).build()) {
-      final SolrRequest<CollectionAdminResponse> request =
-          new CollectionAdminRequest.ClusterStatus();
-      final CollectionAdminResponse response = request.process(client);
-      return true; // throws an exception otherwise
-    } catch (Exception e) {
-      if (SolrCLI.exceptionIsAuthRelated(e)) {
-        throw e;
-      }
-      return false;
+      return SolrCLI.isCloudMode(client);
     }
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/cli/AuthTool.java b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
index 9e9244626c7..8d43af55fdb 100644
--- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
@@ -39,7 +39,7 @@ import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.security.Sha256AuthenticationProvider;
 import org.apache.zookeeper.KeeperException;
 
-// Authentication tool
+/** Supports auth command in the bin/solr script. */
 public class AuthTool extends ToolBase {
   public AuthTool() {
     this(CLIO.getOutStream());
diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java
index 13134507362..8603af39513 100644
--- a/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ConfigSetDownloadTool.java
@@ -29,6 +29,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk downconfig command in the bin/solr script. */
 public class ConfigSetDownloadTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java
index e3decef4b83..a36370784dd 100644
--- a/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ConfigSetUploadTool.java
@@ -29,6 +29,7 @@ import org.apache.solr.core.ConfigSetService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk upconfig command in the bin/solr script. */
 public class ConfigSetUploadTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
index 0d58b3fc88b..e7a4012e263 100644
--- a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
@@ -29,7 +29,11 @@ import org.apache.solr.common.util.NamedList;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
-/** Sends a POST to the Config API to perform a specified action. */
+/**
+ * Supports config command in the bin/solr script.
+ *
+ * <p>Sends a POST to the Config API to perform a specified action.
+ */
 public class ConfigTool extends ToolBase {
 
   public ConfigTool() {
diff --git a/solr/core/src/java/org/apache/solr/cli/CreateCollectionTool.java b/solr/core/src/java/org/apache/solr/cli/CreateCollectionTool.java
index 1a37b5ac058..48c4162dea8 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateCollectionTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateCollectionTool.java
@@ -39,7 +39,12 @@ import org.apache.solr.core.ConfigSetService;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
-/** Supports create_collection command in the bin/solr script. */
+/**
+ * Supports create_collection command in the bin/solr script.
+ *
+ * @deprecated Please use {@link CreateTool}
+ */
+@Deprecated(since = "9.4")
 public class CreateCollectionTool extends ToolBase {
 
   public static final List<Option> CREATE_COLLECTION_OPTIONS =
@@ -108,6 +113,7 @@ public class CreateCollectionTool extends ToolBase {
   @Override
   public void runImpl(CommandLine cli) throws Exception {
     SolrCLI.raiseLogLevelUnlessVerbose(cli);
+    echo("This command has been deprecated in favour of using 'bin/solr create'.");
     String zkHost = SolrCLI.getZkHost(cli);
     if (zkHost == null) {
       throw new IllegalStateException(
diff --git a/solr/core/src/java/org/apache/solr/cli/CreateCoreTool.java b/solr/core/src/java/org/apache/solr/cli/CreateCoreTool.java
index 6c396346b95..0d1eeaa4364 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateCoreTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateCoreTool.java
@@ -32,6 +32,12 @@ import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.response.CoreAdminResponse;
 import org.apache.solr.common.params.CommonParams;
 
+/**
+ * Supports create command in the bin/solr script.
+ *
+ * @deprecated Please use {@link CreateTool}
+ */
+@Deprecated(since = "9.4")
 public class CreateCoreTool extends ToolBase {
 
   public CreateCoreTool() {
@@ -79,6 +85,7 @@ public class CreateCoreTool extends ToolBase {
 
   @Override
   public void runImpl(CommandLine cli) throws Exception {
+    echo("This command has been deprecated in favour of using 'bin/solr create'.");
     String coreName = cli.getOptionValue("name");
     String solrUrl = cli.getOptionValue("solrUrl", SolrCLI.DEFAULT_SOLR_URL);
 
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 fff9379c9a4..e30a30f40bd 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
@@ -16,16 +16,42 @@
  */
 package org.apache.solr.cli;
 
-import static org.apache.solr.common.params.CommonParams.SYSTEM_INFO_PATH;
-
+import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.PathUtils;
+import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
+import org.apache.solr.client.solrj.response.CoreAdminResponse;
+import org.apache.solr.common.cloud.ZkMaintenanceUtils;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.CollectionAdminParams;
+import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.ConfigSetService;
+import org.noggit.CharArr;
+import org.noggit.JSONWriter;
 
+/** Supports create command in the bin/solr script. */
 public class CreateTool extends ToolBase {
 
   public CreateTool() {
@@ -43,7 +69,49 @@ public class CreateTool extends ToolBase {
 
   @Override
   public List<Option> getOptions() {
-    return CreateCollectionTool.CREATE_COLLECTION_OPTIONS;
+    return List.of(
+        SolrCLI.OPTION_ZKHOST,
+        SolrCLI.OPTION_SOLRURL,
+        Option.builder("c")
+            .longOpt("name")
+            .argName("NAME")
+            .hasArg()
+            .required(true)
+            .desc("Name of collection or core to create.")
+            .build(),
+        Option.builder("s")
+            .longOpt("shards")
+            .argName("#")
+            .hasArg()
+            .required(false)
+            .desc("Number of shards; default is 1.")
+            .build(),
+        Option.builder("rf")
+            .longOpt("replicationFactor")
+            .argName("#")
+            .hasArg()
+            .required(false)
+            .desc(
+                "Number of copies of each document across the collection (replicas per shard); default is 1.")
+            .build(),
+        Option.builder("d")
+            .longOpt("confdir")
+            .argName("NAME")
+            .hasArg()
+            .required(false)
+            .desc(
+                "Configuration directory to copy when creating the new collection; default is "
+                    + SolrCLI.DEFAULT_CONFIG_SET
+                    + '.')
+            .build(),
+        Option.builder("n")
+            .longOpt("confname")
+            .argName("NAME")
+            .hasArg()
+            .required(false)
+            .desc("Configuration name; default is the collection name.")
+            .build(),
+        SolrCLI.OPTION_VERBOSE);
   }
 
   @Override
@@ -51,16 +119,251 @@ public class CreateTool extends ToolBase {
     SolrCLI.raiseLogLevelUnlessVerbose(cli);
     String solrUrl = cli.getOptionValue("solrUrl", SolrCLI.DEFAULT_SOLR_URL);
 
-    ToolBase tool;
     try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
-      NamedList<Object> systemInfo =
-          solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH));
-      if ("solrcloud".equals(systemInfo.get("mode"))) {
-        tool = new CreateCollectionTool(stdout);
+      if (SolrCLI.isCloudMode(solrClient)) {
+        createCollection(cli);
       } else {
-        tool = new CreateCoreTool(stdout);
+        createCore(cli, solrClient);
+      }
+    }
+  }
+
+  protected void createCore(CommandLine cli, SolrClient solrClient) throws Exception {
+    String coreName = cli.getOptionValue("name");
+    String solrUrl = cli.getOptionValue("solrUrl", SolrCLI.DEFAULT_SOLR_URL);
+
+    final String solrInstallDir = System.getProperty("solr.install.dir");
+    final String confDirName = cli.getOptionValue("confdir", SolrCLI.DEFAULT_CONFIG_SET);
+
+    // we allow them to pass a directory instead of a configset name
+    Path configsetDir = Paths.get(confDirName);
+    Path solrInstallDirPath = Paths.get(solrInstallDir);
+
+    if (!Files.isDirectory(configsetDir)) {
+      ensureConfDirExists(solrInstallDirPath, configsetDir);
+    }
+    printDefaultConfigsetWarningIfNecessary(cli);
+
+    String coreRootDirectory; // usually same as solr home, but not always
+
+    Map<String, Object> systemInfo =
+        solrClient
+            .request(new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH))
+            .asMap();
+
+    // convert raw JSON into user-friendly output
+    coreRootDirectory = (String) systemInfo.get("core_root");
+
+    if (SolrCLI.safeCheckCoreExists(solrUrl, coreName)) {
+      throw new IllegalArgumentException(
+          "\nCore '"
+              + coreName
+              + "' already exists!\nChecked core existence using Core API command");
+    }
+
+    Path coreInstanceDir = Paths.get(coreRootDirectory, coreName);
+    Path confDir = getFullConfDir(solrInstallDirPath, configsetDir).resolve("conf");
+    if (!Files.isDirectory(coreInstanceDir)) {
+      Files.createDirectories(coreInstanceDir);
+      if (!Files.isDirectory(coreInstanceDir)) {
+        throw new IOException(
+            "Failed to create new core instance directory: " + coreInstanceDir.toAbsolutePath());
       }
-      tool.runImpl(cli);
+
+      FileUtils.copyDirectoryToDirectory(confDir.toFile(), coreInstanceDir.toFile());
+
+      echoIfVerbose(
+          "\nCopying configuration to new core instance directory:\n"
+              + coreInstanceDir.toAbsolutePath(),
+          cli);
+    }
+
+    echoIfVerbose("\nCreating new core '" + coreName + "' using CoreAdminRequest", cli);
+
+    try {
+      CoreAdminResponse res = CoreAdminRequest.createCore(coreName, coreName, solrClient);
+      if (cli.hasOption(SolrCLI.OPTION_VERBOSE.getOpt())) {
+        echo(res.jsonStr());
+        echo("\n");
+      } else {
+        echo(String.format(Locale.ROOT, "\nCreated new core '%s'", coreName));
+      }
+    } catch (Exception e) {
+      /* create-core failed, cleanup the copied configset before propagating the error. */
+      PathUtils.deleteDirectory(coreInstanceDir);
+      throw e;
+    }
+  }
+
+  protected void createCollection(CommandLine cli) throws Exception {
+    String zkHost = SolrCLI.getZkHost(cli);
+    try (CloudSolrClient cloudSolrClient =
+        new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost), Optional.empty())
+            .withInternalClientBuilder(
+                new Http2SolrClient.Builder()
+                    .withIdleTimeout(30, TimeUnit.SECONDS)
+                    .withConnectionTimeout(15, TimeUnit.SECONDS))
+            .build()) {
+      echoIfVerbose("Connecting to ZooKeeper at " + zkHost, cli);
+      cloudSolrClient.connect();
+      createCollection(cloudSolrClient, cli);
+    }
+  }
+
+  protected void createCollection(CloudSolrClient cloudSolrClient, CommandLine cli)
+      throws Exception {
+
+    String collectionName = cli.getOptionValue("name");
+    final String solrInstallDir = System.getProperty("solr.install.dir");
+    String confName = cli.getOptionValue("confname");
+    String confDir = cli.getOptionValue("confdir", "_default");
+    Path solrInstallDirPath = Paths.get(solrInstallDir);
+    Path confDirPath = Paths.get(confDir);
+    ensureConfDirExists(solrInstallDirPath, confDirPath);
+    printDefaultConfigsetWarningIfNecessary(cli);
+
+    Set<String> liveNodes = cloudSolrClient.getClusterState().getLiveNodes();
+    if (liveNodes.isEmpty())
+      throw new IllegalStateException(
+          "No live nodes found! Cannot create a collection until "
+              + "there is at least 1 live node in the cluster.");
+
+    String solrUrl = cli.getOptionValue("solrUrl");
+    if (solrUrl == null) {
+      String firstLiveNode = liveNodes.iterator().next();
+      solrUrl = ZkStateReader.from(cloudSolrClient).getBaseUrlForNodeName(firstLiveNode);
+    }
+
+    // build a URL to create the collection
+    int numShards = Integer.parseInt(cli.getOptionValue("shards", String.valueOf(1)));
+    int replicationFactor =
+        Integer.parseInt(cli.getOptionValue("replicationFactor", String.valueOf(1)));
+
+    boolean configExistsInZk =
+        confName != null
+            && !confName.trim().isEmpty()
+            && ZkStateReader.from(cloudSolrClient)
+                .getZkClient()
+                .exists("/configs/" + confName, true);
+
+    if (CollectionAdminParams.SYSTEM_COLL.equals(collectionName)) {
+      // do nothing
+    } else if (configExistsInZk) {
+      echo("Re-using existing configuration directory " + confName);
+    } else { // if (confdir != null && !confdir.trim().isEmpty()) {
+      if (confName == null || confName.trim().isEmpty()) {
+        confName = collectionName;
+      }
+
+      final Path configsetsDirPath = getConfigSetsDir(solrInstallDirPath);
+      Path confPath = ConfigSetService.getConfigsetPath(confDir, configsetsDirPath.toString());
+
+      echoIfVerbose(
+          "Uploading "
+              + confPath.toAbsolutePath()
+              + " for config "
+              + confName
+              + " to ZooKeeper at "
+              + cloudSolrClient.getClusterStateProvider().getQuorumHosts(),
+          cli);
+      ZkMaintenanceUtils.uploadToZK(
+          ZkStateReader.from(cloudSolrClient).getZkClient(),
+          confPath,
+          ZkMaintenanceUtils.CONFIGS_ZKNODE + "/" + confName,
+          ZkMaintenanceUtils.UPLOAD_FILENAME_EXCLUDE_PATTERN);
+    }
+
+    // since creating a collection is a heavy-weight operation, check for existence first
+    if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+      throw new IllegalStateException(
+          "\nCollection '"
+              + collectionName
+              + "' already exists!\nChecked collection existence using CollectionAdminRequest");
+    }
+
+    // doesn't seem to exist ... try to create
+    echoIfVerbose(
+        "\nCreating new collection '" + collectionName + "' using CollectionAdminRequest", cli);
+
+    NamedList<Object> response;
+    try {
+      response =
+          cloudSolrClient.request(
+              CollectionAdminRequest.createCollection(
+                  collectionName, confName, numShards, replicationFactor));
+    } catch (SolrServerException sse) {
+      throw new Exception(
+          "Failed to create collection '" + collectionName + "' due to: " + sse.getMessage());
+    }
+
+    if (cli.hasOption(SolrCLI.OPTION_VERBOSE.getOpt())) {
+      CharArr arr = new CharArr();
+      new JSONWriter(arr, 2).write(response.asMap());
+      echo(arr.toString());
+    } else {
+      String endMessage =
+          String.format(
+              Locale.ROOT,
+              "Created collection '%s' with %d shard(s), %d replica(s)",
+              collectionName,
+              numShards,
+              replicationFactor);
+      if (confName != null && !confName.trim().isEmpty()) {
+        endMessage += String.format(Locale.ROOT, " with config-set '%s'", confName);
+      }
+
+      echo(endMessage);
+    }
+  }
+
+  private Path getConfigSetsDir(Path solrInstallDir) {
+    Path configSetsPath = Paths.get("server/solr/configsets/");
+    return solrInstallDir.resolve(configSetsPath);
+  }
+
+  private Path getFullConfDir(Path solrInstallDir, Path confDirName) {
+    return getConfigSetsDir(solrInstallDir).resolve(confDirName);
+  }
+
+  private void ensureConfDirExists(Path solrInstallDir, Path confDirName) {
+    if (!Files.isDirectory(confDirName)) {
+
+      Path fullConfDir = getFullConfDir(solrInstallDir, confDirName);
+      if (!Files.isDirectory(fullConfDir)) {
+        echo("Specified configuration directory " + confDirName + " not found!");
+        System.exit(1);
+      }
+    }
+  }
+
+  private void printDefaultConfigsetWarningIfNecessary(CommandLine cli) {
+    final String confDirectoryName = cli.getOptionValue("confdir", "_default");
+    final String confName = cli.getOptionValue("confname", "");
+
+    if (confDirectoryName.equals("_default")
+        && (confName.equals("") || confName.equals("_default"))) {
+      final String collectionName = cli.getOptionValue("collection");
+      final String solrUrl = cli.getOptionValue("solrUrl", SolrCLI.DEFAULT_SOLR_URL);
+      final String curlCommand =
+          String.format(
+              Locale.ROOT,
+              "curl %s/%s/config -d "
+                  + "'{\"set-user-property\": {\"update.autoCreateFields\":\"false\"}}'",
+              solrUrl,
+              collectionName);
+      final String configCommand =
+          String.format(
+              Locale.ROOT,
+              "bin/solr config -c %s -p 8983 -action set-user-property -property update.autoCreateFields -value false",
+              collectionName);
+      echo(
+          "WARNING: Using _default configset. Data driven schema functionality is enabled by default, which is");
+      echo("         NOT RECOMMENDED for production use.");
+      echo("");
+      echo("         To turn it off:");
+      echo("            " + curlCommand);
+      echo("         Or:");
+      echo("            " + configCommand);
     }
   }
 }
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 677b8a6e899..8d80e029c1c 100644
--- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
@@ -17,27 +17,23 @@
 package org.apache.solr.cli;
 
 import static org.apache.solr.common.params.CommonParams.NAME;
-import static org.apache.solr.common.params.CommonParams.SYSTEM_INFO_PATH;
 
 import java.io.PrintStream;
 import java.lang.invoke.MethodHandles;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
-import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.NamedList;
 import org.noggit.CharArr;
@@ -45,6 +41,7 @@ import org.noggit.JSONWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports delete command in the bin/solr script. */
 public class DeleteTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -94,11 +91,7 @@ public class DeleteTool extends ToolBase {
     String solrUrl = cli.getOptionValue("solrUrl", SolrCLI.DEFAULT_SOLR_URL);
 
     try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
-      Map<String, Object> systemInfo =
-          solrClient
-              .request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH))
-              .asMap();
-      if ("solrcloud".equals(systemInfo.get("mode"))) {
+      if (SolrCLI.isCloudMode(solrClient)) {
         deleteCollection(cli);
       } else {
         deleteCore(cli, solrClient);
diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
index a50cfb9bbaa..c14550fbcd5 100644
--- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
@@ -85,6 +85,7 @@ import org.apache.solr.common.util.StrUtils;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
+/** Supports export command in the bin/solr script. */
 public class ExportTool extends ToolBase {
   @Override
   public String getName() {
diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
index e0e5cafd0f8..09ba0eaefb5 100644
--- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
@@ -51,6 +51,7 @@ import org.noggit.JSONWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports healthcheck command in the bin/solr script. */
 public class HealthcheckTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -105,9 +106,6 @@ public class HealthcheckTool extends ToolBase {
   protected void runCloudTool(CloudSolrClient cloudSolrClient, CommandLine cli) throws Exception {
     SolrCLI.raiseLogLevelUnlessVerbose(cli);
     String collection = cli.getOptionValue("name");
-    if (collection == null) {
-      throw new IllegalArgumentException("Must provide a collection to run a healthcheck against!");
-    }
 
     log.debug("Running healthcheck for {}", collection);
 
diff --git a/solr/core/src/java/org/apache/solr/cli/PackageTool.java b/solr/core/src/java/org/apache/solr/cli/PackageTool.java
index c0e0c08e660..6a8714ddb10 100644
--- a/solr/core/src/java/org/apache/solr/cli/PackageTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PackageTool.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.cli;
 
-import static org.apache.solr.cli.SolrCLI.getSolrClient;
 import static org.apache.solr.packagemanager.PackageUtils.print;
 import static org.apache.solr.packagemanager.PackageUtils.printGreen;
 
@@ -31,13 +30,9 @@ import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.config.Configurator;
 import org.apache.lucene.util.SuppressForbidden;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
-import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.Pair;
 import org.apache.solr.packagemanager.PackageManager;
 import org.apache.solr.packagemanager.PackageUtils;
@@ -48,6 +43,7 @@ import org.apache.solr.packagemanager.SolrPackageInstance;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports package command in the bin/solr script. */
 public class PackageTool extends ToolBase {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -87,7 +83,7 @@ public class PackageTool extends ToolBase {
       solrUrl = cli.getOptionValues("solrUrl")[cli.getOptionValues("solrUrl").length - 1];
       solrBaseUrl = solrUrl.replaceAll("/solr$", ""); // strip out ending "/solr"
       log.info("Solr url:{}, solr base url: {}", solrUrl, solrBaseUrl);
-      String zkHost = getZkHost(cli);
+      String zkHost = SolrCLI.getZkHost(cli);
       if (zkHost == null) {
         throw new SolrException(ErrorCode.INVALID_STATE, "Package manager runs only in SolrCloud");
       }
@@ -361,31 +357,4 @@ public class PackageTool extends ToolBase {
             .longOpt("noprompt")
             .build());
   }
-
-  private String getZkHost(CommandLine cli) throws Exception {
-    String zkHost = cli.getOptionValue("zkHost");
-    if (zkHost != null) return zkHost;
-
-    try (SolrClient solrClient = getSolrClient(solrUrl)) {
-      // hit Solr to get system info
-      NamedList<Object> systemInfo =
-          solrClient.request(
-              new GenericSolrRequest(SolrRequest.METHOD.GET, CommonParams.SYSTEM_INFO_PATH));
-
-      // convert raw JSON into user-friendly output
-      StatusTool statusTool = new StatusTool();
-      Map<String, Object> status = statusTool.reportStatus(systemInfo, solrClient);
-      @SuppressWarnings({"unchecked"})
-      Map<String, Object> cloud = (Map<String, Object>) status.get("cloud");
-      if (cloud != null) {
-        String zookeeper = (String) cloud.get("ZooKeeper");
-        if (zookeeper.endsWith("(embedded)")) {
-          zookeeper = zookeeper.substring(0, zookeeper.length() - "(embedded)".length());
-        }
-        zkHost = zookeeper;
-      }
-    }
-
-    return zkHost;
-  }
 }
diff --git a/solr/core/src/java/org/apache/solr/cli/PostTool.java b/solr/core/src/java/org/apache/solr/cli/PostTool.java
index 5e6b5efeef0..b44bfe397a6 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostTool.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 
+/** Supports post command in the bin/solr script. */
 public class PostTool extends ToolBase {
 
   public PostTool() {
diff --git a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
index f5ba560e8dc..31e7a2c926d 100644
--- a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
@@ -51,7 +51,12 @@ import org.apache.solr.common.SolrException;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
-/** Supports an interactive session with the user to launch (or relaunch the -e cloud example) */
+/**
+ * Supports start command in the bin/solr script.
+ *
+ * <p>Enhances start command by providing an interactive session with the user to launch (or
+ * relaunch the -e cloud example)
+ */
 public class RunExampleTool extends ToolBase {
 
   private static final String PROMPT_FOR_NUMBER = "Please enter %s [%d]: ";
@@ -812,7 +817,7 @@ public class RunExampleTool extends ToolBase {
       }
     }
 
-    // invoke the CreateCollectionTool
+    // invoke the CreateTool
     String[] createArgs =
         new String[] {
           "-name", collectionName,
@@ -824,11 +829,11 @@ public class RunExampleTool extends ToolBase {
           "-solrUrl", solrUrl
         };
 
-    CreateCollectionTool createCollectionTool = new CreateCollectionTool(stdout);
+    CreateTool createTool = new CreateTool(stdout);
     int createCode =
-        createCollectionTool.runTool(
+        createTool.runTool(
             SolrCLI.processCommandLineArgs(
-                createCollectionTool.getName(), createCollectionTool.getOptions(), createArgs));
+                createTool.getName(), createTool.getOptions(), createArgs));
 
     if (createCode != 0)
       throw new Exception(
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
index c7eff7622d1..e6ff82d21d4 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -19,11 +19,13 @@ package org.apache.solr.cli;
 import static org.apache.solr.common.SolrException.ErrorCode.FORBIDDEN;
 import static org.apache.solr.common.SolrException.ErrorCode.UNAUTHORIZED;
 import static org.apache.solr.common.params.CommonParams.NAME;
+import static org.apache.solr.common.params.CommonParams.SYSTEM_INFO_PATH;
 import static org.apache.solr.packagemanager.PackageUtils.print;
 import static org.apache.solr.packagemanager.PackageUtils.printGreen;
 
 import com.google.common.annotations.VisibleForTesting;
 import java.io.File;
+import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.net.ConnectException;
 import java.net.SocketException;
@@ -562,6 +564,12 @@ public class SolrCLI implements CLIO {
     return exists;
   }
 
+  public static boolean isCloudMode(SolrClient solrClient) throws SolrServerException, IOException {
+    NamedList<Object> systemInfo =
+        solrClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, SYSTEM_INFO_PATH));
+    return "solrcloud".equals(systemInfo.get("mode"));
+  }
+
   public static class AssertionFailureException extends Exception {
     public AssertionFailureException(String message) {
       super(message);
diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
index b933fe43f98..349804ca7d0 100644
--- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
@@ -38,8 +38,12 @@ import org.apache.solr.common.util.NamedList;
 import org.noggit.CharArr;
 import org.noggit.JSONWriter;
 
+/**
+ * Supports status command in the bin/solr script.
+ *
+ * <p>Get the status of a Solr server.
+ */
 public class StatusTool extends ToolBase {
-  /** Get the status of a Solr server. */
   public StatusTool() {
     this(CLIO.getOutStream());
   }
diff --git a/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java b/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java
index 01df580fbb7..571db1351c2 100644
--- a/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ZkCpTool.java
@@ -27,6 +27,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk cp command in the bin/solr script. */
 public class ZkCpTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ZkLsTool.java b/solr/core/src/java/org/apache/solr/cli/ZkLsTool.java
index 81f14e21e77..5f4d6d83fca 100644
--- a/solr/core/src/java/org/apache/solr/cli/ZkLsTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ZkLsTool.java
@@ -26,6 +26,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk ls command in the bin/solr script. */
 public class ZkLsTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java b/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java
index d9c4e3e20d1..8a9b7d769d8 100644
--- a/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ZkMkrootTool.java
@@ -26,6 +26,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk mkroot command in the bin/solr script. */
 public class ZkMkrootTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ZkMvTool.java b/solr/core/src/java/org/apache/solr/cli/ZkMvTool.java
index 6fed21223d7..6c147c85594 100644
--- a/solr/core/src/java/org/apache/solr/cli/ZkMvTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ZkMvTool.java
@@ -28,6 +28,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk mv command in the bin/solr script. */
 public class ZkMvTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java b/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java
index 0c03397c05b..0ca4694ac89 100644
--- a/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ZkRmTool.java
@@ -28,6 +28,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/** Supports zk rm command in the bin/solr script. */
 public class ZkRmTool extends ToolBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
diff --git a/solr/packaging/test/test_assert.bats b/solr/packaging/test/test_assert.bats
new file mode 100644
index 00000000000..991774add01
--- /dev/null
+++ b/solr/packaging/test/test_assert.bats
@@ -0,0 +1,53 @@
+#!/usr/bin/env bats
+
+# 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.
+
+load bats_helper
+
+setup() {
+  common_clean_setup
+}
+
+teardown() {
+  # save a snapshot of SOLR_HOME for failed tests
+  save_home_on_failure
+
+  solr stop -all >/dev/null 2>&1
+}
+
+@test "assert for non cloud mode" {
+  run solr start
+  
+  run solr assert --not-cloud http://localhost:8983/solr
+  refute_output --partial "ERROR"
+  
+  run solr assert --cloud http://localhost:8983/solr
+  assert_output --partial "ERROR: Solr is not running in cloud mode"
+  
+  run ! solr assert --cloud http://localhost:8983/solr -e
+}
+
+@test "assert for cloud mode" {
+  run solr start -c 
+  
+  run solr assert --cloud http://localhost:8983/solr
+  refute_output --partial "ERROR"
+  
+  run solr assert --not-cloud http://localhost:8983/solr
+  assert_output --partial "ERROR: Solr is not running in standalone mode"
+  
+  run ! solr assert --not-cloud http://localhost:8983/solr -e
+}
diff --git a/solr/packaging/test/test_create_collection.bats b/solr/packaging/test/test_create_collection.bats
index f4037ebccfe..ff776609378 100644
--- a/solr/packaging/test/test_create_collection.bats
+++ b/solr/packaging/test/test_create_collection.bats
@@ -39,29 +39,29 @@ teardown() {
 }
 
 @test "create collection" {
-  run solr create_collection -c COLL_NAME
+  run solr create -c COLL_NAME
   assert_output --partial "Created collection 'COLL_NAME'"
   assert_output --partial "assuming solrUrl is http://localhost:8983/solr"
 }
 
 @test "create collection using solrUrl" {
-  run solr create_collection -c COLL_NAME -solrUrl http://localhost:8983/solr
+  run solr create -c COLL_NAME -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"  
   refute_output --partial "assuming solrUrl is http://localhost:8983/solr"
 }
 
 @test "create collection using Zookeeper" {
-  run solr create_collection -c COLL_NAME -zkHost localhost:9983
+  run solr create -c COLL_NAME -zkHost localhost:9983
   assert_output --partial "Created collection 'COLL_NAME'"
 }
 
 @test "reject d option with invalid config dir" {
-  run ! solr create_collection -c COLL_NAME -d /asdf  -solrUrl http://localhost:8983/solr
+  run ! solr create -c COLL_NAME -d /asdf  -solrUrl http://localhost:8983/solr
   assert_output --partial "Specified configuration directory /asdf not found!"
 }
 
 @test "accept d option with builtin config" {
-  run solr create_collection -c COLL_NAME -d sample_techproducts_configs -solrUrl http://localhost:8983/solr
+  run solr create -c COLL_NAME -d sample_techproducts_configs -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"
 }
 
@@ -71,34 +71,34 @@ teardown() {
   test -d $source_configset_dir
   cp -r "${source_configset_dir}" "${dest_configset_dir}"
 
-  run solr create_collection -c COLL_NAME -d "${dest_configset_dir}" -solrUrl http://localhost:8983/solr
+  run solr create -c COLL_NAME -d "${dest_configset_dir}" -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"
 }
 
 @test "accept n option as config name" {
-  run solr create_collection -c COLL_NAME -n other_conf_name -solrUrl http://localhost:8983/solr
+  run solr create -c COLL_NAME -n other_conf_name -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"
   assert_output --partial "config-set 'other_conf_name'"
 }
 
 @test "allow config reuse when n option specifies same config" {
-  run -0 solr create_collection -c COLL_NAME_1 -n shared_config -solrUrl http://localhost:8983/solr
+  run -0 solr create -c COLL_NAME_1 -n shared_config -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME_1'"
   assert_output --partial "config-set 'shared_config'"
 
-  run -0 solr create_collection -c COLL_NAME_2 -n shared_config -solrUrl http://localhost:8983/solr
+  run -0 solr create -c COLL_NAME_2 -n shared_config -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME_2'"
   assert_output --partial "config-set 'shared_config'"
 }
 
 @test "create multisharded collections when s provided" {
-  run -0 solr create_collection -c COLL_NAME -s 2 -solrUrl http://localhost:8983/solr
+  run -0 solr create -c COLL_NAME -s 2 -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"
   assert_output --partial "2 shard(s)"
 }
 
 @test "create replicated collections when rf provided" {
-  run -0 solr create_collection -c COLL_NAME -rf 2 -solrUrl http://localhost:8983/solr
+  run -0 solr create -c COLL_NAME -rf 2 -solrUrl http://localhost:8983/solr
   assert_output --partial "Created collection 'COLL_NAME'"
   assert_output --partial "2 replica(s)"
 }
diff --git a/solr/packaging/test/test_delete_collection.bats b/solr/packaging/test/test_delete_collection.bats
index 17afe1ec280..6e71d1b52ea 100644
--- a/solr/packaging/test/test_delete_collection.bats
+++ b/solr/packaging/test/test_delete_collection.bats
@@ -39,7 +39,7 @@ teardown() {
 }
 
 @test "can delete collections" {
-  solr create_collection -c "COLL_NAME"
+  solr create -c "COLL_NAME"
   assert collection_exists "COLL_NAME"
 
   solr delete -c "COLL_NAME"
@@ -47,7 +47,7 @@ teardown() {
 }
 
 @test "collection delete also deletes zk config" {
-  solr create_collection -c "COLL_NAME"
+  solr create -c "COLL_NAME"
   assert config_exists "COLL_NAME"
 
   solr delete -c "COLL_NAME"
@@ -55,7 +55,7 @@ teardown() {
 }
 
 @test "deletes accompanying zk config with nondefault name" {
-  solr create_collection -c "COLL_NAME" -n "NONDEFAULT_CONFIG_NAME"
+  solr create -c "COLL_NAME" -n "NONDEFAULT_CONFIG_NAME"
   assert config_exists "NONDEFAULT_CONFIG_NAME"
 
   solr delete -c "COLL_NAME"
@@ -63,7 +63,7 @@ teardown() {
 }
 
 @test "deleteConfig option can opt to leave config in zk" {
-  solr create_collection -c "COLL_NAME"
+  solr create -c "COLL_NAME"
   assert config_exists "COLL_NAME"
 
   solr delete -c "COLL_NAME" -deleteConfig false