mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 21:17:54 -05:00
Force besu to stop on plugin initialization errors (#7662)
Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
This commit is contained in:
committed by
GitHub
parent
2aa3848950
commit
e0518c6d94
@@ -8,6 +8,7 @@
|
||||
- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release
|
||||
|
||||
### Breaking Changes
|
||||
- Besu will now fail to start if any plugins encounter errors during initialization. To allow Besu to continue running despite plugin errors, use the `--plugin-continue-on-error` option. [#7662](https://github.com/hyperledger/besu/pull/7662)
|
||||
|
||||
### Additions and Improvements
|
||||
- Remove privacy test classes support [#7569](https://github.com/hyperledger/besu/pull/7569)
|
||||
|
||||
@@ -503,8 +503,9 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
besuPluginContext.addService(PermissioningService.class, permissioningService);
|
||||
besuPluginContext.addService(PrivacyPluginService.class, new PrivacyPluginServiceImpl());
|
||||
|
||||
besuPluginContext.registerPlugins(
|
||||
besuPluginContext.initialize(
|
||||
new PluginConfiguration.Builder().pluginsDir(pluginsPath).build());
|
||||
besuPluginContext.registerPlugins();
|
||||
commandLine.parseArgs(extraCLIOptions.toArray(new String[0]));
|
||||
|
||||
// register built-in plugins
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -32,16 +32,25 @@ import picocli.CommandLine.Option;
|
||||
public class TestPicoCLIPlugin implements BesuPlugin {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TestPicoCLIPlugin.class);
|
||||
|
||||
private static final String UNSET = "UNSET";
|
||||
private static final String FAIL_REGISTER = "FAILREGISTER";
|
||||
private static final String FAIL_BEFORE_EXTERNAL_SERVICES = "FAILBEFOREEXTERNALSERVICES";
|
||||
private static final String FAIL_START = "FAILSTART";
|
||||
private static final String FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP =
|
||||
"FAILAFTEREXTERNALSERVICEPOSTMAINLOOP";
|
||||
private static final String FAIL_STOP = "FAILSTOP";
|
||||
private static final String PLUGIN_LIFECYCLE_PREFIX = "pluginLifecycle.";
|
||||
|
||||
@Option(
|
||||
names = {"--Xplugin-test-option"},
|
||||
hidden = true,
|
||||
defaultValue = "UNSET")
|
||||
defaultValue = UNSET)
|
||||
String testOption = System.getProperty("testPicoCLIPlugin.testOption");
|
||||
|
||||
@Option(
|
||||
names = {"--plugin-test-stable-option"},
|
||||
hidden = true,
|
||||
defaultValue = "UNSET")
|
||||
defaultValue = UNSET)
|
||||
String stableOption = "";
|
||||
|
||||
private String state = "uninited";
|
||||
@@ -52,7 +61,7 @@ public class TestPicoCLIPlugin implements BesuPlugin {
|
||||
LOG.info("Registering. Test Option is '{}'", testOption);
|
||||
state = "registering";
|
||||
|
||||
if ("FAILREGISTER".equals(testOption)) {
|
||||
if (FAIL_REGISTER.equals(testOption)) {
|
||||
state = "failregister";
|
||||
throw new RuntimeException("I was told to fail at registration");
|
||||
}
|
||||
@@ -66,12 +75,26 @@ public class TestPicoCLIPlugin implements BesuPlugin {
|
||||
state = "registered";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeExternalServices() {
|
||||
LOG.info("Before external services. Test Option is '{}'", testOption);
|
||||
state = "beforeExternalServices";
|
||||
|
||||
if (FAIL_BEFORE_EXTERNAL_SERVICES.equals(testOption)) {
|
||||
state = "failbeforeExternalServices";
|
||||
throw new RuntimeException("I was told to fail before external services");
|
||||
}
|
||||
|
||||
writeSignal("beforeExternalServices");
|
||||
state = "beforeExternalServicesFinished";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
LOG.info("Starting. Test Option is '{}'", testOption);
|
||||
state = "starting";
|
||||
|
||||
if ("FAILSTART".equals(testOption)) {
|
||||
if (FAIL_START.equals(testOption)) {
|
||||
state = "failstart";
|
||||
throw new RuntimeException("I was told to fail at startup");
|
||||
}
|
||||
@@ -80,12 +103,26 @@ public class TestPicoCLIPlugin implements BesuPlugin {
|
||||
state = "started";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExternalServicePostMainLoop() {
|
||||
LOG.info("After external services post main loop. Test Option is '{}'", testOption);
|
||||
state = "afterExternalServicePostMainLoop";
|
||||
|
||||
if (FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP.equals(testOption)) {
|
||||
state = "failafterExternalServicePostMainLoop";
|
||||
throw new RuntimeException("I was told to fail after external services post main loop");
|
||||
}
|
||||
|
||||
writeSignal("afterExternalServicePostMainLoop");
|
||||
state = "afterExternalServicePostMainLoopFinished";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.info("Stopping. Test Option is '{}'", testOption);
|
||||
state = "stopping";
|
||||
|
||||
if ("FAILSTOP".equals(testOption)) {
|
||||
if (FAIL_STOP.equals(testOption)) {
|
||||
state = "failstop";
|
||||
throw new RuntimeException("I was told to fail at stop");
|
||||
}
|
||||
@@ -103,7 +140,7 @@ public class TestPicoCLIPlugin implements BesuPlugin {
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private void writeSignal(final String signal) {
|
||||
try {
|
||||
final File callbackFile = new File(callbackDir, "pluginLifecycle." + signal);
|
||||
final File callbackFile = new File(callbackDir, PLUGIN_LIFECYCLE_PREFIX + signal);
|
||||
if (!callbackFile.getParentFile().exists()) {
|
||||
callbackFile.getParentFile().mkdirs();
|
||||
callbackFile.getParentFile().deleteOnExit();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -40,8 +40,31 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
public class BesuPluginContextImplTest {
|
||||
private static final Path DEFAULT_PLUGIN_DIRECTORY = Paths.get(".");
|
||||
private static final String TEST_PICO_CLI_PLUGIN = "TestPicoCLIPlugin";
|
||||
private static final String TEST_PICO_CLI_PLUGIN_TEST_OPTION = "testPicoCLIPlugin.testOption";
|
||||
private static final String FAIL_REGISTER = "FAILREGISTER";
|
||||
private static final String FAIL_START = "FAILSTART";
|
||||
private static final String FAIL_STOP = "FAILSTOP";
|
||||
private static final String FAIL_BEFORE_EXTERNAL_SERVICES = "FAILBEFOREEXTERNALSERVICES";
|
||||
private static final String FAIL_BEFORE_MAIN_LOOP = "FAILBEFOREMAINLOOP";
|
||||
private static final String FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP =
|
||||
"FAILAFTEREXTERNALSERVICEPOSTMAINLOOP";
|
||||
private static final String NON_EXISTENT_PLUGIN = "NonExistentPlugin";
|
||||
private static final String REGISTERED = "registered";
|
||||
private static final String STARTED = "started";
|
||||
private static final String STOPPED = "stopped";
|
||||
private static final String FAIL_START_STATE = "failstart";
|
||||
private static final String FAIL_STOP_STATE = "failstop";
|
||||
|
||||
private BesuPluginContextImpl contextImpl;
|
||||
|
||||
private static final PluginConfiguration DEFAULT_CONFIGURATION =
|
||||
PluginConfiguration.builder()
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.externalPluginsEnabled(true)
|
||||
.continueOnPluginError(true)
|
||||
.build();
|
||||
|
||||
@BeforeAll
|
||||
public static void createFakePluginDir() throws IOException {
|
||||
if (System.getProperty("besu.plugins.dir") == null) {
|
||||
@@ -53,7 +76,7 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@AfterEach
|
||||
public void clearTestPluginState() {
|
||||
System.clearProperty("testPicoCLIPlugin.testOption");
|
||||
System.clearProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -64,31 +87,31 @@ public class BesuPluginContextImplTest {
|
||||
@Test
|
||||
public void verifyEverythingGoesSmoothly() {
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build());
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotEmpty();
|
||||
|
||||
final Optional<TestPicoCLIPlugin> testPluginOptional =
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class);
|
||||
assertThat(testPluginOptional).isPresent();
|
||||
final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED);
|
||||
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("started");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(STARTED);
|
||||
|
||||
contextImpl.stopPlugins();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("stopped");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(STOPPED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationErrorsHandledSmoothly() {
|
||||
System.setProperty("testPicoCLIPlugin.testOption", "FAILREGISTER");
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER);
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build());
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
|
||||
contextImpl.beforeExternalServices();
|
||||
@@ -103,11 +126,11 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@Test
|
||||
public void startErrorsHandledSmoothly() {
|
||||
System.setProperty("testPicoCLIPlugin.testOption", "FAILSTART");
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_START);
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build());
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins())
|
||||
.extracting("class")
|
||||
.contains(TestPicoCLIPlugin.class);
|
||||
@@ -116,11 +139,11 @@ public class BesuPluginContextImplTest {
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class);
|
||||
assertThat(testPluginOptional).isPresent();
|
||||
final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED);
|
||||
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("failstart");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(FAIL_START_STATE);
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
|
||||
contextImpl.stopPlugins();
|
||||
@@ -129,11 +152,11 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@Test
|
||||
public void stopErrorsHandledSmoothly() {
|
||||
System.setProperty("testPicoCLIPlugin.testOption", "FAILSTOP");
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_STOP);
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build());
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins())
|
||||
.extracting("class")
|
||||
.contains(TestPicoCLIPlugin.class);
|
||||
@@ -142,22 +165,20 @@ public class BesuPluginContextImplTest {
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class);
|
||||
assertThat(testPluginOptional).isPresent();
|
||||
final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED);
|
||||
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("started");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(STARTED);
|
||||
|
||||
contextImpl.stopPlugins();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("failstop");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(FAIL_STOP_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lifecycleExceptions() throws Throwable {
|
||||
final ThrowableAssert.ThrowingCallable registerPlugins =
|
||||
() ->
|
||||
contextImpl.registerPlugins(
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build());
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
final ThrowableAssert.ThrowingCallable registerPlugins = () -> contextImpl.registerPlugins();
|
||||
|
||||
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(contextImpl::startPlugins);
|
||||
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(contextImpl::stopPlugins);
|
||||
@@ -179,30 +200,27 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@Test
|
||||
public void shouldRegisterAllPluginsWhenNoPluginsOption() {
|
||||
final PluginConfiguration config =
|
||||
PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build();
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(config);
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
final Optional<TestPicoCLIPlugin> testPluginOptional =
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class);
|
||||
assertThat(testPluginOptional).isPresent();
|
||||
final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get();
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered");
|
||||
assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRegisterOnlySpecifiedPluginWhenPluginsOptionIsSet() {
|
||||
final PluginConfiguration config = createConfigurationForSpecificPlugin("TestPicoCLIPlugin");
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(config);
|
||||
contextImpl.initialize(createConfigurationForSpecificPlugin(TEST_PICO_CLI_PLUGIN));
|
||||
contextImpl.registerPlugins();
|
||||
|
||||
final Optional<TestPicoCLIPlugin> requestedPlugin =
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class);
|
||||
|
||||
assertThat(requestedPlugin).isPresent();
|
||||
assertThat(requestedPlugin.get().getState()).isEqualTo("registered");
|
||||
assertThat(requestedPlugin.get().getState()).isEqualTo(REGISTERED);
|
||||
|
||||
final Optional<TestPicoCLIPlugin> nonRequestedPlugin =
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestBesuEventsPlugin.class);
|
||||
@@ -212,9 +230,9 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@Test
|
||||
public void shouldNotRegisterUnspecifiedPluginsWhenPluginsOptionIsSet() {
|
||||
final PluginConfiguration config = createConfigurationForSpecificPlugin("TestPicoCLIPlugin");
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
contextImpl.registerPlugins(config);
|
||||
contextImpl.initialize(createConfigurationForSpecificPlugin(TEST_PICO_CLI_PLUGIN));
|
||||
contextImpl.registerPlugins();
|
||||
|
||||
final Optional<TestPicoCLIPlugin> nonRequestedPlugin =
|
||||
findTestPlugin(contextImpl.getRegisteredPlugins(), TestBesuEventsPlugin.class);
|
||||
@@ -223,13 +241,12 @@ public class BesuPluginContextImplTest {
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionIfExplicitlySpecifiedPluginNotFound() {
|
||||
PluginConfiguration config = createConfigurationForSpecificPlugin("NonExistentPlugin");
|
||||
|
||||
contextImpl.initialize(createConfigurationForSpecificPlugin(NON_EXISTENT_PLUGIN));
|
||||
String exceptionMessage =
|
||||
assertThrows(NoSuchElementException.class, () -> contextImpl.registerPlugins(config))
|
||||
assertThrows(NoSuchElementException.class, () -> contextImpl.registerPlugins())
|
||||
.getMessage();
|
||||
final String expectedMessage =
|
||||
"The following requested plugins were not found: NonExistentPlugin";
|
||||
"The following requested plugins were not found: " + NON_EXISTENT_PLUGIN;
|
||||
assertThat(exceptionMessage).isEqualTo(expectedMessage);
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isEmpty();
|
||||
}
|
||||
@@ -241,19 +258,180 @@ public class BesuPluginContextImplTest {
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.externalPluginsEnabled(false)
|
||||
.build();
|
||||
contextImpl.registerPlugins(config);
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterPluginsIfExternalPluginsEnabled() {
|
||||
contextImpl.initialize(DEFAULT_CONFIGURATION);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaltOnRegisterErrorWhenFlagIsFalse() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(false)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
|
||||
String errorMessage =
|
||||
String.format("Error registering plugin of type %s", TestPicoCLIPlugin.class.getName());
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> contextImpl.registerPlugins())
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotHaltOnRegisterErrorWhenFlagIsTrue() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.externalPluginsEnabled(true)
|
||||
.continueOnPluginError(true)
|
||||
.build();
|
||||
contextImpl.registerPlugins(config);
|
||||
assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isFalse();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaltOnBeforeExternalServicesErrorWhenFlagIsFalse() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_EXTERNAL_SERVICES);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(false)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
|
||||
String errorMessage =
|
||||
String.format(
|
||||
"Error calling `beforeExternalServices` on plugin of type %s",
|
||||
TestPicoCLIPlugin.class.getName());
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> contextImpl.beforeExternalServices())
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotHaltOnBeforeExternalServicesErrorWhenFlagIsTrue() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_EXTERNAL_SERVICES);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(true)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
contextImpl.beforeExternalServices();
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaltOnBeforeMainLoopErrorWhenFlagIsFalse() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_START);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(false)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
contextImpl.beforeExternalServices();
|
||||
|
||||
String errorMessage =
|
||||
String.format("Error starting plugin of type %s", TestPicoCLIPlugin.class.getName());
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> contextImpl.startPlugins())
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotHaltOnBeforeMainLoopErrorWhenFlagIsTrue() {
|
||||
System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_MAIN_LOOP);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(true)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaltOnAfterExternalServicePostMainLoopErrorWhenFlagIsFalse() {
|
||||
System.setProperty(
|
||||
TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(false)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
|
||||
String errorMessage =
|
||||
String.format(
|
||||
"Error calling `afterExternalServicePostMainLoop` on plugin of type %s",
|
||||
TestPicoCLIPlugin.class.getName());
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> contextImpl.afterExternalServicesMainLoop())
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotHaltOnAfterExternalServicePostMainLoopErrorWhenFlagIsTrue() {
|
||||
System.setProperty(
|
||||
TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP);
|
||||
|
||||
PluginConfiguration config =
|
||||
PluginConfiguration.builder()
|
||||
.requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN)))
|
||||
.pluginsDir(DEFAULT_PLUGIN_DIRECTORY)
|
||||
.continueOnPluginError(true)
|
||||
.build();
|
||||
|
||||
contextImpl.initialize(config);
|
||||
contextImpl.registerPlugins();
|
||||
contextImpl.beforeExternalServices();
|
||||
contextImpl.startPlugins();
|
||||
contextImpl.afterExternalServicesMainLoop();
|
||||
|
||||
assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class);
|
||||
}
|
||||
|
||||
private PluginConfiguration createConfigurationForSpecificPlugin(final String pluginName) {
|
||||
|
||||
@@ -118,7 +118,6 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
|
||||
import org.hyperledger.besu.ethereum.core.MiningParametersMetrics;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.VersionMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
|
||||
@@ -1080,9 +1079,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
|
||||
private IExecutionStrategy createPluginRegistrationTask(final IExecutionStrategy nextStep) {
|
||||
return parseResult -> {
|
||||
PluginConfiguration configuration =
|
||||
PluginsConfigurationOptions.fromCommandLine(parseResult.commandSpec().commandLine());
|
||||
besuPluginContext.registerPlugins(configuration);
|
||||
besuPluginContext.initialize(PluginsConfigurationOptions.fromCommandLine(commandLine));
|
||||
besuPluginContext.registerPlugins();
|
||||
commandLine.setExecutionStrategy(nextStep);
|
||||
return commandLine.execute(parseResult.originalArgs().toArray(new String[0]));
|
||||
};
|
||||
|
||||
@@ -128,6 +128,9 @@ public interface DefaultCommandValues {
|
||||
/** The constant DEFAULT_PLUGINS_OPTION_NAME. */
|
||||
String DEFAULT_PLUGINS_OPTION_NAME = "--plugins";
|
||||
|
||||
/** The constant DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME. */
|
||||
String DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME = "--plugin-continue-on-error";
|
||||
|
||||
/** The constant DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME. */
|
||||
String DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME = "--Xplugins-external-enabled";
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.cli.options.stable;
|
||||
|
||||
import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME;
|
||||
import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME;
|
||||
import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_PLUGINS_OPTION_NAME;
|
||||
|
||||
@@ -27,7 +28,7 @@ import java.util.List;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
/** The Plugins Options options. */
|
||||
/** The Plugins options. */
|
||||
public class PluginsConfigurationOptions implements CLIOptions<PluginConfiguration> {
|
||||
|
||||
@CommandLine.Option(
|
||||
@@ -44,9 +45,17 @@ public class PluginsConfigurationOptions implements CLIOptions<PluginConfigurati
|
||||
split = ",",
|
||||
hidden = true,
|
||||
converter = PluginInfoConverter.class,
|
||||
arity = "1..*")
|
||||
arity = "1")
|
||||
private List<PluginInfo> plugins;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME},
|
||||
description =
|
||||
"Allow Besu startup even if any plugins fail to initialize correctly (default: ${DEFAULT-VALUE})",
|
||||
defaultValue = "false",
|
||||
arity = "1")
|
||||
private final Boolean continueOnPluginError = false;
|
||||
|
||||
/** Default Constructor. */
|
||||
public PluginsConfigurationOptions() {}
|
||||
|
||||
@@ -55,6 +64,7 @@ public class PluginsConfigurationOptions implements CLIOptions<PluginConfigurati
|
||||
return new PluginConfiguration.Builder()
|
||||
.externalPluginsEnabled(externalPluginsEnabled)
|
||||
.requestedPlugins(plugins)
|
||||
.continueOnPluginError(continueOnPluginError)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -66,10 +76,15 @@ public class PluginsConfigurationOptions implements CLIOptions<PluginConfigurati
|
||||
public void validate(final CommandLine commandLine) {
|
||||
String errorMessage =
|
||||
String.format(
|
||||
"%s option can only be used when %s is true",
|
||||
DEFAULT_PLUGINS_OPTION_NAME, DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME);
|
||||
"%s and %s option can only be used when %s is true",
|
||||
DEFAULT_PLUGINS_OPTION_NAME,
|
||||
DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME,
|
||||
DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME);
|
||||
CommandLineUtils.failIfOptionDoesntMeetRequirement(
|
||||
commandLine, errorMessage, externalPluginsEnabled, List.of(DEFAULT_PLUGINS_OPTION_NAME));
|
||||
commandLine,
|
||||
errorMessage,
|
||||
externalPluginsEnabled,
|
||||
List.of(DEFAULT_PLUGINS_OPTION_NAME, DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,9 +107,14 @@ public class PluginsConfigurationOptions implements CLIOptions<PluginConfigurati
|
||||
CommandLineUtils.getOptionValueOrDefault(
|
||||
commandLine, DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME, Boolean::parseBoolean);
|
||||
|
||||
boolean continueOnPluginError =
|
||||
CommandLineUtils.getOptionValueOrDefault(
|
||||
commandLine, DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME, Boolean::parseBoolean);
|
||||
|
||||
return new PluginConfiguration.Builder()
|
||||
.requestedPlugins(plugins)
|
||||
.externalPluginsEnabled(externalPluginsEnabled)
|
||||
.continueOnPluginError(continueOnPluginError)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
private enum Lifecycle {
|
||||
/** Uninitialized lifecycle. */
|
||||
UNINITIALIZED,
|
||||
/** Initialized lifecycle. */
|
||||
INITIALIZED,
|
||||
/** Registering lifecycle. */
|
||||
REGISTERING,
|
||||
/** Registered lifecycle. */
|
||||
@@ -83,6 +85,7 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
private final List<BesuPlugin> registeredPlugins = new ArrayList<>();
|
||||
|
||||
private final List<String> pluginVersions = new ArrayList<>();
|
||||
private PluginConfiguration config;
|
||||
|
||||
/** Instantiates a new Besu plugin context. */
|
||||
public BesuPluginContextImpl() {}
|
||||
@@ -116,19 +119,30 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
return StreamSupport.stream(serviceLoader.spliterator(), false).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin context with the provided {@link PluginConfiguration}.
|
||||
*
|
||||
* @param config the plugin configuration
|
||||
* @throws IllegalStateException if the system is not in the UNINITIALIZED state.
|
||||
*/
|
||||
public void initialize(final PluginConfiguration config) {
|
||||
checkState(
|
||||
state == Lifecycle.UNINITIALIZED,
|
||||
"Besu plugins have already been initialized. Cannot register additional plugins.");
|
||||
this.config = config;
|
||||
state = Lifecycle.INITIALIZED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers plugins based on the provided {@link PluginConfiguration}. This method finds plugins
|
||||
* according to the configuration settings, filters them if necessary and then registers the
|
||||
* filtered or found plugins
|
||||
*
|
||||
* @param config The configuration settings used to find and filter plugins for registration. The
|
||||
* configuration includes the plugin directory and any configured plugin identifiers if
|
||||
* applicable.
|
||||
* @throws IllegalStateException if the system is not in the UNINITIALIZED state.
|
||||
*/
|
||||
public void registerPlugins(final PluginConfiguration config) {
|
||||
public void registerPlugins() {
|
||||
checkState(
|
||||
state == Lifecycle.UNINITIALIZED,
|
||||
state == Lifecycle.INITIALIZED,
|
||||
"Besu plugins have already been registered. Cannot register additional plugins.");
|
||||
state = Lifecycle.REGISTERING;
|
||||
|
||||
@@ -192,14 +206,18 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
private boolean registerPlugin(final BesuPlugin plugin) {
|
||||
try {
|
||||
plugin.register(this);
|
||||
LOG.info("Registered plugin of type {}.", plugin.getClass().getName());
|
||||
pluginVersions.add(plugin.getVersion());
|
||||
LOG.info("Registered plugin of type {}.", plugin.getClass().getName());
|
||||
} catch (final Exception e) {
|
||||
LOG.error(
|
||||
"Error registering plugin of type "
|
||||
+ plugin.getClass().getName()
|
||||
+ ", start and stop will not be called.",
|
||||
e);
|
||||
if (config.isContinueOnPluginError()) {
|
||||
LOG.error(
|
||||
"Error registering plugin of type {}, start and stop will not be called.",
|
||||
plugin.getClass().getName(),
|
||||
e);
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Error registering plugin of type " + plugin.getClass().getName(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -223,15 +241,20 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
LOG.debug(
|
||||
"beforeExternalServices called on plugin of type {}.", plugin.getClass().getName());
|
||||
} catch (final Exception e) {
|
||||
LOG.error(
|
||||
"Error calling `beforeExternalServices` on plugin of type "
|
||||
+ plugin.getClass().getName()
|
||||
+ ", stop will not be called.",
|
||||
e);
|
||||
pluginsIterator.remove();
|
||||
if (config.isContinueOnPluginError()) {
|
||||
LOG.error(
|
||||
"Error calling `beforeExternalServices` on plugin of type {}, start will not be called.",
|
||||
plugin.getClass().getName(),
|
||||
e);
|
||||
pluginsIterator.remove();
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Error calling `beforeExternalServices` on plugin of type "
|
||||
+ plugin.getClass().getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("Plugin startup complete.");
|
||||
state = Lifecycle.BEFORE_EXTERNAL_SERVICES_FINISHED;
|
||||
}
|
||||
@@ -253,12 +276,16 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
plugin.start();
|
||||
LOG.debug("Started plugin of type {}.", plugin.getClass().getName());
|
||||
} catch (final Exception e) {
|
||||
LOG.error(
|
||||
"Error starting plugin of type "
|
||||
+ plugin.getClass().getName()
|
||||
+ ", stop will not be called.",
|
||||
e);
|
||||
pluginsIterator.remove();
|
||||
if (config.isContinueOnPluginError()) {
|
||||
LOG.error(
|
||||
"Error starting plugin of type {}, stop will not be called.",
|
||||
plugin.getClass().getName(),
|
||||
e);
|
||||
pluginsIterator.remove();
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Error starting plugin of type " + plugin.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +306,20 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide
|
||||
final BesuPlugin plugin = pluginsIterator.next();
|
||||
try {
|
||||
plugin.afterExternalServicePostMainLoop();
|
||||
} finally {
|
||||
pluginsIterator.remove();
|
||||
} catch (final Exception e) {
|
||||
if (config.isContinueOnPluginError()) {
|
||||
LOG.error(
|
||||
"Error calling `afterExternalServicePostMainLoop` on plugin of type "
|
||||
+ plugin.getClass().getName()
|
||||
+ ", stop will not be called.",
|
||||
e);
|
||||
pluginsIterator.remove();
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Error calling `afterExternalServicePostMainLoop` on plugin of type "
|
||||
+ plugin.getClass().getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldParsePluginOptionForSinglePlugin() {
|
||||
parseCommand("--plugins", "pluginA");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().getRequestedPlugins())
|
||||
.isEqualTo(List.of("pluginA"));
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
@@ -43,7 +43,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldParsePluginOptionForMultiplePlugins() {
|
||||
parseCommand("--plugins", "pluginA,pluginB");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().getRequestedPlugins())
|
||||
.isEqualTo(List.of("pluginA", "pluginB"));
|
||||
|
||||
@@ -54,7 +54,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldNotUsePluginOptionWhenNoPluginsSpecified() {
|
||||
parseCommand();
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().getRequestedPlugins())
|
||||
.isEqualTo(List.of());
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
@@ -64,7 +64,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldNotParseAnyPluginsWhenPluginOptionIsEmpty() {
|
||||
parseCommand("--plugins", "");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().getRequestedPlugins())
|
||||
.isEqualTo(List.of());
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
@@ -74,7 +74,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldParsePluginsExternalEnabledOptionWhenFalse() {
|
||||
parseCommand("--Xplugins-external-enabled=false");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().isExternalPluginsEnabled())
|
||||
.isEqualTo(false);
|
||||
@@ -86,7 +86,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldParsePluginsExternalEnabledOptionWhenTrue() {
|
||||
parseCommand("--Xplugins-external-enabled=true");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().isExternalPluginsEnabled())
|
||||
.isEqualTo(true);
|
||||
@@ -98,7 +98,7 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldEnablePluginsExternalByDefault() {
|
||||
parseCommand();
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().isExternalPluginsEnabled())
|
||||
.isEqualTo(true);
|
||||
|
||||
@@ -109,10 +109,43 @@ public class PluginsOptionsTest extends CommandTestAbstract {
|
||||
@Test
|
||||
public void shouldFailWhenPluginsIsDisabledAndPluginsExplicitlyRequested() {
|
||||
parseCommand("--Xplugins-external-enabled=false", "--plugins", "pluginA");
|
||||
verify(mockBesuPluginContext).registerPlugins(pluginConfigurationArgumentCaptor.capture());
|
||||
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
assertThat(commandErrorOutput.toString(UTF_8))
|
||||
.contains("--plugins option can only be used when --Xplugins-external-enabled is true");
|
||||
.contains(
|
||||
"--plugins and --plugin-continue-on-error option can only be used when --Xplugins-external-enabled is true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveContinueOnErrorFalseByDefault() {
|
||||
parseCommand();
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().isContinueOnPluginError())
|
||||
.isEqualTo(false);
|
||||
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseContinueOnErrorWhenTrue() {
|
||||
parseCommand("--plugin-continue-on-error=true");
|
||||
verify(mockBesuPluginContext).initialize(pluginConfigurationArgumentCaptor.capture());
|
||||
|
||||
assertThat(pluginConfigurationArgumentCaptor.getValue().isContinueOnPluginError())
|
||||
.isEqualTo(true);
|
||||
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailWhenPluginsIsDisabledAnHaltOnErrorTrue() {
|
||||
parseCommand("--Xplugins-external-enabled=false", "--plugin-continue-on-error=true");
|
||||
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
assertThat(commandErrorOutput.toString(UTF_8))
|
||||
.contains(
|
||||
"--plugins and --plugin-continue-on-error option can only be used when --Xplugins-external-enabled is true");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,4 +247,5 @@ Xevm-jumpdest-cache-weight-kb=32000
|
||||
|
||||
# plugins
|
||||
Xplugins-external-enabled=true
|
||||
plugins=["none"]
|
||||
plugins=["none"]
|
||||
plugin-continue-on-error=false
|
||||
@@ -23,14 +23,17 @@ public class PluginConfiguration {
|
||||
private final List<PluginInfo> requestedPlugins;
|
||||
private final Path pluginsDir;
|
||||
private final boolean externalPluginsEnabled;
|
||||
private final boolean continueOnPluginError;
|
||||
|
||||
public PluginConfiguration(
|
||||
final List<PluginInfo> requestedPlugins,
|
||||
final Path pluginsDir,
|
||||
final boolean externalPluginsEnabled) {
|
||||
final boolean externalPluginsEnabled,
|
||||
final boolean continueOnPluginError) {
|
||||
this.requestedPlugins = requestedPlugins;
|
||||
this.pluginsDir = pluginsDir;
|
||||
this.externalPluginsEnabled = externalPluginsEnabled;
|
||||
this.continueOnPluginError = continueOnPluginError;
|
||||
}
|
||||
|
||||
public List<String> getRequestedPlugins() {
|
||||
@@ -47,6 +50,10 @@ public class PluginConfiguration {
|
||||
return externalPluginsEnabled;
|
||||
}
|
||||
|
||||
public boolean isContinueOnPluginError() {
|
||||
return continueOnPluginError;
|
||||
}
|
||||
|
||||
public static Path defaultPluginsDir() {
|
||||
String pluginsDirProperty = System.getProperty("besu.plugins.dir");
|
||||
return pluginsDirProperty == null
|
||||
@@ -62,6 +69,7 @@ public class PluginConfiguration {
|
||||
private List<PluginInfo> requestedPlugins;
|
||||
private Path pluginsDir;
|
||||
private boolean externalPluginsEnabled = true;
|
||||
private boolean continueOnPluginError = false;
|
||||
|
||||
public Builder requestedPlugins(final List<PluginInfo> requestedPlugins) {
|
||||
this.requestedPlugins = requestedPlugins;
|
||||
@@ -78,11 +86,17 @@ public class PluginConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder continueOnPluginError(final boolean continueOnPluginError) {
|
||||
this.continueOnPluginError = continueOnPluginError;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginConfiguration build() {
|
||||
if (pluginsDir == null) {
|
||||
pluginsDir = PluginConfiguration.defaultPluginsDir();
|
||||
}
|
||||
return new PluginConfiguration(requestedPlugins, pluginsDir, externalPluginsEnabled);
|
||||
return new PluginConfiguration(
|
||||
requestedPlugins, pluginsDir, externalPluginsEnabled, continueOnPluginError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user