Enable TTY password OS tests, plus refactoring (#57759) (#58200)

* Enable TTY password OS tests, plus refactoring (#57759)

Two keystore tests were unintentionally ignored when the
password-protected keystore work was merged. I've reënabled those tests
here.

I've also refactored the test methods a little bit to reduce the API
surface: instead of having a "startElasticsearchTtyPassword" method and
a "startElasticsearchStandardInputPassword" method, I've made a single
"startElasticsearch" method with a "useTty" boolean argument.

* Separate daemonization and non-daemonization case for tty

Centos 6 uses a version of expect that kills the elasticsearch process
when it tries to daemonize. I will fix this in future work but for now
I'm replacing it with a todo.
This commit is contained in:
William Brafford 2020-06-25 10:49:17 -04:00 committed by GitHub
parent 335505c4e1
commit 958b21d727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 38 deletions

View File

@ -39,6 +39,7 @@ import java.util.HashMap;
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 class KeystoreManagementTests extends PackagingTestCase {
public void test20CreateKeystoreManually() throws Exception {
rmKeystoreIfExists();
createKeystore();
createKeystore(null);
final Installation.Executables bin = installation.executables();
verifyKeystorePermissions();
@ -156,28 +157,30 @@ public class KeystoreManagementTests extends PackagingTestCase {
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()));
@ -185,25 +188,45 @@ public class KeystoreManagementTests extends PackagingTestCase {
String password = "keystorepass";
rmKeystoreIfExists();
createKeystore();
setKeystorePassword(password);
createKeystore(password);
assertPasswordProtectedKeystore();
awaitElasticsearchStartup(startElasticsearchTtyPassword(password, true));
awaitElasticsearchStartup(runElasticsearchStartCommand(password, false, true));
ServerUtils.runElasticsearchTests();
stopElasticsearch();
}
@Ignore /* Ignored for feature branch, awaits fix: https://github.com/elastic/elasticsearch/issues/49340 */
public void test43WrongKeystorePasswordOnTty() throws Exception {
@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()));
String password = "keystorepass";
rmKeystoreIfExists();
createKeystore(password);
assertPasswordProtectedKeystore();
awaitElasticsearchStartup(runElasticsearchStartCommand(password, true, true));
ServerUtils.runElasticsearchTests();
stopElasticsearch();
}
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 class KeystoreManagementTests extends PackagingTestCase {
* 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 class KeystoreManagementTests extends PackagingTestCase {
Path esKeystorePassphraseFile = installation.config.resolve("eks");
rmKeystoreIfExists();
createKeystore();
setKeystorePassword(password);
createKeystore(password);
assertPasswordProtectedKeystore();
@ -268,7 +289,7 @@ public class KeystoreManagementTests extends PackagingTestCase {
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 @@ public class KeystoreManagementTests extends PackagingTestCase {
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 @@ public class KeystoreManagementTests extends PackagingTestCase {
throw new RuntimeException(e);
}
}
if (password != null) {
setKeystorePassword(password);
}
}
private void rmKeystoreIfExists() {

View File

@ -368,7 +368,7 @@ public class PackageTests extends PackagingTestCase {
// 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]"));

View File

@ -217,7 +217,7 @@ public abstract class PackagingTestCase extends Assert {
*/
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 @@ public abstract class PackagingTestCase extends Assert {
* 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 abstract class PackagingTestCase extends Assert {
/**
* 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) {

View File

@ -241,12 +241,16 @@ public class Archives {
final Path pidFile = installation.home.resolve("elasticsearch.pid");
final Installation.Executables bin = installation.executables();
// requires the "expect" utility to be installed
List<String> 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<<EXPECT\n"