diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 001f03e0b67..c7c13701496 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -232,6 +232,8 @@ Other Changes * SOLR-8969: SQLHandler causes NPE in non-cloud mode (Markus Jelsma, Kevin Risden) +* SOLR-9610: New AssertTool in SolrCLI for easier cross platform assertions from command line (janhoy) + ================== 6.2.1 ================== Bug Fixes diff --git a/solr/bin/solr b/solr/bin/solr index ac33cc0f33f..f753d7da8cb 100755 --- a/solr/bin/solr +++ b/solr/bin/solr @@ -636,6 +636,12 @@ if [ "$SCRIPT_CMD" == "status" ]; then exit fi +# assert tool +if [ "$SCRIPT_CMD" == "assert" ]; then + run_tool assert $* + exit $? +fi + # run a healthcheck and exit if requested if [ "$SCRIPT_CMD" == "healthcheck" ]; then @@ -1042,7 +1048,7 @@ if [[ "$SCRIPT_CMD" == "zk" ]]; then fi # verify the command given is supported -if [ "$SCRIPT_CMD" != "stop" ] && [ "$SCRIPT_CMD" != "start" ] && [ "$SCRIPT_CMD" != "restart" ] && [ "$SCRIPT_CMD" != "status" ]; then +if [ "$SCRIPT_CMD" != "stop" ] && [ "$SCRIPT_CMD" != "start" ] && [ "$SCRIPT_CMD" != "restart" ] && [ "$SCRIPT_CMD" != "status" ] && [ "$SCRIPT_CMD" != "assert" ]; then print_usage "" "$SCRIPT_CMD is not a valid command!" exit 1 fi diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd index e1f2ffda4a1..0bfb77348a3 100644 --- a/solr/bin/solr.cmd +++ b/solr/bin/solr.cmd @@ -103,6 +103,7 @@ IF "%1"=="status" goto get_info IF "%1"=="version" goto get_version IF "%1"=="-v" goto get_version IF "%1"=="-version" goto get_version +IF "%1"=="assert" goto run_assert REM Only allow the command to be the first argument, assume start if not supplied IF "%1"=="start" goto set_script_cmd @@ -1076,6 +1077,15 @@ IF NOT DEFINED HEALTHCHECK_ZK_HOST set "HEALTHCHECK_ZK_HOST=localhost:9983" org.apache.solr.util.SolrCLI healthcheck -collection !HEALTHCHECK_COLLECTION! -zkHost !HEALTHCHECK_ZK_HOST! goto done +:run_assert +"%JAVA%" %SOLR_SSL_OPTS% %SOLR_ZK_CREDS_AND_ACLS% -Dsolr.install.dir="%SOLR_TIP%" -Dlog4j.configuration="file:%DEFAULT_SERVER_DIR%\scripts\cloud-scripts\log4j.properties" ^ + -classpath "%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*;%DEFAULT_SERVER_DIR%\lib\ext\*" ^ + org.apache.solr.util.SolrCLI %* +if errorlevel 1 ( + exit /b 1 +) +goto done + :get_version "%JAVA%" %SOLR_SSL_OPTS% %SOLR_ZK_CREDS_AND_ACLS% -Dsolr.install.dir="%SOLR_TIP%" -Dlog4j.configuration="file:%DEFAULT_SERVER_DIR%\scripts\cloud-scripts\log4j.properties" ^ -classpath "%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*;%DEFAULT_SERVER_DIR%\lib\ext\*" ^ diff --git a/solr/core/src/java/org/apache/solr/util/SolrCLI.java b/solr/core/src/java/org/apache/solr/util/SolrCLI.java index 574911cb15a..a60620e12b7 100644 --- a/solr/core/src/java/org/apache/solr/util/SolrCLI.java +++ b/solr/core/src/java/org/apache/solr/util/SolrCLI.java @@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.FileOwnerAttributeView; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -159,7 +160,7 @@ public class SolrCLI { return toolExitStatus; } - private void setBasicAuth(CommandLine cli) throws Exception { + protected void setBasicAuth(CommandLine cli) throws Exception { String basicauth = System.getProperty("basicauth", null); if (basicauth != null) { List ss = StrUtils.splitSmart(basicauth, ':'); @@ -367,6 +368,8 @@ public class SolrCLI { return new ZkCpTool(); else if ("ls".equals(toolType)) return new ZkLsTool(); + else if ("assert".equals(toolType)) + return new AssertTool(); // If you add a built-in tool to this class, add it here to avoid // classpath scanning @@ -3129,4 +3132,214 @@ public class SolrCLI { } } // end RunExampleTool class + + /** + * Asserts various conditions and exists with error code if fails, else continues with no output + */ + public static class AssertTool extends ToolBase { + + private static String message = null; + private static boolean useExitCode = false; + + public AssertTool() { this(System.out); } + public AssertTool(PrintStream stdout) { super(stdout); } + + public String getName() { + return "assert"; + } + + @SuppressWarnings("static-access") + public Option[] getOptions() { + return new Option[] { + OptionBuilder + .withDescription("Asserts that we are NOT the root user") + .withLongOpt("not-root") + .create("R"), + OptionBuilder + .withDescription("Asserts that we are the root user") + .withLongOpt("root") + .create("r"), + OptionBuilder + .withDescription("Asserts that Solr is NOT started on a certain URL") + .withLongOpt("not-started") + .hasArg(true) + .withArgName("url") + .create("S"), + OptionBuilder + .withDescription("Asserts that Solr is started on a certain URL") + .withLongOpt("started") + .hasArg(true) + .withArgName("url") + .create("s"), + OptionBuilder + .withDescription("Asserts that we run as same user that owns ") + .withLongOpt("same-user") + .hasArg(true) + .withArgName("directory") + .create("u"), + OptionBuilder + .withDescription("Asserts that directory exists") + .withLongOpt("exists") + .hasArg(true) + .withArgName("directory") + .create("x"), + OptionBuilder + .withDescription("Asserts that directory does NOT exist") + .withLongOpt("not-exists") + .hasArg(true) + .withArgName("directory") + .create("X"), + OptionBuilder + .withDescription("Exception message to be used in place of the default error message") + .withLongOpt("message") + .hasArg(true) + .withArgName("message") + .create("m"), + OptionBuilder + .withDescription("Return an exit code instead of printing error message on assert fail.") + .withLongOpt("exitcode") + .create("e") + }; + } + + public int runTool(CommandLine cli) throws Exception { + verbose = cli.hasOption("verbose"); + + int toolExitStatus = 0; + try { + setBasicAuth(cli); + toolExitStatus = runAssert(cli); + } catch (Exception exc) { + // since this is a CLI, spare the user the stacktrace + String excMsg = exc.getMessage(); + if (excMsg != null) { + System.err.println("\nERROR: " + excMsg + "\n"); + toolExitStatus = 1; + } else { + throw exc; + } + } + return toolExitStatus; + } + + @Override + protected void runImpl(CommandLine cli) throws Exception { + runAssert(cli); + } + + // Custom run method which may return exit code + protected int runAssert(CommandLine cli) throws Exception { + if (cli.getOptions().length == 0 || cli.getArgs().length > 0 || cli.hasOption("h")) { + new HelpFormatter().printHelp("bin/solr assert [-m ] [-e] [-rR] [-s ] [-S ] [-u ] [-x ] [-X ]", getToolOptions(this)); + return 1; + } + if (cli.hasOption("m")) { + message = cli.getOptionValue("m"); + } + if (cli.hasOption("e")) { + useExitCode = true; + } + if (cli.hasOption("r")) { + if (assertRootUser() > 0) return 1; + } + if (cli.hasOption("R")) { + if (assertNotRootUser() > 0) return 1; + } + if (cli.hasOption("x")) { + if (assertFileExists(cli.getOptionValue("x")) > 0) return 1; + } + if (cli.hasOption("X")) { + if (assertFileNotExists(cli.getOptionValue("X")) > 0) return 1; + } + if (cli.hasOption("u")) { + if (sameUser(cli.getOptionValue("u")) > 0) return 1; + } + if (cli.hasOption("s")) { + if (assertSolrRunning(cli.getOptionValue("s")) > 0) return 1; + } + if (cli.hasOption("s")) { + if (assertSolrNotRunning(cli.getOptionValue("S")) > 0) return 1; + } + return 0; + } + + public static int assertSolrRunning(String url) throws Exception { + StatusTool status = new StatusTool(); + try { + status.waitToSeeSolrUp(url, 5); + } catch (Exception e) { + return exitOrException("Solr is not running on url " + url); + } + return 0; + } + + public static int assertSolrNotRunning(String url) throws Exception { + StatusTool status = new StatusTool(); + try { + status.waitToSeeSolrUp(url, 5); + return exitOrException("Solr is running on url " + url); + } catch (Exception e) { return 0; } + } + + public static int sameUser(String directory) throws Exception { + if (Files.exists(Paths.get(directory))) { + String userForDir = userForDir(Paths.get(directory)); + if (!currentUser().equals(userForDir)) { + return exitOrException("Must run as user " + userForDir + ". We are " + currentUser()); + } + } else { + return exitOrException("Directory " + directory + " does not exist."); + } + return 0; + } + + public static int assertFileExists(String directory) throws Exception { + if (! Files.exists(Paths.get(directory))) { + return exitOrException("Directory " + directory + " does not exist."); + } + return 0; + } + + public static int assertFileNotExists(String directory) throws Exception { + if (Files.exists(Paths.get(directory))) { + return exitOrException("Directory " + directory + " should not exist."); + } + return 0; + } + + public static int assertRootUser() throws Exception { + if (!currentUser().equals("root")) { + return exitOrException("Must run as root user"); + } + return 0; + } + + public static int assertNotRootUser() throws Exception { + if (currentUser().equals("root")) { + return exitOrException("Not allowed to run as root user"); + } + return 0; + } + + public static String currentUser() { + return System.getProperty("user.name"); + } + + public static String userForDir(Path pathToDir) { + try { + FileOwnerAttributeView ownerAttributeView = Files.getFileAttributeView(pathToDir, FileOwnerAttributeView.class); + return ownerAttributeView.getOwner().getName(); + } catch (IOException e) { + return "N/A"; + } + } + + private static int exitOrException(String msg) throws Exception { + if (useExitCode) { + return 1; + } else { + throw new Exception(message != null ? message : msg); + } + } + } // end AssertTool class }