Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Avoid port conflicts in acceptance tests #1025

Merged
merged 27 commits into from
Mar 4, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c3dad3f
Launch bootnode first.
ajsutton Mar 1, 2019
e455d6d
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 1, 2019
8c133f2
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 1, 2019
04e052b
Report the correct tcp port in PING packets when it differs from the …
ajsutton Mar 1, 2019
2ff99a1
Set network ID and genesis for the selected bootnode correctly.
ajsutton Mar 1, 2019
8b8cbf7
Only add discovery port to enode URI if discovery is enabled.
ajsutton Mar 1, 2019
3387ab6
Start P2P network synchronously so the ports are guaranteed to be kno…
ajsutton Mar 1, 2019
bf4b3c8
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 1, 2019
6d9573e
Disable trace logging and fix discport arg.
ajsutton Mar 1, 2019
3d1e0f4
Fix race condition in WebSocketService.
ajsutton Mar 1, 2019
3c436e3
Adjust AdminAddPeerWithP2PDisabledAT to just check that adding a enod…
ajsutton Mar 3, 2019
aa6ce89
Fix NodesWhitelistAcceptanceTest.
ajsutton Mar 3, 2019
d344576
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 3, 2019
543974d
Remove isBootnode config.
ajsutton Mar 3, 2019
0cbd066
boolean not Boolean.
ajsutton Mar 3, 2019
2f73059
Improve log message.
ajsutton Mar 3, 2019
5c4da31
Make things more readable.
ajsutton Mar 3, 2019
14f02fd
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 4, 2019
91bb674
Use URI for bootnodes in preference to String.
ajsutton Mar 4, 2019
cbcab46
Remove EthNetworkConfig from NodeConfiguration and just specify genes…
ajsutton Mar 4, 2019
d8c3537
Fix naming.
ajsutton Mar 4, 2019
4ea8ccd
Merge branch 'master' into port-conflicts
ajsutton Mar 4, 2019
7f77ba3
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 4, 2019
ab2ffe5
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 4, 2019
7e11052
Add explicit flag to prevent a node being used as the bootnode.
ajsutton Mar 4, 2019
5d6f7b1
Default to allowing nodes to be bootnodes.
ajsutton Mar 4, 2019
a49b273
Merge branch 'master' of github.com:PegaSysEng/pantheon into port-con…
ajsutton Mar 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
*/
package tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc;

import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor;
import static java.util.Arrays.asList;

