Addition of Profile Configuration CLI Option (#6418)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
pull/6453/head
Gabriel-Trintinalia 10 months ago committed by GitHub
parent 921bc175c8
commit 1c1f538534
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  2. 16
      besu/src/main/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilder.java
  3. 4
      besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java
  4. 41
      besu/src/main/java/org/hyperledger/besu/cli/config/ProfileName.java
  5. 5
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/ValidateConfigSubCommand.java
  6. 125
      besu/src/main/java/org/hyperledger/besu/cli/util/AbstractConfigurationFinder.java
  7. 6
      besu/src/main/java/org/hyperledger/besu/cli/util/CascadingDefaultProvider.java
  8. 100
      besu/src/main/java/org/hyperledger/besu/cli/util/ConfigFileFinder.java
  9. 67
      besu/src/main/java/org/hyperledger/besu/cli/util/ConfigOptionSearchAndRunHandler.java
  10. 76
      besu/src/main/java/org/hyperledger/besu/cli/util/ProfileFinder.java
  11. 95
      besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java
  12. 168
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  13. 306
      besu/src/test/java/org/hyperledger/besu/cli/CascadingDefaultProviderTest.java
  14. 4
      besu/src/test/java/org/hyperledger/besu/cli/CommandLineUtilsTest.java
  15. 5
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  16. 8
      besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java
  17. 41
      besu/src/test/java/org/hyperledger/besu/cli/TomlConfigurationDefaultProviderTest.java
  18. 5
      besu/src/test/java/org/hyperledger/besu/cli/util/ConfigOptionSearchAndRunHandlerTest.java
  19. 2
      besu/src/test/resources/everything_config.toml
  20. 5
      besu/src/test/resources/partial_config.toml
  21. 1
      config/src/main/resources/profiles/dev.toml

@ -46,6 +46,7 @@ import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.cli.config.ProfileName;
import org.hyperledger.besu.cli.converter.MetricCategoryConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.custom.CorsAllowedOriginsProperty;
@ -536,6 +537,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
+ " (default: ${DEFAULT-VALUE})")
private final NetworkName network = null;
@Option(
names = {PROFILE_OPTION_NAME},
paramLabel = PROFILE_FORMAT_HELP,
description =
"Overwrite default settings. Possible values are ${COMPLETION-CANDIDATES}. (default: none)")
private final ProfileName profile = null;
@Option(
names = {"--nat-method"},
description =
@ -3508,6 +3516,10 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
builder.setNetwork(network.normalize());
}
if (profile != null) {
builder.setProfile(profile.toString());
}
builder.setHasCustomGenesis(genesisFile != null);
if (genesisFile != null) {
builder.setCustomGenesis(genesisFile.getAbsolutePath());

@ -41,6 +41,7 @@ public class ConfigurationOverviewBuilder {
private String network;
private BigInteger networkId;
private String profile;
private boolean hasCustomGenesis;
private String customGenesisFileName;
private String dataStorage;
@ -88,6 +89,17 @@ public class ConfigurationOverviewBuilder {
return this;
}
/**
* Sets profile.
*
* @param profile the profile
* @return the profile
*/
public ConfigurationOverviewBuilder setProfile(final String profile) {
this.profile = profile;
return this;
}
/**
* Sets whether a custom genesis has been specified.
*
@ -290,6 +302,10 @@ public class ConfigurationOverviewBuilder {
lines.add("Network Id: " + networkId);
}
if (profile != null) {
lines.add("Profile: " + profile);
}
if (dataStorage != null) {
lines.add("Data storage: " + dataStorage);
}

@ -54,6 +54,10 @@ public interface DefaultCommandValues {
String MANDATORY_MODE_FORMAT_HELP = "<MODE>";
/** The constant MANDATORY_NETWORK_FORMAT_HELP. */
String MANDATORY_NETWORK_FORMAT_HELP = "<NETWORK>";
/** The constant PROFILE_OPTION_NAME. */
String PROFILE_OPTION_NAME = "--profile";
/** The constant PROFILE_FORMAT_HELP. */
String PROFILE_FORMAT_HELP = "<PROFILE>";
/** The constant MANDATORY_NODE_ID_FORMAT_HELP. */
String MANDATORY_NODE_ID_FORMAT_HELP = "<NODEID>";
/** The constant PERMISSIONING_CONFIG_LOCATION. */

@ -0,0 +1,41 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.config;
/** Enum for profile names. Each profile corresponds to a configuration file. */
public enum ProfileName {
/** The 'DEV' profile. Corresponds to the 'profiles/dev.toml' configuration file. */
DEV("profiles/dev.toml");
private final String configFile;
/**
* Constructs a new ProfileName.
*
* @param configFile the configuration file corresponding to the profile
*/
ProfileName(final String configFile) {
this.configFile = configFile;
}
/**
* Gets the configuration file corresponding to the profile.
*
* @return the configuration file
*/
public String getConfigFile() {
return configFile;
}
}

@ -19,7 +19,7 @@ import static org.hyperledger.besu.cli.subcommands.ValidateConfigSubCommand.COMM
import org.hyperledger.besu.cli.BesuCommand;
import org.hyperledger.besu.cli.DefaultCommandValues;
import org.hyperledger.besu.cli.util.TomlConfigFileDefaultProvider;
import org.hyperledger.besu.cli.util.TomlConfigurationDefaultProvider;
import org.hyperledger.besu.cli.util.VersionProvider;
import java.io.PrintWriter;
@ -69,7 +69,8 @@ public class ValidateConfigSubCommand implements Runnable {
public void run() {
checkNotNull(parentCommand);
try {
new TomlConfigFileDefaultProvider(commandLine, dataPath.toFile()).loadConfigurationFromFile();
TomlConfigurationDefaultProvider.fromFile(commandLine, dataPath.toFile())
.loadConfigurationFromFile();
} catch (Exception e) {
this.out.println(e);
return;

@ -0,0 +1,125 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;
import java.util.Map;
import java.util.Optional;
import picocli.CommandLine;
/**
* Abstract class for finding configuration resources. This class provides a common structure for
* classes that need to find configuration resources based on command line options and environment
* variables.
*
* @param <T> the type of configuration resource this finder will return
*/
public abstract class AbstractConfigurationFinder<T> {
/**
* Returns the name of the configuration option.
*
* @return the name of the configuration option
*/
protected abstract String getConfigOptionName();
/**
* Returns the name of the environment variable for the configuration.
*
* @return the name of the environment variable for the configuration
*/
protected abstract String getConfigEnvName();
/**
* Finds the configuration resource based on command line options and environment variables.
*
* @param environment the environment variables
* @param parseResult the command line parse result
* @return an Optional containing the configuration resource, or an empty Optional if no
* configuration resource was found
*/
public Optional<T> findConfiguration(
final Map<String, String> environment, final CommandLine.ParseResult parseResult) {
final CommandLine commandLine = parseResult.commandSpec().commandLine();
if (isConfigSpecifiedInBothSources(environment, parseResult)) {
throwExceptionForBothSourcesSpecified(environment, parseResult, commandLine);
}
if (parseResult.hasMatchedOption(getConfigOptionName())) {
return getFromOption(parseResult, commandLine);
}
if (environment.containsKey(getConfigEnvName())) {
return getFromEnvironment(environment, commandLine);
}
return Optional.empty();
}
/**
* Gets the configuration resource from the command line option.
*
* @param parseResult the command line parse result
* @param commandLine the command line
* @return an Optional containing the configuration resource, or an empty Optional if the
* configuration resource was not specified in the command line option
*/
protected abstract Optional<T> getFromOption(
final CommandLine.ParseResult parseResult, final CommandLine commandLine);
/**
* Gets the configuration resource from the environment variable.
*
* @param environment the environment variables
* @param commandLine the command line
* @return an Optional containing the configuration resource, or an empty Optional if the
* configuration resource was not specified in the environment variable
*/
protected abstract Optional<T> getFromEnvironment(
final Map<String, String> environment, final CommandLine commandLine);
/**
* Checks if the configuration resource is specified in both command line options and environment
* variables.
*
* @param environment the environment variables
* @param parseResult the command line parse result
* @return true if the configuration resource is specified in both places, false otherwise
*/
public boolean isConfigSpecifiedInBothSources(
final Map<String, String> environment, final CommandLine.ParseResult parseResult) {
return parseResult.hasMatchedOption(getConfigOptionName())
&& environment.containsKey(getConfigEnvName());
}
/**
* Throws an exception if the configuration resource is specified in both command line options and
* environment variables.
*
* @param environment the environment variables
* @param parseResult the command line parse result
* @param commandLine the command line
*/
public void throwExceptionForBothSourcesSpecified(
final Map<String, String> environment,
final CommandLine.ParseResult parseResult,
final CommandLine commandLine) {
throw new CommandLine.ParameterException(
commandLine,
String.format(
"Both %s=%s and %s %s specified. Please specify only one.",
getConfigEnvName(),
getConfigOptionName(),
environment.get(getConfigEnvName()),
parseResult.matchedOption(getConfigOptionName()).stringValues()));
}
}

@ -14,8 +14,6 @@
*/
package org.hyperledger.besu.cli.util;
import static java.util.Arrays.asList;
import java.util.List;
import picocli.CommandLine.IDefaultValueProvider;
@ -34,8 +32,8 @@ public class CascadingDefaultProvider implements IDefaultValueProvider {
*
* @param defaultValueProviders List of default value providers
*/
public CascadingDefaultProvider(final IDefaultValueProvider... defaultValueProviders) {
this.defaultValueProviders = asList(defaultValueProviders);
public CascadingDefaultProvider(final List<IDefaultValueProvider> defaultValueProviders) {
this.defaultValueProviders = defaultValueProviders;
}
@Override

@ -0,0 +1,100 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;
import static org.hyperledger.besu.cli.DefaultCommandValues.CONFIG_FILE_OPTION_NAME;
import java.io.File;
import java.util.Map;
import java.util.Optional;
import picocli.CommandLine;
/**
* Class for finding configuration files. This class extends the AbstractConfigurationFinder and
* provides methods for finding configuration files based on command line options and environment
* variables.
*/
public class ConfigFileFinder extends AbstractConfigurationFinder<File> {
private static final String CONFIG_FILE_ENV_NAME = "BESU_CONFIG_FILE";
/**
* Returns the name of the configuration option.
*
* @return the name of the configuration option
*/
@Override
protected String getConfigOptionName() {
return CONFIG_FILE_OPTION_NAME;
}
/**
* Returns the name of the environment variable for the configuration.
*
* @return the name of the environment variable for the configuration
*/
@Override
protected String getConfigEnvName() {
return CONFIG_FILE_ENV_NAME;
}
/**
* Gets the configuration file from the command line option.
*
* @param parseResult the command line parse result
* @param commandLine the command line
* @return an Optional containing the configuration file, or an empty Optional if the
* configuration file was not specified in the command line option
*/
@Override
public Optional<File> getFromOption(
final CommandLine.ParseResult parseResult, final CommandLine commandLine) {
final CommandLine.Model.OptionSpec configFileOption =
parseResult.matchedOption(CONFIG_FILE_OPTION_NAME);
try {
File file = configFileOption.getter().get();
if (!file.exists()) {
throw new CommandLine.ParameterException(
commandLine,
String.format("Unable to read TOML configuration, file not found: %s", file));
}
return Optional.of(file);
} catch (final Exception e) {
throw new CommandLine.ParameterException(commandLine, e.getMessage(), e);
}
}
/**
* Gets the configuration file from the environment variable.
*
* @param environment the environment variables
* @param commandLine the command line
* @return an Optional containing the configuration file, or an empty Optional if the
* configuration file was not specified in the environment variable
*/
@Override
public Optional<File> getFromEnvironment(
final Map<String, String> environment, final CommandLine commandLine) {
final File toml = new File(environment.get(CONFIG_FILE_ENV_NAME));
if (!toml.exists()) {
throw new CommandLine.ParameterException(
commandLine,
String.format(
"TOML file %s specified in environment variable %s not found",
CONFIG_FILE_ENV_NAME, environment.get(CONFIG_FILE_ENV_NAME)));
}
return Optional.of(toml);
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.cli.util;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -25,7 +26,6 @@ import picocli.CommandLine;
import picocli.CommandLine.IDefaultValueProvider;
import picocli.CommandLine.IExecutionStrategy;
import picocli.CommandLine.IParameterExceptionHandler;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.ParseResult;
@ -54,8 +54,13 @@ public class ConfigOptionSearchAndRunHandler extends CommandLine.RunLast {
@Override
public List<Object> handle(final ParseResult parseResult) throws ParameterException {
final CommandLine commandLine = parseResult.commandSpec().commandLine();
final Optional<File> configFile = findConfigFile(parseResult, commandLine);
commandLine.setDefaultValueProvider(createDefaultValueProvider(commandLine, configFile));
commandLine.setDefaultValueProvider(
createDefaultValueProvider(
commandLine,
new ConfigFileFinder().findConfiguration(environment, parseResult),
new ProfileFinder().findConfiguration(environment, parseResult)));
commandLine.setExecutionStrategy(resultHandler);
commandLine.setParameterExceptionHandler(parameterExceptionHandler);
commandLine.execute(parseResult.originalArgs().toArray(new String[0]));
@ -63,38 +68,6 @@ public class ConfigOptionSearchAndRunHandler extends CommandLine.RunLast {
return new ArrayList<>();
}
private Optional<File> findConfigFile(
final ParseResult parseResult, final CommandLine commandLine) {
if (parseResult.hasMatchedOption("--config-file")
&& environment.containsKey("BESU_CONFIG_FILE")) {
throw new ParameterException(
commandLine,
String.format(
"TOML file specified using BESU_CONFIG_FILE=%s and --config-file %s",
environment.get("BESU_CONFIG_FILE"),
parseResult.matchedOption("--config-file").stringValues()));
} else if (parseResult.hasMatchedOption("--config-file")) {
final OptionSpec configFileOption = parseResult.matchedOption("--config-file");
try {
return Optional.of(configFileOption.getter().get());
} catch (final Exception e) {
throw new ParameterException(commandLine, e.getMessage(), e);
}
} else if (environment.containsKey("BESU_CONFIG_FILE")) {
final File toml = new File(environment.get("BESU_CONFIG_FILE"));
if (!toml.exists()) {
throw new ParameterException(
commandLine,
String.format(
"TOML file %s specified in environment variable BESU_CONFIG_FILE not found",
environment.get("BESU_CONFIG_FILE")));
}
return Optional.of(toml);
}
return Optional.empty();
}
/**
* Create default value provider default value provider.
*
@ -104,14 +77,22 @@ public class ConfigOptionSearchAndRunHandler extends CommandLine.RunLast {
*/
@VisibleForTesting
IDefaultValueProvider createDefaultValueProvider(
final CommandLine commandLine, final Optional<File> configFile) {
if (configFile.isPresent()) {
return new CascadingDefaultProvider(
new EnvironmentVariableDefaultProvider(environment),
new TomlConfigFileDefaultProvider(commandLine, configFile.get()));
} else {
return new EnvironmentVariableDefaultProvider(environment);
}
final CommandLine commandLine,
final Optional<File> configFile,
final Optional<InputStream> profile) {
List<IDefaultValueProvider> providers = new ArrayList<>();
providers.add(new EnvironmentVariableDefaultProvider(environment));
configFile.ifPresent(
config -> {
if (config.exists()) {
providers.add(TomlConfigurationDefaultProvider.fromFile(commandLine, config));
}
});
profile.ifPresent(
p -> providers.add(TomlConfigurationDefaultProvider.fromInputStream(commandLine, p)));
return new CascadingDefaultProvider(providers);
}
@Override

@ -0,0 +1,76 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.util;
import static org.hyperledger.besu.cli.DefaultCommandValues.PROFILE_OPTION_NAME;
import org.hyperledger.besu.cli.config.ProfileName;
import java.io.InputStream;
import java.util.Map;
import java.util.Optional;
import picocli.CommandLine;
/**
* Class for finding profile configurations. This class extends the AbstractConfigurationFinder and
* provides methods for finding profile configurations based on command line options and environment
* variables. Each profile corresponds to a TOML configuration file that contains settings for
* various options. The profile to use can be specified with the '--profile' command line option or
* the 'BESU_PROFILE' environment variable.
*/
public class ProfileFinder extends AbstractConfigurationFinder<InputStream> {
private static final String PROFILE_ENV_NAME = "BESU_PROFILE";
@Override
protected String getConfigOptionName() {
return PROFILE_OPTION_NAME;
}
@Override
protected String getConfigEnvName() {
return PROFILE_ENV_NAME;
}
@Override
public Optional<InputStream> getFromOption(
final CommandLine.ParseResult parseResult, final CommandLine commandLine) {
try {
return getProfile(parseResult.matchedOption(PROFILE_OPTION_NAME).getter().get(), commandLine);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Optional<InputStream> getFromEnvironment(
final Map<String, String> environment, final CommandLine commandLine) {
return getProfile(ProfileName.valueOf(environment.get(PROFILE_ENV_NAME)), commandLine);
}
private static Optional<InputStream> getProfile(
final ProfileName profileName, final CommandLine commandLine) {
return Optional.of(getTomlFile(commandLine, profileName.getConfigFile()));
}
private static InputStream getTomlFile(final CommandLine commandLine, final String file) {
InputStream resourceUrl = ProfileFinder.class.getClassLoader().getResourceAsStream(file);
if (resourceUrl == null) {
throw new CommandLine.ParameterException(
commandLine, String.format("TOML file %s not found", file));
}
return resourceUrl;
}
}

@ -19,7 +19,10 @@ import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
@ -41,21 +44,52 @@ import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.ParameterException;
/** The Toml config file default value provider used by PicoCli. */
public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
public class TomlConfigurationDefaultProvider implements IDefaultValueProvider {
private final CommandLine commandLine;
private final File configFile;
private final InputStream configurationInputStream;
private TomlParseResult result;
/**
* Instantiates a new Toml config file default value provider.
*
* @param commandLine the command line
* @param configFile the config file
* @param configurationInputStream the input stream
*/
public TomlConfigFileDefaultProvider(final CommandLine commandLine, final File configFile) {
private TomlConfigurationDefaultProvider(
final CommandLine commandLine, final InputStream configurationInputStream) {
this.commandLine = commandLine;
this.configFile = configFile;
this.configurationInputStream = configurationInputStream;
}
/**
* Creates a new TomlConfigurationDefaultProvider from a file.
*
* @param commandLine the command line
* @param configFile the configuration file
* @return a new TomlConfigurationDefaultProvider
* @throws ParameterException if the configuration file is not found
*/
public static TomlConfigurationDefaultProvider fromFile(
final CommandLine commandLine, final File configFile) {
try {
return new TomlConfigurationDefaultProvider(commandLine, new FileInputStream(configFile));
} catch (final FileNotFoundException e) {
throw new ParameterException(
commandLine, "Unable to read TOML configuration, file not found.");
}
}
/**
* Creates a new TomlConfigurationDefaultProvider from an input stream.
*
* @param commandLine the command line
* @param inputStream the input stream
* @return a new TomlConfigurationDefaultProvider
*/
public static TomlConfigurationDefaultProvider fromInputStream(
final CommandLine commandLine, final InputStream inputStream) {
return new TomlConfigurationDefaultProvider(commandLine, inputStream);
}
@Override
@ -70,35 +104,32 @@ public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
private String getConfigurationValue(final OptionSpec optionSpec) {
// NOTE: This temporary fix is necessary to make certain options be treated as a multi-value.
// This can be done automatically by picocli if the object implements Collection.
final boolean isArray =
getKeyName(optionSpec).map(keyName -> result.isArray(keyName)).orElse(false);
final String defaultValue;
final boolean isArray = getKeyName(optionSpec).map(result::isArray).orElse(false);
// Convert config values to the right string representation for default string value
if (optionSpec.type().equals(Boolean.class) || optionSpec.type().equals(boolean.class)) {
defaultValue = getBooleanEntryAsString(optionSpec);
return getBooleanEntryAsString(optionSpec);
} else if (optionSpec.isMultiValue() || isArray) {
defaultValue = getListEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Integer.class) || optionSpec.type().equals(int.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Long.class) || optionSpec.type().equals(long.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Wei.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(BigInteger.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Double.class) || optionSpec.type().equals(double.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Float.class) || optionSpec.type().equals(float.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Percentage.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
} else if (optionSpec.type().equals(Fraction.class)) {
defaultValue = getNumericEntryAsString(optionSpec);
return getListEntryAsString(optionSpec);
} else if (isNumericType(optionSpec.type())) {
return getNumericEntryAsString(optionSpec);
} else { // else will be treated as String
defaultValue = getEntryAsString(optionSpec);
return getEntryAsString(optionSpec);
}
return defaultValue;
}
private boolean isNumericType(final Class<?> type) {
return type.equals(Integer.class)
|| type.equals(int.class)
|| type.equals(Long.class)
|| type.equals(long.class)
|| type.equals(Wei.class)
|| type.equals(BigInteger.class)
|| type.equals(Double.class)
|| type.equals(double.class)
|| type.equals(Float.class)
|| type.equals(float.class)
|| type.equals(Percentage.class)
|| type.equals(Fraction.class);
}
private String getEntryAsString(final OptionSpec spec) {
@ -195,7 +226,8 @@ public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
private void checkConfigurationValidity() {
if (result == null || result.isEmpty())
throw new ParameterException(
commandLine, String.format("Unable to read TOML configuration file %s", configFile));
commandLine,
String.format("Unable to read TOML configuration file %s", configurationInputStream));
}
/** Load configuration from file. */
@ -203,7 +235,7 @@ public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
if (result == null) {
try {
final TomlParseResult result = Toml.parse(configFile.toPath());
final TomlParseResult result = Toml.parse(configurationInputStream);
if (result.hasErrors()) {
final String errors =
@ -224,7 +256,6 @@ public class TomlConfigFileDefaultProvider implements IDefaultValueProvider {
commandLine, "Unable to read TOML configuration, file not found.");
}
}
checkConfigurationValidity();
}

@ -15,7 +15,6 @@
package org.hyperledger.besu.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;
@ -33,7 +32,6 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ENGINE;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ETH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.NET;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.PERM;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.WEB3;
import static org.hyperledger.besu.ethereum.p2p.config.DefaultDiscoveryConfiguration.GOERLI_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DefaultDiscoveryConfiguration.GOERLI_DISCOVERY_URL;
import static org.hyperledger.besu.ethereum.p2p.config.DefaultDiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES;
@ -71,8 +69,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
@ -123,7 +119,6 @@ import com.google.common.collect.Lists;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.toml.Toml;
import org.apache.tuweni.toml.TomlParseResult;
@ -306,17 +301,18 @@ public class BesuCommandTest extends CommandTestAbstract {
final Path tempConfigFilePath = createTempFile("an-invalid-file-name-without-extension", "");
parseCommand("--config-file", tempConfigFilePath.toString());
final String expectedOutputStart =
"Unable to read TOML configuration file " + tempConfigFilePath;
final String expectedOutputStart = "Unable to read TOML configuration file";
assertThat(commandErrorOutput.toString(UTF_8)).startsWith(expectedOutputStart);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@Test
public void callingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() {
parseCommand("--config-file", "./an-invalid-file-name-sdsd87sjhqoi34io23.toml");
String invalidFile = "./an-invalid-file-name-sdsd87sjhqoi34io23.toml";
parseCommand("--config-file", invalidFile);
final String expectedOutputStart = "Unable to read TOML configuration, file not found.";
final String expectedOutputStart =
String.format("Unable to read TOML configuration, file not found: %s", invalidFile);
assertThat(commandErrorOutput.toString(UTF_8)).startsWith(expectedOutputStart);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@ -364,81 +360,6 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}
@Test
public void overrideDefaultValuesIfKeyIsPresentInConfigFile(final @TempDir File dataFolder)
throws IOException {
final URL configFile = this.getClass().getResource("/complete_config.toml");
final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON);
final String updatedConfig =
Resources.toString(configFile, UTF_8)
.replace("/opt/besu/genesis.json", escapeTomlString(genesisFile.toString()))
.replace(
"data-path=\"/opt/besu\"",
"data-path=\"" + escapeTomlString(dataFolder.getPath()) + "\"");
final Path toml = createTempFile("toml", updatedConfig.getBytes(UTF_8));
final List<String> expectedApis = asList(ETH.name(), WEB3.name());
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(false);
jsonRpcConfiguration.setHost("5.6.7.8");
jsonRpcConfiguration.setPort(5678);
jsonRpcConfiguration.setCorsAllowedDomains(Collections.emptyList());
jsonRpcConfiguration.setRpcApis(expectedApis);
jsonRpcConfiguration.setMaxActiveConnections(1000);
final GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration.createDefault();
graphQLConfiguration.setEnabled(false);
graphQLConfiguration.setHost("6.7.8.9");
graphQLConfiguration.setPort(6789);
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(false);
webSocketConfiguration.setHost("9.10.11.12");
webSocketConfiguration.setPort(9101);
webSocketConfiguration.setRpcApis(expectedApis);
final MetricsConfiguration metricsConfiguration =
MetricsConfiguration.builder().enabled(false).host("8.6.7.5").port(309).build();
parseCommand("--config-file", toml.toString());
verify(mockRunnerBuilder).discovery(eq(false));
verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4"));
verify(mockRunnerBuilder).p2pListenPort(eq(1234));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
verify(mockRunnerBuilder).metricsConfiguration(eq(metricsConfiguration));
verify(mockRunnerBuilder).build();
final List<EnodeURL> nodes =
asList(
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"),
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"),
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"));
assertThat(ethNetworkConfigArgumentCaptor.getValue().getBootNodes()).isEqualTo(nodes);
final EthNetworkConfig networkConfig =
new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(MAINNET))
.setNetworkId(BigInteger.valueOf(42))
.setGenesisConfig(encodeJsonGenesis(GENESIS_VALID_JSON))
.setBootNodes(nodes)
.setDnsDiscoveryUrl(null)
.build();
verify(mockControllerBuilder).dataDirectory(eq(dataFolder.toPath()));
verify(mockControllerBuilderFactory).fromEthNetworkConfig(eq(networkConfig), any(), any());
verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture());
assertThat(syncConfigurationCaptor.getValue().getSyncMode()).isEqualTo(SyncMode.FAST);
assertThat(syncConfigurationCaptor.getValue().getFastSyncMinimumPeerCount()).isEqualTo(13);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void nodePermissionsSmartContractWithoutOptionMustError() {
parseCommand("--permissions-nodes-contract-address");
@ -861,81 +782,6 @@ public class BesuCommandTest extends CommandTestAbstract {
.isEmpty();
}
@Test
public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
parseCommand("--config-file", configFile);
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
final GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration.createDefault();
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
final MetricsConfiguration metricsConfiguration = MetricsConfiguration.builder().build();
verify(mockRunnerBuilder).discovery(eq(true));
verify(mockRunnerBuilder)
.ethNetworkConfig(
new EthNetworkConfig(
EthNetworkConfig.jsonConfig(MAINNET),
MAINNET.getNetworkId(),
MAINNET_BOOTSTRAP_NODES,
MAINNET_DISCOVERY_URL));
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
verify(mockRunnerBuilder).p2pListenPort(eq(30303));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
verify(mockRunnerBuilder).metricsConfiguration(eq(metricsConfiguration));
verify(mockRunnerBuilder).build();
verify(mockControllerBuilder).build();
verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture());
final SynchronizerConfiguration syncConfig = syncConfigurationCaptor.getValue();
assertThat(syncConfig.getSyncMode()).isEqualTo(SyncMode.FAST);
assertThat(syncConfig.getFastSyncMinimumPeerCount()).isEqualTo(5);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void envVariableOverridesValueFromConfigFile() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
final String expectedCoinbase = "0x0000000000000000000000000000000000000004";
setEnvironmentVariable("BESU_MINER_COINBASE", expectedCoinbase);
parseCommand("--config-file", configFile);
verify(mockControllerBuilder)
.miningParameters(
ImmutableMiningParameters.builder()
.mutableInitValues(
MutableInitValues.builder()
.coinbase(Address.fromHexString(expectedCoinbase))
.build())
.build());
}
@Test
public void cliOptionOverridesEnvVariableAndConfig() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
final String expectedCoinbase = "0x0000000000000000000000000000000000000006";
setEnvironmentVariable("BESU_MINER_COINBASE", "0x0000000000000000000000000000000000000004");
parseCommand("--config-file", configFile, "--miner-coinbase", expectedCoinbase);
verify(mockControllerBuilder)
.miningParameters(
ImmutableMiningParameters.builder()
.mutableInitValues(
MutableInitValues.builder()
.coinbase(Address.fromHexString(expectedCoinbase))
.build())
.build());
}
@Test
public void nodekeyOptionMustBeUsed() throws Exception {
final File file = new File("./specific/enclavePrivateKey");
@ -4300,10 +4146,6 @@ public class BesuCommandTest extends CommandTestAbstract {
"No Payload Provider has been provided. You must register one when enabling privacy plugin!");
}
private static String escapeTomlString(final String s) {
return StringEscapeUtils.escapeJava(s);
}
/**
* Check logger calls
*

@ -0,0 +1,306 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.cli.config.NetworkName.DEV;
import static org.hyperledger.besu.cli.config.NetworkName.MAINNET;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ETH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.WEB3;
import static org.hyperledger.besu.ethereum.p2p.config.DefaultDiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES;
import static org.hyperledger.besu.ethereum.p2p.config.DefaultDiscoveryConfiguration.MAINNET_DISCOVERY_URL;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.plugin.data.EnodeURL;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URL;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
public class CascadingDefaultProviderTest extends CommandTestAbstract {
private static final int GENESIS_CONFIG_TEST_CHAINID = 3141592;
private static final String VALID_NODE_ID =
"6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0";
private static final JsonObject GENESIS_VALID_JSON =
(new JsonObject())
.put("config", (new JsonObject()).put("chainId", GENESIS_CONFIG_TEST_CHAINID));
/**
* Test if the default values are overridden if the key is present in the configuration file. The
* test checks if the configuration file correctly overrides the default values for various
* settings, such as the JSON-RPC configuration, GraphQL configuration, WebSocket configuration,
* and metrics configuration.
*/
@Test
public void overrideDefaultValuesIfKeyIsPresentInConfigFile(final @TempDir File dataFolder)
throws IOException {
final URL configFile = this.getClass().getResource("/complete_config.toml");
final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON);
final String updatedConfig =
Resources.toString(configFile, UTF_8)
.replace("/opt/besu/genesis.json", escapeTomlString(genesisFile.toString()))
.replace(
"data-path=\"/opt/besu\"",
"data-path=\"" + escapeTomlString(dataFolder.getPath()) + "\"");
final Path toml = createTempFile("toml", updatedConfig.getBytes(UTF_8));
final List<String> expectedApis = asList(ETH.name(), WEB3.name());
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(false);
jsonRpcConfiguration.setHost("5.6.7.8");
jsonRpcConfiguration.setPort(5678);
jsonRpcConfiguration.setCorsAllowedDomains(Collections.emptyList());
jsonRpcConfiguration.setRpcApis(expectedApis);
jsonRpcConfiguration.setMaxActiveConnections(1000);
final GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration.createDefault();
graphQLConfiguration.setEnabled(false);
graphQLConfiguration.setHost("6.7.8.9");
graphQLConfiguration.setPort(6789);
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(false);
webSocketConfiguration.setHost("9.10.11.12");
webSocketConfiguration.setPort(9101);
webSocketConfiguration.setRpcApis(expectedApis);
final MetricsConfiguration metricsConfiguration =
MetricsConfiguration.builder().enabled(false).host("8.6.7.5").port(309).build();
parseCommand("--config-file", toml.toString());
verify(mockRunnerBuilder).discovery(eq(false));
verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("1.2.3.4"));
verify(mockRunnerBuilder).p2pListenPort(eq(1234));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
verify(mockRunnerBuilder).metricsConfiguration(eq(metricsConfiguration));
verify(mockRunnerBuilder).build();
final List<EnodeURL> nodes =
asList(
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"),
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"),
EnodeURLImpl.fromString("enode://" + VALID_NODE_ID + "@192.168.0.1:4567"));
assertThat(ethNetworkConfigArgumentCaptor.getValue().getBootNodes()).isEqualTo(nodes);
final EthNetworkConfig networkConfig =
new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(MAINNET))
.setNetworkId(BigInteger.valueOf(42))
.setGenesisConfig(encodeJsonGenesis(GENESIS_VALID_JSON))
.setBootNodes(nodes)
.setDnsDiscoveryUrl(null)
.build();
verify(mockControllerBuilder).dataDirectory(eq(dataFolder.toPath()));
verify(mockControllerBuilderFactory).fromEthNetworkConfig(eq(networkConfig), any(), any());
verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture());
assertThat(syncConfigurationCaptor.getValue().getSyncMode()).isEqualTo(SyncMode.FAST);
assertThat(syncConfigurationCaptor.getValue().getFastSyncMinimumPeerCount()).isEqualTo(13);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
/**
* Test if the default values are not overridden if the key is not present in the configuration
* file. The test checks if the default values for various settings remain unchanged when the
* corresponding keys are not present in the configuration file.
*/
@Test
public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
parseCommand("--config-file", configFile);
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
final GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration.createDefault();
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
final MetricsConfiguration metricsConfiguration = MetricsConfiguration.builder().build();
verify(mockRunnerBuilder).discovery(eq(true));
verify(mockRunnerBuilder)
.ethNetworkConfig(
new EthNetworkConfig(
EthNetworkConfig.jsonConfig(MAINNET),
MAINNET.getNetworkId(),
MAINNET_BOOTSTRAP_NODES,
MAINNET_DISCOVERY_URL));
verify(mockRunnerBuilder).p2pAdvertisedHost(eq("127.0.0.1"));
verify(mockRunnerBuilder).p2pListenPort(eq(30303));
verify(mockRunnerBuilder).jsonRpcConfiguration(eq(jsonRpcConfiguration));
verify(mockRunnerBuilder).graphQLConfiguration(eq(graphQLConfiguration));
verify(mockRunnerBuilder).webSocketConfiguration(eq(webSocketConfiguration));
verify(mockRunnerBuilder).metricsConfiguration(eq(metricsConfiguration));
verify(mockRunnerBuilder).build();
verify(mockControllerBuilder).build();
verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture());
final SynchronizerConfiguration syncConfig = syncConfigurationCaptor.getValue();
assertThat(syncConfig.getSyncMode()).isEqualTo(SyncMode.FAST);
assertThat(syncConfig.getFastSyncMinimumPeerCount()).isEqualTo(5);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
/**
* Test if the environment variable overrides the value from the configuration file. The test
* checks if the value of the miner's coinbase address set through an environment variable
* correctly overrides the value specified in the configuration file.
*/
@Test
public void envVariableOverridesValueFromConfigFile() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
final String expectedCoinbase = "0x0000000000000000000000000000000000000004";
setEnvironmentVariable("BESU_MINER_COINBASE", expectedCoinbase);
parseCommand("--config-file", configFile);
verify(mockControllerBuilder)
.miningParameters(
ImmutableMiningParameters.builder()
.mutableInitValues(
ImmutableMiningParameters.MutableInitValues.builder()
.coinbase(Address.fromHexString(expectedCoinbase))
.build())
.build());
}
/**
* Test if the command line option overrides the environment variable and configuration. The test
* checks if the value of the miner's coinbase address set through a command line option correctly
* overrides the value specified in the environment variable and the configuration file.
*/
@Test
public void cliOptionOverridesEnvVariableAndConfig() {
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
final String expectedCoinbase = "0x0000000000000000000000000000000000000006";
setEnvironmentVariable("BESU_MINER_COINBASE", "0x0000000000000000000000000000000000000004");
parseCommand("--config-file", configFile, "--miner-coinbase", expectedCoinbase);
verify(mockControllerBuilder)
.miningParameters(
ImmutableMiningParameters.builder()
.mutableInitValues(
ImmutableMiningParameters.MutableInitValues.builder()
.coinbase(Address.fromHexString(expectedCoinbase))
.build())
.build());
}
/**
* Test if the profile option sets the correct defaults. The test checks if the 'dev' profile
* correctly sets the network ID to the expected value.
*/
@Test
public void profileOptionShouldSetCorrectDefaults() {
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
parseCommand("--profile", "dev");
verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any(), any());
verify(mockControllerBuilder).build();
final EthNetworkConfig config = networkArg.getValue();
assertThat(config.getNetworkId()).isEqualTo(DEV.getNetworkId());
}
/**
* Test if the command line option overrides the profile configuration. The test checks if the
* network ID set through a command line option correctly overrides the value specified in the
* 'dev' profile.
*/
@Test
public void cliOptionOverridesProfileConfiguration() {
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
parseCommand("--profile", "dev", "--network", "MAINNET");
verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any(), any());
verify(mockControllerBuilder).build();
final EthNetworkConfig config = networkArg.getValue();
assertThat(config.getNetworkId()).isEqualTo(MAINNET.getNetworkId());
}
/**
* Test if the configuration file overrides the profile configuration. The test checks if the
* network ID specified in the configuration file correctly overrides the value specified in the
* 'dev' profile.
*/
@Test
public void configFileOverridesProfileConfiguration() {
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
final String configFile = this.getClass().getResource("/partial_config.toml").getFile();
parseCommand("--profile", "dev", "--config-file", configFile);
verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any(), any());
verify(mockControllerBuilder).build();
final EthNetworkConfig config = networkArg.getValue();
assertThat(config.getNetworkId()).isEqualTo(MAINNET.getNetworkId());
}
/**
* Test if the environment variable overrides the profile configuration. The test checks if the
* network ID set through an environment variable correctly overrides the value specified in the
* 'dev' profile.
*/
@Test
public void environmentVariableOverridesProfileConfiguration() {
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
setEnvironmentVariable("BESU_NETWORK", "MAINNET");
parseCommand("--profile", "dev");
verify(mockControllerBuilderFactory).fromEthNetworkConfig(networkArg.capture(), any(), any());
verify(mockControllerBuilder).build();
final EthNetworkConfig config = networkArg.getValue();
assertThat(config.getNetworkId()).isEqualTo(MAINNET.getNetworkId());
}
}

@ -22,7 +22,7 @@ import static picocli.CommandLine.defaultExceptionHandler;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.cli.util.EnvironmentVariableDefaultProvider;
import org.hyperledger.besu.cli.util.TomlConfigFileDefaultProvider;
import org.hyperledger.besu.cli.util.TomlConfigurationDefaultProvider;
import org.hyperledger.besu.util.StringUtils;
import java.io.IOException;
@ -252,7 +252,7 @@ public class CommandLineUtilsTest {
final AbstractTestCommand testCommand = new TestMultiCommandWithDeps(mockLogger);
testCommand.commandLine.setDefaultValueProvider(
new TomlConfigFileDefaultProvider(testCommand.commandLine, toml.toFile()));
TomlConfigurationDefaultProvider.fromFile(testCommand.commandLine, toml.toFile()));
testCommand.commandLine.parseWithHandlers(new RunLast(), defaultExceptionHandler());
verifyMultiOptionsConstraintLoggerCall(

@ -109,6 +109,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.JsonObject;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.awaitility.Awaitility;
@ -682,4 +683,8 @@ public abstract class CommandTestAbstract {
PORT_CHECK,
NO_PORT_CHECK
}
protected static String escapeTomlString(final String s) {
return StringEscapeUtils.escapeJava(s);
}
}

@ -20,6 +20,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConf
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.SEQUENCED;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.cli.config.ProfileName;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
@ -209,4 +210,11 @@ class ConfigurationOverviewBuilderTest {
final String layeredTxPoolSelected = builder.build();
assertThat(layeredTxPoolSelected).contains("Using JOURNALED worldstate update mode");
}
@Test
void setProfile() {
builder.setProfile(ProfileName.DEV.name());
final String profileSelected = builder.build();
assertThat(profileSelected).contains("Profile: DEV");
}
}

@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.cli.util.TomlConfigFileDefaultProvider;
import org.hyperledger.besu.cli.util.TomlConfigurationDefaultProvider;
import org.hyperledger.besu.datatypes.Wei;
import java.io.BufferedWriter;
@ -42,7 +42,7 @@ import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.ParameterException;
@ExtendWith(MockitoExtension.class)
public class TomlConfigFileDefaultProviderTest {
public class TomlConfigurationDefaultProviderTest {
@Mock CommandLine mockCommandLine;
@Mock CommandSpec mockCommandSpec;
@ -67,8 +67,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.write("a-longer-option='1234'");
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
// this option must be found in config
assertThat(
@ -152,8 +152,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.write("a-double-value-option-int=1"); // should be able to parse int as double
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThat(
providerUnderTest.defaultValue(
@ -221,16 +221,9 @@ public class TomlConfigFileDefaultProviderTest {
@Test
public void configFileNotFoundMustThrow() {
final File nonExistingFile = new File("doesnt.exit");
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, nonExistingFile);
assertThatThrownBy(
() ->
providerUnderTest.defaultValue(
OptionSpec.builder("an-option").type(String.class).build()))
() -> TomlConfigurationDefaultProvider.fromFile(mockCommandLine, nonExistingFile))
.isInstanceOf(ParameterException.class)
.hasMessage("Unable to read TOML configuration, file not found.");
}
@ -240,8 +233,8 @@ public class TomlConfigFileDefaultProviderTest {
final File tempConfigFile = Files.createTempFile("invalid", "toml").toFile();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThatThrownBy(
() ->
@ -260,8 +253,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.write("an-invalid-syntax=======....");
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThatThrownBy(
() ->
@ -286,8 +279,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.write("invalid_option=true");
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThatThrownBy(
() ->
@ -321,8 +314,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.newLine();
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThat(
providerUnderTest.defaultValue(
@ -361,8 +354,8 @@ public class TomlConfigFileDefaultProviderTest {
fileWriter.newLine();
fileWriter.flush();
final TomlConfigFileDefaultProvider providerUnderTest =
new TomlConfigFileDefaultProvider(mockCommandLine, tempConfigFile);
final TomlConfigurationDefaultProvider providerUnderTest =
TomlConfigurationDefaultProvider.fromFile(mockCommandLine, tempConfigFile);
assertThatThrownBy(
() ->

@ -145,7 +145,7 @@ public class ConfigOptionSearchAndRunHandlerTest {
public void shouldRetrieveConfigFromEnvironmentWhenConfigFileSpecified() throws Exception {
final IDefaultValueProvider defaultValueProvider =
configParsingHandler.createDefaultValueProvider(
mockCommandLine, Optional.of(new File("foo")));
mockCommandLine, Optional.of(new File("foo")), Optional.empty());
final String value = defaultValueProvider.defaultValue(OptionSpec.builder("--logging").build());
assertThat(value).isEqualTo("ERROR");
}
@ -153,7 +153,8 @@ public class ConfigOptionSearchAndRunHandlerTest {
@Test
public void shouldRetrieveConfigFromEnvironmentWhenConfigFileNotSpecified() throws Exception {
final IDefaultValueProvider defaultValueProvider =
configParsingHandler.createDefaultValueProvider(mockCommandLine, Optional.empty());
configParsingHandler.createDefaultValueProvider(
mockCommandLine, Optional.empty(), Optional.empty());
final String value = defaultValueProvider.defaultValue(OptionSpec.builder("--logging").build());
assertThat(value).isEqualTo("ERROR");
}

@ -16,7 +16,7 @@ node-private-key-file="./path/to/privateKey"
pid-path="~/.pid"
reorg-logging-threshold=0
static-nodes-file="~/besudata/static-nodes.json"
profile="NONE"
# Security Module plugin to use
security-module="localfile"

@ -1,4 +1,7 @@
# this is a valid partial TOML config file
#mining
miner-coinbase="0x0000000000000000000000000000000000000002"
miner-coinbase="0x0000000000000000000000000000000000000002"
#network
network="mainnet"
Loading…
Cancel
Save