diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java index dfde44ee9fcd0..a20986c7a30ff 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean; import static java.util.Collections.singletonList; import static org.elasticsearch.packaging.util.Archives.ARCHIVE_OWNER; import static org.elasticsearch.packaging.util.Archives.installArchive; @@ -124,7 +125,7 @@ public void test12InstallDockerDistribution() throws Exception { public void test20CreateKeystoreManually() throws Exception { rmKeystoreIfExists(); - createKeystore(); + createKeystore(null); final Installation.Executables bin = installation.executables(); verifyKeystorePermissions(); @@ -156,28 +157,49 @@ public void test40KeystorePasswordOnStandardInput() throws Exception { String password = "^|<>\\&exit"; // code insertion on Windows if special characters are not escaped rmKeystoreIfExists(); - createKeystore(); - setKeystorePassword(password); + createKeystore(password); assertPasswordProtectedKeystore(); - awaitElasticsearchStartup(startElasticsearchStandardInputPassword(password, true)); + awaitElasticsearchStartup(runElasticsearchStartCommand(password, true, false)); ServerUtils.runElasticsearchTests(); stopElasticsearch(); } - public void test41WrongKeystorePasswordOnStandardInput() { + public void test41WrongKeystorePasswordOnStandardInput() throws Exception { assumeTrue("packages will use systemd, which doesn't handle stdin", distribution.isArchive()); assumeThat(installation, is(notNullValue())); assertPasswordProtectedKeystore(); - Shell.Result result = startElasticsearchStandardInputPassword("wrong", false); + Shell.Result result = runElasticsearchStartCommand("wrong", false, false); assertElasticsearchFailure(result, Arrays.asList(ERROR_INCORRECT_PASSWORD, ERROR_CORRUPTED_KEYSTORE), null); } - @Ignore /* Ignored for feature branch, awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */ - public void test42KeystorePasswordOnTty() throws Exception { + /** + * This test simulates a user starting Elasticsearch on the command line without daemonizing + */ + public void test42KeystorePasswordOnTtyRunningInForeground() throws Exception { + /* Windows issue awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */ + assumeTrue("expect command isn't on Windows", distribution.platform != Distribution.Platform.WINDOWS); + assumeTrue("packages will use systemd, which doesn't handle stdin", distribution.isArchive()); + assumeThat(installation, is(notNullValue())); + + String password = "keystorepass"; + + rmKeystoreIfExists(); + createKeystore(password); + + assertPasswordProtectedKeystore(); + + awaitElasticsearchStartup(runElasticsearchStartCommand(password, false, true)); + ServerUtils.runElasticsearchTests(); + stopElasticsearch(); + } + + @Ignore // awaits fix: https://github.com/elastic/elasticsearch/issues/49340 + public void test43KeystorePasswordOnTtyDaemonized() throws Exception { + /* Windows issue awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */ assumeTrue("expect command isn't on Windows", distribution.platform != Distribution.Platform.WINDOWS); assumeTrue("packages will use systemd, which doesn't handle stdin", distribution.isArchive()); assumeThat(installation, is(notNullValue())); @@ -185,25 +207,26 @@ public void test42KeystorePasswordOnTty() throws Exception { String password = "keystorepass"; rmKeystoreIfExists(); - createKeystore(); - setKeystorePassword(password); + createKeystore(password); assertPasswordProtectedKeystore(); - awaitElasticsearchStartup(startElasticsearchTtyPassword(password, true)); + awaitElasticsearchStartup(runElasticsearchStartCommand(password, true, true)); ServerUtils.runElasticsearchTests(); stopElasticsearch(); } - @Ignore /* Ignored for feature branch, awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */ - public void test43WrongKeystorePasswordOnTty() throws Exception { + public void test44WrongKeystorePasswordOnTty() throws Exception { + /* Windows issue awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */ assumeTrue("expect command isn't on Windows", distribution.platform != Distribution.Platform.WINDOWS); assumeTrue("packages will use systemd, which doesn't handle stdin", distribution.isArchive()); assumeThat(installation, is(notNullValue())); assertPasswordProtectedKeystore(); - Shell.Result result = startElasticsearchTtyPassword("wrong", false); + // daemonization shouldn't matter for this test + boolean daemonize = randomBoolean(); + Shell.Result result = runElasticsearchStartCommand("wrong", daemonize, true); // error will be on stdout for "expect" assertThat(result.stdout, anyOf(containsString(ERROR_INCORRECT_PASSWORD), containsString(ERROR_CORRUPTED_KEYSTORE))); } @@ -212,14 +235,13 @@ public void test43WrongKeystorePasswordOnTty() throws Exception { * If we have an encrypted keystore, we shouldn't require a password to * view help information. */ - public void test44EncryptedKeystoreAllowsHelpMessage() throws Exception { + public void test45EncryptedKeystoreAllowsHelpMessage() throws Exception { assumeTrue("users call elasticsearch directly in archive case", distribution.isArchive()); String password = "keystorepass"; rmKeystoreIfExists(); - createKeystore(); - setKeystorePassword(password); + createKeystore(password); assertPasswordProtectedKeystore(); Shell.Result r = installation.executables().elasticsearch.run("--help"); @@ -232,8 +254,7 @@ public void test50KeystorePasswordFromFile() throws Exception { Path esKeystorePassphraseFile = installation.config.resolve("eks"); rmKeystoreIfExists(); - createKeystore(); - setKeystorePassword(password); + createKeystore(password); assertPasswordProtectedKeystore(); @@ -268,7 +289,7 @@ public void test51WrongKeystorePasswordFromFile() throws Exception { Files.write(esKeystorePassphraseFile, singletonList("wrongpassword")); Packages.JournaldWrapper journaldWrapper = new Packages.JournaldWrapper(sh); - Shell.Result result = runElasticsearchStartCommand(false); + Shell.Result result = runElasticsearchStartCommand(null, false, false); assertElasticsearchFailure(result, Arrays.asList(ERROR_INCORRECT_PASSWORD, ERROR_CORRUPTED_KEYSTORE), journaldWrapper); } finally { sh.run("sudo systemctl unset-environment ES_KEYSTORE_PASSPHRASE_FILE"); @@ -399,7 +420,8 @@ private Path getKeystoreFileFromDockerContainer(String password, Path dockerKeys return tempDirectory.resolve("elasticsearch.keystore"); } - private void createKeystore() throws Exception { + /** Create a keystore. Provide a password to password-protect it, otherwise use null */ + private void createKeystore(String password) throws Exception { Path keystore = installation.config("elasticsearch.keystore"); final Installation.Executables bin = installation.executables(); bin.keystoreTool.run("create"); @@ -417,6 +439,10 @@ private void createKeystore() throws Exception { throw new RuntimeException(e); } } + + if (password != null) { + setKeystorePassword(password); + } } private void rmKeystoreIfExists() { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index f195a24aa7d61..75d00b6485661 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -368,7 +368,7 @@ public void test90DoNotCloseStderrWhenQuiet() throws Exception { // Make sure we don't pick up the journal entries for previous ES instances. Packages.JournaldWrapper journald = new Packages.JournaldWrapper(sh); - runElasticsearchStartCommand(true); + runElasticsearchStartCommand(null, true, false); final Result logs = journald.getLogs(); assertThat(logs.stdout, containsString("Failed to load settings from [elasticsearch.yml]")); diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java index 0e4a7758a47cb..356f81c2136a2 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java @@ -217,7 +217,7 @@ protected static void cleanup() throws Exception { */ protected void assertWhileRunning(Platforms.PlatformAction assertions) throws Exception { try { - awaitElasticsearchStartup(runElasticsearchStartCommand(true)); + awaitElasticsearchStartup(runElasticsearchStartCommand(null, true, false)); } catch (Exception e) { if (Files.exists(installation.home.resolve("elasticsearch.pid"))) { String pid = FileUtils.slurp(installation.home.resolve("elasticsearch.pid")).trim(); @@ -249,14 +249,27 @@ protected void assertWhileRunning(Platforms.PlatformAction assertions) throws Ex * Run the command to start Elasticsearch, but don't wait or test for success. * This method is useful for testing failure conditions in startup. To await success, * use {@link #startElasticsearch()}. + * @param password Password for password-protected keystore, null for no password; + * this option will fail for non-archive distributions + * @param daemonize Run Elasticsearch in the background + * @param useTty Use a tty for inputting the password rather than standard input; + * this option will fail for non-archive distributions * @return Shell results of the startup command. * @throws Exception when command fails immediately. */ - public Shell.Result runElasticsearchStartCommand(boolean daemonize) throws Exception { + public Shell.Result runElasticsearchStartCommand(String password, boolean daemonize, boolean useTty) throws Exception { + if (password != null) { + assertTrue("Only archives support user-entered passwords", distribution().isArchive()); + } + switch (distribution.packaging) { case TAR: case ZIP: - return Archives.runElasticsearchStartCommand(installation, sh, null, daemonize); + if (useTty) { + return Archives.startElasticsearchWithTty(installation, sh, password, daemonize); + } else { + return Archives.runElasticsearchStartCommand(installation, sh, password, daemonize); + } case DEB: case RPM: return Packages.runElasticsearchStartCommand(sh); @@ -307,21 +320,11 @@ public void awaitElasticsearchStartup(Shell.Result result) throws Exception { /** * Start Elasticsearch and wait until it's up and running. If you just want to run - * the start command, use {@link #runElasticsearchStartCommand(boolean)}. + * the start command, use {@link #runElasticsearchStartCommand(String, boolean, boolean)}. * @throws Exception if Elasticsearch can't start */ public void startElasticsearch() throws Exception { - awaitElasticsearchStartup(runElasticsearchStartCommand(true)); - } - - public Shell.Result startElasticsearchStandardInputPassword(String password, boolean daemonize) { - assertTrue("Only archives support passwords on standard input", distribution().isArchive()); - return Archives.runElasticsearchStartCommand(installation, sh, password, daemonize); - } - - public Shell.Result startElasticsearchTtyPassword(String password, boolean daemonize) throws Exception { - assertTrue("Only archives support passwords on TTY", distribution().isArchive()); - return Archives.startElasticsearchWithTty(installation, sh, password, daemonize); + awaitElasticsearchStartup(runElasticsearchStartCommand(null, true, false)); } public void assertElasticsearchFailure(Shell.Result result, String expectedMessage, Packages.JournaldWrapper journaldWrapper) { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java index db9abb5d2971c..2cda7f7e5de72 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java @@ -241,12 +241,16 @@ public static Shell.Result startElasticsearchWithTty(Installation installation, final Path pidFile = installation.home.resolve("elasticsearch.pid"); final Installation.Executables bin = installation.executables(); - // requires the "expect" utility to be installed List command = new ArrayList<>(); command.add("sudo -E -u %s %s -p %s"); + + // requires the "expect" utility to be installed + // TODO: daemonization isn't working with expect versions prior to 5.45, but centos-6 has 5.45.1.15 + // TODO: try using pty4j to make daemonization work if (daemonize) { command.add("-d"); } + String script = String.format( Locale.ROOT, "expect -c \"$(cat<