import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor.WHITELIST_TYPE;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.AddAccountsToWhitelistSuccessfully;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.AddNodeSuccess;
Expand All @@ -21,12 +23,14 @@
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.RemoveAccountsFromWhitelistSuccessfully;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.RemoveNodeSuccess;
import tech.pegasys.pantheon.tests.acceptance.dsl.condition.perm.WhiteListContainsKeyAndValue;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.RunnableNode;
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Perm {

Expand All @@ -47,14 +51,32 @@ public Condition removeAccountsFromWhitelist(final String... accounts) {

public Condition expectAccountsWhitelist(final String... expectedAccounts) {
return new GetExpectedAccountsWhitelist(
transactions.getAccountsWhiteList(), Arrays.asList(expectedAccounts));
transactions.getAccountsWhiteList(), asList(expectedAccounts));
}

public Condition addNodesToWhitelist(final String... nodes) {
return addNodesToWhitelist(asList(nodes));
}

public Condition addNodesToWhitelist(final List<String> enodeList) {
public Condition addNodesToWhitelist(final Node... nodes) {
final List<String> enodeList = toEnodeUris(nodes);
return addNodesToWhitelist(enodeList);
}

private Condition addNodesToWhitelist(final List<String> enodeList) {
return new AddNodeSuccess(transactions.addNodesToWhitelist(enodeList));
}

public Condition removeNodesFromWhitelist(final List<String> enodeList) {
public Condition removeNodesFromWhitelist(final String... nodes) {
return removeNodesFromWhitelist(asList(nodes));
}

public Condition removeNodesFromWhitelist(final Node... nodes) {
final List<String> enodeList = toEnodeUris(nodes);
return removeNodesFromWhitelist(enodeList);
}

private Condition removeNodesFromWhitelist(final List<String> enodeList) {
return new RemoveNodeSuccess(transactions.removeNodesFromWhitelist(enodeList));
}

Expand All @@ -63,9 +85,14 @@ public Condition getNodesWhitelist(final int expectedNodeNum) {
}

public Condition expectPermissioningWhitelistFileKeyValue(
final WhitelistPersistor.WHITELIST_TYPE whitelistType,
final Collection<String> val,
final Path configFilePath) {
return new WhiteListContainsKeyAndValue(whitelistType, val, configFilePath);
final WHITELIST_TYPE whitelistType, final Path configFilePath, final String... val) {
return new WhiteListContainsKeyAndValue(whitelistType, asList(val), configFilePath);
}

private List<String> toEnodeUris(final Node[] nodes) {
return Stream.of(nodes)
.map(node -> (RunnableNode) node)
.map(RunnableNode::enodeUrl)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition;

public interface Node {

<T> T execute(Transaction<T> transaction);

<T> T executeHttpTransaction(HttpTransaction<T> transaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@

public interface NodeConfiguration {

String enodeUrl();

void bootnodes(List<String> bootnodes);

List<URI> bootnodes();
Expand All @@ -42,5 +40,7 @@ public interface NodeConfiguration {

void ethNetworkConfig(Optional<EthNetworkConfig> ethNetworkConfig);

boolean isBootnode();
boolean isP2pEnabled();

boolean isDiscoveryEnabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,13 @@
import org.web3j.protocol.websocket.WebSocketService;
import org.web3j.utils.Async;

public class PantheonNode implements Node, NodeConfiguration, RunnableNode, AutoCloseable {
ajsutton marked this conversation as resolved.
Show resolved Hide resolved
public class PantheonNode implements NodeConfiguration, RunnableNode, AutoCloseable {

private static final String LOCALHOST = "127.0.0.1";
private static final Logger LOG = getLogger();

private final Path homeDirectory;
private final KeyPair keyPair;
private final int p2pPort;
private final Properties portsProperties = new Properties();
private final Boolean p2pEnabled;

Expand All @@ -88,7 +87,6 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private final GenesisConfigProvider genesisConfigProvider;
private final boolean devMode;
private final boolean discoveryEnabled;
private final boolean isBootnode;
private final List<String> bootnodes;

private JsonRequestFactories jsonRequestFactories;
Expand All @@ -107,15 +105,12 @@ public PantheonNode(
final Optional<PermissioningConfiguration> permissioningConfiguration,
final boolean devMode,
final GenesisConfigProvider genesisConfigProvider,
final int p2pPort,
final Boolean p2pEnabled,
final boolean discoveryEnabled,
final boolean isBootnode,
final List<String> bootnodes)
throws IOException {
this.homeDirectory = Files.createTempDirectory("acctest");
this.keyPair = KeyPairUtil.loadKeyPair(homeDirectory);
this.p2pPort = p2pPort;
this.name = name;
this.miningParameters = miningParameters;
this.privacyParameters = privacyParameters;
Expand All @@ -127,7 +122,6 @@ public PantheonNode(
this.devMode = devMode;
this.p2pEnabled = p2pEnabled;
this.discoveryEnabled = discoveryEnabled;
this.isBootnode = isBootnode;
this.bootnodes = bootnodes;
LOG.info("Created PantheonNode {}", this.toString());
}
Expand All @@ -147,12 +141,31 @@ public String getName() {

@Override
public String enodeUrl() {
final String discport = isDiscoveryEnabled() ? "?discport=" + getDiscoveryPort() : "";
return "enode://"
+ keyPair.getPublicKey().toString().substring(2)
+ "@"
+ LOCALHOST
+ ":"
+ p2pPort;
+ getP2pPort()
+ discport;
}

private String getP2pPort() {
final String port = portsProperties.getProperty("p2p");
if (port == null) {
throw new IllegalStateException("Requested p2p port before ports properties was written");
}
return port;
}

private String getDiscoveryPort() {
final String port = portsProperties.getProperty("discovery");
if (port == null) {
throw new IllegalStateException(
"Requested discovery port before ports properties was written");
}
return port;
}

private Optional<String> jsonRpcBaseUrl() {
Expand Down Expand Up @@ -218,7 +231,7 @@ private JsonRequestFactories jsonRequestFactories() {
web3jService = new WebSocketService(wsClient, false);
try {
((WebSocketService) web3jService).connect();
} catch (ConnectException e) {
} catch (final ConnectException e) {
throw new RuntimeException(e);
}

Expand Down Expand Up @@ -299,10 +312,6 @@ public void useAuthenticationTokenInHeaderForJsonRpc(final String token) {
this.token = token;
}

public KeyPair getKeyPair() {
return keyPair;
}

private void checkIfWebSocketEndpointIsAvailable(final String url) {
final WebSocketClient webSocketClient = new WebSocketClient(URI.create(url));
// Web3j implementation always invoke the listener (even when one hasn't been set). We are using
Expand Down Expand Up @@ -359,6 +368,7 @@ private void loadPortsFile() {
try (final FileInputStream fis =
new FileInputStream(new File(homeDirectory.toFile(), "pantheon.ports"))) {
portsProperties.load(fis);
LOG.info("Ports for node {}: {}", name, portsProperties);
} catch (final IOException e) {
throw new RuntimeException("Error reading Pantheon ports file", e);
}
Expand Down Expand Up @@ -418,27 +428,17 @@ MetricsConfiguration metricsConfiguration() {
return metricsConfiguration;
}

int p2pPort() {
return p2pPort;
}

String p2pListenHost() {
return LOCALHOST;
}

int p2pListenPort() {
return p2pPort;
}

@Override
public List<URI> bootnodes() {
return bootnodes.stream()
.filter(node -> !node.equals(this.enodeUrl()))
.map(URI::create)
.collect(Collectors.toList());
return bootnodes.stream().map(URI::create).collect(Collectors.toList());
}

Boolean p2pEnabled() {
@Override
public boolean isP2pEnabled() {
return p2pEnabled;
}

Expand All @@ -460,15 +460,11 @@ public boolean isDevMode() {
return devMode;
}

@Override
public boolean isDiscoveryEnabled() {
return discoveryEnabled;
}

@Override
public boolean isBootnode() {
return isBootnode;
}

Optional<PermissioningConfiguration> getPermissioningConfiguration() {
return permissioningConfiguration;
}
Expand All @@ -477,7 +473,6 @@ Optional<PermissioningConfiguration> getPermissioningConfiguration() {
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("p2pPort", p2pPort)
.add("homeDirectory", homeDirectory)
.add("keyPair", keyPair)
.add("p2pEnabled", p2pEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ProcessBuilder.Redirect;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -28,6 +31,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

Expand All @@ -38,8 +43,10 @@
public class ProcessPantheonNodeRunner implements PantheonNodeRunner {

private final Logger LOG = LogManager.getLogger();
private final Logger PROCESS_LOG = LogManager.getLogger("tech.pegasys.pantheon.SubProcessLog");

private final Map<String, Process> pantheonProcesses = new HashMap<>();
private final ExecutorService outputProcessorExecutor = Executors.newCachedThreadPool();

ProcessPantheonNodeRunner() {
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
Expand All @@ -51,6 +58,7 @@ public void startNode(final PantheonNode node) {

final List<String> params = new ArrayList<>();
params.add("build/install/pantheon/bin/pantheon");

params.add("--data-path");
params.add(dataDir.toAbsolutePath().toString());

Expand All @@ -66,7 +74,7 @@ public void startNode(final PantheonNode node) {
params.add(node.p2pListenHost());

params.add("--p2p-port");
params.add(Integer.toString(node.p2pListenPort()));
params.add("0");

if (node.getMiningParameters().isMiningEnabled()) {
params.add("--miner-enabled");
Expand Down Expand Up @@ -133,7 +141,7 @@ public void startNode(final PantheonNode node) {
params.add(Integer.toString(ethNetworkConfig.getNetworkId()));
}

if (!node.p2pEnabled()) {
if (!node.isP2pEnabled()) {
params.add("--p2p-enabled");
params.add("false");
}
Expand All @@ -157,10 +165,12 @@ public void startNode(final PantheonNode node) {
final ProcessBuilder processBuilder =
new ProcessBuilder(params)
.directory(new File(System.getProperty("user.dir")).getParentFile())
.inheritIO();
.redirectErrorStream(true)
.redirectInput(Redirect.INHERIT);

try {
final Process process = processBuilder.start();
outputProcessorExecutor.submit(() -> printOutput(node, process));
pantheonProcesses.put(node.getName(), process);
} catch (final IOException e) {
LOG.error("Error starting PantheonNode process", e);
Expand All @@ -169,6 +179,19 @@ public void startNode(final PantheonNode node) {
waitForPortsFile(dataDir);
}

private void printOutput(final PantheonNode node, final Process process) {
try (final BufferedReader in =
new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8))) {
String line = in.readLine();
while (line != null) {
PROCESS_LOG.info("{}: {}", node.getName(), line);
line = in.readLine();
}
} catch (final IOException e) {
LOG.error("Failed to read output from process", e);
}
}

private Path createGenesisFile(final PantheonNode node, final EthNetworkConfig ethNetworkConfig) {
try {
final Path genesisFile = Files.createTempFile(node.homeDirectory(), "genesis", "");
Expand Down Expand Up @@ -197,6 +220,15 @@ public void stopNode(final PantheonNode node) {
public synchronized void shutdown() {
final HashMap<String, Process> localMap = new HashMap<>(pantheonProcesses);
localMap.forEach(this::killPantheonProcess);
outputProcessorExecutor.shutdown();
try {
if (!outputProcessorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
LOG.error("Output processor executor did not shutdown cleanly.");
}
} catch (final InterruptedException e) {
LOG.error("Interrupted while already shutting down", e);
Thread.currentThread().interrupt();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public interface RunnableNode extends Node {
String getName();

Address getAddress();

String enodeUrl();
}
Loading