Merge main into multi-project

This commit is contained in:
Yang Wang 2025-02-06 10:04:49 +11:00
commit 38d74f7408
352 changed files with 4243 additions and 2565 deletions

View file

@ -74,7 +74,7 @@ class AddFileKeyStoreCommand extends BaseKeyStoreCommand {
keyStore.setFile(setting, Files.readAllBytes(file)); keyStore.setFile(setting, Files.readAllBytes(file));
} }
keyStore.save(env.configFile(), getKeyStorePassword().getChars()); keyStore.save(env.configDir(), getKeyStorePassword().getChars());
} }
@SuppressForbidden(reason = "file arg for cli") @SuppressForbidden(reason = "file arg for cli")

View file

@ -100,7 +100,7 @@ class AddStringKeyStoreCommand extends BaseKeyStoreCommand {
} }
} }
keyStore.save(env.configFile(), getKeyStorePassword().getChars()); keyStore.save(env.configDir(), getKeyStorePassword().getChars());
} }
} }

View file

@ -39,14 +39,14 @@ public abstract class BaseKeyStoreCommand extends KeyStoreAwareCommand {
@Override @Override
public final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
try { try {
final Path configFile = env.configFile(); final Path configFile = env.configDir();
keyStore = KeyStoreWrapper.load(configFile); keyStore = KeyStoreWrapper.load(configFile);
if (keyStore == null) { if (keyStore == null) {
if (keyStoreMustExist) { if (keyStoreMustExist) {
throw new UserException( throw new UserException(
ExitCodes.DATA_ERROR, ExitCodes.DATA_ERROR,
"Elasticsearch keystore not found at [" "Elasticsearch keystore not found at ["
+ KeyStoreWrapper.keystorePath(env.configFile()) + KeyStoreWrapper.keystorePath(env.configDir())
+ "]. Use 'create' command to create one." + "]. Use 'create' command to create one."
); );
} else if (options.has(forceOption) == false) { } else if (options.has(forceOption) == false) {

View file

@ -31,7 +31,7 @@ class ChangeKeyStorePasswordCommand extends BaseKeyStoreCommand {
protected void executeCommand(Terminal terminal, OptionSet options, Environment env) throws Exception { protected void executeCommand(Terminal terminal, OptionSet options, Environment env) throws Exception {
try (SecureString newPassword = readPassword(terminal, true)) { try (SecureString newPassword = readPassword(terminal, true)) {
final KeyStoreWrapper keyStore = getKeyStore(); final KeyStoreWrapper keyStore = getKeyStore();
keyStore.save(env.configFile(), newPassword.getChars()); keyStore.save(env.configDir(), newPassword.getChars());
terminal.println("Elasticsearch keystore password changed successfully."); terminal.println("Elasticsearch keystore password changed successfully.");
} catch (SecurityException e) { } catch (SecurityException e) {
throw new UserException(ExitCodes.DATA_ERROR, e.getMessage()); throw new UserException(ExitCodes.DATA_ERROR, e.getMessage());

View file

@ -40,7 +40,7 @@ class CreateKeyStoreCommand extends KeyStoreAwareCommand {
@Override @Override
public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
try (SecureString password = options.has(passwordOption) ? readPassword(terminal, true) : new SecureString(new char[0])) { try (SecureString password = options.has(passwordOption) ? readPassword(terminal, true) : new SecureString(new char[0])) {
Path keystoreFile = KeyStoreWrapper.keystorePath(env.configFile()); Path keystoreFile = KeyStoreWrapper.keystorePath(env.configDir());
if (Files.exists(keystoreFile)) { if (Files.exists(keystoreFile)) {
if (terminal.promptYesNo("An elasticsearch keystore already exists. Overwrite?", false) == false) { if (terminal.promptYesNo("An elasticsearch keystore already exists. Overwrite?", false) == false) {
terminal.println("Exiting without creating keystore."); terminal.println("Exiting without creating keystore.");
@ -48,8 +48,8 @@ class CreateKeyStoreCommand extends KeyStoreAwareCommand {
} }
} }
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
keystore.save(env.configFile(), password.getChars()); keystore.save(env.configDir(), password.getChars());
terminal.println("Created elasticsearch keystore in " + KeyStoreWrapper.keystorePath(env.configFile())); terminal.println("Created elasticsearch keystore in " + KeyStoreWrapper.keystorePath(env.configDir()));
} catch (SecurityException e) { } catch (SecurityException e) {
throw new UserException(ExitCodes.IO_ERROR, "Error creating the elasticsearch keystore."); throw new UserException(ExitCodes.IO_ERROR, "Error creating the elasticsearch keystore.");
} }

View file

@ -32,7 +32,7 @@ public class HasPasswordKeyStoreCommand extends KeyStoreAwareCommand {
@Override @Override
public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
final Path configFile = env.configFile(); final Path configFile = env.configDir();
final KeyStoreWrapper keyStore = KeyStoreWrapper.load(configFile); final KeyStoreWrapper keyStore = KeyStoreWrapper.load(configFile);
// We handle error printing here so we can respect the "--silent" flag // We handle error printing here so we can respect the "--silent" flag

View file

@ -45,6 +45,6 @@ class RemoveSettingKeyStoreCommand extends BaseKeyStoreCommand {
} }
keyStore.remove(setting); keyStore.remove(setting);
} }
keyStore.save(env.configFile(), getKeyStorePassword().getChars()); keyStore.save(env.configDir(), getKeyStorePassword().getChars());
} }
} }

View file

@ -26,7 +26,7 @@ public class UpgradeKeyStoreCommand extends BaseKeyStoreCommand {
@Override @Override
protected void executeCommand(final Terminal terminal, final OptionSet options, final Environment env) throws Exception { protected void executeCommand(final Terminal terminal, final OptionSet options, final Environment env) throws Exception {
KeyStoreWrapper.upgrade(getKeyStore(), env.configFile(), getKeyStorePassword().getChars()); KeyStoreWrapper.upgrade(getKeyStore(), env.configDir(), getKeyStorePassword().getChars());
} }
} }

View file

@ -46,14 +46,14 @@ public class AddFileKeyStoreCommandTests extends KeyStoreCommandTestCase {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
bytes[i] = randomByte(); bytes[i] = randomByte();
} }
Path file = env.configFile().resolve(randomAlphaOfLength(16)); Path file = env.configDir().resolve(randomAlphaOfLength(16));
Files.write(file, bytes); Files.write(file, bytes);
return file; return file;
} }
private void addFile(KeyStoreWrapper keystore, String setting, Path file, String password) throws Exception { private void addFile(KeyStoreWrapper keystore, String setting, Path file, String password) throws Exception {
keystore.setFile(setting, Files.readAllBytes(file)); keystore.setFile(setting, Files.readAllBytes(file));
keystore.save(env.configFile(), password.toCharArray()); keystore.save(env.configDir(), password.toCharArray());
} }
public void testMissingCreateWithEmptyPasswordWhenPrompted() throws Exception { public void testMissingCreateWithEmptyPasswordWhenPrompted() throws Exception {
@ -77,7 +77,7 @@ public class AddFileKeyStoreCommandTests extends KeyStoreCommandTestCase {
terminal.addSecretInput(randomFrom("", "keystorepassword")); terminal.addSecretInput(randomFrom("", "keystorepassword"));
terminal.addTextInput("n"); // explicit no terminal.addTextInput("n"); // explicit no
execute("foo"); execute("foo");
assertNull(KeyStoreWrapper.load(env.configFile())); assertNull(KeyStoreWrapper.load(env.configDir()));
} }
public void testOverwritePromptDefault() throws Exception { public void testOverwritePromptDefault() throws Exception {

View file

@ -83,7 +83,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testMissingNoCreate() throws Exception { public void testMissingNoCreate() throws Exception {
terminal.addTextInput("n"); // explicit no terminal.addTextInput("n"); // explicit no
execute("foo"); execute("foo");
assertNull(KeyStoreWrapper.load(env.configFile())); assertNull(KeyStoreWrapper.load(env.configDir()));
} }
public void testOverwritePromptDefault() throws Exception { public void testOverwritePromptDefault() throws Exception {
@ -143,7 +143,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testPromptForValue() throws Exception { public void testPromptForValue() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
terminal.addSecretInput("secret value"); terminal.addSecretInput("secret value");
execute("foo"); execute("foo");
@ -152,7 +152,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testPromptForMultipleValues() throws Exception { public void testPromptForMultipleValues() throws Exception {
final String password = "keystorepassword"; final String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
terminal.addSecretInput("bar1"); terminal.addSecretInput("bar1");
terminal.addSecretInput("bar2"); terminal.addSecretInput("bar2");
@ -165,7 +165,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinShort() throws Exception { public void testStdinShort() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput("secret value 1"); setInput("secret value 1");
execute("-x", "foo"); execute("-x", "foo");
@ -174,7 +174,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinLong() throws Exception { public void testStdinLong() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput("secret value 2"); setInput("secret value 2");
execute("--stdin", "foo"); execute("--stdin", "foo");
@ -183,7 +183,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinNoInput() throws Exception { public void testStdinNoInput() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput(""); setInput("");
execute("-x", "foo"); execute("-x", "foo");
@ -192,7 +192,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinInputWithLineBreaks() throws Exception { public void testStdinInputWithLineBreaks() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput("Typedthisandhitenter\n"); setInput("Typedthisandhitenter\n");
execute("-x", "foo"); execute("-x", "foo");
@ -201,7 +201,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinInputWithCarriageReturn() throws Exception { public void testStdinInputWithCarriageReturn() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput("Typedthisandhitenter\r"); setInput("Typedthisandhitenter\r");
execute("-x", "foo"); execute("-x", "foo");
@ -210,7 +210,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testStdinWithMultipleValues() throws Exception { public void testStdinWithMultipleValues() throws Exception {
final String password = "keystorepassword"; final String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
setInput("bar1\nbar2\nbar3"); setInput("bar1\nbar2\nbar3");
execute(randomFrom("-x", "--stdin"), "foo1", "foo2", "foo3"); execute(randomFrom("-x", "--stdin"), "foo1", "foo2", "foo3");
@ -221,7 +221,7 @@ public class AddStringKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testAddUtf8String() throws Exception { public void testAddUtf8String() throws Exception {
String password = "keystorepassword"; String password = "keystorepassword";
KeyStoreWrapper.create().save(env.configFile(), password.toCharArray()); KeyStoreWrapper.create().save(env.configDir(), password.toCharArray());
terminal.addSecretInput(password); terminal.addSecretInput(password);
final int stringSize = randomIntBetween(8, 16); final int stringSize = randomIntBetween(8, 16);
try (CharArrayWriter secretChars = new CharArrayWriter(stringSize)) { try (CharArrayWriter secretChars = new CharArrayWriter(stringSize)) {

View file

@ -42,7 +42,7 @@ public class BootstrapTests extends ESTestCase {
public void testLoadSecureSettings() throws Exception { public void testLoadSecureSettings() throws Exception {
final char[] password = KeyStoreWrapperTests.getPossibleKeystorePassword(); final char[] password = KeyStoreWrapperTests.getPossibleKeystorePassword();
final Path configPath = env.configFile(); final Path configPath = env.configDir();
final SecureString seed; final SecureString seed;
try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) { try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) {
seed = KeyStoreWrapper.SEED_SETTING.get(Settings.builder().setSecureSettings(keyStoreWrapper).build()); seed = KeyStoreWrapper.SEED_SETTING.get(Settings.builder().setSecureSettings(keyStoreWrapper).build());

View file

@ -48,7 +48,7 @@ public class CreateKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testDefaultNotPromptForPassword() throws Exception { public void testDefaultNotPromptForPassword() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm()); assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
execute(); execute();
Path configDir = env.configFile(); Path configDir = env.configDir();
assertNotNull(KeyStoreWrapper.load(configDir)); assertNotNull(KeyStoreWrapper.load(configDir));
} }
@ -63,7 +63,7 @@ public class CreateKeyStoreCommandTests extends KeyStoreCommandTestCase {
} else { } else {
execute(); execute();
} }
Path configDir = env.configFile(); Path configDir = env.configDir();
assertNotNull(KeyStoreWrapper.load(configDir)); assertNotNull(KeyStoreWrapper.load(configDir));
} }
@ -79,13 +79,13 @@ public class CreateKeyStoreCommandTests extends KeyStoreCommandTestCase {
} else { } else {
execute(); execute();
} }
Path configDir = env.configFile(); Path configDir = env.configDir();
assertNotNull(KeyStoreWrapper.load(configDir)); assertNotNull(KeyStoreWrapper.load(configDir));
} }
public void testOverwrite() throws Exception { public void testOverwrite() throws Exception {
String password = getPossibleKeystorePassword(); String password = getPossibleKeystorePassword();
Path keystoreFile = KeyStoreWrapper.keystorePath(env.configFile()); Path keystoreFile = KeyStoreWrapper.keystorePath(env.configDir());
byte[] content = "not a keystore".getBytes(StandardCharsets.UTF_8); byte[] content = "not a keystore".getBytes(StandardCharsets.UTF_8);
Files.write(keystoreFile, content); Files.write(keystoreFile, content);
@ -110,6 +110,6 @@ public class CreateKeyStoreCommandTests extends KeyStoreCommandTestCase {
} else { } else {
execute(); execute();
} }
assertNotNull(KeyStoreWrapper.load(env.configFile())); assertNotNull(KeyStoreWrapper.load(env.configDir()));
} }
} }

View file

@ -77,11 +77,11 @@ public abstract class KeyStoreCommandTestCase extends CommandTestCase {
} }
void saveKeystore(KeyStoreWrapper keystore, String password) throws Exception { void saveKeystore(KeyStoreWrapper keystore, String password) throws Exception {
keystore.save(env.configFile(), password.toCharArray()); keystore.save(env.configDir(), password.toCharArray());
} }
KeyStoreWrapper loadKeystore(String password) throws Exception { KeyStoreWrapper loadKeystore(String password) throws Exception {
KeyStoreWrapper keystore = KeyStoreWrapper.load(env.configFile()); KeyStoreWrapper keystore = KeyStoreWrapper.load(env.configDir());
keystore.decrypt(password.toCharArray()); keystore.decrypt(password.toCharArray());
return keystore; return keystore;
} }

View file

@ -84,8 +84,8 @@ public class KeyStoreWrapperTests extends ESTestCase {
bytes[i] = (byte) i; bytes[i] = (byte) i;
} }
keystore.setFile("foo", bytes); keystore.setFile("foo", bytes);
keystore.save(env.configFile(), password); keystore.save(env.configDir(), password);
keystore = KeyStoreWrapper.load(env.configFile()); keystore = KeyStoreWrapper.load(env.configDir());
keystore.decrypt(password); keystore.decrypt(password);
try (InputStream stream = keystore.getFile("foo")) { try (InputStream stream = keystore.getFile("foo")) {
for (int i = 0; i < 256; ++i) { for (int i = 0; i < 256; ++i) {
@ -114,8 +114,8 @@ public class KeyStoreWrapperTests extends ESTestCase {
invalidPassword[realPassword.length] = '#'; invalidPassword[realPassword.length] = '#';
} }
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
keystore.save(env.configFile(), realPassword); keystore.save(env.configDir(), realPassword);
final KeyStoreWrapper loadedkeystore = KeyStoreWrapper.load(env.configFile()); final KeyStoreWrapper loadedkeystore = KeyStoreWrapper.load(env.configDir());
final SecurityException exception = expectThrows(SecurityException.class, () -> loadedkeystore.decrypt(invalidPassword)); final SecurityException exception = expectThrows(SecurityException.class, () -> loadedkeystore.decrypt(invalidPassword));
if (inFipsJvm()) { if (inFipsJvm()) {
assertThat( assertThat(
@ -133,8 +133,8 @@ public class KeyStoreWrapperTests extends ESTestCase {
public void testDecryptKeyStoreWithShortPasswordInFips() throws Exception { public void testDecryptKeyStoreWithShortPasswordInFips() throws Exception {
assumeTrue("This should run only in FIPS mode", inFipsJvm()); assumeTrue("This should run only in FIPS mode", inFipsJvm());
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
keystore.save(env.configFile(), "alongenoughpassword".toCharArray()); keystore.save(env.configDir(), "alongenoughpassword".toCharArray());
final KeyStoreWrapper loadedkeystore = KeyStoreWrapper.load(env.configFile()); final KeyStoreWrapper loadedkeystore = KeyStoreWrapper.load(env.configDir());
final GeneralSecurityException exception = expectThrows( final GeneralSecurityException exception = expectThrows(
GeneralSecurityException.class, GeneralSecurityException.class,
() -> loadedkeystore.decrypt("shortpwd".toCharArray()) // shorter than 14 characters () -> loadedkeystore.decrypt("shortpwd".toCharArray()) // shorter than 14 characters
@ -147,7 +147,7 @@ public class KeyStoreWrapperTests extends ESTestCase {
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
final GeneralSecurityException exception = expectThrows( final GeneralSecurityException exception = expectThrows(
GeneralSecurityException.class, GeneralSecurityException.class,
() -> keystore.save(env.configFile(), "shortpwd".toCharArray()) // shorter than 14 characters () -> keystore.save(env.configDir(), "shortpwd".toCharArray()) // shorter than 14 characters
); );
assertThat(exception.getMessage(), containsString("Error generating an encryption key from the provided password")); assertThat(exception.getMessage(), containsString("Error generating an encryption key from the provided password"));
} }
@ -192,18 +192,18 @@ public class KeyStoreWrapperTests extends ESTestCase {
final char[] password = getPossibleKeystorePassword(); final char[] password = getPossibleKeystorePassword();
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()); SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey());
keystore.save(env.configFile(), password); keystore.save(env.configDir(), password);
// upgrade does not overwrite seed // upgrade does not overwrite seed
KeyStoreWrapper.upgrade(keystore, env.configFile(), password); KeyStoreWrapper.upgrade(keystore, env.configDir(), password);
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString()); assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
keystore = KeyStoreWrapper.load(env.configFile()); keystore = KeyStoreWrapper.load(env.configDir());
keystore.decrypt(password); keystore.decrypt(password);
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString()); assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
} }
public void testFailWhenCannotConsumeSecretStream() throws Exception { public void testFailWhenCannotConsumeSecretStream() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm()); assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
Path configDir = env.configFile(); Path configDir = env.configDir();
try ( try (
Directory directory = newFSDirectory(configDir); Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT) IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
@ -234,7 +234,7 @@ public class KeyStoreWrapperTests extends ESTestCase {
public void testFailWhenCannotConsumeEncryptedBytesStream() throws Exception { public void testFailWhenCannotConsumeEncryptedBytesStream() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm()); assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
Path configDir = env.configFile(); Path configDir = env.configDir();
try ( try (
Directory directory = newFSDirectory(configDir); Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT) IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
@ -266,7 +266,7 @@ public class KeyStoreWrapperTests extends ESTestCase {
public void testFailWhenSecretStreamNotConsumed() throws Exception { public void testFailWhenSecretStreamNotConsumed() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm()); assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
Path configDir = env.configFile(); Path configDir = env.configDir();
try ( try (
Directory directory = newFSDirectory(configDir); Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT) IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
@ -296,7 +296,7 @@ public class KeyStoreWrapperTests extends ESTestCase {
public void testFailWhenEncryptedBytesStreamIsNotConsumed() throws Exception { public void testFailWhenEncryptedBytesStreamIsNotConsumed() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm()); assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
Path configDir = env.configFile(); Path configDir = env.configDir();
try ( try (
Directory directory = newFSDirectory(configDir); Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT) IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
@ -359,11 +359,11 @@ public class KeyStoreWrapperTests extends ESTestCase {
final char[] password = getPossibleKeystorePassword(); final char[] password = getPossibleKeystorePassword();
KeyStoreWrapper keystore = KeyStoreWrapper.create(); KeyStoreWrapper keystore = KeyStoreWrapper.create();
keystore.remove(KeyStoreWrapper.SEED_SETTING.getKey()); keystore.remove(KeyStoreWrapper.SEED_SETTING.getKey());
keystore.save(env.configFile(), password); keystore.save(env.configDir(), password);
KeyStoreWrapper.upgrade(keystore, env.configFile(), password); KeyStoreWrapper.upgrade(keystore, env.configDir(), password);
SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()); SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey());
assertNotNull(seed); assertNotNull(seed);
keystore = KeyStoreWrapper.load(env.configFile()); keystore = KeyStoreWrapper.load(env.configDir());
keystore.decrypt(password); keystore.decrypt(password);
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString()); assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
} }
@ -380,7 +380,7 @@ public class KeyStoreWrapperTests extends ESTestCase {
public void testBackcompatV4() throws Exception { public void testBackcompatV4() throws Exception {
assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm()); assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm());
Path configDir = env.configFile(); Path configDir = env.configDir();
try ( try (
Directory directory = newFSDirectory(configDir); Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT) IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
@ -421,10 +421,10 @@ public class KeyStoreWrapperTests extends ESTestCase {
final Path temp = createTempDir(); final Path temp = createTempDir();
Files.writeString(temp.resolve("file_setting"), "file_value", StandardCharsets.UTF_8); Files.writeString(temp.resolve("file_setting"), "file_value", StandardCharsets.UTF_8);
wrapper.setFile("file_setting", Files.readAllBytes(temp.resolve("file_setting"))); wrapper.setFile("file_setting", Files.readAllBytes(temp.resolve("file_setting")));
wrapper.save(env.configFile(), password); wrapper.save(env.configDir(), password);
wrapper.close(); wrapper.close();
final KeyStoreWrapper afterSave = KeyStoreWrapper.load(env.configFile()); final KeyStoreWrapper afterSave = KeyStoreWrapper.load(env.configDir());
assertNotNull(afterSave); assertNotNull(afterSave);
afterSave.decrypt(password); afterSave.decrypt(password);
assertThat(afterSave.getSettingNames(), equalTo(Set.of("keystore.seed", "string_setting", "file_setting"))); assertThat(afterSave.getSettingNames(), equalTo(Set.of("keystore.seed", "string_setting", "file_setting")));
@ -510,8 +510,8 @@ public class KeyStoreWrapperTests extends ESTestCase {
// testing with password and raw dataBytes[] // testing with password and raw dataBytes[]
final char[] password = getPossibleKeystorePassword(); final char[] password = getPossibleKeystorePassword();
wrapper.save(env.configFile(), password); wrapper.save(env.configDir(), password);
final KeyStoreWrapper fromFile = KeyStoreWrapper.load(env.configFile()); final KeyStoreWrapper fromFile = KeyStoreWrapper.load(env.configDir());
fromFile.decrypt(password); fromFile.decrypt(password);
assertThat(fromFile.getSettingNames(), hasSize(2)); assertThat(fromFile.getSettingNames(), hasSize(2));

View file

@ -62,11 +62,11 @@ public class UpgradeKeyStoreCommandTests extends KeyStoreCommandTestCase {
} }
private void assertKeystoreUpgrade(String file, int version, @Nullable String password) throws Exception { private void assertKeystoreUpgrade(String file, int version, @Nullable String password) throws Exception {
final Path keystore = KeyStoreWrapper.keystorePath(env.configFile()); final Path keystore = KeyStoreWrapper.keystorePath(env.configDir());
try (InputStream is = KeyStoreWrapperTests.class.getResourceAsStream(file); OutputStream os = Files.newOutputStream(keystore)) { try (InputStream is = KeyStoreWrapperTests.class.getResourceAsStream(file); OutputStream os = Files.newOutputStream(keystore)) {
is.transferTo(os); is.transferTo(os);
} }
try (KeyStoreWrapper beforeUpgrade = KeyStoreWrapper.load(env.configFile())) { try (KeyStoreWrapper beforeUpgrade = KeyStoreWrapper.load(env.configDir())) {
assertNotNull(beforeUpgrade); assertNotNull(beforeUpgrade);
assertThat(beforeUpgrade.getFormatVersion(), equalTo(version)); assertThat(beforeUpgrade.getFormatVersion(), equalTo(version));
} }
@ -77,7 +77,7 @@ public class UpgradeKeyStoreCommandTests extends KeyStoreCommandTestCase {
execute(); execute();
terminal.reset(); terminal.reset();
try (KeyStoreWrapper afterUpgrade = KeyStoreWrapper.load(env.configFile())) { try (KeyStoreWrapper afterUpgrade = KeyStoreWrapper.load(env.configDir())) {
assertNotNull(afterUpgrade); assertNotNull(afterUpgrade);
assertThat(afterUpgrade.getFormatVersion(), equalTo(KeyStoreWrapper.CURRENT_VERSION)); assertThat(afterUpgrade.getFormatVersion(), equalTo(KeyStoreWrapper.CURRENT_VERSION));
afterUpgrade.decrypt(password != null ? password.toCharArray() : new char[0]); afterUpgrade.decrypt(password != null ? password.toCharArray() : new char[0]);
@ -87,6 +87,6 @@ public class UpgradeKeyStoreCommandTests extends KeyStoreCommandTestCase {
public void testKeystoreDoesNotExist() { public void testKeystoreDoesNotExist() {
final UserException e = expectThrows(UserException.class, this::execute); final UserException e = expectThrows(UserException.class, this::execute);
assertThat(e, hasToString(containsString("keystore not found at [" + KeyStoreWrapper.keystorePath(env.configFile()) + "]"))); assertThat(e, hasToString(containsString("keystore not found at [" + KeyStoreWrapper.keystorePath(env.configDir()) + "]")));
} }
} }

View file

@ -249,8 +249,8 @@ public class InstallPluginAction implements Closeable {
final List<Path> deleteOnFailure = new ArrayList<>(); final List<Path> deleteOnFailure = new ArrayList<>();
deleteOnFailures.put(pluginId, deleteOnFailure); deleteOnFailures.put(pluginId, deleteOnFailure);
final Path pluginZip = download(plugin, env.tmpFile()); final Path pluginZip = download(plugin, env.tmpDir());
final Path extractedZip = unzip(pluginZip, env.pluginsFile()); final Path extractedZip = unzip(pluginZip, env.pluginsDir());
deleteOnFailure.add(extractedZip); deleteOnFailure.add(extractedZip);
final PluginDescriptor pluginDescriptor = installPlugin(plugin, extractedZip, deleteOnFailure); final PluginDescriptor pluginDescriptor = installPlugin(plugin, extractedZip, deleteOnFailure);
terminal.println(logPrefix + "Installed " + pluginDescriptor.getName()); terminal.println(logPrefix + "Installed " + pluginDescriptor.getName());
@ -868,14 +868,14 @@ public class InstallPluginAction implements Closeable {
PluginsUtils.verifyCompatibility(info); PluginsUtils.verifyCompatibility(info);
// checking for existing version of the plugin // checking for existing version of the plugin
verifyPluginName(env.pluginsFile(), info.getName()); verifyPluginName(env.pluginsDir(), info.getName());
PluginsUtils.checkForFailedPluginRemovals(env.pluginsFile()); PluginsUtils.checkForFailedPluginRemovals(env.pluginsDir());
terminal.println(VERBOSE, info.toString()); terminal.println(VERBOSE, info.toString());
// check for jar hell before any copying // check for jar hell before any copying
jarHellCheck(info, pluginRoot, env.pluginsFile(), env.modulesFile()); jarHellCheck(info, pluginRoot, env.pluginsDir(), env.modulesDir());
if (info.isStable() && hasNamedComponentFile(pluginRoot) == false) { if (info.isStable() && hasNamedComponentFile(pluginRoot) == false) {
generateNameComponentFile(pluginRoot); generateNameComponentFile(pluginRoot);
@ -922,9 +922,9 @@ public class InstallPluginAction implements Closeable {
*/ */
private PluginDescriptor installPlugin(InstallablePlugin descriptor, Path tmpRoot, List<Path> deleteOnFailure) throws Exception { private PluginDescriptor installPlugin(InstallablePlugin descriptor, Path tmpRoot, List<Path> deleteOnFailure) throws Exception {
final PluginDescriptor info = loadPluginInfo(tmpRoot); final PluginDescriptor info = loadPluginInfo(tmpRoot);
PluginPolicyInfo pluginPolicy = PolicyUtil.getPluginPolicyInfo(tmpRoot, env.tmpFile()); PluginPolicyInfo pluginPolicy = PolicyUtil.getPluginPolicyInfo(tmpRoot, env.tmpDir());
if (pluginPolicy != null) { if (pluginPolicy != null) {
Set<String> permissions = PluginSecurity.getPermissionDescriptions(pluginPolicy, env.tmpFile()); Set<String> permissions = PluginSecurity.getPermissionDescriptions(pluginPolicy, env.tmpDir());
PluginSecurity.confirmPolicyExceptions(terminal, permissions, batch); PluginSecurity.confirmPolicyExceptions(terminal, permissions, batch);
} }
@ -938,14 +938,14 @@ public class InstallPluginAction implements Closeable {
); );
} }
final Path destination = env.pluginsFile().resolve(info.getName()); final Path destination = env.pluginsDir().resolve(info.getName());
deleteOnFailure.add(destination); deleteOnFailure.add(destination);
installPluginSupportFiles( installPluginSupportFiles(
info, info,
tmpRoot, tmpRoot,
env.binFile().resolve(info.getName()), env.binDir().resolve(info.getName()),
env.configFile().resolve(info.getName()), env.configDir().resolve(info.getName()),
deleteOnFailure deleteOnFailure
); );
movePlugin(tmpRoot, destination); movePlugin(tmpRoot, destination);

View file

@ -40,13 +40,13 @@ class ListPluginsCommand extends EnvironmentAwareCommand {
@Override @Override
public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
if (Files.exists(env.pluginsFile()) == false) { if (Files.exists(env.pluginsDir()) == false) {
throw new IOException("Plugins directory missing: " + env.pluginsFile()); throw new IOException("Plugins directory missing: " + env.pluginsDir());
} }
terminal.println(Terminal.Verbosity.VERBOSE, "Plugins directory: " + env.pluginsFile()); terminal.println(Terminal.Verbosity.VERBOSE, "Plugins directory: " + env.pluginsDir());
final List<Path> plugins = new ArrayList<>(); final List<Path> plugins = new ArrayList<>();
try (DirectoryStream<Path> paths = Files.newDirectoryStream(env.pluginsFile())) { try (DirectoryStream<Path> paths = Files.newDirectoryStream(env.pluginsDir())) {
for (Path path : paths) { for (Path path : paths) {
if (path.getFileName().toString().equals(ELASTICSEARCH_PLUGINS_YML_CACHE) == false) { if (path.getFileName().toString().equals(ELASTICSEARCH_PLUGINS_YML_CACHE) == false) {
plugins.add(path); plugins.add(path);
@ -61,7 +61,7 @@ class ListPluginsCommand extends EnvironmentAwareCommand {
private static void printPlugin(Environment env, Terminal terminal, Path plugin, String prefix) throws IOException { private static void printPlugin(Environment env, Terminal terminal, Path plugin, String prefix) throws IOException {
terminal.println(Terminal.Verbosity.SILENT, prefix + plugin.getFileName().toString()); terminal.println(Terminal.Verbosity.SILENT, prefix + plugin.getFileName().toString());
PluginDescriptor info = PluginDescriptor.readFromProperties(env.pluginsFile().resolve(plugin)); PluginDescriptor info = PluginDescriptor.readFromProperties(env.pluginsDir().resolve(plugin));
terminal.println(Terminal.Verbosity.VERBOSE, info.toString(prefix)); terminal.println(Terminal.Verbosity.VERBOSE, info.toString(prefix));
// When PluginDescriptor#getElasticsearchVersion returns a string, we can revisit the need // When PluginDescriptor#getElasticsearchVersion returns a string, we can revisit the need

View file

@ -93,7 +93,7 @@ public class RemovePluginAction {
// We build a new map where the keys are plugins that extend plugins // We build a new map where the keys are plugins that extend plugins
// we want to remove and the values are the plugins we can't remove // we want to remove and the values are the plugins we can't remove
// because of this dependency // because of this dependency
Map<String, List<String>> pluginDependencyMap = PluginsUtils.getDependencyMapView(env.pluginsFile()); Map<String, List<String>> pluginDependencyMap = PluginsUtils.getDependencyMapView(env.pluginsDir());
for (Map.Entry<String, List<String>> entry : pluginDependencyMap.entrySet()) { for (Map.Entry<String, List<String>> entry : pluginDependencyMap.entrySet()) {
for (String extendedPlugin : entry.getValue()) { for (String extendedPlugin : entry.getValue()) {
for (InstallablePlugin plugin : plugins) { for (InstallablePlugin plugin : plugins) {
@ -121,9 +121,9 @@ public class RemovePluginAction {
private void checkCanRemove(InstallablePlugin plugin) throws UserException { private void checkCanRemove(InstallablePlugin plugin) throws UserException {
String pluginId = plugin.getId(); String pluginId = plugin.getId();
final Path pluginDir = env.pluginsFile().resolve(pluginId); final Path pluginDir = env.pluginsDir().resolve(pluginId);
final Path pluginConfigDir = env.configFile().resolve(pluginId); final Path pluginConfigDir = env.configDir().resolve(pluginId);
final Path removing = env.pluginsFile().resolve(".removing-" + pluginId); final Path removing = env.pluginsDir().resolve(".removing-" + pluginId);
/* /*
* If the plugin does not exist and the plugin config does not exist, fail to the user that the plugin is not found, unless there's * If the plugin does not exist and the plugin config does not exist, fail to the user that the plugin is not found, unless there's
@ -147,7 +147,7 @@ public class RemovePluginAction {
} }
} }
final Path pluginBinDir = env.binFile().resolve(pluginId); final Path pluginBinDir = env.binDir().resolve(pluginId);
if (Files.exists(pluginBinDir)) { if (Files.exists(pluginBinDir)) {
if (Files.isDirectory(pluginBinDir) == false) { if (Files.isDirectory(pluginBinDir) == false) {
throw new UserException(ExitCodes.IO_ERROR, "bin dir for " + pluginId + " is not a directory"); throw new UserException(ExitCodes.IO_ERROR, "bin dir for " + pluginId + " is not a directory");
@ -157,9 +157,9 @@ public class RemovePluginAction {
private void removePlugin(InstallablePlugin plugin) throws IOException { private void removePlugin(InstallablePlugin plugin) throws IOException {
final String pluginId = plugin.getId(); final String pluginId = plugin.getId();
final Path pluginDir = env.pluginsFile().resolve(pluginId); final Path pluginDir = env.pluginsDir().resolve(pluginId);
final Path pluginConfigDir = env.configFile().resolve(pluginId); final Path pluginConfigDir = env.configDir().resolve(pluginId);
final Path removing = env.pluginsFile().resolve(".removing-" + pluginId); final Path removing = env.pluginsDir().resolve(".removing-" + pluginId);
terminal.println("-> removing [" + pluginId + "]..."); terminal.println("-> removing [" + pluginId + "]...");
@ -176,7 +176,7 @@ public class RemovePluginAction {
terminal.println(VERBOSE, "removing [" + pluginDir + "]"); terminal.println(VERBOSE, "removing [" + pluginDir + "]");
} }
final Path pluginBinDir = env.binFile().resolve(pluginId); final Path pluginBinDir = env.binDir().resolve(pluginId);
if (Files.exists(pluginBinDir)) { if (Files.exists(pluginBinDir)) {
try (Stream<Path> paths = Files.list(pluginBinDir)) { try (Stream<Path> paths = Files.list(pluginBinDir)) {
pluginPaths.addAll(paths.toList()); pluginPaths.addAll(paths.toList());

View file

@ -61,7 +61,7 @@ public class SyncPluginsAction {
* @throws UserException if a plugins config file is found. * @throws UserException if a plugins config file is found.
*/ */
public static void ensureNoConfigFile(Environment env) throws UserException { public static void ensureNoConfigFile(Environment env) throws UserException {
final Path pluginsConfig = env.configFile().resolve(ELASTICSEARCH_PLUGINS_YML); final Path pluginsConfig = env.configDir().resolve(ELASTICSEARCH_PLUGINS_YML);
if (Files.exists(pluginsConfig)) { if (Files.exists(pluginsConfig)) {
throw new UserException( throw new UserException(
ExitCodes.USAGE, ExitCodes.USAGE,
@ -79,16 +79,16 @@ public class SyncPluginsAction {
* @throws Exception if anything goes wrong * @throws Exception if anything goes wrong
*/ */
public void execute() throws Exception { public void execute() throws Exception {
final Path configPath = this.env.configFile().resolve(ELASTICSEARCH_PLUGINS_YML); final Path configPath = this.env.configDir().resolve(ELASTICSEARCH_PLUGINS_YML);
final Path previousConfigPath = this.env.pluginsFile().resolve(ELASTICSEARCH_PLUGINS_YML_CACHE); final Path previousConfigPath = this.env.pluginsDir().resolve(ELASTICSEARCH_PLUGINS_YML_CACHE);
if (Files.exists(configPath) == false) { if (Files.exists(configPath) == false) {
// The `PluginsManager` will have checked that this file exists before invoking the action. // The `PluginsManager` will have checked that this file exists before invoking the action.
throw new PluginSyncException("Plugins config does not exist: " + configPath.toAbsolutePath()); throw new PluginSyncException("Plugins config does not exist: " + configPath.toAbsolutePath());
} }
if (Files.exists(env.pluginsFile()) == false) { if (Files.exists(env.pluginsDir()) == false) {
throw new PluginSyncException("Plugins directory missing: " + env.pluginsFile()); throw new PluginSyncException("Plugins directory missing: " + env.pluginsDir());
} }
// Parse descriptor file // Parse descriptor file
@ -267,14 +267,14 @@ public class SyncPluginsAction {
final List<PluginDescriptor> plugins = new ArrayList<>(); final List<PluginDescriptor> plugins = new ArrayList<>();
try { try {
try (DirectoryStream<Path> paths = Files.newDirectoryStream(env.pluginsFile())) { try (DirectoryStream<Path> paths = Files.newDirectoryStream(env.pluginsDir())) {
for (Path pluginPath : paths) { for (Path pluginPath : paths) {
String filename = pluginPath.getFileName().toString(); String filename = pluginPath.getFileName().toString();
if (filename.startsWith(".")) { if (filename.startsWith(".")) {
continue; continue;
} }
PluginDescriptor info = PluginDescriptor.readFromProperties(env.pluginsFile().resolve(pluginPath)); PluginDescriptor info = PluginDescriptor.readFromProperties(env.pluginsDir().resolve(pluginPath));
plugins.add(info); plugins.add(info);
// Check for a version mismatch, unless it's an official plugin since we can upgrade them. // Check for a version mismatch, unless it's an official plugin since we can upgrade them.

View file

@ -37,7 +37,7 @@ public class SyncPluginsCliProvider implements CliToolProvider {
@Override @Override
public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
var action = new SyncPluginsAction(terminal, env); var action = new SyncPluginsAction(terminal, env);
if (Files.exists(env.configFile().resolve(ELASTICSEARCH_PLUGINS_YML)) == false) { if (Files.exists(env.configDir().resolve(ELASTICSEARCH_PLUGINS_YML)) == false) {
return; return;
} }
if (Build.current().type() != Build.Type.DOCKER) { if (Build.current().type() != Build.Type.DOCKER) {

View file

@ -354,7 +354,7 @@ public class InstallPluginActionTests extends ESTestCase {
} }
void assertPlugin(String name, Path original, Environment environment) throws IOException { void assertPlugin(String name, Path original, Environment environment) throws IOException {
assertPluginInternal(name, environment.pluginsFile(), original); assertPluginInternal(name, environment.pluginsDir(), original);
assertConfigAndBin(name, original, environment); assertConfigAndBin(name, original, environment);
assertInstallCleaned(environment); assertInstallCleaned(environment);
} }
@ -395,7 +395,7 @@ public class InstallPluginActionTests extends ESTestCase {
void assertConfigAndBin(String name, Path original, Environment environment) throws IOException { void assertConfigAndBin(String name, Path original, Environment environment) throws IOException {
if (Files.exists(original.resolve("bin"))) { if (Files.exists(original.resolve("bin"))) {
Path binDir = environment.binFile().resolve(name); Path binDir = environment.binDir().resolve(name);
assertTrue("bin dir exists", Files.exists(binDir)); assertTrue("bin dir exists", Files.exists(binDir));
assertTrue("bin is a dir", Files.isDirectory(binDir)); assertTrue("bin is a dir", Files.isDirectory(binDir));
try (DirectoryStream<Path> stream = Files.newDirectoryStream(binDir)) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(binDir)) {
@ -409,7 +409,7 @@ public class InstallPluginActionTests extends ESTestCase {
} }
} }
if (Files.exists(original.resolve("config"))) { if (Files.exists(original.resolve("config"))) {
Path configDir = environment.configFile().resolve(name); Path configDir = environment.configDir().resolve(name);
assertTrue("config dir exists", Files.exists(configDir)); assertTrue("config dir exists", Files.exists(configDir));
assertTrue("config is a dir", Files.isDirectory(configDir)); assertTrue("config is a dir", Files.isDirectory(configDir));
@ -417,7 +417,7 @@ public class InstallPluginActionTests extends ESTestCase {
GroupPrincipal group = null; GroupPrincipal group = null;
if (isPosix) { if (isPosix) {
PosixFileAttributes configAttributes = Files.getFileAttributeView(environment.configFile(), PosixFileAttributeView.class) PosixFileAttributes configAttributes = Files.getFileAttributeView(environment.configDir(), PosixFileAttributeView.class)
.readAttributes(); .readAttributes();
user = configAttributes.owner(); user = configAttributes.owner();
group = configAttributes.group(); group = configAttributes.group();
@ -446,7 +446,7 @@ public class InstallPluginActionTests extends ESTestCase {
} }
void assertInstallCleaned(Environment environment) throws IOException { void assertInstallCleaned(Environment environment) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(environment.pluginsFile())) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(environment.pluginsDir())) {
for (Path file : stream) { for (Path file : stream) {
if (file.getFileName().toString().startsWith(".installing")) { if (file.getFileName().toString().startsWith(".installing")) {
fail("Installation dir still exists, " + file); fail("Installation dir still exists, " + file);
@ -549,7 +549,7 @@ public class InstallPluginActionTests extends ESTestCase {
() -> installPlugins(List.of(pluginZip, nonexistentPluginZip), env.v1()) () -> installPlugins(List.of(pluginZip, nonexistentPluginZip), env.v1())
); );
assertThat(e.getMessage(), containsString("does-not-exist")); assertThat(e.getMessage(), containsString("does-not-exist"));
final Path fakeInstallPath = env.v2().pluginsFile().resolve("fake"); final Path fakeInstallPath = env.v2().pluginsDir().resolve("fake");
// fake should have been removed when the file not found exception occurred // fake should have been removed when the file not found exception occurred
assertFalse(Files.exists(fakeInstallPath)); assertFalse(Files.exists(fakeInstallPath));
assertInstallCleaned(env.v2()); assertInstallCleaned(env.v2());
@ -557,7 +557,7 @@ public class InstallPluginActionTests extends ESTestCase {
public void testInstallFailsIfPreviouslyRemovedPluginFailed() throws Exception { public void testInstallFailsIfPreviouslyRemovedPluginFailed() throws Exception {
InstallablePlugin pluginZip = createPluginZip("fake", pluginDir); InstallablePlugin pluginZip = createPluginZip("fake", pluginDir);
final Path removing = env.v2().pluginsFile().resolve(".removing-failed"); final Path removing = env.v2().pluginsDir().resolve(".removing-failed");
Files.createDirectory(removing); Files.createDirectory(removing);
final IllegalStateException e = expectThrows(IllegalStateException.class, () -> installPlugin(pluginZip)); final IllegalStateException e = expectThrows(IllegalStateException.class, () -> installPlugin(pluginZip));
final String expected = Strings.format( final String expected = Strings.format(
@ -603,11 +603,11 @@ public class InstallPluginActionTests extends ESTestCase {
public void testPluginsDirReadOnly() throws Exception { public void testPluginsDirReadOnly() throws Exception {
assumeTrue("posix and filesystem", isPosix && isReal); assumeTrue("posix and filesystem", isPosix && isReal);
try (PosixPermissionsResetter pluginsAttrs = new PosixPermissionsResetter(env.v2().pluginsFile())) { try (PosixPermissionsResetter pluginsAttrs = new PosixPermissionsResetter(env.v2().pluginsDir())) {
pluginsAttrs.setPermissions(new HashSet<>()); pluginsAttrs.setPermissions(new HashSet<>());
InstallablePlugin pluginZip = createPluginZip("fake", pluginDir); InstallablePlugin pluginZip = createPluginZip("fake", pluginDir);
IOException e = expectThrows(IOException.class, () -> installPlugin(pluginZip)); IOException e = expectThrows(IOException.class, () -> installPlugin(pluginZip));
assertThat(e.getMessage(), containsString(env.v2().pluginsFile().toString())); assertThat(e.getMessage(), containsString(env.v2().pluginsDir().toString()));
} }
assertInstallCleaned(env.v2()); assertInstallCleaned(env.v2());
} }
@ -694,7 +694,7 @@ public class InstallPluginActionTests extends ESTestCase {
Files.createFile(binDir.resolve("somescript")); Files.createFile(binDir.resolve("somescript"));
InstallablePlugin pluginZip = createPluginZip("elasticsearch", pluginDir); InstallablePlugin pluginZip = createPluginZip("elasticsearch", pluginDir);
FileAlreadyExistsException e = expectThrows(FileAlreadyExistsException.class, () -> installPlugin(pluginZip)); FileAlreadyExistsException e = expectThrows(FileAlreadyExistsException.class, () -> installPlugin(pluginZip));
assertThat(e.getMessage(), containsString(env.v2().binFile().resolve("elasticsearch").toString())); assertThat(e.getMessage(), containsString(env.v2().binDir().resolve("elasticsearch").toString()));
assertInstallCleaned(env.v2()); assertInstallCleaned(env.v2());
} }
@ -704,7 +704,7 @@ public class InstallPluginActionTests extends ESTestCase {
Files.createDirectory(binDir); Files.createDirectory(binDir);
Files.createFile(binDir.resolve("somescript")); Files.createFile(binDir.resolve("somescript"));
InstallablePlugin pluginZip = createPluginZip("fake", pluginDir); InstallablePlugin pluginZip = createPluginZip("fake", pluginDir);
try (PosixPermissionsResetter binAttrs = new PosixPermissionsResetter(env.v2().binFile())) { try (PosixPermissionsResetter binAttrs = new PosixPermissionsResetter(env.v2().binDir())) {
Set<PosixFilePermission> perms = binAttrs.getCopyPermissions(); Set<PosixFilePermission> perms = binAttrs.getCopyPermissions();
// make sure at least one execute perm is missing, so we know we forced it during installation // make sure at least one execute perm is missing, so we know we forced it during installation
perms.remove(PosixFilePermission.GROUP_EXECUTE); perms.remove(PosixFilePermission.GROUP_EXECUTE);
@ -734,7 +734,7 @@ public class InstallPluginActionTests extends ESTestCase {
installPlugin(pluginZip); installPlugin(pluginZip);
assertPlugin("fake", tempPluginDir, env.v2()); assertPlugin("fake", tempPluginDir, env.v2());
final Path fake = env.v2().pluginsFile().resolve("fake"); final Path fake = env.v2().pluginsDir().resolve("fake");
final Path resources = fake.resolve("resources"); final Path resources = fake.resolve("resources");
final Path platform = fake.resolve("platform"); final Path platform = fake.resolve("platform");
final Path platformName = platform.resolve("linux-x86_64"); final Path platformName = platform.resolve("linux-x86_64");
@ -784,7 +784,7 @@ public class InstallPluginActionTests extends ESTestCase {
} }
public void testExistingConfig() throws Exception { public void testExistingConfig() throws Exception {
Path envConfigDir = env.v2().configFile().resolve("fake"); Path envConfigDir = env.v2().configDir().resolve("fake");
Files.createDirectories(envConfigDir); Files.createDirectories(envConfigDir);
Files.write(envConfigDir.resolve("custom.yml"), "existing config".getBytes(StandardCharsets.UTF_8)); Files.write(envConfigDir.resolve("custom.yml"), "existing config".getBytes(StandardCharsets.UTF_8));
Path configDir = pluginDir.resolve("config"); Path configDir = pluginDir.resolve("config");
@ -921,7 +921,7 @@ public class InstallPluginActionTests extends ESTestCase {
e.getMessage(), e.getMessage(),
equalTo( equalTo(
"plugin directory [" "plugin directory ["
+ env.v2().pluginsFile().resolve("fake") + env.v2().pluginsDir().resolve("fake")
+ "] already exists; " + "] already exists; "
+ "if you need to update the plugin, uninstall it first using command 'remove fake'" + "if you need to update the plugin, uninstall it first using command 'remove fake'"
) )
@ -1499,7 +1499,7 @@ public class InstallPluginActionTests extends ESTestCase {
assertThat(e.getMessage(), containsString("installation aborted by user")); assertThat(e.getMessage(), containsString("installation aborted by user"));
assertThat(terminal.getErrorOutput(), containsString("WARNING: " + warning)); assertThat(terminal.getErrorOutput(), containsString("WARNING: " + warning));
try (Stream<Path> fileStream = Files.list(pathEnvironmentTuple.v2().pluginsFile())) { try (Stream<Path> fileStream = Files.list(pathEnvironmentTuple.v2().pluginsDir())) {
assertThat(fileStream.collect(Collectors.toList()), empty()); assertThat(fileStream.collect(Collectors.toList()), empty());
} }
@ -1512,7 +1512,7 @@ public class InstallPluginActionTests extends ESTestCase {
e = expectThrows(UserException.class, () -> installPlugin(pluginZip)); e = expectThrows(UserException.class, () -> installPlugin(pluginZip));
assertThat(e.getMessage(), containsString("installation aborted by user")); assertThat(e.getMessage(), containsString("installation aborted by user"));
assertThat(terminal.getErrorOutput(), containsString("WARNING: " + warning)); assertThat(terminal.getErrorOutput(), containsString("WARNING: " + warning));
try (Stream<Path> fileStream = Files.list(pathEnvironmentTuple.v2().pluginsFile())) { try (Stream<Path> fileStream = Files.list(pathEnvironmentTuple.v2().pluginsDir())) {
assertThat(fileStream.collect(Collectors.toList()), empty()); assertThat(fileStream.collect(Collectors.toList()), empty());
} }
} }
@ -1566,7 +1566,7 @@ public class InstallPluginActionTests extends ESTestCase {
InstallablePlugin stablePluginZip = createStablePlugin("stable1", pluginDir, true); InstallablePlugin stablePluginZip = createStablePlugin("stable1", pluginDir, true);
installPlugins(List.of(stablePluginZip), env.v1()); installPlugins(List.of(stablePluginZip), env.v1());
assertPlugin("stable1", pluginDir, env.v2()); assertPlugin("stable1", pluginDir, env.v2());
assertNamedComponentFile("stable1", env.v2().pluginsFile(), namedComponentsJSON()); assertNamedComponentFile("stable1", env.v2().pluginsDir(), namedComponentsJSON());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1577,7 +1577,7 @@ public class InstallPluginActionTests extends ESTestCase {
installPlugins(List.of(stablePluginZip), env.v1()); installPlugins(List.of(stablePluginZip), env.v1());
assertPlugin("stable1", pluginDir, env.v2()); assertPlugin("stable1", pluginDir, env.v2());
assertNamedComponentFile("stable1", env.v2().pluginsFile(), namedComponentsJSON()); assertNamedComponentFile("stable1", env.v2().pluginsDir(), namedComponentsJSON());
} }
public void testGetSemanticVersion() { public void testGetSemanticVersion() {

View file

@ -65,7 +65,7 @@ public class ListPluginsCommandTests extends CommandTestCase {
final boolean hasNativeController final boolean hasNativeController
) throws IOException { ) throws IOException {
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
env.pluginsFile().resolve(name), env.pluginsDir().resolve(name),
"description", "description",
description, description,
"name", "name",
@ -84,9 +84,9 @@ public class ListPluginsCommandTests extends CommandTestCase {
} }
public void testPluginsDirMissing() throws Exception { public void testPluginsDirMissing() throws Exception {
Files.delete(env.pluginsFile()); Files.delete(env.pluginsDir());
IOException e = expectThrows(IOException.class, () -> execute()); IOException e = expectThrows(IOException.class, () -> execute());
assertEquals("Plugins directory missing: " + env.pluginsFile(), e.getMessage()); assertEquals("Plugins directory missing: " + env.pluginsDir(), e.getMessage());
} }
public void testNoPlugins() throws Exception { public void testNoPlugins() throws Exception {
@ -112,7 +112,7 @@ public class ListPluginsCommandTests extends CommandTestCase {
execute("-v"); execute("-v");
assertEquals( assertEquals(
buildMultiline( buildMultiline(
"Plugins directory: " + env.pluginsFile(), "Plugins directory: " + env.pluginsDir(),
"fake_plugin", "fake_plugin",
"- Plugin information:", "- Plugin information:",
"Name: fake_plugin", "Name: fake_plugin",
@ -134,7 +134,7 @@ public class ListPluginsCommandTests extends CommandTestCase {
execute("-v"); execute("-v");
assertEquals( assertEquals(
buildMultiline( buildMultiline(
"Plugins directory: " + env.pluginsFile(), "Plugins directory: " + env.pluginsDir(),
"fake_plugin1", "fake_plugin1",
"- Plugin information:", "- Plugin information:",
"Name: fake_plugin1", "Name: fake_plugin1",
@ -157,7 +157,7 @@ public class ListPluginsCommandTests extends CommandTestCase {
execute("-v"); execute("-v");
assertEquals( assertEquals(
buildMultiline( buildMultiline(
"Plugins directory: " + env.pluginsFile(), "Plugins directory: " + env.pluginsDir(),
"fake_plugin1", "fake_plugin1",
"- Plugin information:", "- Plugin information:",
"Name: fake_plugin1", "Name: fake_plugin1",
@ -193,14 +193,14 @@ public class ListPluginsCommandTests extends CommandTestCase {
} }
public void testPluginWithoutDescriptorFile() throws Exception { public void testPluginWithoutDescriptorFile() throws Exception {
final Path pluginDir = env.pluginsFile().resolve("fake1"); final Path pluginDir = env.pluginsDir().resolve("fake1");
Files.createDirectories(pluginDir); Files.createDirectories(pluginDir);
var e = expectThrows(IllegalStateException.class, () -> execute()); var e = expectThrows(IllegalStateException.class, () -> execute());
assertThat(e.getMessage(), equalTo("Plugin [fake1] is missing a descriptor properties file.")); assertThat(e.getMessage(), equalTo("Plugin [fake1] is missing a descriptor properties file."));
} }
public void testPluginWithWrongDescriptorFile() throws Exception { public void testPluginWithWrongDescriptorFile() throws Exception {
final Path pluginDir = env.pluginsFile().resolve("fake1"); final Path pluginDir = env.pluginsDir().resolve("fake1");
PluginTestUtil.writePluginProperties(pluginDir, "description", "fake desc"); PluginTestUtil.writePluginProperties(pluginDir, "description", "fake desc");
var e = expectThrows(IllegalArgumentException.class, () -> execute()); var e = expectThrows(IllegalArgumentException.class, () -> execute());
assertThat(e.getMessage(), startsWith("property [name] is missing for plugin")); assertThat(e.getMessage(), startsWith("property [name] is missing for plugin"));
@ -208,7 +208,7 @@ public class ListPluginsCommandTests extends CommandTestCase {
public void testExistingIncompatiblePlugin() throws Exception { public void testExistingIncompatiblePlugin() throws Exception {
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
env.pluginsFile().resolve("fake_plugin1"), env.pluginsDir().resolve("fake_plugin1"),
"description", "description",
"fake desc 1", "fake desc 1",
"name", "name",

View file

@ -58,11 +58,11 @@ public class RemovePluginActionTests extends ESTestCase {
} }
void createPlugin(String name) throws IOException { void createPlugin(String name) throws IOException {
createPlugin(env.pluginsFile(), name, Version.CURRENT); createPlugin(env.pluginsDir(), name, Version.CURRENT);
} }
void createPlugin(String name, Version version) throws IOException { void createPlugin(String name, Version version) throws IOException {
createPlugin(env.pluginsFile(), name, version); createPlugin(env.pluginsDir(), name, version);
} }
void createPlugin(Path path, String name, Version version) throws IOException { void createPlugin(Path path, String name, Version version) throws IOException {
@ -98,7 +98,7 @@ public class RemovePluginActionTests extends ESTestCase {
} }
static void assertRemoveCleaned(Environment env) throws IOException { static void assertRemoveCleaned(Environment env) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(env.pluginsFile())) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(env.pluginsDir())) {
for (Path file : stream) { for (Path file : stream) {
if (file.getFileName().toString().startsWith(".removing")) { if (file.getFileName().toString().startsWith(".removing")) {
fail("Removal dir still exists, " + file); fail("Removal dir still exists, " + file);
@ -115,84 +115,84 @@ public class RemovePluginActionTests extends ESTestCase {
public void testBasic() throws Exception { public void testBasic() throws Exception {
createPlugin("fake"); createPlugin("fake");
Files.createFile(env.pluginsFile().resolve("fake").resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve("fake").resolve("plugin.jar"));
Files.createDirectory(env.pluginsFile().resolve("fake").resolve("subdir")); Files.createDirectory(env.pluginsDir().resolve("fake").resolve("subdir"));
createPlugin("other"); createPlugin("other");
removePlugin("fake", home, randomBoolean()); removePlugin("fake", home, randomBoolean());
assertFalse(Files.exists(env.pluginsFile().resolve("fake"))); assertFalse(Files.exists(env.pluginsDir().resolve("fake")));
assertTrue(Files.exists(env.pluginsFile().resolve("other"))); assertTrue(Files.exists(env.pluginsDir().resolve("other")));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
/** Check that multiple plugins can be removed at the same time. */ /** Check that multiple plugins can be removed at the same time. */
public void testRemoveMultiple() throws Exception { public void testRemoveMultiple() throws Exception {
createPlugin("fake"); createPlugin("fake");
Files.createFile(env.pluginsFile().resolve("fake").resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve("fake").resolve("plugin.jar"));
Files.createDirectory(env.pluginsFile().resolve("fake").resolve("subdir")); Files.createDirectory(env.pluginsDir().resolve("fake").resolve("subdir"));
createPlugin("other"); createPlugin("other");
Files.createFile(env.pluginsFile().resolve("other").resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve("other").resolve("plugin.jar"));
Files.createDirectory(env.pluginsFile().resolve("other").resolve("subdir")); Files.createDirectory(env.pluginsDir().resolve("other").resolve("subdir"));
removePlugin("fake", home, randomBoolean()); removePlugin("fake", home, randomBoolean());
removePlugin("other", home, randomBoolean()); removePlugin("other", home, randomBoolean());
assertFalse(Files.exists(env.pluginsFile().resolve("fake"))); assertFalse(Files.exists(env.pluginsDir().resolve("fake")));
assertFalse(Files.exists(env.pluginsFile().resolve("other"))); assertFalse(Files.exists(env.pluginsDir().resolve("other")));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
public void testBin() throws Exception { public void testBin() throws Exception {
createPlugin("fake"); createPlugin("fake");
Path binDir = env.binFile().resolve("fake"); Path binDir = env.binDir().resolve("fake");
Files.createDirectories(binDir); Files.createDirectories(binDir);
Files.createFile(binDir.resolve("somescript")); Files.createFile(binDir.resolve("somescript"));
removePlugin("fake", home, randomBoolean()); removePlugin("fake", home, randomBoolean());
assertFalse(Files.exists(env.pluginsFile().resolve("fake"))); assertFalse(Files.exists(env.pluginsDir().resolve("fake")));
assertTrue(Files.exists(env.binFile().resolve("elasticsearch"))); assertTrue(Files.exists(env.binDir().resolve("elasticsearch")));
assertFalse(Files.exists(binDir)); assertFalse(Files.exists(binDir));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
public void testBinNotDir() throws Exception { public void testBinNotDir() throws Exception {
createPlugin("fake"); createPlugin("fake");
Files.createFile(env.binFile().resolve("fake")); Files.createFile(env.binDir().resolve("fake"));
UserException e = expectThrows(UserException.class, () -> removePlugin("fake", home, randomBoolean())); UserException e = expectThrows(UserException.class, () -> removePlugin("fake", home, randomBoolean()));
assertThat(e.getMessage(), containsString("not a directory")); assertThat(e.getMessage(), containsString("not a directory"));
assertTrue(Files.exists(env.pluginsFile().resolve("fake"))); // did not remove assertTrue(Files.exists(env.pluginsDir().resolve("fake"))); // did not remove
assertTrue(Files.exists(env.binFile().resolve("fake"))); assertTrue(Files.exists(env.binDir().resolve("fake")));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
public void testConfigDirPreserved() throws Exception { public void testConfigDirPreserved() throws Exception {
createPlugin("fake"); createPlugin("fake");
final Path configDir = env.configFile().resolve("fake"); final Path configDir = env.configDir().resolve("fake");
Files.createDirectories(configDir); Files.createDirectories(configDir);
Files.createFile(configDir.resolve("fake.yml")); Files.createFile(configDir.resolve("fake.yml"));
final MockTerminal terminal = removePlugin("fake", home, false); final MockTerminal terminal = removePlugin("fake", home, false);
assertTrue(Files.exists(env.configFile().resolve("fake"))); assertTrue(Files.exists(env.configDir().resolve("fake")));
assertThat(terminal.getOutput(), containsString(expectedConfigDirPreservedMessage(configDir))); assertThat(terminal.getOutput(), containsString(expectedConfigDirPreservedMessage(configDir)));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
public void testPurgePluginExists() throws Exception { public void testPurgePluginExists() throws Exception {
createPlugin("fake"); createPlugin("fake");
final Path configDir = env.configFile().resolve("fake"); final Path configDir = env.configDir().resolve("fake");
if (randomBoolean()) { if (randomBoolean()) {
Files.createDirectories(configDir); Files.createDirectories(configDir);
Files.createFile(configDir.resolve("fake.yml")); Files.createFile(configDir.resolve("fake.yml"));
} }
final MockTerminal terminal = removePlugin("fake", home, true); final MockTerminal terminal = removePlugin("fake", home, true);
assertFalse(Files.exists(env.configFile().resolve("fake"))); assertFalse(Files.exists(env.configDir().resolve("fake")));
assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir)))); assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir))));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
public void testPurgePluginDoesNotExist() throws Exception { public void testPurgePluginDoesNotExist() throws Exception {
final Path configDir = env.configFile().resolve("fake"); final Path configDir = env.configDir().resolve("fake");
Files.createDirectories(configDir); Files.createDirectories(configDir);
Files.createFile(configDir.resolve("fake.yml")); Files.createFile(configDir.resolve("fake.yml"));
final MockTerminal terminal = removePlugin("fake", home, true); final MockTerminal terminal = removePlugin("fake", home, true);
assertFalse(Files.exists(env.configFile().resolve("fake"))); assertFalse(Files.exists(env.configDir().resolve("fake")));
assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir)))); assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir))));
assertRemoveCleaned(env); assertRemoveCleaned(env);
} }
@ -203,8 +203,8 @@ public class RemovePluginActionTests extends ESTestCase {
} }
public void testPurgeOnlyMarkerFileExists() throws Exception { public void testPurgeOnlyMarkerFileExists() throws Exception {
final Path configDir = env.configFile().resolve("fake"); final Path configDir = env.configDir().resolve("fake");
final Path removing = env.pluginsFile().resolve(".removing-fake"); final Path removing = env.pluginsDir().resolve(".removing-fake");
Files.createFile(removing); Files.createFile(removing);
final MockTerminal terminal = removePlugin("fake", home, randomBoolean()); final MockTerminal terminal = removePlugin("fake", home, randomBoolean());
assertFalse(Files.exists(removing)); assertFalse(Files.exists(removing));
@ -213,7 +213,7 @@ public class RemovePluginActionTests extends ESTestCase {
public void testNoConfigDirPreserved() throws Exception { public void testNoConfigDirPreserved() throws Exception {
createPlugin("fake"); createPlugin("fake");
final Path configDir = env.configFile().resolve("fake"); final Path configDir = env.configDir().resolve("fake");
final MockTerminal terminal = removePlugin("fake", home, randomBoolean()); final MockTerminal terminal = removePlugin("fake", home, randomBoolean());
assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir)))); assertThat(terminal.getOutput(), not(containsString(expectedConfigDirPreservedMessage(configDir))));
} }
@ -250,8 +250,8 @@ public class RemovePluginActionTests extends ESTestCase {
public void testRemoveWhenRemovingMarker() throws Exception { public void testRemoveWhenRemovingMarker() throws Exception {
createPlugin("fake"); createPlugin("fake");
Files.createFile(env.pluginsFile().resolve("fake").resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve("fake").resolve("plugin.jar"));
Files.createFile(env.pluginsFile().resolve(".removing-fake")); Files.createFile(env.pluginsDir().resolve(".removing-fake"));
removePlugin("fake", home, randomBoolean()); removePlugin("fake", home, randomBoolean());
} }
@ -262,10 +262,10 @@ public class RemovePluginActionTests extends ESTestCase {
public void testRemoveMigratedPluginsWhenInstalled() throws Exception { public void testRemoveMigratedPluginsWhenInstalled() throws Exception {
for (String id : List.of("repository-azure", "repository-gcs", "repository-s3")) { for (String id : List.of("repository-azure", "repository-gcs", "repository-s3")) {
createPlugin(id); createPlugin(id);
Files.createFile(env.pluginsFile().resolve(id).resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve(id).resolve("plugin.jar"));
final MockTerminal terminal = removePlugin(id, home, randomBoolean()); final MockTerminal terminal = removePlugin(id, home, randomBoolean());
assertThat(Files.exists(env.pluginsFile().resolve(id)), is(false)); assertThat(Files.exists(env.pluginsDir().resolve(id)), is(false));
// This message shouldn't be printed if plugin was actually installed. // This message shouldn't be printed if plugin was actually installed.
assertThat(terminal.getErrorOutput(), not(containsString("plugin [" + id + "] is no longer a plugin"))); assertThat(terminal.getErrorOutput(), not(containsString("plugin [" + id + "] is no longer a plugin")));
} }
@ -288,11 +288,11 @@ public class RemovePluginActionTests extends ESTestCase {
*/ */
public void testRemoveRegularInstalledPluginAndMigratedUninstalledPlugin() throws Exception { public void testRemoveRegularInstalledPluginAndMigratedUninstalledPlugin() throws Exception {
createPlugin("fake"); createPlugin("fake");
Files.createFile(env.pluginsFile().resolve("fake").resolve("plugin.jar")); Files.createFile(env.pluginsDir().resolve("fake").resolve("plugin.jar"));
final MockTerminal terminal = removePlugin(List.of("fake", "repository-s3"), home, randomBoolean()); final MockTerminal terminal = removePlugin(List.of("fake", "repository-s3"), home, randomBoolean());
assertThat(Files.exists(env.pluginsFile().resolve("fake")), is(false)); assertThat(Files.exists(env.pluginsDir().resolve("fake")), is(false));
assertThat(terminal.getErrorOutput(), containsString("plugin [repository-s3] is no longer a plugin")); assertThat(terminal.getErrorOutput(), containsString("plugin [repository-s3] is no longer a plugin"));
} }

View file

@ -55,10 +55,10 @@ public class SyncPluginsActionTests extends ESTestCase {
Path home = createTempDir(); Path home = createTempDir();
Settings settings = Settings.builder().put("path.home", home).build(); Settings settings = Settings.builder().put("path.home", home).build();
env = TestEnvironment.newEnvironment(settings); env = TestEnvironment.newEnvironment(settings);
Files.createDirectories(env.binFile()); Files.createDirectories(env.binDir());
Files.createFile(env.binFile().resolve("elasticsearch")); Files.createFile(env.binDir().resolve("elasticsearch"));
Files.createDirectories(env.configFile()); Files.createDirectories(env.configDir());
Files.createDirectories(env.pluginsFile()); Files.createDirectories(env.pluginsDir());
terminal = MockTerminal.create(); terminal = MockTerminal.create();
action = new SyncPluginsAction(terminal, env); action = new SyncPluginsAction(terminal, env);
@ -78,7 +78,7 @@ public class SyncPluginsActionTests extends ESTestCase {
* then an exception is thrown. * then an exception is thrown.
*/ */
public void test_ensureNoConfigFile_withConfig_throwsException() throws Exception { public void test_ensureNoConfigFile_withConfig_throwsException() throws Exception {
Files.createFile(env.configFile().resolve("elasticsearch-plugins.yml")); Files.createFile(env.configDir().resolve("elasticsearch-plugins.yml"));
final UserException e = expectThrows(UserException.class, () -> SyncPluginsAction.ensureNoConfigFile(env)); final UserException e = expectThrows(UserException.class, () -> SyncPluginsAction.ensureNoConfigFile(env));
assertThat(e.getMessage(), Matchers.matchesPattern("^Plugins config \\[.*] exists.*$")); assertThat(e.getMessage(), Matchers.matchesPattern("^Plugins config \\[.*] exists.*$"));
@ -354,7 +354,7 @@ public class SyncPluginsActionTests extends ESTestCase {
private void createPlugin(String name, String version) throws IOException { private void createPlugin(String name, String version) throws IOException {
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
env.pluginsFile().resolve(name), env.pluginsDir().resolve(name),
"description", "description",
"dummy", "dummy",
"name", "name",

View file

@ -24,7 +24,7 @@ public class KeyStoreLoader implements SecureSettingsLoader {
@Override @Override
public LoadedSecrets load(Environment environment, Terminal terminal) throws Exception { public LoadedSecrets load(Environment environment, Terminal terminal) throws Exception {
// See if we have a keystore already present // See if we have a keystore already present
KeyStoreWrapper secureSettings = KeyStoreWrapper.load(environment.configFile()); KeyStoreWrapper secureSettings = KeyStoreWrapper.load(environment.configDir());
// If there's no keystore or the keystore has no password, set an empty password // If there's no keystore or the keystore has no password, set an empty password
var password = (secureSettings == null || secureSettings.hasPassword() == false) var password = (secureSettings == null || secureSettings.hasPassword() == false)
? new SecureString(new char[0]) ? new SecureString(new char[0])
@ -35,7 +35,7 @@ public class KeyStoreLoader implements SecureSettingsLoader {
@Override @Override
public SecureSettings bootstrap(Environment environment, SecureString password) throws Exception { public SecureSettings bootstrap(Environment environment, SecureString password) throws Exception {
return KeyStoreWrapper.bootstrap(environment.configFile(), () -> password); return KeyStoreWrapper.bootstrap(environment.configDir(), () -> password);
} }
@Override @Override

View file

@ -150,7 +150,7 @@ class ServerCli extends EnvironmentAwareCommand {
throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed");
} }
Path log4jConfig = env.configFile().resolve("log4j2.properties"); Path log4jConfig = env.configDir().resolve("log4j2.properties");
if (Files.exists(log4jConfig) == false) { if (Files.exists(log4jConfig) == false) {
throw new UserException(ExitCodes.CONFIG, "Missing logging config file at " + log4jConfig); throw new UserException(ExitCodes.CONFIG, "Missing logging config file at " + log4jConfig);
} }
@ -239,7 +239,7 @@ class ServerCli extends EnvironmentAwareCommand {
} }
validatePidFile(pidFile); validatePidFile(pidFile);
} }
return new ServerArgs(daemonize, quiet, pidFile, secrets, env.settings(), env.configFile(), env.logsFile()); return new ServerArgs(daemonize, quiet, pidFile, secrets, env.settings(), env.configDir(), env.logsDir());
} }
@Override @Override

View file

@ -43,8 +43,8 @@ class WindowsServiceDaemon extends EnvironmentAwareCommand {
@Override @Override
public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception {
// the Windows service daemon doesn't support secure settings implementations other than the keystore // the Windows service daemon doesn't support secure settings implementations other than the keystore
try (var loadedSecrets = KeyStoreWrapper.bootstrap(env.configFile(), () -> new SecureString(new char[0]))) { try (var loadedSecrets = KeyStoreWrapper.bootstrap(env.configDir(), () -> new SecureString(new char[0]))) {
var args = new ServerArgs(false, true, null, loadedSecrets, env.settings(), env.configFile(), env.logsFile()); var args = new ServerArgs(false, true, null, loadedSecrets, env.settings(), env.configDir(), env.logsDir());
var tempDir = ServerProcessUtils.setupTempDir(processInfo); var tempDir = ServerProcessUtils.setupTempDir(processInfo);
var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir, new MachineDependentHeap()); var jvmOptions = JvmOptionsParser.determineJvmOptions(args, processInfo, tempDir, new MachineDependentHeap());
var serverProcessBuilder = new ServerProcessBuilder().withTerminal(terminal) var serverProcessBuilder = new ServerProcessBuilder().withTerminal(terminal)

View file

@ -0,0 +1,5 @@
pr: 119546
summary: Introduce `FallbackSyntheticSourceBlockLoader` and apply it to keyword fields
area: Mapping
type: enhancement
issues: []

View file

@ -0,0 +1,5 @@
pr: 121396
summary: Change format for Unified Chat
area: Machine Learning
type: bug
issues: []

View file

@ -0,0 +1,5 @@
pr: 121552
summary: Fix a bug in TOP
area: ES|QL
type: bug
issues: []

View file

@ -0,0 +1,6 @@
pr: 121559
summary: Skip Usage stats update when ML is disabled
area: Machine Learning
type: bug
issues:
- 121532

View file

@ -0,0 +1,6 @@
pr: 121568
summary: Analyze API to return 400 for wrong custom analyzer
area: Analysis
type: bug
issues:
- 121443

View file

@ -0,0 +1,5 @@
pr: 121715
summary: Fix synthetic source issue with deeply nested ignored source fields
area: Mapping
type: bug
issues: []

View file

@ -20,6 +20,7 @@ public @interface EntitlementTest {
enum ExpectedAccess { enum ExpectedAccess {
PLUGINS, PLUGINS,
ES_MODULES_ONLY, ES_MODULES_ONLY,
SERVER_ONLY,
ALWAYS_DENIED ALWAYS_DENIED
} }

View file

@ -13,18 +13,6 @@ import org.elasticsearch.client.internal.node.NodeClient;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.core.CheckedRunnable; import org.elasticsearch.core.CheckedRunnable;
import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyBreakIteratorProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyCalendarDataProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyCalendarNameProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyCollatorProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyCurrencyNameProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyDateFormatProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyDateFormatSymbolsProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyDecimalFormatSymbolsProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyLocaleNameProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyLocaleServiceProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyNumberFormatProvider;
import org.elasticsearch.entitlement.qa.test.DummyImplementations.DummyTimeZoneNameProvider;
import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger; import org.elasticsearch.logging.Logger;
import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.BaseRestHandler;
@ -59,6 +47,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -75,7 +64,6 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class RestEntitlementsCheckAction extends BaseRestHandler { public class RestEntitlementsCheckAction extends BaseRestHandler {
private static final Logger logger = LogManager.getLogger(RestEntitlementsCheckAction.class); private static final Logger logger = LogManager.getLogger(RestEntitlementsCheckAction.class);
public static final Thread NO_OP_SHUTDOWN_HOOK = new Thread(() -> {}, "Shutdown hook for testing");
record CheckAction(CheckedRunnable<Exception> action, boolean isAlwaysDeniedToPlugins, Integer fromJavaVersion) { record CheckAction(CheckedRunnable<Exception> action, boolean isAlwaysDeniedToPlugins, Integer fromJavaVersion) {
/** /**
@ -94,11 +82,8 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
} }
} }
private static final Map<String, CheckAction> checkActions = Stream.concat( private static final Map<String, CheckAction> checkActions = Stream.of(
Stream.<Entry<String, CheckAction>>of( Stream.<Entry<String, CheckAction>>of(
entry("runtime_exit", deniedToPlugins(RestEntitlementsCheckAction::runtimeExit)),
entry("runtime_halt", deniedToPlugins(RestEntitlementsCheckAction::runtimeHalt)),
entry("system_exit", deniedToPlugins(RestEntitlementsCheckAction::systemExit)),
entry("create_classloader", forPlugins(RestEntitlementsCheckAction::createClassLoader)), entry("create_classloader", forPlugins(RestEntitlementsCheckAction::createClassLoader)),
entry("processBuilder_start", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_start)), entry("processBuilder_start", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_start)),
entry("processBuilder_startPipeline", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_startPipeline)), entry("processBuilder_startPipeline", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_startPipeline)),
@ -106,27 +91,10 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
entry("set_default_ssl_socket_factory", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLSocketFactory)), entry("set_default_ssl_socket_factory", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLSocketFactory)),
entry("set_default_hostname_verifier", alwaysDenied(RestEntitlementsCheckAction::setDefaultHostnameVerifier)), entry("set_default_hostname_verifier", alwaysDenied(RestEntitlementsCheckAction::setDefaultHostnameVerifier)),
entry("set_default_ssl_context", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLContext)), entry("set_default_ssl_context", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLContext)),
entry("system_setIn", alwaysDenied(RestEntitlementsCheckAction::system$$setIn)),
entry("system_setOut", alwaysDenied(RestEntitlementsCheckAction::system$$setOut)),
entry("system_setErr", alwaysDenied(RestEntitlementsCheckAction::system$$setErr)),
entry("runtime_addShutdownHook", alwaysDenied(RestEntitlementsCheckAction::runtime$addShutdownHook)),
entry("runtime_removeShutdownHook", alwaysDenied(RestEntitlementsCheckAction::runtime$$removeShutdownHook)),
entry( entry(
"thread_setDefaultUncaughtExceptionHandler", "thread_setDefaultUncaughtExceptionHandler",
alwaysDenied(RestEntitlementsCheckAction::thread$$setDefaultUncaughtExceptionHandler) alwaysDenied(RestEntitlementsCheckAction::thread$$setDefaultUncaughtExceptionHandler)
), ),
entry("localeServiceProvider", alwaysDenied(RestEntitlementsCheckAction::localeServiceProvider$)),
entry("breakIteratorProvider", alwaysDenied(RestEntitlementsCheckAction::breakIteratorProvider$)),
entry("collatorProvider", alwaysDenied(RestEntitlementsCheckAction::collatorProvider$)),
entry("dateFormatProvider", alwaysDenied(RestEntitlementsCheckAction::dateFormatProvider$)),
entry("dateFormatSymbolsProvider", alwaysDenied(RestEntitlementsCheckAction::dateFormatSymbolsProvider$)),
entry("decimalFormatSymbolsProvider", alwaysDenied(RestEntitlementsCheckAction::decimalFormatSymbolsProvider$)),
entry("numberFormatProvider", alwaysDenied(RestEntitlementsCheckAction::numberFormatProvider$)),
entry("calendarDataProvider", alwaysDenied(RestEntitlementsCheckAction::calendarDataProvider$)),
entry("calendarNameProvider", alwaysDenied(RestEntitlementsCheckAction::calendarNameProvider$)),
entry("currencyNameProvider", alwaysDenied(RestEntitlementsCheckAction::currencyNameProvider$)),
entry("localeNameProvider", alwaysDenied(RestEntitlementsCheckAction::localeNameProvider$)),
entry("timeZoneNameProvider", alwaysDenied(RestEntitlementsCheckAction::timeZoneNameProvider$)),
entry("logManager", alwaysDenied(RestEntitlementsCheckAction::logManager$)), entry("logManager", alwaysDenied(RestEntitlementsCheckAction::logManager$)),
entry("locale_setDefault", alwaysDenied(WritePropertiesCheckActions::setDefaultLocale)), entry("locale_setDefault", alwaysDenied(WritePropertiesCheckActions::setDefaultLocale)),
@ -230,8 +198,11 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
entry("symbol_lookup_name", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithName, false, 22)), entry("symbol_lookup_name", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithName, false, 22)),
entry("symbol_lookup_path", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithPath, false, 22)) entry("symbol_lookup_path", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithPath, false, 22))
), ),
getTestEntries(FileCheckActions.class) getTestEntries(FileCheckActions.class),
getTestEntries(SpiActions.class),
getTestEntries(SystemActions.class)
) )
.flatMap(Function.identity())
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion()) .filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
.collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue)); .collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue));
@ -267,7 +238,7 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
} }
} }
}; };
boolean deniedToPlugins = testAnnotation.expectedAccess() == PLUGINS; boolean deniedToPlugins = testAnnotation.expectedAccess() != PLUGINS;
Integer fromJavaVersion = testAnnotation.fromJavaVersion() == -1 ? null : testAnnotation.fromJavaVersion(); Integer fromJavaVersion = testAnnotation.fromJavaVersion() == -1 ? null : testAnnotation.fromJavaVersion();
entries.add(entry(method.getName(), new CheckAction(runnable, deniedToPlugins, fromJavaVersion))); entries.add(entry(method.getName(), new CheckAction(runnable, deniedToPlugins, fromJavaVersion)));
} }
@ -323,21 +294,6 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
HttpsURLConnection.setDefaultSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory()); HttpsURLConnection.setDefaultSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory());
} }
@SuppressForbidden(reason = "Specifically testing Runtime.exit")
private static void runtimeExit() {
Runtime.getRuntime().exit(123);
}
@SuppressForbidden(reason = "Specifically testing Runtime.halt")
private static void runtimeHalt() {
Runtime.getRuntime().halt(123);
}
@SuppressForbidden(reason = "Specifically testing System.exit")
private static void systemExit() {
System.exit(123);
}
private static void createClassLoader() throws IOException { private static void createClassLoader() throws IOException {
try (var classLoader = new URLClassLoader("test", new URL[0], RestEntitlementsCheckAction.class.getClassLoader())) { try (var classLoader = new URLClassLoader("test", new URL[0], RestEntitlementsCheckAction.class.getClassLoader())) {
logger.info("Created URLClassLoader [{}]", classLoader.getName()); logger.info("Created URLClassLoader [{}]", classLoader.getName());
@ -356,80 +312,10 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
new DummyImplementations.DummyHttpsURLConnection().setSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory()); new DummyImplementations.DummyHttpsURLConnection().setSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory());
} }
private static void system$$setIn() {
System.setIn(System.in);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
private static void system$$setOut() {
System.setOut(System.out);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
private static void system$$setErr() {
System.setErr(System.err);
}
private static void runtime$addShutdownHook() {
Runtime.getRuntime().addShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
private static void runtime$$removeShutdownHook() {
Runtime.getRuntime().removeShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
private static void thread$$setDefaultUncaughtExceptionHandler() { private static void thread$$setDefaultUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(Thread.getDefaultUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(Thread.getDefaultUncaughtExceptionHandler());
} }
private static void localeServiceProvider$() {
new DummyLocaleServiceProvider();
}
private static void breakIteratorProvider$() {
new DummyBreakIteratorProvider();
}
private static void collatorProvider$() {
new DummyCollatorProvider();
}
private static void dateFormatProvider$() {
new DummyDateFormatProvider();
}
private static void dateFormatSymbolsProvider$() {
new DummyDateFormatSymbolsProvider();
}
private static void decimalFormatSymbolsProvider$() {
new DummyDecimalFormatSymbolsProvider();
}
private static void numberFormatProvider$() {
new DummyNumberFormatProvider();
}
private static void calendarDataProvider$() {
new DummyCalendarDataProvider();
}
private static void calendarNameProvider$() {
new DummyCalendarNameProvider();
}
private static void currencyNameProvider$() {
new DummyCurrencyNameProvider();
}
private static void localeNameProvider$() {
new DummyLocaleNameProvider();
}
private static void timeZoneNameProvider$() {
new DummyTimeZoneNameProvider();
}
private static void logManager$() { private static void logManager$() {
new java.util.logging.LogManager() { new java.util.logging.LogManager() {
}; };

View file

@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.entitlement.qa.test;
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED;
class SpiActions {
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createBreakIteratorProvider() {
new DummyImplementations.DummyBreakIteratorProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createCollatorProvider() {
new DummyImplementations.DummyCollatorProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createDateFormatProvider() {
new DummyImplementations.DummyDateFormatProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createDateFormatSymbolsProvider() {
new DummyImplementations.DummyDateFormatSymbolsProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createDecimalFormatSymbolsProvider() {
new DummyImplementations.DummyDecimalFormatSymbolsProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createNumberFormatProvider() {
new DummyImplementations.DummyNumberFormatProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createCalendarDataProvider() {
new DummyImplementations.DummyCalendarDataProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createCalendarNameProvider() {
new DummyImplementations.DummyCalendarNameProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createCurrencyNameProvider() {
new DummyImplementations.DummyCurrencyNameProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createLocaleNameProvider() {
new DummyImplementations.DummyLocaleNameProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createTimeZoneNameProvider() {
new DummyImplementations.DummyTimeZoneNameProvider();
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createLocaleServiceProvider() {
new DummyImplementations.DummyLocaleServiceProvider();
}
private SpiActions() {}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.entitlement.qa.test;
import org.elasticsearch.core.SuppressForbidden;
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED;
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.SERVER_ONLY;
class SystemActions {
@SuppressForbidden(reason = "Specifically testing Runtime.exit")
@EntitlementTest(expectedAccess = SERVER_ONLY)
static void runtimeExit() {
Runtime.getRuntime().exit(123);
}
@SuppressForbidden(reason = "Specifically testing Runtime.halt")
@EntitlementTest(expectedAccess = SERVER_ONLY)
static void runtimeHalt() {
Runtime.getRuntime().halt(123);
}
@SuppressForbidden(reason = "Specifically testing System.exit")
@EntitlementTest(expectedAccess = SERVER_ONLY)
static void systemExit() {
System.exit(123);
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void systemSetIn() {
System.setIn(System.in);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void systemSetOut() {
System.setOut(System.out);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void systemSetErr() {
System.setErr(System.err);
}
private static final Thread NO_OP_SHUTDOWN_HOOK = new Thread(() -> {}, "Shutdown hook for testing");
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void runtimeAddShutdownHook() {
Runtime.getRuntime().addShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void runtimeRemoveShutdownHook() {
Runtime.getRuntime().removeShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
private SystemActions() {}
}

View file

@ -22,7 +22,7 @@ import java.lang.annotation.Target;
* using this annotation is considered parseable as part of a policy file * using this annotation is considered parseable as part of a policy file
* for entitlements. * for entitlements.
*/ */
@Target(ElementType.CONSTRUCTOR) @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ExternalEntitlement { public @interface ExternalEntitlement {

View file

@ -17,8 +17,11 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
public final class FileAccessTree { public final class FileAccessTree {
public static final FileAccessTree EMPTY = new FileAccessTree(List.of()); public static final FileAccessTree EMPTY = new FileAccessTree(List.of());
private static final String FILE_SEPARATOR = getDefaultFileSystem().getSeparator();
private final String[] readPaths; private final String[] readPaths;
private final String[] writePaths; private final String[] writePaths;
@ -27,11 +30,11 @@ public final class FileAccessTree {
List<String> readPaths = new ArrayList<>(); List<String> readPaths = new ArrayList<>();
List<String> writePaths = new ArrayList<>(); List<String> writePaths = new ArrayList<>();
for (FileEntitlement fileEntitlement : fileEntitlements) { for (FileEntitlement fileEntitlement : fileEntitlements) {
var mode = fileEntitlement.mode(); String path = normalizePath(Path.of(fileEntitlement.path()));
if (mode == FileEntitlement.Mode.READ_WRITE) { if (fileEntitlement.mode() == FileEntitlement.Mode.READ_WRITE) {
writePaths.add(fileEntitlement.path()); writePaths.add(path);
} }
readPaths.add(fileEntitlement.path()); readPaths.add(path);
} }
readPaths.sort(String::compareTo); readPaths.sort(String::compareTo);
@ -46,14 +49,20 @@ public final class FileAccessTree {
} }
boolean canRead(Path path) { boolean canRead(Path path) {
return checkPath(normalize(path), readPaths); return checkPath(normalizePath(path), readPaths);
} }
boolean canWrite(Path path) { boolean canWrite(Path path) {
return checkPath(normalize(path), writePaths); return checkPath(normalizePath(path), writePaths);
} }
private static String normalize(Path path) { /**
* @return the "canonical" form of the given {@code path}, to be used for entitlement checks.
*/
static String normalizePath(Path path) {
// Note that toAbsolutePath produces paths separated by the default file separator,
// so on Windows, if the given path uses forward slashes, this consistently
// converts it to backslashes.
return path.toAbsolutePath().normalize().toString(); return path.toAbsolutePath().normalize().toString();
} }
@ -64,7 +73,7 @@ public final class FileAccessTree {
int ndx = Arrays.binarySearch(paths, path); int ndx = Arrays.binarySearch(paths, path);
if (ndx < -1) { if (ndx < -1) {
String maybeParent = paths[-ndx - 2]; String maybeParent = paths[-ndx - 2];
return path.startsWith(maybeParent); return path.startsWith(maybeParent) && path.startsWith(FILE_SEPARATOR, maybeParent.length());
} }
return ndx >= 0; return ndx >= 0;
} }

View file

@ -27,6 +27,8 @@ import java.io.InputStream;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -147,6 +149,7 @@ public class PolicyParser {
} }
Constructor<?> entitlementConstructor = null; Constructor<?> entitlementConstructor = null;
Method entitlementMethod = null;
ExternalEntitlement entitlementMetadata = null; ExternalEntitlement entitlementMetadata = null;
for (var ctor : entitlementClass.getConstructors()) { for (var ctor : entitlementClass.getConstructors()) {
var metadata = ctor.getAnnotation(ExternalEntitlement.class); var metadata = ctor.getAnnotation(ExternalEntitlement.class);
@ -161,8 +164,27 @@ public class PolicyParser {
entitlementConstructor = ctor; entitlementConstructor = ctor;
entitlementMetadata = metadata; entitlementMetadata = metadata;
} }
} }
for (var method : entitlementClass.getMethods()) {
var metadata = method.getAnnotation(ExternalEntitlement.class);
if (metadata != null) {
if (Modifier.isStatic(method.getModifiers()) == false) {
throw new IllegalStateException(
"entitlement class [" + entitlementClass.getName() + "] has non-static method annotated with ExternalEntitlement"
);
}
if (entitlementMetadata != null) {
throw new IllegalStateException(
"entitlement class ["
+ entitlementClass.getName()
+ "] has more than one constructor and/or method annotated with ExternalEntitlement"
);
}
entitlementMethod = method;
entitlementMetadata = metadata;
}
}
if (entitlementMetadata == null) { if (entitlementMetadata == null) {
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]"); throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
} }
@ -171,7 +193,9 @@ public class PolicyParser {
throw newPolicyParserException("entitlement type [" + entitlementType + "] is allowed only on modules"); throw newPolicyParserException("entitlement type [" + entitlementType + "] is allowed only on modules");
} }
Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes(); Class<?>[] parameterTypes = entitlementConstructor != null
? entitlementConstructor.getParameterTypes()
: entitlementMethod.getParameterTypes();
String[] parametersNames = entitlementMetadata.parameterNames(); String[] parametersNames = entitlementMetadata.parameterNames();
if (parameterTypes.length != 0 || parametersNames.length != 0) { if (parameterTypes.length != 0 || parametersNames.length != 0) {
@ -204,7 +228,11 @@ public class PolicyParser {
} }
try { try {
return (Entitlement) entitlementConstructor.newInstance(parameterValues); if (entitlementConstructor != null) {
return (Entitlement) entitlementConstructor.newInstance(parameterValues);
} else {
return (Entitlement) entitlementMethod.invoke(null, parameterValues);
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
if (e.getCause() instanceof PolicyValidationException piae) { if (e.getCause() instanceof PolicyValidationException piae) {
throw newPolicyParserException(startLocation, scopeName, entitlementType, piae); throw newPolicyParserException(startLocation, scopeName, entitlementType, piae);

View file

@ -12,10 +12,12 @@ package org.elasticsearch.entitlement.runtime.policy.entitlements;
import org.elasticsearch.entitlement.runtime.policy.ExternalEntitlement; import org.elasticsearch.entitlement.runtime.policy.ExternalEntitlement;
import org.elasticsearch.entitlement.runtime.policy.PolicyValidationException; import org.elasticsearch.entitlement.runtime.policy.PolicyValidationException;
import java.nio.file.Paths;
/** /**
* Describes a file entitlement with a path and mode. * Describes entitlement to access files at a particular location.
*
* @param path the location of the files. For directories, implicitly includes access to
* all contained files and (recursively) subdirectories.
* @param mode the type of operation
*/ */
public record FileEntitlement(String path, Mode mode) implements Entitlement { public record FileEntitlement(String path, Mode mode) implements Entitlement {
@ -24,14 +26,6 @@ public record FileEntitlement(String path, Mode mode) implements Entitlement {
READ_WRITE READ_WRITE
} }
public FileEntitlement {
path = normalizePath(path);
}
private static String normalizePath(String path) {
return Paths.get(path).toAbsolutePath().normalize().toString();
}
private static Mode parseMode(String mode) { private static Mode parseMode(String mode) {
if (mode.equals("read")) { if (mode.equals("read")) {
return Mode.READ; return Mode.READ;
@ -43,7 +37,7 @@ public record FileEntitlement(String path, Mode mode) implements Entitlement {
} }
@ExternalEntitlement(parameterNames = { "path", "mode" }, esModulesOnly = false) @ExternalEntitlement(parameterNames = { "path", "mode" }, esModulesOnly = false)
public FileEntitlement(String path, String mode) { public static FileEntitlement create(String path, String mode) {
this(path, parseMode(mode)); return new FileEntitlement(path, parseMode(mode));
} }
} }

View file

@ -16,6 +16,7 @@ import org.junit.BeforeClass;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
public class FileAccessTreeTests extends ESTestCase { public class FileAccessTreeTests extends ESTestCase {
@ -41,7 +42,9 @@ public class FileAccessTreeTests extends ESTestCase {
var tree = FileAccessTree.of(List.of(entitlement("foo", "read"))); var tree = FileAccessTree.of(List.of(entitlement("foo", "read")));
assertThat(tree.canRead(path("foo")), is(true)); assertThat(tree.canRead(path("foo")), is(true));
assertThat(tree.canRead(path("foo/subdir")), is(true)); assertThat(tree.canRead(path("foo/subdir")), is(true));
assertThat(tree.canRead(path("food")), is(false));
assertThat(tree.canWrite(path("foo")), is(false)); assertThat(tree.canWrite(path("foo")), is(false));
assertThat(tree.canWrite(path("food")), is(false));
assertThat(tree.canRead(path("before")), is(false)); assertThat(tree.canRead(path("before")), is(false));
assertThat(tree.canRead(path("later")), is(false)); assertThat(tree.canRead(path("later")), is(false));
@ -51,7 +54,9 @@ public class FileAccessTreeTests extends ESTestCase {
var tree = FileAccessTree.of(List.of(entitlement("foo", "read_write"))); var tree = FileAccessTree.of(List.of(entitlement("foo", "read_write")));
assertThat(tree.canWrite(path("foo")), is(true)); assertThat(tree.canWrite(path("foo")), is(true));
assertThat(tree.canWrite(path("foo/subdir")), is(true)); assertThat(tree.canWrite(path("foo/subdir")), is(true));
assertThat(tree.canWrite(path("food")), is(false));
assertThat(tree.canRead(path("foo")), is(true)); assertThat(tree.canRead(path("foo")), is(true));
assertThat(tree.canRead(path("food")), is(false));
assertThat(tree.canWrite(path("before")), is(false)); assertThat(tree.canWrite(path("before")), is(false));
assertThat(tree.canWrite(path("later")), is(false)); assertThat(tree.canWrite(path("later")), is(false));
@ -83,8 +88,24 @@ public class FileAccessTreeTests extends ESTestCase {
assertThat(tree.canRead(path("")), is(false)); assertThat(tree.canRead(path("")), is(false));
} }
public void testForwardSlashes() {
String sep = getDefaultFileSystem().getSeparator();
var tree = FileAccessTree.of(List.of(entitlement("a/b", "read"), entitlement("m" + sep + "n", "read")));
// Native separators work
assertThat(tree.canRead(path("a" + sep + "b")), is(true));
assertThat(tree.canRead(path("m" + sep + "n")), is(true));
// Forward slashes also work
assertThat(tree.canRead(path("a/b")), is(true));
assertThat(tree.canRead(path("m/n")), is(true));
// In case the native separator is a backslash, don't treat that as an escape
assertThat(tree.canRead(path("m\n")), is(false));
}
FileEntitlement entitlement(String path, String mode) { FileEntitlement entitlement(String path, String mode) {
Path p = path(path); Path p = path(path);
return new FileEntitlement(p.toString(), mode); return FileEntitlement.create(p.toString(), mode);
} }
} }

View file

@ -40,6 +40,35 @@ public class PolicyParserTests extends ESTestCase {
public ManyConstructorsEntitlement(int i) {} public ManyConstructorsEntitlement(int i) {}
} }
public static class ManyMethodsEntitlement implements Entitlement {
@ExternalEntitlement
public static ManyMethodsEntitlement create(String s) {
return new ManyMethodsEntitlement();
}
@ExternalEntitlement
public static ManyMethodsEntitlement create(int i) {
return new ManyMethodsEntitlement();
}
}
public static class ConstructorAndMethodEntitlement implements Entitlement {
@ExternalEntitlement
public static ConstructorAndMethodEntitlement create(String s) {
return new ConstructorAndMethodEntitlement(s);
}
@ExternalEntitlement
public ConstructorAndMethodEntitlement(String s) {}
}
public static class NonStaticMethodEntitlement implements Entitlement {
@ExternalEntitlement
public NonStaticMethodEntitlement create() {
return new NonStaticMethodEntitlement();
}
}
public void testGetEntitlementTypeName() { public void testGetEntitlementTypeName() {
assertEquals("create_class_loader", PolicyParser.getEntitlementTypeName(CreateClassLoaderEntitlement.class)); assertEquals("create_class_loader", PolicyParser.getEntitlementTypeName(CreateClassLoaderEntitlement.class));
@ -55,7 +84,7 @@ public class PolicyParserTests extends ESTestCase {
.parsePolicy(); .parsePolicy();
Policy expected = new Policy( Policy expected = new Policy(
"test-policy.yaml", "test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write")))) List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
); );
assertEquals(expected, parsedPolicy); assertEquals(expected, parsedPolicy);
} }
@ -65,7 +94,7 @@ public class PolicyParserTests extends ESTestCase {
.parsePolicy(); .parsePolicy();
Policy expected = new Policy( Policy expected = new Policy(
"test-policy.yaml", "test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write")))) List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
); );
assertEquals(expected, parsedPolicy); assertEquals(expected, parsedPolicy);
} }
@ -174,4 +203,60 @@ public class PolicyParserTests extends ESTestCase {
) )
); );
} }
public void testMultipleMethodsAnnotated() throws IOException {
var parser = new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name:
- many_methods
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("many_methods", ManyMethodsEntitlement.class));
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ManyMethodsEntitlement]"
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
)
);
}
public void testConstructorAndMethodAnnotated() throws IOException {
var parser = new PolicyParser(
new ByteArrayInputStream("""
entitlement-module-name:
- constructor_and_method
""".getBytes(StandardCharsets.UTF_8)),
"test-policy.yaml",
true,
Map.of("constructor_and_method", ConstructorAndMethodEntitlement.class)
);
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ConstructorAndMethodEntitlement]"
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
)
);
}
public void testNonStaticMethodAnnotated() throws IOException {
var parser = new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name:
- non_static
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("non_static", NonStaticMethodEntitlement.class));
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$NonStaticMethodEntitlement]"
+ " has non-static method annotated with ExternalEntitlement"
)
);
}
} }

View file

@ -20,7 +20,7 @@ esplugin {
restResources { restResources {
restApi { restApi {
include '_common', 'indices', 'index', 'cluster', 'search', 'nodes', 'bulk', 'termvectors', 'explain', 'count' include '_common', 'indices', 'index', 'cluster', 'search', 'nodes', 'bulk', 'termvectors', 'explain', 'count', 'capabilities'
} }
} }

View file

@ -207,7 +207,7 @@ public class ReloadAnalyzerTests extends ESSingleNodeTestCase {
public void testUpdateableSynonymsRejectedAtIndexTime() throws FileNotFoundException, IOException { public void testUpdateableSynonymsRejectedAtIndexTime() throws FileNotFoundException, IOException {
String synonymsFileName = "synonyms.txt"; String synonymsFileName = "synonyms.txt";
setupResourceFile(synonymsFileName, "foo, baz"); setupResourceFile(synonymsFileName, "foo, baz");
Path configDir = node().getEnvironment().configFile(); Path configDir = node().getEnvironment().configDir();
if (Files.exists(configDir) == false) { if (Files.exists(configDir) == false) {
Files.createDirectory(configDir); Files.createDirectory(configDir);
} }
@ -319,7 +319,7 @@ public class ReloadAnalyzerTests extends ESSingleNodeTestCase {
} }
private Path setupResourceFile(String fileName, String... content) throws IOException { private Path setupResourceFile(String fileName, String... content) throws IOException {
Path configDir = node().getEnvironment().configFile(); Path configDir = node().getEnvironment().configDir();
if (Files.exists(configDir) == false) { if (Files.exists(configDir) == false) {
Files.createDirectory(configDir); Files.createDirectory(configDir);
} }

View file

@ -57,7 +57,7 @@ public class ReloadSynonymAnalyzerIT extends ESIntegTestCase {
} }
private void testSynonymsUpdate(boolean preview) throws FileNotFoundException, IOException, InterruptedException { private void testSynonymsUpdate(boolean preview) throws FileNotFoundException, IOException, InterruptedException {
Path config = internalCluster().getInstance(Environment.class).configFile(); Path config = internalCluster().getInstance(Environment.class).configDir();
String synonymsFileName = "synonyms.txt"; String synonymsFileName = "synonyms.txt";
Path synonymsFile = config.resolve(synonymsFileName); Path synonymsFile = config.resolve(synonymsFileName);
writeFile(synonymsFile, "foo, baz"); writeFile(synonymsFile, "foo, baz");
@ -106,7 +106,7 @@ public class ReloadSynonymAnalyzerIT extends ESIntegTestCase {
final String synonymsFileName = "synonyms.txt"; final String synonymsFileName = "synonyms.txt";
final String fieldName = "field"; final String fieldName = "field";
Path config = internalCluster().getInstance(Environment.class).configFile(); Path config = internalCluster().getInstance(Environment.class).configDir();
Path synonymsFile = config.resolve(synonymsFileName); Path synonymsFile = config.resolve(synonymsFileName);
writeFile(synonymsFile, "foo, baz"); writeFile(synonymsFile, "foo, baz");

View file

@ -40,7 +40,7 @@ public class HyphenationCompoundWordTokenFilterFactory extends AbstractCompoundW
throw new IllegalArgumentException("hyphenation_patterns_path is a required setting."); throw new IllegalArgumentException("hyphenation_patterns_path is a required setting.");
} }
Path hyphenationPatternsFile = env.configFile().resolve(hyphenationPatternsPath); Path hyphenationPatternsFile = env.configDir().resolve(hyphenationPatternsPath);
try { try {
InputStream in = Files.newInputStream(hyphenationPatternsFile); InputStream in = Files.newInputStream(hyphenationPatternsFile);

View file

@ -59,3 +59,28 @@
- match: { detail.tokenizer.tokens.0.token: ABc } - match: { detail.tokenizer.tokens.0.token: ABc }
- match: { detail.tokenfilters.0.name: lowercase } - match: { detail.tokenfilters.0.name: lowercase }
- match: { detail.tokenfilters.0.tokens.0.token: abc } - match: { detail.tokenfilters.0.tokens.0.token: abc }
---
"Custom analyzer is not buildable":
- requires:
test_runner_features: [ capabilities ]
reason: This capability required to run test
capabilities:
- method: GET
path: /_analyze
capabilities: [ wrong_custom_analyzer_returns_400 ]
- do:
catch: bad_request
indices.analyze:
body:
text: the foxes jumping quickly
tokenizer:
standard
filter:
type: hunspell
locale: en_US
- match: { status: 400 }
- match: { error.type: illegal_argument_exception }
- match: { error.reason: "Can not build a custom analyzer" }

View file

@ -251,7 +251,7 @@ public class DataStreamGetWriteIndexTests extends ESTestCase {
MetadataCreateIndexService createIndexService; MetadataCreateIndexService createIndexService;
{ {
Environment env = mock(Environment.class); Environment env = mock(Environment.class);
when(env.sharedDataFile()).thenReturn(null); when(env.sharedDataDir()).thenReturn(null);
AllocationService allocationService = mock(AllocationService.class); AllocationService allocationService = mock(AllocationService.class);
when(allocationService.reroute(any(ClusterState.class), any(String.class), any())).then(i -> i.getArguments()[0]); when(allocationService.reroute(any(ClusterState.class), any(String.class), any())).then(i -> i.getArguments()[0]);
when(allocationService.getShardRoutingRoleStrategy()).thenReturn(TestShardRoutingRoleStrategies.DEFAULT_ROLE_ONLY); when(allocationService.getShardRoutingRoleStrategy()).thenReturn(TestShardRoutingRoleStrategies.DEFAULT_ROLE_ONLY);

View file

@ -664,7 +664,7 @@ public class GeoIpDownloaderIT extends AbstractGeoIpIT {
.map(DiscoveryNode::getId) .map(DiscoveryNode::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// All nodes share the same geoip base dir in the shared tmp dir: // All nodes share the same geoip base dir in the shared tmp dir:
Path geoipBaseTmpDir = internalCluster().getDataNodeInstance(Environment.class).tmpFile().resolve("geoip-databases"); Path geoipBaseTmpDir = internalCluster().getDataNodeInstance(Environment.class).tmpDir().resolve("geoip-databases");
assertThat(Files.exists(geoipBaseTmpDir), is(true)); assertThat(Files.exists(geoipBaseTmpDir), is(true));
final List<Path> geoipTmpDirs; final List<Path> geoipTmpDirs;
try (Stream<Path> files = Files.list(geoipBaseTmpDir)) { try (Stream<Path> files = Files.list(geoipBaseTmpDir)) {
@ -676,7 +676,7 @@ public class GeoIpDownloaderIT extends AbstractGeoIpIT {
private void setupDatabasesInConfigDirectory() throws Exception { private void setupDatabasesInConfigDirectory() throws Exception {
StreamSupport.stream(internalCluster().getInstances(Environment.class).spliterator(), false) StreamSupport.stream(internalCluster().getInstances(Environment.class).spliterator(), false)
.map(Environment::configFile) .map(Environment::configDir)
.map(path -> path.resolve("ingest-geoip")) .map(path -> path.resolve("ingest-geoip"))
.distinct() .distinct()
.forEach(path -> { .forEach(path -> {
@ -704,7 +704,7 @@ public class GeoIpDownloaderIT extends AbstractGeoIpIT {
private void deleteDatabasesInConfigDirectory() throws Exception { private void deleteDatabasesInConfigDirectory() throws Exception {
StreamSupport.stream(internalCluster().getInstances(Environment.class).spliterator(), false) StreamSupport.stream(internalCluster().getInstances(Environment.class).spliterator(), false)
.map(Environment::configFile) .map(Environment::configDir)
.map(path -> path.resolve("ingest-geoip")) .map(path -> path.resolve("ingest-geoip"))
.distinct() .distinct()
.forEach(path -> { .forEach(path -> {

View file

@ -42,7 +42,7 @@ final class ConfigDatabases implements Closeable {
private final ConcurrentMap<String, DatabaseReaderLazyLoader> configDatabases; private final ConcurrentMap<String, DatabaseReaderLazyLoader> configDatabases;
ConfigDatabases(Environment environment, GeoIpCache cache) { ConfigDatabases(Environment environment, GeoIpCache cache) {
this(environment.configFile().resolve("ingest-geoip"), cache); this(environment.configDir().resolve("ingest-geoip"), cache);
} }
ConfigDatabases(Path geoipConfigDir, GeoIpCache cache) { ConfigDatabases(Path geoipConfigDir, GeoIpCache cache) {

View file

@ -116,7 +116,7 @@ public final class DatabaseNodeService implements IpDatabaseProvider {
ClusterService clusterService ClusterService clusterService
) { ) {
this( this(
environment.tmpFile(), environment.tmpDir(),
new OriginSettingClient(client, IngestService.INGEST_ORIGIN), new OriginSettingClient(client, IngestService.INGEST_ORIGIN),
cache, cache,
new ConfigDatabases(environment, cache), new ConfigDatabases(environment, cache),

View file

@ -41,7 +41,7 @@ public class IngestUserAgentPlugin extends Plugin implements IngestPlugin {
@Override @Override
public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) { public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {
Path userAgentConfigDirectory = parameters.env.configFile().resolve("ingest-user-agent"); Path userAgentConfigDirectory = parameters.env.configDir().resolve("ingest-user-agent");
if (Files.exists(userAgentConfigDirectory) == false && Files.isDirectory(userAgentConfigDirectory)) { if (Files.exists(userAgentConfigDirectory) == false && Files.isDirectory(userAgentConfigDirectory)) {
throw new IllegalStateException( throw new IllegalStateException(

View file

@ -106,7 +106,7 @@ public class ReindexSslConfig {
return settings.getAsList(key); return settings.getAsList(key);
} }
}; };
configuration = loader.load(environment.configFile()); configuration = loader.load(environment.configDir());
reload(); reload();
final FileChangesListener listener = new FileChangesListener() { final FileChangesListener listener = new FileChangesListener() {

View file

@ -369,7 +369,7 @@ class S3Service implements Closeable {
} }
// Make sure that a readable symlink to the token file exists in the plugin config directory // Make sure that a readable symlink to the token file exists in the plugin config directory
// AWS_WEB_IDENTITY_TOKEN_FILE exists but we only use Web Identity Tokens if a corresponding symlink exists and is readable // AWS_WEB_IDENTITY_TOKEN_FILE exists but we only use Web Identity Tokens if a corresponding symlink exists and is readable
Path webIdentityTokenFileSymlink = environment.configFile().resolve(WEB_IDENTITY_TOKEN_FILE_LOCATION); Path webIdentityTokenFileSymlink = environment.configDir().resolve(WEB_IDENTITY_TOKEN_FILE_LOCATION);
if (Files.exists(webIdentityTokenFileSymlink) == false) { if (Files.exists(webIdentityTokenFileSymlink) == false) {
LOGGER.warn( LOGGER.warn(
"Cannot use AWS Web Identity Tokens: AWS_WEB_IDENTITY_TOKEN_FILE is defined but no corresponding symlink exists " "Cannot use AWS Web Identity Tokens: AWS_WEB_IDENTITY_TOKEN_FILE is defined but no corresponding symlink exists "

View file

@ -65,7 +65,7 @@ public class CustomWebIdentityTokenCredentialsProviderTests extends ESTestCase {
Files.createDirectory(configDirectory.resolve("repository-s3")); Files.createDirectory(configDirectory.resolve("repository-s3"));
Files.writeString(configDirectory.resolve("repository-s3/aws-web-identity-token-file"), "YXdzLXdlYi1pZGVudGl0eS10b2tlbi1maWxl"); Files.writeString(configDirectory.resolve("repository-s3/aws-web-identity-token-file"), "YXdzLXdlYi1pZGVudGl0eS10b2tlbi1maWxl");
Environment environment = Mockito.mock(Environment.class); Environment environment = Mockito.mock(Environment.class);
Mockito.when(environment.configFile()).thenReturn(configDirectory); Mockito.when(environment.configDir()).thenReturn(configDirectory);
return environment; return environment;
} }
@ -212,7 +212,7 @@ public class CustomWebIdentityTokenCredentialsProviderTests extends ESTestCase {
latch.countDown(); latch.countDown();
} }
}); });
Files.writeString(environment.configFile().resolve("repository-s3/aws-web-identity-token-file"), newWebIdentityToken); Files.writeString(environment.configDir().resolve("repository-s3/aws-web-identity-token-file"), newWebIdentityToken);
safeAwait(latch); safeAwait(latch);
assertCredentials(awsCredentialsProvider.getCredentials()); assertCredentials(awsCredentialsProvider.getCredentials());

View file

@ -158,7 +158,7 @@ public class URLRepository extends BlobStoreRepository {
if (normalizedUrl == null) { if (normalizedUrl == null) {
String logMessage = "The specified url [{}] doesn't start with any repository paths specified by the " String logMessage = "The specified url [{}] doesn't start with any repository paths specified by the "
+ "path.repo setting or by {} setting: [{}] "; + "path.repo setting or by {} setting: [{}] ";
logger.warn(logMessage, urlToCheck, ALLOWED_URLS_SETTING.getKey(), environment.repoFiles()); logger.warn(logMessage, urlToCheck, ALLOWED_URLS_SETTING.getKey(), environment.repoDirs());
String exceptionMessage = "file url [" String exceptionMessage = "file url ["
+ urlToCheck + urlToCheck
+ "] doesn't match any of the locations specified by path.repo or " + "] doesn't match any of the locations specified by path.repo or "

View file

@ -134,6 +134,12 @@ tests:
- class: org.elasticsearch.datastreams.DataStreamsClientYamlTestSuiteIT - class: org.elasticsearch.datastreams.DataStreamsClientYamlTestSuiteIT
method: test {p0=data_stream/120_data_streams_stats/Multiple data stream} method: test {p0=data_stream/120_data_streams_stats/Multiple data stream}
issue: https://github.com/elastic/elasticsearch/issues/118217 issue: https://github.com/elastic/elasticsearch/issues/118217
# TODO: re-enable after backporting https://github.com/elastic/elasticsearch/pull/119110
- class: org.elasticsearch.test.rest.ClientYamlTestSuiteIT
method: test {yaml=update/100_synthetic_source/keyword}
# TODO: re-enable after backporting https://github.com/elastic/elasticsearch/pull/119110
- class: org.elasticsearch.test.rest.ClientYamlTestSuiteIT
method: test {yaml=update/100_synthetic_source/stored text}
- class: org.elasticsearch.xpack.searchablesnapshots.RetrySearchIntegTests - class: org.elasticsearch.xpack.searchablesnapshots.RetrySearchIntegTests
method: testSearcherId method: testSearcherId
issue: https://github.com/elastic/elasticsearch/issues/118374 issue: https://github.com/elastic/elasticsearch/issues/118374
@ -385,9 +391,11 @@ tests:
- class: org.elasticsearch.xpack.esql.action.CrossClusterAsyncQueryStopIT - class: org.elasticsearch.xpack.esql.action.CrossClusterAsyncQueryStopIT
method: testStopQueryLocal method: testStopQueryLocal
issue: https://github.com/elastic/elasticsearch/issues/121672 issue: https://github.com/elastic/elasticsearch/issues/121672
- class: org.elasticsearch.xpack.esql.heap_attack.HeapAttackIT - class: org.elasticsearch.xpack.esql.qa.multi_node.EsqlSpecIT
method: testLookupExplosionBigStringManyMatches issue: https://github.com/elastic/elasticsearch/issues/121411
issue: https://github.com/elastic/elasticsearch/issues/121465 - class: org.elasticsearch.transport.InboundHandlerTests
method: testLogsSlowInboundProcessing
issue: https://github.com/elastic/elasticsearch/issues/121816
# Examples: # Examples:
# #

View file

@ -51,7 +51,7 @@ public class IcuCollationTokenFilterFactory extends AbstractTokenFilterFactory {
if (rules != null) { if (rules != null) {
Exception failureToResolve = null; Exception failureToResolve = null;
try { try {
rules = Streams.copyToString(Files.newBufferedReader(environment.configFile().resolve(rules), Charset.forName("UTF-8"))); rules = Streams.copyToString(Files.newBufferedReader(environment.configDir().resolve(rules), Charset.forName("UTF-8")));
} catch (IOException | SecurityException | InvalidPathException e) { } catch (IOException | SecurityException | InvalidPathException e) {
failureToResolve = e; failureToResolve = e;
} }

View file

@ -99,7 +99,7 @@ public class IcuTokenizerFactory extends AbstractTokenizerFactory {
// parse a single RBBi rule file // parse a single RBBi rule file
private static BreakIterator parseRules(String filename, Environment env) throws IOException { private static BreakIterator parseRules(String filename, Environment env) throws IOException {
final Path path = env.configFile().resolve(filename); final Path path = env.configDir().resolve(filename);
String rules = Files.readAllLines(path).stream().filter((v) -> v.startsWith("#") == false).collect(Collectors.joining("\n")); String rules = Files.readAllLines(path).stream().filter((v) -> v.startsWith("#") == false).collect(Collectors.joining("\n"));
return new RuleBasedBreakIterator(rules.toString()); return new RuleBasedBreakIterator(rules.toString());

View file

@ -81,7 +81,7 @@ class HdfsSecurityContext {
* Expects keytab file to exist at {@code $CONFIG_DIR$/repository-hdfs/krb5.keytab} * Expects keytab file to exist at {@code $CONFIG_DIR$/repository-hdfs/krb5.keytab}
*/ */
static Path locateKeytabFile(Environment environment) { static Path locateKeytabFile(Environment environment) {
Path keytabPath = environment.configFile().resolve("repository-hdfs").resolve("krb5.keytab"); Path keytabPath = environment.configDir().resolve("repository-hdfs").resolve("krb5.keytab");
try { try {
if (Files.exists(keytabPath) == false) { if (Files.exists(keytabPath) == false) {
throw new RuntimeException("Could not locate keytab at [" + keytabPath + "]."); throw new RuntimeException("Could not locate keytab at [" + keytabPath + "].");

View file

@ -103,23 +103,23 @@ public class EvilSecurityTests extends ESTestCase {
// check that all directories got permissions: // check that all directories got permissions:
// bin file: ro // bin file: ro
assertExactPermissions(new FilePermission(environment.binFile().toString(), "read,readlink"), permissions); assertExactPermissions(new FilePermission(environment.binDir().toString(), "read,readlink"), permissions);
// lib file: ro // lib file: ro
assertExactPermissions(new FilePermission(environment.libFile().toString(), "read,readlink"), permissions); assertExactPermissions(new FilePermission(environment.libDir().toString(), "read,readlink"), permissions);
// modules file: ro // modules file: ro
assertExactPermissions(new FilePermission(environment.modulesFile().toString(), "read,readlink"), permissions); assertExactPermissions(new FilePermission(environment.modulesDir().toString(), "read,readlink"), permissions);
// config file: ro // config file: ro
assertExactPermissions(new FilePermission(environment.configFile().toString(), "read,readlink"), permissions); assertExactPermissions(new FilePermission(environment.configDir().toString(), "read,readlink"), permissions);
// plugins: ro // plugins: ro
assertExactPermissions(new FilePermission(environment.pluginsFile().toString(), "read,readlink"), permissions); assertExactPermissions(new FilePermission(environment.pluginsDir().toString(), "read,readlink"), permissions);
// data paths: r/w // data paths: r/w
for (Path dataPath : environment.dataFiles()) { for (Path dataPath : environment.dataDirs()) {
assertExactPermissions(new FilePermission(dataPath.toString(), "read,readlink,write,delete"), permissions); assertExactPermissions(new FilePermission(dataPath.toString(), "read,readlink,write,delete"), permissions);
} }
assertExactPermissions(new FilePermission(environment.sharedDataFile().toString(), "read,readlink,write,delete"), permissions); assertExactPermissions(new FilePermission(environment.sharedDataDir().toString(), "read,readlink,write,delete"), permissions);
// logs: r/w // logs: r/w
assertExactPermissions(new FilePermission(environment.logsFile().toString(), "read,readlink,write,delete"), permissions); assertExactPermissions(new FilePermission(environment.logsDir().toString(), "read,readlink,write,delete"), permissions);
// temp dir: r/w // temp dir: r/w
assertExactPermissions(new FilePermission(fakeTmpDir.toString(), "read,readlink,write,delete"), permissions); assertExactPermissions(new FilePermission(fakeTmpDir.toString(), "read,readlink,write,delete"), permissions);
} }

View file

@ -80,8 +80,8 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
Environment environment = TestEnvironment.newEnvironment(settings); Environment environment = TestEnvironment.newEnvironment(settings);
// This plugin will NOT have a controller daemon // This plugin will NOT have a controller daemon
Path plugin = environment.modulesFile().resolve("a_plugin"); Path plugin = environment.modulesDir().resolve("a_plugin");
Files.createDirectories(environment.modulesFile()); Files.createDirectories(environment.modulesDir());
Files.createDirectories(plugin); Files.createDirectories(plugin);
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
plugin, plugin,
@ -111,8 +111,8 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
* Two plugins - one with a controller daemon and one without. * Two plugins - one with a controller daemon and one without.
*/ */
public void testControllerSpawn() throws Exception { public void testControllerSpawn() throws Exception {
assertControllerSpawns(Environment::pluginsFile, false); assertControllerSpawns(Environment::pluginsDir, false);
assertControllerSpawns(Environment::modulesFile, true); assertControllerSpawns(Environment::modulesDir, true);
} }
private void assertControllerSpawns(final Function<Environment, Path> pluginsDirFinder, boolean expectSpawn) throws Exception { private void assertControllerSpawns(final Function<Environment, Path> pluginsDirFinder, boolean expectSpawn) throws Exception {
@ -131,8 +131,8 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
// this plugin will have a controller daemon // this plugin will have a controller daemon
Path plugin = pluginsDirFinder.apply(environment).resolve("test_plugin"); Path plugin = pluginsDirFinder.apply(environment).resolve("test_plugin");
Files.createDirectories(environment.modulesFile()); Files.createDirectories(environment.modulesDir());
Files.createDirectories(environment.pluginsFile()); Files.createDirectories(environment.pluginsDir());
Files.createDirectories(plugin); Files.createDirectories(plugin);
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
plugin, plugin,
@ -217,7 +217,7 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
Environment environment = TestEnvironment.newEnvironment(settings); Environment environment = TestEnvironment.newEnvironment(settings);
Path plugin = environment.modulesFile().resolve("test_plugin"); Path plugin = environment.modulesDir().resolve("test_plugin");
Files.createDirectories(plugin); Files.createDirectories(plugin);
PluginTestUtil.writePluginProperties( PluginTestUtil.writePluginProperties(
plugin, plugin,
@ -250,10 +250,10 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
final Environment environment = TestEnvironment.newEnvironment(settings); final Environment environment = TestEnvironment.newEnvironment(settings);
Files.createDirectories(environment.modulesFile()); Files.createDirectories(environment.modulesDir());
Files.createDirectories(environment.pluginsFile()); Files.createDirectories(environment.pluginsDir());
final Path desktopServicesStore = environment.modulesFile().resolve(".DS_Store"); final Path desktopServicesStore = environment.modulesDir().resolve(".DS_Store");
Files.createFile(desktopServicesStore); Files.createFile(desktopServicesStore);
final Spawner spawner = new Spawner(); final Spawner spawner = new Spawner();

View file

@ -82,8 +82,7 @@ public class DesiredNodesUpgradeIT extends AbstractRollingUpgradeTestCase {
Settings.builder().put(NODE_NAME_SETTING.getKey(), nodeName).build(), Settings.builder().put(NODE_NAME_SETTING.getKey(), nodeName).build(),
randomDoubleProcessorCount(), randomDoubleProcessorCount(),
ByteSizeValue.ofGb(randomIntBetween(10, 24)), ByteSizeValue.ofGb(randomIntBetween(10, 24)),
ByteSizeValue.ofGb(randomIntBetween(128, 256)), ByteSizeValue.ofGb(randomIntBetween(128, 256))
null
) )
) )
.toList(); .toList();
@ -94,8 +93,7 @@ public class DesiredNodesUpgradeIT extends AbstractRollingUpgradeTestCase {
Settings.builder().put(NODE_NAME_SETTING.getKey(), nodeName).build(), Settings.builder().put(NODE_NAME_SETTING.getKey(), nodeName).build(),
new DesiredNode.ProcessorsRange(minProcessors, minProcessors + randomIntBetween(10, 20)), new DesiredNode.ProcessorsRange(minProcessors, minProcessors + randomIntBetween(10, 20)),
ByteSizeValue.ofGb(randomIntBetween(10, 24)), ByteSizeValue.ofGb(randomIntBetween(10, 24)),
ByteSizeValue.ofGb(randomIntBetween(128, 256)), ByteSizeValue.ofGb(randomIntBetween(128, 256))
null
); );
}).toList(); }).toList();
} }

View file

@ -10,7 +10,6 @@
package org.elasticsearch.http.snapshots; package org.elasticsearch.http.snapshots;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
@ -37,7 +36,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -516,10 +514,9 @@ public class RestGetSnapshotsIT extends AbstractSnapshotRestTestCase {
true, true,
(args) -> new GetSnapshotsResponse( (args) -> new GetSnapshotsResponse(
(List<SnapshotInfo>) args[0], (List<SnapshotInfo>) args[0],
(Map<String, ElasticsearchException>) args[1], (String) args[1],
(String) args[2], args[2] == null ? UNKNOWN_COUNT : (int) args[2],
args[3] == null ? UNKNOWN_COUNT : (int) args[3], args[3] == null ? UNKNOWN_COUNT : (int) args[3]
args[4] == null ? UNKNOWN_COUNT : (int) args[4]
) )
); );
@ -529,11 +526,6 @@ public class RestGetSnapshotsIT extends AbstractSnapshotRestTestCase {
(p, c) -> SnapshotInfoUtils.snapshotInfoFromXContent(p), (p, c) -> SnapshotInfoUtils.snapshotInfoFromXContent(p),
new ParseField("snapshots") new ParseField("snapshots")
); );
GET_SNAPSHOT_PARSER.declareObject(
ConstructingObjectParser.optionalConstructorArg(),
(p, c) -> p.map(HashMap::new, ElasticsearchException::fromXContent),
new ParseField("failures")
);
GET_SNAPSHOT_PARSER.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), new ParseField("next")); GET_SNAPSHOT_PARSER.declareStringOrNull(ConstructingObjectParser.optionalConstructorArg(), new ParseField("next"));
GET_SNAPSHOT_PARSER.declareIntOrNull(ConstructingObjectParser.optionalConstructorArg(), UNKNOWN_COUNT, new ParseField("total")); GET_SNAPSHOT_PARSER.declareIntOrNull(ConstructingObjectParser.optionalConstructorArg(), UNKNOWN_COUNT, new ParseField("total"));
GET_SNAPSHOT_PARSER.declareIntOrNull(ConstructingObjectParser.optionalConstructorArg(), UNKNOWN_COUNT, new ParseField("remaining")); GET_SNAPSHOT_PARSER.declareIntOrNull(ConstructingObjectParser.optionalConstructorArg(), UNKNOWN_COUNT, new ParseField("remaining"));

View file

@ -28,5 +28,7 @@ tasks.named("yamlRestTest").configure {
'cat.templates/10_basic/No templates', 'cat.templates/10_basic/No templates',
'cat.templates/10_basic/Sort templates', 'cat.templates/10_basic/Sort templates',
'cat.templates/10_basic/Multiple template', 'cat.templates/10_basic/Multiple template',
'update/100_synthetic_source/keyword',
'update/100_synthetic_source/stored text'
].join(',') ].join(',')
} }

View file

@ -30,8 +30,8 @@
"matchDatasources": [ "matchDatasources": [
"docker" "docker"
], ],
"matchPackagePatterns": [ "matchPackageNames": [
"^docker.elastic.co/wolfi/chainguard-base$" "/^docker.elastic.co/wolfi/chainguard-base$/"
] ]
} }
], ],

View file

@ -74,4 +74,12 @@ tasks.named("yamlRestCompatTestTransform").configure ({ task ->
task.skipTest("index/91_metrics_no_subobjects/Metrics object indexing with synthetic source", "_source.mode mapping attribute is no-op since 9.0.0") task.skipTest("index/91_metrics_no_subobjects/Metrics object indexing with synthetic source", "_source.mode mapping attribute is no-op since 9.0.0")
task.skipTest("index/91_metrics_no_subobjects/Root without subobjects with synthetic source", "_source.mode mapping attribute is no-op since 9.0.0") task.skipTest("index/91_metrics_no_subobjects/Root without subobjects with synthetic source", "_source.mode mapping attribute is no-op since 9.0.0")
task.skipTest("indices.create/20_synthetic_source/synthetic_source with copy_to inside nested object", "temporary until backported") task.skipTest("indices.create/20_synthetic_source/synthetic_source with copy_to inside nested object", "temporary until backported")
task.skipTest(
"cluster.desired_nodes/10_basic/Test delete desired nodes with node_version generates a warning",
"node_version warning is removed in 9.0"
)
task.skipTest(
"cluster.desired_nodes/10_basic/Test update desired nodes with node_version generates a warning",
"node_version warning is removed in 9.0"
)
}) })

View file

@ -59,61 +59,6 @@ teardown:
- contains: { nodes: { settings: { node: { name: "instance-000187" } }, processors: 8.5, memory: "64gb", storage: "128gb" } } - contains: { nodes: { settings: { node: { name: "instance-000187" } }, processors: 8.5, memory: "64gb", storage: "128gb" } }
- contains: { nodes: { settings: { node: { name: "instance-000188" } }, processors: 16.0, memory: "128gb", storage: "1tb" } } - contains: { nodes: { settings: { node: { name: "instance-000188" } }, processors: 16.0, memory: "128gb", storage: "1tb" } }
--- ---
"Test update desired nodes with node_version generates a warning":
- skip:
reason: "contains is a newly added assertion"
features: ["contains", "allowed_warnings"]
- do:
cluster.state: {}
# Get master node id
- set: { master_node: master }
- do:
nodes.info: {}
- set: { nodes.$master.version: es_version }
- do:
_internal.update_desired_nodes:
history_id: "test"
version: 1
body:
nodes:
- { settings: { "node.name": "instance-000187" }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version }
allowed_warnings:
- "[version removal] Specifying node_version in desired nodes requests is deprecated."
- match: { replaced_existing_history_id: false }
- do:
_internal.get_desired_nodes: {}
- match:
$body:
history_id: "test"
version: 1
nodes:
- { settings: { node: { name: "instance-000187" } }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version }
- do:
_internal.update_desired_nodes:
history_id: "test"
version: 2
body:
nodes:
- { settings: { "node.name": "instance-000187" }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version }
- { settings: { "node.name": "instance-000188" }, processors: 16.0, memory: "128gb", storage: "1tb", node_version: $es_version }
allowed_warnings:
- "[version removal] Specifying node_version in desired nodes requests is deprecated."
- match: { replaced_existing_history_id: false }
- do:
_internal.get_desired_nodes: {}
- match: { history_id: "test" }
- match: { version: 2 }
- length: { nodes: 2 }
- contains: { nodes: { settings: { node: { name: "instance-000187" } }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version } }
- contains: { nodes: { settings: { node: { name: "instance-000188" } }, processors: 16.0, memory: "128gb", storage: "1tb", node_version: $es_version } }
---
"Test update move to a new history id": "Test update move to a new history id":
- skip: - skip:
reason: "contains is a newly added assertion" reason: "contains is a newly added assertion"
@ -199,46 +144,6 @@ teardown:
_internal.get_desired_nodes: {} _internal.get_desired_nodes: {}
- match: { status: 404 } - match: { status: 404 }
--- ---
"Test delete desired nodes with node_version generates a warning":
- skip:
features: allowed_warnings
- do:
cluster.state: {}
- set: { master_node: master }
- do:
nodes.info: {}
- set: { nodes.$master.version: es_version }
- do:
_internal.update_desired_nodes:
history_id: "test"
version: 1
body:
nodes:
- { settings: { "node.external_id": "instance-000187" }, processors: 8.0, memory: "64gb", storage: "128gb", node_version: $es_version }
allowed_warnings:
- "[version removal] Specifying node_version in desired nodes requests is deprecated."
- match: { replaced_existing_history_id: false }
- do:
_internal.get_desired_nodes: {}
- match:
$body:
history_id: "test"
version: 1
nodes:
- { settings: { node: { external_id: "instance-000187" } }, processors: 8.0, memory: "64gb", storage: "128gb", node_version: $es_version }
- do:
_internal.delete_desired_nodes: {}
- do:
catch: missing
_internal.get_desired_nodes: {}
- match: { status: 404 }
---
"Test update desired nodes is idempotent": "Test update desired nodes is idempotent":
- skip: - skip:
reason: "contains is a newly added assertion" reason: "contains is a newly added assertion"

View file

@ -6,8 +6,8 @@ setup:
--- ---
keyword: keyword:
- requires: - requires:
cluster_features: ["gte_v8.4.0"] cluster_features: [ "mapper.synthetic_recovery_source" ]
reason: introduced in 8.4.0 reason: requires synthetic recovery source
- do: - do:
indices.create: indices.create:
@ -60,13 +60,14 @@ keyword:
index: test index: test
run_expensive_tasks: true run_expensive_tasks: true
- is_false: test.fields._source - is_false: test.fields._source
- is_true: test.fields._recovery_source # When synthetic source is used there is no _recovery_source field
- match: { test.fields._recovery_source: null }
--- ---
stored text: stored text:
- requires: - requires:
cluster_features: ["gte_v8.5.0"] cluster_features: [ "mapper.synthetic_recovery_source" ]
reason: introduced in 8.5.0 reason: requires synthetic recovery source
- do: - do:
indices.create: indices.create:
@ -121,4 +122,5 @@ stored text:
index: test index: test
run_expensive_tasks: true run_expensive_tasks: true
- is_false: test.fields._source - is_false: test.fields._source
- is_true: test.fields._recovery_source # When synthetic source is used there is no _recovery_source field
- match: { test.fields._recovery_source: null }

View file

@ -85,7 +85,7 @@ public class ReloadSecureSettingsIT extends ESIntegTestCase {
final Environment environment = internalCluster().getInstance(Environment.class); final Environment environment = internalCluster().getInstance(Environment.class);
final AtomicReference<AssertionError> reloadSettingsError = new AtomicReference<>(); final AtomicReference<AssertionError> reloadSettingsError = new AtomicReference<>();
// keystore file should be missing for this test case // keystore file should be missing for this test case
Files.deleteIfExists(KeyStoreWrapper.keystorePath(environment.configFile())); Files.deleteIfExists(KeyStoreWrapper.keystorePath(environment.configDir()));
final int initialReloadCount = mockReloadablePlugin.getReloadCount(); final int initialReloadCount = mockReloadablePlugin.getReloadCount();
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
executeReloadSecureSettings(Strings.EMPTY_ARRAY, emptyPassword(), new ActionListener<>() { executeReloadSecureSettings(Strings.EMPTY_ARRAY, emptyPassword(), new ActionListener<>() {
@ -129,10 +129,10 @@ public class ReloadSecureSettingsIT extends ESIntegTestCase {
final int initialReloadCount = mockReloadablePlugin.getReloadCount(); final int initialReloadCount = mockReloadablePlugin.getReloadCount();
// invalid "keystore" file should be present in the config dir // invalid "keystore" file should be present in the config dir
try (InputStream keystore = ReloadSecureSettingsIT.class.getResourceAsStream("invalid.txt.keystore")) { try (InputStream keystore = ReloadSecureSettingsIT.class.getResourceAsStream("invalid.txt.keystore")) {
if (Files.exists(environment.configFile()) == false) { if (Files.exists(environment.configDir()) == false) {
Files.createDirectory(environment.configFile()); Files.createDirectory(environment.configDir());
} }
Files.copy(keystore, KeyStoreWrapper.keystorePath(environment.configFile()), StandardCopyOption.REPLACE_EXISTING); Files.copy(keystore, KeyStoreWrapper.keystorePath(environment.configDir()), StandardCopyOption.REPLACE_EXISTING);
} }
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
executeReloadSecureSettings(Strings.EMPTY_ARRAY, emptyPassword(), new ActionListener<>() { executeReloadSecureSettings(Strings.EMPTY_ARRAY, emptyPassword(), new ActionListener<>() {
@ -363,7 +363,7 @@ public class ReloadSecureSettingsIT extends ESIntegTestCase {
try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) { try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) {
keyStoreWrapper.setString(VALID_SECURE_SETTING_NAME, new char[0]); keyStoreWrapper.setString(VALID_SECURE_SETTING_NAME, new char[0]);
keyStoreWrapper.save(environment.configFile(), new char[0], false); keyStoreWrapper.save(environment.configDir(), new char[0], false);
} }
PlainActionFuture<NodesReloadSecureSettingsResponse> actionFuture = new PlainActionFuture<>(); PlainActionFuture<NodesReloadSecureSettingsResponse> actionFuture = new PlainActionFuture<>();
@ -374,7 +374,7 @@ public class ReloadSecureSettingsIT extends ESIntegTestCase {
try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) { try (KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create()) {
assertThat(keyStoreWrapper, notNullValue()); assertThat(keyStoreWrapper, notNullValue());
keyStoreWrapper.setString("some.setting.that.does.not.exist", new char[0]); keyStoreWrapper.setString("some.setting.that.does.not.exist", new char[0]);
keyStoreWrapper.save(environment.configFile(), new char[0], false); keyStoreWrapper.save(environment.configDir(), new char[0], false);
} }
actionFuture = new PlainActionFuture<>(); actionFuture = new PlainActionFuture<>();
@ -432,7 +432,7 @@ public class ReloadSecureSettingsIT extends ESIntegTestCase {
private SecureSettings writeEmptyKeystore(Environment environment, char[] password) throws Exception { private SecureSettings writeEmptyKeystore(Environment environment, char[] password) throws Exception {
final KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create(); final KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create();
keyStoreWrapper.save(environment.configFile(), password, false); keyStoreWrapper.save(environment.configDir(), password, false);
return keyStoreWrapper; return keyStoreWrapper;
} }

View file

@ -220,7 +220,7 @@ public class IndexShardIT extends ESSingleNodeTestCase {
public void testIndexDirIsDeletedWhenShardRemoved() throws Exception { public void testIndexDirIsDeletedWhenShardRemoved() throws Exception {
Environment env = getInstanceFromNode(Environment.class); Environment env = getInstanceFromNode(Environment.class);
Path idxPath = env.sharedDataFile().resolve(randomAlphaOfLength(10)); Path idxPath = env.sharedDataDir().resolve(randomAlphaOfLength(10));
logger.info("--> idxPath: [{}]", idxPath); logger.info("--> idxPath: [{}]", idxPath);
Settings idxSettings = Settings.builder().put(IndexMetadata.SETTING_DATA_PATH, idxPath).build(); Settings idxSettings = Settings.builder().put(IndexMetadata.SETTING_DATA_PATH, idxPath).build();
createIndex("test", idxSettings); createIndex("test", idxSettings);
@ -254,7 +254,7 @@ public class IndexShardIT extends ESSingleNodeTestCase {
public void testIndexCanChangeCustomDataPath() throws Exception { public void testIndexCanChangeCustomDataPath() throws Exception {
final String index = "test-custom-data-path"; final String index = "test-custom-data-path";
final Path sharedDataPath = getInstanceFromNode(Environment.class).sharedDataFile().resolve(randomAsciiLettersOfLength(10)); final Path sharedDataPath = getInstanceFromNode(Environment.class).sharedDataDir().resolve(randomAsciiLettersOfLength(10));
final Path indexDataPath = sharedDataPath.resolve("start-" + randomAsciiLettersOfLength(10)); final Path indexDataPath = sharedDataPath.resolve("start-" + randomAsciiLettersOfLength(10));
logger.info("--> creating index [{}] with data_path [{}]", index, indexDataPath); logger.info("--> creating index [{}] with data_path [{}]", index, indexDataPath);

View file

@ -32,7 +32,6 @@ import org.elasticsearch.cli.ProcessInfo;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.cluster.routing.ShardRoutingState;
@ -531,8 +530,7 @@ public class RemoveCorruptedShardDataCommandIT extends ESIntegTestCase {
nodeNameToNodeId.put(cursor.getValue().getName(), cursor.getKey()); nodeNameToNodeId.put(cursor.getValue().getName(), cursor.getKey());
} }
final GroupShardsIterator<ShardIterator> shardIterators = state.getRoutingTable() final List<ShardIterator> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[] { indexName }, false);
.activePrimaryShardsGrouped(new String[] { indexName }, false);
final List<ShardIterator> iterators = iterableAsArrayList(shardIterators); final List<ShardIterator> iterators = iterableAsArrayList(shardIterators);
final ShardRouting shardRouting = iterators.iterator().next().nextOrNull(); final ShardRouting shardRouting = iterators.iterator().next().nextOrNull();
assertThat(shardRouting, notNullValue()); assertThat(shardRouting, notNullValue());
@ -562,7 +560,7 @@ public class RemoveCorruptedShardDataCommandIT extends ESIntegTestCase {
command.findAndProcessShardPath( command.findAndProcessShardPath(
options, options,
environmentByNodeName.get(nodeName), environmentByNodeName.get(nodeName),
environmentByNodeName.get(nodeName).dataFiles(), environmentByNodeName.get(nodeName).dataDirs(),
state, state,
shardPath -> assertThat(shardPath.resolveIndex(), equalTo(indexPath)) shardPath -> assertThat(shardPath.resolveIndex(), equalTo(indexPath))
); );
@ -571,8 +569,7 @@ public class RemoveCorruptedShardDataCommandIT extends ESIntegTestCase {
private Path getPathToShardData(String indexName, String dirSuffix) { private Path getPathToShardData(String indexName, String dirSuffix) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<ShardIterator> shardIterators = state.getRoutingTable() List<ShardIterator> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[] { indexName }, false);
.activePrimaryShardsGrouped(new String[] { indexName }, false);
List<ShardIterator> iterators = iterableAsArrayList(shardIterators); List<ShardIterator> iterators = iterableAsArrayList(shardIterators);
ShardIterator shardIterator = RandomPicks.randomFrom(random(), iterators); ShardIterator shardIterator = RandomPicks.randomFrom(random(), iterators);
ShardRouting shardRouting = shardIterator.nextOrNull(); ShardRouting shardRouting = shardIterator.nextOrNull();

View file

@ -34,7 +34,6 @@ import org.elasticsearch.cluster.ClusterStateObserver;
import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
@ -311,8 +310,7 @@ public class CorruptedFileIT extends ESIntegTestCase {
} }
assertThat(response.getStatus(), is(ClusterHealthStatus.RED)); assertThat(response.getStatus(), is(ClusterHealthStatus.RED));
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<ShardIterator> shardIterators = state.getRoutingTable() List<ShardIterator> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[] { "test" }, false);
.activePrimaryShardsGrouped(new String[] { "test" }, false);
for (ShardIterator iterator : shardIterators) { for (ShardIterator iterator : shardIterators) {
ShardRouting routing; ShardRouting routing;
while ((routing = iterator.nextOrNull()) != null) { while ((routing = iterator.nextOrNull()) != null) {
@ -667,7 +665,7 @@ public class CorruptedFileIT extends ESIntegTestCase {
private int numShards(String... index) { private int numShards(String... index) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<?> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(index, false); List<?> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(index, false);
return shardIterators.size(); return shardIterators.size();
} }
@ -695,8 +693,7 @@ public class CorruptedFileIT extends ESIntegTestCase {
private ShardRouting corruptRandomPrimaryFile(final boolean includePerCommitFiles) throws IOException { private ShardRouting corruptRandomPrimaryFile(final boolean includePerCommitFiles) throws IOException {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
Index test = state.metadata().getProject().index("test").getIndex(); Index test = state.metadata().getProject().index("test").getIndex();
GroupShardsIterator<ShardIterator> shardIterators = state.getRoutingTable() List<ShardIterator> shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[] { "test" }, false);
.activePrimaryShardsGrouped(new String[] { "test" }, false);
List<ShardIterator> iterators = iterableAsArrayList(shardIterators); List<ShardIterator> iterators = iterableAsArrayList(shardIterators);
ShardIterator shardIterator = RandomPicks.randomFrom(random(), iterators); ShardIterator shardIterator = RandomPicks.randomFrom(random(), iterators);
ShardRouting shardRouting = shardIterator.nextOrNull(); ShardRouting shardRouting = shardIterator.nextOrNull();

View file

@ -14,7 +14,6 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.index.search.stats.SearchStats; import org.elasticsearch.index.search.stats.SearchStats;
@ -24,6 +23,7 @@ import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
@ -146,7 +146,7 @@ public class SuggestStatsIT extends ESIntegTestCase {
private Set<String> nodeIdsWithIndex(String... indices) { private Set<String> nodeIdsWithIndex(String... indices) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<ShardIterator> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); List<ShardIterator> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true);
Set<String> nodes = new HashSet<>(); Set<String> nodes = new HashSet<>();
for (ShardIterator shardIterator : allAssignedShardsGrouped) { for (ShardIterator shardIterator : allAssignedShardsGrouped) {
for (ShardRouting routing : shardIterator) { for (ShardRouting routing : shardIterator) {
@ -161,7 +161,7 @@ public class SuggestStatsIT extends ESIntegTestCase {
protected int numAssignedShards(String... indices) { protected int numAssignedShards(String... indices) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<?> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); List<?> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true);
return allAssignedShardsGrouped.size(); return allAssignedShardsGrouped.size();
} }
} }

View file

@ -14,7 +14,6 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
@ -165,7 +164,7 @@ public class SearchStatsIT extends ESIntegTestCase {
private Set<String> nodeIdsWithIndex(String... indices) { private Set<String> nodeIdsWithIndex(String... indices) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<ShardIterator> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); List<ShardIterator> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true);
Set<String> nodes = new HashSet<>(); Set<String> nodes = new HashSet<>();
for (ShardIterator shardIterator : allAssignedShardsGrouped) { for (ShardIterator shardIterator : allAssignedShardsGrouped) {
for (ShardRouting routing : shardIterator) { for (ShardRouting routing : shardIterator) {
@ -248,7 +247,7 @@ public class SearchStatsIT extends ESIntegTestCase {
protected int numAssignedShards(String... indices) { protected int numAssignedShards(String... indices) {
ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState(); ClusterState state = clusterAdmin().prepareState(TEST_REQUEST_TIMEOUT).get().getState();
GroupShardsIterator<?> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true); List<?> allAssignedShardsGrouped = state.routingTable().allAssignedShardsGrouped(indices, true);
return allAssignedShardsGrouped.size(); return allAssignedShardsGrouped.size();
} }

View file

@ -195,7 +195,7 @@ public class MultiClusterRepoAccessIT extends AbstractSnapshotIntegTestCase {
); );
assertAcked(clusterAdmin().prepareDeleteRepository(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT, repoName)); assertAcked(clusterAdmin().prepareDeleteRepository(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT, repoName));
IOUtils.rm(internalCluster().getCurrentMasterNodeInstance(Environment.class).resolveRepoFile(repoPath.toString())); IOUtils.rm(internalCluster().getCurrentMasterNodeInstance(Environment.class).resolveRepoDir(repoPath.toString()));
createRepository(repoName, "fs", repoPath); createRepository(repoName, "fs", repoPath);
createFullSnapshot(repoName, "snap-1"); createFullSnapshot(repoName, "snap-1");

View file

@ -316,7 +316,6 @@ public class SnapshotStatusApisIT extends AbstractSnapshotIntegTestCase {
.get(); .get();
assertTrue(getSnapshotsResponse.getSnapshots().isEmpty()); assertTrue(getSnapshotsResponse.getSnapshots().isEmpty());
assertTrue(getSnapshotsResponse.getFailures().isEmpty());
} }
public void testGetSnapshotsMultipleRepos() throws Exception { public void testGetSnapshotsMultipleRepos() throws Exception {

View file

@ -118,6 +118,14 @@ public record TransportVersion(int id) implements VersionId<TransportVersion> {
return VersionsHolder.ALL_VERSIONS; return VersionsHolder.ALL_VERSIONS;
} }
/**
* @return whether this is a known {@link TransportVersion}, i.e. one declared in {@link TransportVersions}. Other versions may exist
* in the wild (they're sent over the wire by numeric ID) but we don't know how to communicate using such versions.
*/
public boolean isKnown() {
return VersionsHolder.ALL_VERSIONS_MAP.containsKey(id);
}
public static TransportVersion fromString(String str) { public static TransportVersion fromString(String str) {
return TransportVersion.fromId(Integer.parseInt(str)); return TransportVersion.fromId(Integer.parseInt(str));
} }

View file

@ -176,6 +176,10 @@ public class TransportVersions {
public static final TransportVersion COHERE_BIT_EMBEDDING_TYPE_SUPPORT_ADDED_BACKPORT_8_X = def(8_840_0_01); public static final TransportVersion COHERE_BIT_EMBEDDING_TYPE_SUPPORT_ADDED_BACKPORT_8_X = def(8_840_0_01);
public static final TransportVersion ELASTICSEARCH_9_0 = def(9_000_0_00); public static final TransportVersion ELASTICSEARCH_9_0 = def(9_000_0_00);
public static final TransportVersion COHERE_BIT_EMBEDDING_TYPE_SUPPORT_ADDED = def(9_001_0_00); public static final TransportVersion COHERE_BIT_EMBEDDING_TYPE_SUPPORT_ADDED = def(9_001_0_00);
public static final TransportVersion REMOVE_SNAPSHOT_FAILURES = def(9_002_0_00);
public static final TransportVersion TRANSPORT_STATS_HANDLING_TIME_REQUIRED = def(9_003_0_00);
public static final TransportVersion REMOVE_DESIRED_NODE_VERSION = def(9_004_0_00);
public static final TransportVersion ESQL_DRIVER_TASK_DESCRIPTION = def(9_005_0_00);
/* /*
* WARNING: DO NOT MERGE INTO MAIN! * WARNING: DO NOT MERGE INTO MAIN!
* This is the transport version used for all multi-project changes. * This is the transport version used for all multi-project changes.

View file

@ -108,7 +108,7 @@ public class TransportNodesReloadSecureSettingsAction extends TransportNodesActi
Task task Task task
) { ) {
// We default to using an empty string as the keystore password so that we mimic pre 7.3 API behavior // We default to using an empty string as the keystore password so that we mimic pre 7.3 API behavior
try (KeyStoreWrapper keystore = KeyStoreWrapper.load(environment.configFile())) { try (KeyStoreWrapper keystore = KeyStoreWrapper.load(environment.configDir())) {
// reread keystore from config file // reread keystore from config file
if (keystore == null) { if (keystore == null) {
return new NodesReloadSecureSettingsResponse.NodeResponse( return new NodesReloadSecureSettingsResponse.NodeResponse(

View file

@ -22,7 +22,6 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.ResolvedEx
import org.elasticsearch.cluster.metadata.ProjectMetadata; import org.elasticsearch.cluster.metadata.ProjectMetadata;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
@ -37,6 +36,7 @@ import org.elasticsearch.transport.TransportService;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -117,7 +117,7 @@ public class TransportClusterSearchShardsAction extends TransportMasterNodeReadA
} }
Set<String> nodeIds = new HashSet<>(); Set<String> nodeIds = new HashSet<>();
GroupShardsIterator<ShardIterator> groupShardsIterator = clusterService.operationRouting() List<ShardIterator> groupShardsIterator = clusterService.operationRouting()
.searchShards(project, concreteIndices, routingMap, request.preference()); .searchShards(project, concreteIndices, routingMap, request.preference());
ShardRouting shard; ShardRouting shard;
ClusterSearchShardsGroup[] groupResponses = new ClusterSearchShardsGroup[groupShardsIterator.size()]; ClusterSearchShardsGroup[] groupResponses = new ClusterSearchShardsGroup[groupShardsIterator.size()];

View file

@ -9,7 +9,7 @@
package org.elasticsearch.action.admin.cluster.snapshots.get; package org.elasticsearch.action.admin.cluster.snapshots.get;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.collect.Iterators;
@ -17,12 +17,10 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.common.xcontent.ChunkedToXContentObject;
import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.UpdateForV9;
import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotInfo;
import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContent;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -35,9 +33,6 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
private final List<SnapshotInfo> snapshots; private final List<SnapshotInfo> snapshots;
@UpdateForV9(owner = UpdateForV9.Owner.DISTRIBUTED_COORDINATION) // always empty, can be dropped
private final Map<String, ElasticsearchException> failures;
@Nullable @Nullable
private final String next; private final String next;
@ -45,15 +40,8 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
private final int remaining; private final int remaining;
public GetSnapshotsResponse( public GetSnapshotsResponse(List<SnapshotInfo> snapshots, @Nullable String next, final int total, final int remaining) {
List<SnapshotInfo> snapshots,
Map<String, ElasticsearchException> failures,
@Nullable String next,
final int total,
final int remaining
) {
this.snapshots = List.copyOf(snapshots); this.snapshots = List.copyOf(snapshots);
this.failures = failures == null ? Map.of() : Map.copyOf(failures);
this.next = next; this.next = next;
this.total = total; this.total = total;
this.remaining = remaining; this.remaining = remaining;
@ -61,7 +49,10 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
public GetSnapshotsResponse(StreamInput in) throws IOException { public GetSnapshotsResponse(StreamInput in) throws IOException {
this.snapshots = in.readCollectionAsImmutableList(SnapshotInfo::readFrom); this.snapshots = in.readCollectionAsImmutableList(SnapshotInfo::readFrom);
this.failures = Collections.unmodifiableMap(in.readMap(StreamInput::readException)); if (in.getTransportVersion().before(TransportVersions.REMOVE_SNAPSHOT_FAILURES)) {
// Deprecated `failures` field
in.readMap(StreamInput::readException);
}
this.next = in.readOptionalString(); this.next = in.readOptionalString();
this.total = in.readVInt(); this.total = in.readVInt();
this.remaining = in.readVInt(); this.remaining = in.readVInt();
@ -76,25 +67,11 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
return snapshots; return snapshots;
} }
/**
* Returns a map of repository name to {@link ElasticsearchException} for each unsuccessful response.
*/
public Map<String, ElasticsearchException> getFailures() {
return failures;
}
@Nullable @Nullable
public String next() { public String next() {
return next; return next;
} }
/**
* Returns true if there is at least one failed response.
*/
public boolean isFailed() {
return failures.isEmpty() == false;
}
public int totalCount() { public int totalCount() {
return total; return total;
} }
@ -106,7 +83,10 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
out.writeCollection(snapshots); out.writeCollection(snapshots);
out.writeMap(failures, StreamOutput::writeException); if (out.getTransportVersion().before(TransportVersions.REMOVE_SNAPSHOT_FAILURES)) {
// Deprecated `failures` field
out.writeMap(Map.of(), StreamOutput::writeException);
}
out.writeOptionalString(next); out.writeOptionalString(next);
out.writeVInt(total); out.writeVInt(total);
out.writeVInt(remaining); out.writeVInt(remaining);
@ -120,18 +100,6 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
return b; return b;
}), Iterators.map(getSnapshots().iterator(), snapshotInfo -> snapshotInfo::toXContentExternal), Iterators.single((b, p) -> { }), Iterators.map(getSnapshots().iterator(), snapshotInfo -> snapshotInfo::toXContentExternal), Iterators.single((b, p) -> {
b.endArray(); b.endArray();
if (failures.isEmpty() == false) {
b.startObject("failures");
for (Map.Entry<String, ElasticsearchException> error : failures.entrySet()) {
b.field(error.getKey(), (bb, pa) -> {
bb.startObject();
error.getValue().toXContent(bb, pa);
bb.endObject();
return bb;
});
}
b.endObject();
}
if (next != null) { if (next != null) {
b.field("next", next); b.field("next", next);
} }
@ -151,12 +119,12 @@ public class GetSnapshotsResponse extends ActionResponse implements ChunkedToXCo
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
GetSnapshotsResponse that = (GetSnapshotsResponse) o; GetSnapshotsResponse that = (GetSnapshotsResponse) o;
return Objects.equals(snapshots, that.snapshots) && Objects.equals(failures, that.failures) && Objects.equals(next, that.next); return Objects.equals(snapshots, that.snapshots) && Objects.equals(next, that.next);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(snapshots, failures, next); return Objects.hash(snapshots, next);
} }
@Override @Override

View file

@ -543,7 +543,6 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction<GetSn
} }
return new GetSnapshotsResponse( return new GetSnapshotsResponse(
snapshotInfos, snapshotInfos,
null,
remaining > 0 ? sortBy.encodeAfterQueryParam(snapshotInfos.get(snapshotInfos.size() - 1)) : null, remaining > 0 ? sortBy.encodeAfterQueryParam(snapshotInfos.get(snapshotInfos.size() - 1)) : null,
totalCount.get(), totalCount.get(),
remaining remaining

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.action.admin.indices.analyze;
import java.util.Set;
public final class AnalyzeCapabilities {
private AnalyzeCapabilities() {}
private static final String WRONG_CUSTOM_ANALYZER_RETURNS_400_CAPABILITY = "wrong_custom_analyzer_returns_400";
public static final Set<String> CAPABILITIES = Set.of(WRONG_CUSTOM_ANALYZER_RETURNS_400_CAPABILITY);
}

View file

@ -147,6 +147,8 @@ public class TransportAnalyzeAction extends TransportSingleShardAction<AnalyzeAc
if (analyzer != null) { if (analyzer != null) {
return analyze(request, analyzer, maxTokenCount); return analyze(request, analyzer, maxTokenCount);
} }
} catch (IllegalStateException e) {
throw new IllegalArgumentException("Can not build a custom analyzer", e);
} }
// Otherwise we use a built-in analyzer, which should not be closed // Otherwise we use a built-in analyzer, which should not be closed

View file

@ -23,7 +23,6 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
@ -219,14 +218,10 @@ public class TransportAnalyzeIndexDiskUsageAction extends TransportBroadcastActi
} }
@Override @Override
protected GroupShardsIterator<ShardIterator> shards( protected List<ShardIterator> shards(ClusterState clusterState, AnalyzeIndexDiskUsageRequest request, String[] concreteIndices) {
ClusterState clusterState,
AnalyzeIndexDiskUsageRequest request,
String[] concreteIndices
) {
ProjectState project = projectResolver.getProjectState(clusterState); ProjectState project = projectResolver.getProjectState(clusterState);
final GroupShardsIterator<ShardIterator> groups = clusterService.operationRouting() final List<ShardIterator> groups = clusterService.operationRouting().searchShards(project, concreteIndices, null, null);
.searchShards(project, concreteIndices, null, null);
for (ShardIterator group : groups) { for (ShardIterator group : groups) {
// fails fast if any non-active groups // fails fast if any non-active groups
if (group.size() == 0) { if (group.size() == 0) {

View file

@ -24,7 +24,6 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.ResolvedExpression; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.ResolvedExpression;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
@ -158,7 +157,7 @@ public class TransportValidateQueryAction extends TransportBroadcastAction<
} }
@Override @Override
protected GroupShardsIterator<ShardIterator> shards(ClusterState clusterState, ValidateQueryRequest request, String[] concreteIndices) { protected List<ShardIterator> shards(ClusterState clusterState, ValidateQueryRequest request, String[] concreteIndices) {
final String routing; final String routing;
if (request.allShards()) { if (request.allShards()) {
routing = null; routing = null;

View file

@ -20,7 +20,6 @@ import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ProjectState; import org.elasticsearch.cluster.ProjectState;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
@ -99,7 +98,7 @@ final class RequestDispatcher {
ProjectState project = projectResolver.getProjectState(clusterState); ProjectState project = projectResolver.getProjectState(clusterState);
for (String index : indices) { for (String index : indices) {
final GroupShardsIterator<ShardIterator> shardIts; final List<ShardIterator> shardIts;
try { try {
shardIts = clusterService.operationRouting().searchShards(project, new String[] { index }, null, null); shardIts = clusterService.operationRouting().searchShards(project, new String[] { index }, null, null);
} catch (Exception e) { } catch (Exception e) {
@ -256,7 +255,7 @@ final class RequestDispatcher {
private final Set<ShardId> unmatchedShardIds = new HashSet<>(); private final Set<ShardId> unmatchedShardIds = new HashSet<>();
private final Map<ShardId, Exception> failures = new HashMap<>(); private final Map<ShardId, Exception> failures = new HashMap<>();
IndexSelector(GroupShardsIterator<ShardIterator> shardIts) { IndexSelector(List<ShardIterator> shardIts) {
for (ShardIterator shardIt : shardIts) { for (ShardIterator shardIt : shardIts) {
for (ShardRouting shard : shardIt) { for (ShardRouting shard : shardIt) {
nodeToShards.computeIfAbsent(shard.currentNodeId(), node -> new ArrayList<>()).add(shard); nodeToShards.computeIfAbsent(shard.currentNodeId(), node -> new ArrayList<>()).add(shard);

View file

@ -22,7 +22,6 @@ import org.elasticsearch.action.search.TransportSearchAction.SearchTimeProvider;
import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.action.support.SubscribableListener;
import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.action.support.TransportActions;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.Maps;
@ -60,9 +59,9 @@ import java.util.stream.Collectors;
import static org.elasticsearch.core.Strings.format; import static org.elasticsearch.core.Strings.format;
/** /**
* This is an abstract base class that encapsulates the logic to fan out to all shards in provided {@link GroupShardsIterator} * This is an abstract base class that encapsulates the logic to fan out to all shards in provided {@link List<SearchShardIterator>}
* and collect the results. If a shard request returns a failure this class handles the advance to the next replica of the shard until * and collect the results. If a shard request returns a failure this class handles the advance to the next replica of the shard until
* the shards replica iterator is exhausted. Each shard is referenced by position in the {@link GroupShardsIterator} which is later * the shards replica iterator is exhausted. Each shard is referenced by position in the {@link List<SearchShardIterator>} which is later
* referred to as the {@code shardIndex}. * referred to as the {@code shardIndex}.
* The fan out and collect algorithm is traditionally used as the initial phase which can either be a query execution or collection of * The fan out and collect algorithm is traditionally used as the initial phase which can either be a query execution or collection of
* distributed frequencies * distributed frequencies
@ -90,15 +89,13 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
private final Object shardFailuresMutex = new Object(); private final Object shardFailuresMutex = new Object();
private final AtomicBoolean hasShardResponse = new AtomicBoolean(false); private final AtomicBoolean hasShardResponse = new AtomicBoolean(false);
private final AtomicInteger successfulOps = new AtomicInteger(); private final AtomicInteger successfulOps = new AtomicInteger();
private final AtomicInteger skippedOps = new AtomicInteger();
private final SearchTimeProvider timeProvider; private final SearchTimeProvider timeProvider;
private final SearchResponse.Clusters clusters; private final SearchResponse.Clusters clusters;
protected final GroupShardsIterator<SearchShardIterator> toSkipShardsIts; protected final List<SearchShardIterator> toSkipShardsIts;
protected final GroupShardsIterator<SearchShardIterator> shardsIts; protected final List<SearchShardIterator> shardsIts;
private final SearchShardIterator[] shardIterators; private final SearchShardIterator[] shardIterators;
private final int expectedTotalOps; private final AtomicInteger outstandingShards;
private final AtomicInteger totalOps = new AtomicInteger();
private final int maxConcurrentRequestsPerNode; private final int maxConcurrentRequestsPerNode;
private final Map<String, PendingExecutions> pendingExecutionsPerNode = new ConcurrentHashMap<>(); private final Map<String, PendingExecutions> pendingExecutionsPerNode = new ConcurrentHashMap<>();
private final boolean throttleConcurrentRequests; private final boolean throttleConcurrentRequests;
@ -118,7 +115,7 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
Executor executor, Executor executor,
SearchRequest request, SearchRequest request,
ActionListener<SearchResponse> listener, ActionListener<SearchResponse> listener,
GroupShardsIterator<SearchShardIterator> shardsIts, List<SearchShardIterator> shardsIts,
SearchTimeProvider timeProvider, SearchTimeProvider timeProvider,
ClusterState clusterState, ClusterState clusterState,
SearchTask task, SearchTask task,
@ -137,20 +134,14 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
iterators.add(iterator); iterators.add(iterator);
} }
} }
this.toSkipShardsIts = new GroupShardsIterator<>(toSkipIterators); this.toSkipShardsIts = toSkipIterators;
this.shardsIts = new GroupShardsIterator<>(iterators); this.shardsIts = iterators;
outstandingShards = new AtomicInteger(shardsIts.size());
this.shardIterators = iterators.toArray(new SearchShardIterator[0]); this.shardIterators = iterators.toArray(new SearchShardIterator[0]);
// we later compute the shard index based on the natural order of the shards // we later compute the shard index based on the natural order of the shards
// that participate in the search request. This means that this number is // that participate in the search request. This means that this number is
// consistent between two requests that target the same shards. // consistent between two requests that target the same shards.
Arrays.sort(shardIterators); Arrays.sort(shardIterators);
// we need to add 1 for non active partition, since we count it in the total. This means for each shard in the iterator we sum up
// it's number of active shards but use 1 as the default if no replica of a shard is active at this point.
// on a per shards level we use shardIt.remaining() to increment the totalOps pointer but add 1 for the current shard result
// we process hence we add one for the non active partition here.
this.expectedTotalOps = shardsIts.totalSizeWith1ForEmpty();
this.maxConcurrentRequestsPerNode = maxConcurrentRequestsPerNode; this.maxConcurrentRequestsPerNode = maxConcurrentRequestsPerNode;
// in the case were we have less shards than maxConcurrentRequestsPerNode we don't need to throttle // in the case were we have less shards than maxConcurrentRequestsPerNode we don't need to throttle
this.throttleConcurrentRequests = maxConcurrentRequestsPerNode < shardsIts.size(); this.throttleConcurrentRequests = maxConcurrentRequestsPerNode < shardsIts.size();
@ -179,8 +170,8 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
SearchSourceBuilder sourceBuilder SearchSourceBuilder sourceBuilder
) { ) {
progressListener.notifyListShards( progressListener.notifyListShards(
SearchProgressListener.buildSearchShards(this.shardsIts), SearchProgressListener.buildSearchShardsFromIter(this.shardsIts),
SearchProgressListener.buildSearchShards(toSkipShardsIts), SearchProgressListener.buildSearchShardsFromIter(toSkipShardsIts),
clusters, clusters,
sourceBuilder == null || sourceBuilder.size() > 0, sourceBuilder == null || sourceBuilder.size() > 0,
timeProvider timeProvider
@ -251,9 +242,8 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
void skipShard(SearchShardIterator iterator) { void skipShard(SearchShardIterator iterator) {
successfulOps.incrementAndGet(); successfulOps.incrementAndGet();
skippedOps.incrementAndGet();
assert iterator.skip(); assert iterator.skip();
successfulShardExecution(iterator); successfulShardExecution();
} }
private static boolean assertExecuteOnStartThread() { private static boolean assertExecuteOnStartThread() {
@ -380,7 +370,7 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
"Partial shards failure (unavailable: {}, successful: {}, skipped: {}, num-shards: {}, phase: {})", "Partial shards failure (unavailable: {}, successful: {}, skipped: {}, num-shards: {}, phase: {})",
discrepancy, discrepancy,
successfulOps.get(), successfulOps.get(),
skippedOps.get(), toSkipShardsIts.size(),
getNumShards(), getNumShards(),
currentPhase currentPhase
); );
@ -449,17 +439,14 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
} }
onShardGroupFailure(shardIndex, shard, e); onShardGroupFailure(shardIndex, shard, e);
} }
final int totalOps = this.totalOps.incrementAndGet(); if (lastShard == false) {
if (totalOps == expectedTotalOps) { performPhaseOnShard(shardIndex, shardIt, nextShard);
onPhaseDone();
} else if (totalOps > expectedTotalOps) {
throw new AssertionError(
"unexpected higher total ops [" + totalOps + "] compared to expected [" + expectedTotalOps + "]",
new SearchPhaseExecutionException(getName(), "Shard failures", null, buildShardFailures())
);
} else { } else {
if (lastShard == false) { // count down outstanding shards, we're done with this shard as there's no more copies to try
performPhaseOnShard(shardIndex, shardIt, nextShard); final int outstanding = outstandingShards.decrementAndGet();
assert outstanding >= 0 : "outstanding: " + outstanding;
if (outstanding == 0) {
onPhaseDone();
} }
} }
} }
@ -535,10 +522,10 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("got first-phase result from {}", result != null ? result.getSearchShardTarget() : null); logger.trace("got first-phase result from {}", result != null ? result.getSearchShardTarget() : null);
} }
results.consumeResult(result, () -> onShardResultConsumed(result, shardIt)); results.consumeResult(result, () -> onShardResultConsumed(result));
} }
private void onShardResultConsumed(Result result, SearchShardIterator shardIt) { private void onShardResultConsumed(Result result) {
successfulOps.incrementAndGet(); successfulOps.incrementAndGet();
// clean a previous error on this shard group (note, this code will be serialized on the same shardIndex value level // clean a previous error on this shard group (note, this code will be serialized on the same shardIndex value level
// so its ok concurrency wise to miss potentially the shard failures being created because of another failure // so its ok concurrency wise to miss potentially the shard failures being created because of another failure
@ -552,28 +539,14 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
// cause the successor to read a wrong value from successfulOps if second phase is very fast ie. count etc. // cause the successor to read a wrong value from successfulOps if second phase is very fast ie. count etc.
// increment all the "future" shards to update the total ops since we some may work and some may not... // increment all the "future" shards to update the total ops since we some may work and some may not...
// and when that happens, we break on total ops, so we must maintain them // and when that happens, we break on total ops, so we must maintain them
successfulShardExecution(shardIt); successfulShardExecution();
} }
private void successfulShardExecution(SearchShardIterator shardsIt) { private void successfulShardExecution() {
final int remainingOpsOnIterator; final int outstanding = outstandingShards.decrementAndGet();
if (shardsIt.skip()) { assert outstanding >= 0 : "outstanding: " + outstanding;
// It's possible that we're skipping a shard that's unavailable if (outstanding == 0) {
// but its range was available in the IndexMetadata, in that
// case the shardsIt.remaining() would be 0, expectedTotalOps
// accounts for unavailable shards too.
remainingOpsOnIterator = Math.max(shardsIt.remaining(), 1);
} else {
remainingOpsOnIterator = shardsIt.remaining() + 1;
}
final int xTotalOps = totalOps.addAndGet(remainingOpsOnIterator);
if (xTotalOps == expectedTotalOps) {
onPhaseDone(); onPhaseDone();
} else if (xTotalOps > expectedTotalOps) {
throw new AssertionError(
"unexpected higher total ops [" + xTotalOps + "] compared to expected [" + expectedTotalOps + "]",
new SearchPhaseExecutionException(getName(), "Shard failures", null, buildShardFailures())
);
} }
} }
@ -640,7 +613,7 @@ abstract class AbstractSearchAsyncAction<Result extends SearchPhaseResult> exten
scrollId, scrollId,
getNumShards(), getNumShards(),
numSuccess, numSuccess,
skippedOps.get(), toSkipShardsIts.size(),
buildTookInMillis(), buildTookInMillis(),
failures, failures,
clusters, clusters,

View file

@ -12,7 +12,6 @@ package org.elasticsearch.action.search;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.common.util.concurrent.CountDown;
@ -61,8 +60,8 @@ final class CanMatchPreFilterSearchPhase {
private final Logger logger; private final Logger logger;
private final SearchRequest request; private final SearchRequest request;
private final GroupShardsIterator<SearchShardIterator> shardsIts; private final List<SearchShardIterator> shardsIts;
private final ActionListener<GroupShardsIterator<SearchShardIterator>> listener; private final ActionListener<List<SearchShardIterator>> listener;
private final TransportSearchAction.SearchTimeProvider timeProvider; private final TransportSearchAction.SearchTimeProvider timeProvider;
private final BiFunction<String, String, Transport.Connection> nodeIdToConnection; private final BiFunction<String, String, Transport.Connection> nodeIdToConnection;
private final SearchTransportService searchTransportService; private final SearchTransportService searchTransportService;
@ -86,12 +85,12 @@ final class CanMatchPreFilterSearchPhase {
Map<String, Float> concreteIndexBoosts, Map<String, Float> concreteIndexBoosts,
Executor executor, Executor executor,
SearchRequest request, SearchRequest request,
GroupShardsIterator<SearchShardIterator> shardsIts, List<SearchShardIterator> shardsIts,
TransportSearchAction.SearchTimeProvider timeProvider, TransportSearchAction.SearchTimeProvider timeProvider,
SearchTask task, SearchTask task,
boolean requireAtLeastOneMatch, boolean requireAtLeastOneMatch,
CoordinatorRewriteContextProvider coordinatorRewriteContextProvider, CoordinatorRewriteContextProvider coordinatorRewriteContextProvider,
ActionListener<GroupShardsIterator<SearchShardIterator>> listener ActionListener<List<SearchShardIterator>> listener
) { ) {
this.logger = logger; this.logger = logger;
this.searchTransportService = searchTransportService; this.searchTransportService = searchTransportService;
@ -169,10 +168,9 @@ final class CanMatchPreFilterSearchPhase {
if (matchedShardLevelRequests.isEmpty()) { if (matchedShardLevelRequests.isEmpty()) {
finishPhase(); finishPhase();
} else { } else {
GroupShardsIterator<SearchShardIterator> matchingShards = new GroupShardsIterator<>(matchedShardLevelRequests);
// verify missing shards only for the shards that we hit for the query // verify missing shards only for the shards that we hit for the query
checkNoMissingShards(matchingShards); checkNoMissingShards(matchedShardLevelRequests);
new Round(matchingShards).run(); new Round(matchedShardLevelRequests).run();
} }
} }
@ -202,12 +200,12 @@ final class CanMatchPreFilterSearchPhase {
minAndMaxes[shardIndex] = minAndMax; minAndMaxes[shardIndex] = minAndMax;
} }
private void checkNoMissingShards(GroupShardsIterator<SearchShardIterator> shards) { private void checkNoMissingShards(List<SearchShardIterator> shards) {
assert assertSearchCoordinationThread(); assert assertSearchCoordinationThread();
SearchPhase.doCheckNoMissingShards("can_match", request, shards, SearchPhase::makeMissingShardsError); SearchPhase.doCheckNoMissingShards("can_match", request, shards, SearchPhase::makeMissingShardsError);
} }
private Map<SendingTarget, List<SearchShardIterator>> groupByNode(GroupShardsIterator<SearchShardIterator> shards) { private Map<SendingTarget, List<SearchShardIterator>> groupByNode(List<SearchShardIterator> shards) {
Map<SendingTarget, List<SearchShardIterator>> requests = new HashMap<>(); Map<SendingTarget, List<SearchShardIterator>> requests = new HashMap<>();
for (int i = 0; i < shards.size(); i++) { for (int i = 0; i < shards.size(); i++) {
final SearchShardIterator shardRoutings = shards.get(i); final SearchShardIterator shardRoutings = shards.get(i);
@ -230,11 +228,11 @@ final class CanMatchPreFilterSearchPhase {
* to retry on other available shard copies. * to retry on other available shard copies.
*/ */
class Round extends AbstractRunnable { class Round extends AbstractRunnable {
private final GroupShardsIterator<SearchShardIterator> shards; private final List<SearchShardIterator> shards;
private final CountDown countDown; private final CountDown countDown;
private final AtomicReferenceArray<Exception> failedResponses; private final AtomicReferenceArray<Exception> failedResponses;
Round(GroupShardsIterator<SearchShardIterator> shards) { Round(List<SearchShardIterator> shards) {
this.shards = shards; this.shards = shards;
this.countDown = new CountDown(shards.size()); this.countDown = new CountDown(shards.size());
this.failedResponses = new AtomicReferenceArray<>(shardsIts.size()); this.failedResponses = new AtomicReferenceArray<>(shardsIts.size());
@ -328,7 +326,7 @@ final class CanMatchPreFilterSearchPhase {
finishPhase(); finishPhase();
} else { } else {
// trigger another round, forcing execution // trigger another round, forcing execution
executor.execute(new Round(new GroupShardsIterator<>(remainingShards)) { executor.execute(new Round(remainingShards) {
@Override @Override
public boolean isForceExecution() { public boolean isForceExecution() {
return true; return true;
@ -419,7 +417,7 @@ final class CanMatchPreFilterSearchPhase {
listener.onFailure(new SearchPhaseExecutionException("can_match", msg, cause, ShardSearchFailure.EMPTY_ARRAY)); listener.onFailure(new SearchPhaseExecutionException("can_match", msg, cause, ShardSearchFailure.EMPTY_ARRAY));
} }
private synchronized GroupShardsIterator<SearchShardIterator> getIterator(GroupShardsIterator<SearchShardIterator> shardsIts) { private synchronized List<SearchShardIterator> getIterator(List<SearchShardIterator> shardsIts) {
// TODO: pick the local shard when possible // TODO: pick the local shard when possible
if (requireAtLeastOneMatch && numPossibleMatches == 0) { if (requireAtLeastOneMatch && numPossibleMatches == 0) {
// this is a special case where we have no hit but we need to get at least one search response in order // this is a special case where we have no hit but we need to get at least one search response in order
@ -452,14 +450,10 @@ final class CanMatchPreFilterSearchPhase {
return shardsIts; return shardsIts;
} }
FieldSortBuilder fieldSort = FieldSortBuilder.getPrimaryFieldSortOrNull(request.source()); FieldSortBuilder fieldSort = FieldSortBuilder.getPrimaryFieldSortOrNull(request.source());
return new GroupShardsIterator<>(sortShards(shardsIts, minAndMaxes, fieldSort.order())); return sortShards(shardsIts, minAndMaxes, fieldSort.order());
} }
private static List<SearchShardIterator> sortShards( private static List<SearchShardIterator> sortShards(List<SearchShardIterator> shardsIts, MinAndMax<?>[] minAndMaxes, SortOrder order) {
GroupShardsIterator<SearchShardIterator> shardsIts,
MinAndMax<?>[] minAndMaxes,
SortOrder order
) {
int bound = shardsIts.size(); int bound = shardsIts.size();
List<Integer> toSort = new ArrayList<>(bound); List<Integer> toSort = new ArrayList<>(bound);
for (int i = 0; i < bound; i++) { for (int i = 0; i < bound; i++) {

View file

@ -20,7 +20,6 @@ import org.apache.lucene.util.SetOnce;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.SearchShardTarget;
@ -56,7 +55,7 @@ final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction
SearchPhaseResults<SearchPhaseResult> queryPhaseResultConsumer, SearchPhaseResults<SearchPhaseResult> queryPhaseResultConsumer,
SearchRequest request, SearchRequest request,
ActionListener<SearchResponse> listener, ActionListener<SearchResponse> listener,
GroupShardsIterator<SearchShardIterator> shardsIts, List<SearchShardIterator> shardsIts,
TransportSearchAction.SearchTimeProvider timeProvider, TransportSearchAction.SearchTimeProvider timeProvider,
ClusterState clusterState, ClusterState clusterState,
SearchTask task, SearchTask task,

View file

@ -8,11 +8,11 @@
*/ */
package org.elasticsearch.action.search; package org.elasticsearch.action.search;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.Transport;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
@ -45,14 +45,14 @@ abstract class SearchPhase {
+ "]. Consider using `allow_partial_search_results` setting to bypass this error."; + "]. Consider using `allow_partial_search_results` setting to bypass this error.";
} }
protected void doCheckNoMissingShards(String phaseName, SearchRequest request, GroupShardsIterator<SearchShardIterator> shardsIts) { protected void doCheckNoMissingShards(String phaseName, SearchRequest request, List<SearchShardIterator> shardsIts) {
doCheckNoMissingShards(phaseName, request, shardsIts, this::missingShardsErrorMessage); doCheckNoMissingShards(phaseName, request, shardsIts, this::missingShardsErrorMessage);
} }
protected static void doCheckNoMissingShards( protected static void doCheckNoMissingShards(
String phaseName, String phaseName,
SearchRequest request, SearchRequest request,
GroupShardsIterator<SearchShardIterator> shardsIts, List<SearchShardIterator> shardsIts,
Function<StringBuilder, String> makeErrorMessage Function<StringBuilder, String> makeErrorMessage
) { ) {
assert request.allowPartialSearchResults() != null : "SearchRequest missing setting for allowPartialSearchResults"; assert request.allowPartialSearchResults() != null : "SearchRequest missing setting for allowPartialSearchResults";

View file

@ -13,7 +13,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchResponse.Clusters; import org.elasticsearch.action.search.SearchResponse.Clusters;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalAggregations;
@ -21,7 +20,6 @@ import org.elasticsearch.search.query.QuerySearchResult;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.StreamSupport;
/** /**
* A listener that allows to track progress of the {@link TransportSearchAction}. * A listener that allows to track progress of the {@link TransportSearchAction}.
@ -225,7 +223,7 @@ public abstract class SearchProgressListener {
.toList(); .toList();
} }
static List<SearchShard> buildSearchShards(GroupShardsIterator<SearchShardIterator> its) { static List<SearchShard> buildSearchShardsFromIter(List<SearchShardIterator> its) {
return StreamSupport.stream(its.spliterator(), false).map(e -> new SearchShard(e.getClusterAlias(), e.shardId())).toList(); return its.stream().map(e -> new SearchShard(e.getClusterAlias(), e.shardId())).toList();
} }
} }

View file

@ -14,7 +14,6 @@ import org.apache.lucene.search.TopFieldDocs;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.SearchShardTarget;
@ -25,6 +24,7 @@ import org.elasticsearch.search.internal.ShardSearchRequest;
import org.elasticsearch.search.query.QuerySearchResult; import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.Transport;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -52,7 +52,7 @@ class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction<SearchPh
SearchPhaseResults<SearchPhaseResult> resultConsumer, SearchPhaseResults<SearchPhaseResult> resultConsumer,
SearchRequest request, SearchRequest request,
ActionListener<SearchResponse> listener, ActionListener<SearchResponse> listener,
GroupShardsIterator<SearchShardIterator> shardsIts, List<SearchShardIterator> shardsIts,
TransportSearchAction.SearchTimeProvider timeProvider, TransportSearchAction.SearchTimeProvider timeProvider,
ClusterState clusterState, ClusterState clusterState,
SearchTask task, SearchTask task,

View file

@ -23,7 +23,6 @@ import org.elasticsearch.action.support.ChannelActionListener;
import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
@ -49,6 +48,7 @@ import org.elasticsearch.transport.TransportResponseHandler;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -150,7 +150,7 @@ public class TransportOpenPointInTimeAction extends HandledTransportAction<OpenP
SearchTask task, SearchTask task,
SearchRequest searchRequest, SearchRequest searchRequest,
Executor executor, Executor executor,
GroupShardsIterator<SearchShardIterator> shardIterators, List<SearchShardIterator> shardIterators,
TransportSearchAction.SearchTimeProvider timeProvider, TransportSearchAction.SearchTimeProvider timeProvider,
BiFunction<String, String, Transport.Connection> connectionLookup, BiFunction<String, String, Transport.Connection> connectionLookup,
ClusterState clusterState, ClusterState clusterState,
@ -212,7 +212,7 @@ public class TransportOpenPointInTimeAction extends HandledTransportAction<OpenP
SearchTask task, SearchTask task,
SearchRequest searchRequest, SearchRequest searchRequest,
Executor executor, Executor executor,
GroupShardsIterator<SearchShardIterator> shardIterators, List<SearchShardIterator> shardIterators,
TransportSearchAction.SearchTimeProvider timeProvider, TransportSearchAction.SearchTimeProvider timeProvider,
BiFunction<String, String, Transport.Connection> connectionLookup, BiFunction<String, String, Transport.Connection> connectionLookup,
ClusterState clusterState, ClusterState clusterState,

View file

@ -11,6 +11,7 @@ package org.elasticsearch.action.search;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.TransportVersions; import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
@ -44,7 +45,6 @@ import org.elasticsearch.cluster.metadata.ProjectMetadata;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.OperationRouting; import org.elasticsearch.cluster.routing.OperationRouting;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
@ -1293,7 +1293,7 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
); );
} }
} }
final GroupShardsIterator<SearchShardIterator> shardIterators = mergeShardsIterators(localShardIterators, remoteShardIterators); final List<SearchShardIterator> shardIterators = mergeShardsIterators(localShardIterators, remoteShardIterators);
failIfOverShardCountLimit(clusterService, shardIterators.size()); failIfOverShardCountLimit(clusterService, shardIterators.size());
@ -1427,7 +1427,7 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
} }
// package private for testing // package private for testing
static GroupShardsIterator<SearchShardIterator> mergeShardsIterators( static List<SearchShardIterator> mergeShardsIterators(
List<SearchShardIterator> localShardIterators, List<SearchShardIterator> localShardIterators,
List<SearchShardIterator> remoteShardIterators List<SearchShardIterator> remoteShardIterators
) { ) {
@ -1437,7 +1437,8 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
} else { } else {
shards = CollectionUtils.concatLists(remoteShardIterators, localShardIterators); shards = CollectionUtils.concatLists(remoteShardIterators, localShardIterators);
} }
return GroupShardsIterator.sortAndCreate(shards); CollectionUtil.timSort(shards);
return shards;
} }
interface SearchPhaseProvider { interface SearchPhaseProvider {
@ -1445,7 +1446,7 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
SearchTask task, SearchTask task,
SearchRequest searchRequest, SearchRequest searchRequest,
Executor executor, Executor executor,
GroupShardsIterator<SearchShardIterator> shardIterators, List<SearchShardIterator> shardIterators,
SearchTimeProvider timeProvider, SearchTimeProvider timeProvider,
BiFunction<String, String, Transport.Connection> connectionLookup, BiFunction<String, String, Transport.Connection> connectionLookup,
ClusterState clusterState, ClusterState clusterState,
@ -1469,7 +1470,7 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
SearchTask task, SearchTask task,
SearchRequest searchRequest, SearchRequest searchRequest,
Executor executor, Executor executor,
GroupShardsIterator<SearchShardIterator> shardIterators, List<SearchShardIterator> shardIterators,
SearchTimeProvider timeProvider, SearchTimeProvider timeProvider,
BiFunction<String, String, Transport.Connection> connectionLookup, BiFunction<String, String, Transport.Connection> connectionLookup,
ClusterState clusterState, ClusterState clusterState,
@ -1866,7 +1867,7 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
searchRequest.routing(), searchRequest.routing(),
searchRequest.indices() searchRequest.indices()
); );
GroupShardsIterator<ShardIterator> shardRoutings = clusterService.operationRouting() List<ShardIterator> shardRoutings = clusterService.operationRouting()
.searchShards( .searchShards(
projectState, projectState,
concreteIndices, concreteIndices,

View file

@ -9,6 +9,7 @@
package org.elasticsearch.action.search; package org.elasticsearch.action.search;
import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionType; import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.RemoteClusterActionType; import org.elasticsearch.action.RemoteClusterActionType;
@ -19,7 +20,6 @@ import org.elasticsearch.cluster.ProjectState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.ResolvedExpression; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.ResolvedExpression;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.index.query.Rewriteable;
@ -145,15 +145,14 @@ public class TransportSearchShardsAction extends HandledTransportAction<SearchSh
concreteIndices concreteIndices
); );
String[] concreteIndexNames = Arrays.stream(concreteIndices).map(Index::getName).toArray(String[]::new); String[] concreteIndexNames = Arrays.stream(concreteIndices).map(Index::getName).toArray(String[]::new);
GroupShardsIterator<SearchShardIterator> shardIts = GroupShardsIterator.sortAndCreate( List<SearchShardIterator> shardIts = transportSearchAction.getLocalShardsIterator(
transportSearchAction.getLocalShardsIterator( project,
project, searchRequest,
searchRequest, searchShardsRequest.clusterAlias(),
searchShardsRequest.clusterAlias(), indicesAndAliases,
indicesAndAliases, concreteIndexNames
concreteIndexNames
)
); );
CollectionUtil.timSort(shardIts);
if (SearchService.canRewriteToMatchNone(searchRequest.source()) == false) { if (SearchService.canRewriteToMatchNone(searchRequest.source()) == false) {
delegate.onResponse( delegate.onResponse(
new SearchShardsResponse(toGroups(shardIts), project.cluster().nodes().getAllNodes(), aliasFilters) new SearchShardsResponse(toGroups(shardIts), project.cluster().nodes().getAllNodes(), aliasFilters)
@ -179,7 +178,7 @@ public class TransportSearchShardsAction extends HandledTransportAction<SearchSh
); );
} }
private static List<SearchShardsGroup> toGroups(GroupShardsIterator<SearchShardIterator> shardIts) { private static List<SearchShardsGroup> toGroups(List<SearchShardIterator> shardIts) {
List<SearchShardsGroup> groups = new ArrayList<>(shardIts.size()); List<SearchShardsGroup> groups = new ArrayList<>(shardIts.size());
for (SearchShardIterator shardIt : shardIts) { for (SearchShardIterator shardIt : shardIts) {
boolean skip = shardIt.skip(); boolean skip = shardIt.skip();

View file

@ -22,7 +22,6 @@ import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
@ -36,6 +35,7 @@ import org.elasticsearch.transport.TransportService;
import org.elasticsearch.transport.Transports; import org.elasticsearch.transport.Transports;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.atomic.AtomicReferenceArray;
@ -110,7 +110,7 @@ public abstract class TransportBroadcastAction<
* on the first shard in it. If the operation fails, it will be retried on the next shard in the iterator. * on the first shard in it. If the operation fails, it will be retried on the next shard in the iterator.
*/ */
@FixForMultiProject // add ProjectMetadata to this method @FixForMultiProject // add ProjectMetadata to this method
protected abstract GroupShardsIterator<ShardIterator> shards(ClusterState clusterState, Request request, String[] concreteIndices); protected abstract List<ShardIterator> shards(ClusterState clusterState, Request request, String[] concreteIndices);
protected abstract ClusterBlockException checkGlobalBlock(ClusterState state, Request request); protected abstract ClusterBlockException checkGlobalBlock(ClusterState state, Request request);
@ -123,7 +123,7 @@ public abstract class TransportBroadcastAction<
final ActionListener<Response> listener; final ActionListener<Response> listener;
final ClusterState clusterState; final ClusterState clusterState;
final DiscoveryNodes nodes; final DiscoveryNodes nodes;
final GroupShardsIterator<ShardIterator> shardsIts; final List<ShardIterator> shardsIts;
final int expectedOps; final int expectedOps;
final AtomicInteger counterOps = new AtomicInteger(); final AtomicInteger counterOps = new AtomicInteger();
// ShardResponse or Exception // ShardResponse or Exception

View file

@ -15,7 +15,6 @@ import org.elasticsearch.action.support.single.shard.TransportSingleShardAction;
import org.elasticsearch.cluster.ProjectState; import org.elasticsearch.cluster.ProjectState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.project.ProjectResolver; import org.elasticsearch.cluster.project.ProjectResolver;
import org.elasticsearch.cluster.routing.GroupShardsIterator;
import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardIterator;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
@ -67,13 +66,8 @@ public class TransportTermVectorsAction extends TransportSingleShardAction<TermV
final var operationRouting = clusterService.operationRouting(); final var operationRouting = clusterService.operationRouting();
if (request.request().doc() != null && request.request().routing() == null) { if (request.request().doc() != null && request.request().routing() == null) {
// artificial document without routing specified, ignore its "id" and use either random shard or according to preference // artificial document without routing specified, ignore its "id" and use either random shard or according to preference
GroupShardsIterator<ShardIterator> groupShardsIter = operationRouting.searchShards( return operationRouting.searchShards(project, new String[] { request.concreteIndex() }, null, request.request().preference())
project, .getFirst();
new String[] { request.concreteIndex() },
null,
request.request().preference()
);
return groupShardsIter.iterator().next();
} }
return operationRouting.useOnlyPromotableShardsForStateless( return operationRouting.useOnlyPromotableShardsForStateless(

Some files were not shown because too many files have changed in this diff Show more