diff --git a/changelog/unreleased/SOLR-18118.yml b/changelog/unreleased/SOLR-18118.yml new file mode 100644 index 000000000000..e3d703091ff7 --- /dev/null +++ b/changelog/unreleased/SOLR-18118.yml @@ -0,0 +1,9 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Script creating a cluster using bin/solr start -e cloud with --prompt-inputs option. +type: added # added, changed, fixed, deprecated, removed, dependency_update, security, other +authors: + - name: Eric Pugh + - name: Rahul Goswami +links: + - name: SOLR-18118 + url: https://issues.apache.org/jira/browse/SOLR-18118 diff --git a/solr/bin/solr b/solr/bin/solr index ec7f596144db..eaa417ca90d0 100755 --- a/solr/bin/solr +++ b/solr/bin/solr @@ -407,12 +407,6 @@ function print_usage() { echo " --data-home Sets the solr.data.home system property, where Solr will store index data in /data subdirectories." echo " If not set, Solr uses solr.solr.home for config and data." echo "" - echo " -e/--example Name of the example to run; available examples:" - echo " cloud: SolrCloud example" - echo " techproducts: Comprehensive example illustrating many of Solr's core capabilities" - echo " schemaless: Schema-less example (schema is inferred from data during indexing)" - echo " films: Example of starting with _default configset and adding explicit fields dynamically" - echo "" echo " --jvm-opts Additional parameters to pass to the JVM when starting Solr, such as to setup" echo " Java debug options. For example, to enable a Java debugger to attach to the Solr JVM" echo " you could pass: --jvm-opts \"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=18983\"" @@ -423,7 +417,15 @@ function print_usage() { echo " you could pass: -j \"--include-jetty-dir=/etc/jetty/custom/server/\"" echo " In most cases, you should wrap the additional parameters in double quotes." echo "" - echo " -y/--no-prompt Don't prompt for input; accept all defaults when running examples that accept user input" + echo " -e/--example Name of the example to run; available examples:" + echo " cloud: SolrCloud example" + echo " techproducts: Comprehensive example illustrating many of Solr's core capabilities" + echo " schemaless: Schema-less example (schema is inferred from data during indexing)" + echo " films: Example of starting with _default configset and adding explicit fields dynamically" + echo "" + echo " -y/--no-prompt Don't prompt for input; accept all defaults when running examples that accept user input." + echo "" + echo " --prompt-inputs Don't prompt for input; comma delimited list of inputs read when running examples that accept user input." echo "" echo " --force If attempting to start Solr as the root user, the script will exit with a warning that running Solr as \"root\" can cause problems." echo " It is possible to override this warning with the '--force' parameter." @@ -817,6 +819,14 @@ if [ $# -gt 0 ]; then PASS_TO_RUN_EXAMPLE+=("--no-prompt") shift ;; + --prompt-inputs) + if [[ -z "$2" || "${2:0:1}" == "-" ]]; then + print_usage "$SCRIPT_CMD" "Prompt values are required when using the $1 option!" + exit 1 + fi + PASS_TO_RUN_EXAMPLE+=("--prompt-inputs" "$2") + shift 2 + ;; --verbose) verbose=true SOLR_LOG_LEVEL=DEBUG diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd index 52be92ed3de3..e30cc4416fa8 100755 --- a/solr/bin/solr.cmd +++ b/solr/bin/solr.cmd @@ -325,12 +325,6 @@ goto err @echo --data-home dir Sets the solr.data.home system property, where Solr will store index data in ^/data subdirectories. @echo If not set, Solr uses solr.solr.home for both config and data. @echo. -@echo -e/--example name Name of the example to run; available examples: -@echo cloud: SolrCloud example -@echo techproducts: Comprehensive example illustrating many of Solr's core capabilities -@echo schemaless: Schema-less example (schema is inferred from data during indexing) -@echo films: Example of starting with _default configset and defining explicit fields dynamically -@echo. @echo --jvm-opts opts Additional parameters to pass to the JVM when starting Solr, such as to setup @echo Java debug options. For example, to enable a Java debugger to attach to the Solr JVM @echo you could pass: --jvm-opts "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=18983" @@ -341,8 +335,16 @@ goto err @echo you could pass: -j "--include-jetty-dir=/etc/jetty/custom/server/" @echo In most cases, you should wrap the additional parameters in double quotes. @echo. +@echo -e/--example name Name of the example to run; available examples: +@echo cloud: SolrCloud example +@echo techproducts: Comprehensive example illustrating many of Solr's core capabilities +@echo schemaless: Schema-less example (schema is inferred from data during indexing) +@echo films: Example of starting with _default configset and defining explicit fields dynamically +@echo. @echo -y/--no-prompt Don't prompt for input; accept all defaults when running examples that accept user input @echo. +@echo --prompt-inputs values Don't prompt for input; comma delimited list of inputs read when running examples that accept user input. +@echo. @echo --verbose and -q/--quiet Verbose or quiet logging. Sets default log level to DEBUG or WARN instead of INFO @echo. goto done @@ -399,6 +401,7 @@ IF "%1"=="-j" goto set_addl_jetty_config IF "%1"=="--jettyconfig" goto set_addl_jetty_config IF "%1"=="-y" goto set_noprompt IF "%1"=="--no-prompt" goto set_noprompt +IF "%1"=="--prompt-inputs" goto set_prompt_inputs REM Skip stop arg parsing if not stop command IF NOT "%SCRIPT_CMD%"=="stop" goto parse_general_args @@ -695,6 +698,13 @@ set "PASS_TO_RUN_EXAMPLE=--no-prompt !PASS_TO_RUN_EXAMPLE!" SHIFT goto parse_args +:set_prompt_inputs +set "PASS_TO_RUN_EXAMPLE=--prompt-inputs %~2 !PASS_TO_RUN_EXAMPLE!" + +SHIFT +SHIFT +goto parse_args + REM Handle invalid arguments passed to special commands (start, stop, restart) :invalid_cmd_line @echo. 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 9fcd71c911d0..b5b21f6450fd 100644 --- a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java +++ b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java @@ -73,6 +73,16 @@ public class RunExampleTool extends ToolBase { "Don't prompt for input; accept all defaults when running examples that accept user input.") .build(); + private static final Option PROMPT_INPUTS_OPTION = + Option.builder() + .longOpt("prompt-inputs") + .hasArg() + .argName("VALUES") + .desc( + "Provide comma-separated values for prompts. Same as --no-prompt but uses provided values instead of defaults. " + + "Example: --prompt-inputs 3,8983,8984,8985,\"gettingstarted\",2,2,_default") + .build(); + private static final Option EXAMPLE_OPTION = Option.builder("e") .longOpt("example") @@ -176,6 +186,7 @@ public class RunExampleTool extends ToolBase { protected Path exampleDir; protected Path solrHomeDir; protected String urlScheme; + private boolean usingPromptInputs = false; /** Default constructor used by the framework when running as a command-line application. */ public RunExampleTool(ToolRuntime runtime) { @@ -197,6 +208,7 @@ public String getName() { public Options getOptions() { return super.getOptions() .addOption(NO_PROMPT_OPTION) + .addOption(PROMPT_INPUTS_OPTION) .addOption(EXAMPLE_OPTION) .addOption(SCRIPT_OPTION) .addOption(SERVER_DIR_OPTION) @@ -214,6 +226,12 @@ public Options getOptions() { @Override public void runImpl(CommandLine cli) throws Exception { + if (cli.hasOption(NO_PROMPT_OPTION) && cli.hasOption(PROMPT_INPUTS_OPTION)) { + throw new IllegalArgumentException( + "Cannot use both --no-prompt and --prompt-inputs options together. " + + "Use --no-prompt to accept defaults, or --prompt-inputs to provide specific values."); + } + this.urlScheme = cli.getOptionValue(URL_SCHEME_OPTION, "http"); String exampleType = cli.getOptionValue(EXAMPLE_OPTION); @@ -515,6 +533,7 @@ protected void runExample(CommandLine cli, String exampleName) throws Exception protected void runCloudExample(CommandLine cli) throws Exception { + usingPromptInputs = cli.hasOption(PROMPT_INPUTS_OPTION); boolean prompt = !cli.hasOption(NO_PROMPT_OPTION); int numNodes = 2; int[] cloudPorts = new int[] {8983, 7574, 8984, 7575}; @@ -530,10 +549,24 @@ protected void runCloudExample(CommandLine cli) throws Exception { echo("\nWelcome to the SolrCloud example!\n"); - Scanner readInput = prompt ? new Scanner(userInput, StandardCharsets.UTF_8) : null; + Scanner readInput = null; + if (usingPromptInputs) { + // Create a scanner from the provided prompts + String promptsValue = cli.getOptionValue(PROMPT_INPUTS_OPTION); + InputStream promptsStream = + new java.io.ByteArrayInputStream(promptsValue.getBytes(StandardCharsets.UTF_8)); + readInput = new Scanner(promptsStream, StandardCharsets.UTF_8); + readInput.useDelimiter(","); + prompt = true; // Enable prompting code path, but reading from prompts instead of user + } else if (prompt) { + readInput = new Scanner(userInput, StandardCharsets.UTF_8); + } + if (prompt) { - echo( - "This interactive session will help you launch a SolrCloud cluster on your local workstation."); + if (!usingPromptInputs) { + echo( + "This interactive session will help you launch a SolrCloud cluster on your local workstation."); + } // get the number of nodes to start numNodes = @@ -1121,9 +1154,24 @@ protected String prompt(Scanner s, String prompt) { protected String prompt(Scanner s, String prompt, String defaultValue) { echo(prompt); - String nextInput = s.nextLine(); + String nextInput; + if (usingPromptInputs) { + // Reading from prompts option - use next() instead of nextLine() + nextInput = s.hasNext() ? s.next() : null; + // Echo the value being used from prompts + if (nextInput != null) { + echo(nextInput); + } + } else { + // Reading from user input - use nextLine() + nextInput = s.nextLine(); + } if (nextInput != null) { nextInput = nextInput.trim(); + // Remove quotes if present (for values like "gettingstarted") + if (nextInput.startsWith("\"") && nextInput.endsWith("\"")) { + nextInput = nextInput.substring(1, nextInput.length() - 1); + } if (nextInput.isEmpty()) nextInput = null; } return (nextInput != null) ? nextInput : defaultValue; diff --git a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java index db26383eb7f7..298bbfb0644e 100644 --- a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java +++ b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java @@ -525,6 +525,116 @@ public void testInteractiveSolrCloudExample() throws Exception { executor.execute(org.apache.commons.exec.CommandLine.parse("bin/solr stop -p " + bindPort)); } + /** + * Test the --prompt-inputs option that allows providing all prompt values as a comma-separated + * string without requiring interactive input. + */ + @Test + public void testSolrCloudExampleWithPrompts() throws Exception { + Path solrHomeDir = ExternalPaths.SERVER_HOME; + if (!Files.isDirectory(solrHomeDir)) + fail(solrHomeDir + " not found and is required to run this test!"); + + Path solrExampleDir = createTempDir(); + Path solrServerDir = solrHomeDir.getParent(); + + int bindPort = -1; + try (ServerSocket socket = new ServerSocket(0)) { + bindPort = socket.getLocalPort(); + } + + String collectionName = "testCloudExampleWithPrompts"; + + // Provide all prompt values via --prompt-inputs option: + // numNodes, port1, collectionName, numShards, replicationFactor, configName + String promptsValue = "1," + bindPort + ",\"" + collectionName + "\",2,2,_default"; + + String[] toolArgs = + new String[] { + "--example", + "cloud", + "--server-dir", + solrServerDir.toString(), + "--example-dir", + solrExampleDir.toString(), + "--prompt-inputs", + promptsValue + }; + + // capture tool output to stdout + CLITestHelper.TestingRuntime runtime = new CLITestHelper.TestingRuntime(true); + + RunExampleExecutor executor = new RunExampleExecutor(); + closeables.add(executor); + + RunExampleTool tool = new RunExampleTool(executor, System.in, runtime); + try { + tool.runTool(SolrCLI.processCommandLineArgs(tool, toolArgs)); + } catch (Exception e) { + System.err.println( + "RunExampleTool failed due to: " + + e + + "; stdout from tool prior to failure: " + + runtime.getOutput()); + throw e; + } + + String toolOutput = runtime.getOutput(); + + // verify Solr is running on the expected port and verify the collection exists + String solrUrl = "http://localhost:" + bindPort + "/solr"; + if (!CLIUtils.safeCheckCollectionExists(solrUrl, collectionName, null)) { + fail( + "After running Solr cloud example with --prompt-inputs, test collection '" + + collectionName + + "' not found in Solr at: " + + solrUrl + + "; tool output: " + + toolOutput); + } + + // verify the collection was created with the specified parameters + try (CloudSolrClient cloudClient = + new RandomizingCloudSolrClientBuilder( + Collections.singletonList(executor.solrCloudCluster.getZkServer().getZkAddress()), + Optional.empty()) + .withDefaultCollection(collectionName) + .build()) { + + // index some test docs to verify the collection works + int numDocs = 5; + for (int d = 0; d < numDocs; d++) { + SolrInputDocument doc = new SolrInputDocument(); + doc.setField("id", "doc" + d); + doc.setField("test_s", "prompts"); + cloudClient.add(doc); + } + cloudClient.commit(); + + QueryResponse qr = cloudClient.query(new SolrQuery("test_s:prompts")); + assertEquals( + "Expected " + + numDocs + + " docs in the " + + collectionName + + " collection created via --prompts", + numDocs, + qr.getResults().getNumFound()); + } + + // Verify output contains the prompts values + assertTrue( + "Tool output should contain the collection name", toolOutput.contains(collectionName)); + + // delete the collection + DeleteTool deleteTool = new DeleteTool(runtime); + String[] deleteArgs = new String[] {"--name", collectionName, "--solr-url", solrUrl}; + deleteTool.runTool(SolrCLI.processCommandLineArgs(deleteTool, deleteArgs)); + + // stop the test instance + executor.execute(org.apache.commons.exec.CommandLine.parse("bin/solr stop -p " + bindPort)); + } + @Test public void testFailExecuteScript() throws Exception { Path solrHomeDir = ExternalPaths.SERVER_HOME; diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc index 577c566eb4a2..a7348059cc2e 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc @@ -106,27 +106,6 @@ When running multiple instances of Solr on the same host, it is more common to u + *Example*: `bin/solr start --server-dir newServerDir` -`-e `:: -+ -[%autowidth,frame=none] -|=== -|Optional |Default: none -|=== -+ -Start Solr with an example configuration. -These examples are provided to help you get started faster with Solr generally, or just try a specific feature. -+ -The available options are: - -* `cloud`: SolrCloud example -* `techproducts`: Comprehensive example illustrating many of Solr's core capabilities -* `schemaless`: Schema-less example (schema is inferred from data during indexing) -* `films`: Example of starting with _default configset and adding explicit fields dynamically -+ -See the section <> below for more details on the example configurations. -+ -*Example*: `bin/solr start -e schemaless` - `-f`:: + [%autowidth,frame=none] @@ -162,20 +141,6 @@ Sets the min (`-Xms`) and max (`-Xmx`) heap size for the JVM running Solr. + *Example*: `bin/solr start -m 4g` results in `-Xms4g -Xmx4g` settings. -`--no-prompt`:: -+ -[%autowidth,frame=none] -|=== -|Optional |Default: none -|=== -+ -Don't prompt for input; accept all defaults when running examples that accept user input. -+ -For example, when using the "cloud" example, an interactive session guides you through several options for your SolrCloud cluster. -If you want to accept all of the defaults, you can simply add the `--no-prompt` option to your request. -+ -*Example*: `bin/solr start -e cloud --no-prompt` - `-p `:: + [%autowidth,frame=none] @@ -277,6 +242,56 @@ To emphasize how the default settings work take a moment to understand that the `bin/solr start --host localhost -p 8983 --server-dir server --solr-home solr -m 512m` +`-e ` or `--example `:: ++ +[%autowidth,frame=none] +|=== +|Optional |Default: none +|=== ++ +Start Solr with an example configuration. +These examples are provided to help you get started faster with Solr generally, or just try a specific feature. ++ +The available options are: + +* `cloud`: SolrCloud example +* `techproducts`: Comprehensive example illustrating many of Solr's core capabilities +* `schemaless`: Schema-less example (schema is inferred from data during indexing) +* `films`: Example of starting with _default configset and adding explicit fields dynamically ++ +See the section <> below for more details on the example configurations. ++ +*Example*: `bin/solr start -e schemaless` + +`--no-prompt`:: ++ +[%autowidth,frame=none] +|=== +|Optional |Default: none +|=== ++ +Don't prompt for input; accept all defaults when running examples that accept input. ++ +For example, when using the "cloud" example, an interactive session guides you through several options for your SolrCloud cluster. +If you want to accept all of the defaults, you can simply add the `--no-prompt` option to your request. ++ +*Example*: `bin/solr start -e cloud --no-prompt` + +`--prompt-inputs `:: ++ +[%autowidth,frame=none] +|=== +|Optional |Default: none +|=== ++ +Don't prompt for input; instead supply ordered answers in comma delimited format when running examples that accept input. ++ +For example, when using the "cloud" example, you can answer the prompts non-interactively to start a three node cluster on specific ports: ++ +*Example*: `bin/solr start -e cloud --prompt-inputs 3,9000,9001,9002,"mycollection",2,2,_default` + +On Windows please wrap the prompts value in double quotes to preserve the comma delimited format. + It is not necessary to define all of the options when starting if the defaults are fine for your needs. ==== Setting Java System Properties @@ -676,7 +691,7 @@ This defaults to the same name as the collection or core. + *Example*: `bin/solr create -n basic` -`-sh ` or `-shards `:: +`-sh ` or `--shards `:: + [%autowidth,frame=none] |=== @@ -937,7 +952,7 @@ Either `--credentials` or `--prompt` *must* be specified. When `true`, this blocks out access to unauthenticated users from accessing Solr. When `false`, unauthenticated users will still be able to access Solr, but only for operations not explicitly requiring a user role in the Authorization plugin configuration. -`--solrIncludeFile `:: +`--solr-include-file `:: + [%autowidth,frame=none] |===