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

Plugin Framework #1435

Merged
merged 22 commits into from
May 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
49ebfc3
rough implementation
shemnon May 10, 2019
0dead54
stop
shemnon May 10, 2019
fb486a2
Merge branch 'master' into plugin_framework
shemnon May 12, 2019
4f0b4ef
Merge branch 'master' of github.com:PegaSysEng/pantheon into plugin_f…
shemnon May 13, 2019
6dfb20a
Merge branch 'master' of github.com:PegaSysEng/pantheon into plugin_f…
shemnon May 14, 2019
613705b
Demo plugin with some error handling
shemnon May 14, 2019
ce7928b
Merge remote-tracking branch 'origin/plugin_framework' into plugin_fr…
shemnon May 14, 2019
042150d
acceptance test for process execution only
shemnon May 15, 2019
3ae2fce
spotless
shemnon May 15, 2019
b9b5e46
jenkins debug
shemnon May 15, 2019
3afabf6
more jenkins debugging
shemnon May 15, 2019
efeabf7
a different jenkins approach, SourceSets
shemnon May 15, 2019
4640c8c
spotless
shemnon May 16, 2019
94f0ba8
Merge branch 'master' into plugin_framework
shemnon May 16, 2019
92b383b
Update plugins/src/main/java/tech/pegasys/pantheon/plugins/internal/P…
shemnon May 16, 2019
581c842
CLI tests
shemnon May 16, 2019
e5a4583
Merge branch 'master' of github.com:PegaSysEng/pantheon into plugin_f…
shemnon May 16, 2019
ebd4246
update to properties cleanup
shemnon May 16, 2019
a79fb04
Update acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acc…
shemnon May 16, 2019
9c96867
Merge branch 'master' of github.com:PegaSysEng/pantheon into plugin_f…
shemnon May 17, 2019
2c81b2b
Merge branch 'master' of github.com:PegaSysEng/pantheon into plugin_f…
shemnon May 17, 2019
b0d0774
spotless
shemnon May 17, 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
17 changes: 12 additions & 5 deletions acceptance-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@ dependencies {
testImplementation project(':consensus:ibft')
testImplementation project(':crypto')
testImplementation project(':enclave')
testImplementation project(':ethereum:eth')
testImplementation project(':ethereum:core')
testImplementation project(':ethereum:blockcreation')
testImplementation project(':ethereum:core')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
testImplementation project(':ethereum:eth')
testImplementation project(':ethereum:graphqlrpc')
testImplementation project(':ethereum:jsonrpc')
testImplementation project(':ethereum:permissioning')
testImplementation project(':ethereum:graphqlrpc')
testImplementation project(':ethereum:rlp')
testImplementation project(':metrics:core')
testImplementation project(path: ':plugins', configuration: 'testArtifacts')
testImplementation project(':pantheon')
testImplementation project(':services:kvstore')
testImplementation project(':testutil')
testImplementation project(':util')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')

testImplementation 'com.google.guava:guava'
testImplementation 'com.squareup.okhttp3:okhttp'
Expand All @@ -52,10 +53,16 @@ dependencies {

test.enabled = false

sourceSets {
test { resources { srcDirs "${rootDir}/plugins/build/libs" } }
}

processTestResources.dependsOn(':plugins:testJar')

task acceptanceTest(type: Test) {
dependsOn(rootProject.installDist)
systemProperty 'acctests.runPantheonAsProcess', 'true'
mustRunAfter rootProject.subprojects*.test
description = 'Runs Pantheon acceptance tests.'
group = 'verification'
}
acceptanceTest.dependsOn(rootProject.installDist)
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ public interface NodeConfiguration {
boolean isDiscoveryEnabled();

boolean isBootnodeEligible();

List<String> getExtraCLIOptions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ public class PantheonNode implements NodeConfiguration, RunnableNode, AutoClosea
private HttpRequestFactory httpRequestFactory;
private boolean useWsForJsonRpc = false;
private String token = null;
private final List<String> plugins = new ArrayList<>();
private final List<String> extraCLIOptions;

public PantheonNode(
final String name,
Expand All @@ -110,15 +112,17 @@ public PantheonNode(
final GenesisConfigProvider genesisConfigProvider,
final boolean p2pEnabled,
final boolean discoveryEnabled,
final boolean bootnodeEligible)
final boolean bootnodeEligible,
final List<String> plugins,
final List<String> extraCLIOptions)
throws IOException {
this.bootnodeEligible = bootnodeEligible;
this.homeDirectory = Files.createTempDirectory("acctest");
keyfilePath.ifPresent(
path -> {
try {
copyResource(path, homeDirectory.resolve("key"));
} catch (IOException e) {
} catch (final IOException e) {
LOG.error("Could not find key file \"{}\" in resources", path);
}
});
Expand All @@ -135,6 +139,18 @@ public PantheonNode(
this.devMode = devMode;
this.p2pEnabled = p2pEnabled;
this.discoveryEnabled = discoveryEnabled;
plugins.forEach(
pluginName -> {
try {
homeDirectory.resolve("plugins").toFile().mkdirs();
copyResource(
pluginName + ".jar", homeDirectory.resolve("plugins/" + pluginName + ".jar"));
PantheonNode.this.plugins.add(pluginName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice trick - haven't seen this syntax before 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's essential for inner classes where names are shadowed.

} catch (final IOException e) {
LOG.error("Could not find plugin \"{}\" in resources", pluginName);
}
});
this.extraCLIOptions = extraCLIOptions;
LOG.info("Created PantheonNode {}", this.toString());
}

Expand Down Expand Up @@ -391,7 +407,7 @@ public Address getAddress() {
return Util.publicKeyToAddress(keyPair.getPublicKey());
}

Path homeDirectory() {
public Path homeDirectory() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) getHomeDirectory ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for consistency of getters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed at the interface, along with other inconsistent names.

return homeDirectory;
}

Expand Down Expand Up @@ -481,6 +497,15 @@ Optional<PermissioningConfiguration> getPermissioningConfiguration() {
return permissioningConfiguration;
}

public List<String> getPlugins() {
return plugins;
}

@Override
public List<String> getExtraCLIOptions() {
return extraCLIOptions;
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,21 @@ public void startNode(final PantheonNode node) {
params.add(permissioningConfiguration.getNodeSmartContractAddress().toString());
}
});
params.addAll(node.getExtraCLIOptions());

LOG.info("Creating pantheon process with params {}", params);
final ProcessBuilder processBuilder =
new ProcessBuilder(params)
.directory(new File(System.getProperty("user.dir")).getParentFile())
.redirectErrorStream(true)
.redirectInput(Redirect.INHERIT);
if (!node.getPlugins().isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm quite sceptical to put environment variables directly in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a test harness and environmental variables is the documented way we add Java options (such as memory) to the invocation. This reflects real world usage.

processBuilder
.environment()
.put(
"PANTHEON_OPTS",
"-Dpantheon.plugins.dir=" + dataDir.resolve("plugins").toAbsolutePath().toString());
}

try {
final Process process = processBuilder.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider;

import java.util.List;
import java.util.Optional;

class PantheonFactoryConfiguration {
Expand All @@ -37,6 +38,8 @@ class PantheonFactoryConfiguration {
private final boolean p2pEnabled;
private final boolean discoveryEnabled;
private final boolean bootnodeEligible;
private final List<String> plugins;
private final List<String> extraCLIOptions;

PantheonFactoryConfiguration(
final String name,
Expand All @@ -51,7 +54,9 @@ class PantheonFactoryConfiguration {
final GenesisConfigProvider genesisConfigProvider,
final boolean p2pEnabled,
final boolean discoveryEnabled,
final boolean bootnodeEligible) {
final boolean bootnodeEligible,
final List<String> plugins,
final List<String> extraCLIOptions) {
this.name = name;
this.miningParameters = miningParameters;
this.privacyParameters = privacyParameters;
Expand All @@ -65,6 +70,8 @@ class PantheonFactoryConfiguration {
this.p2pEnabled = p2pEnabled;
this.discoveryEnabled = discoveryEnabled;
this.bootnodeEligible = bootnodeEligible;
this.plugins = plugins;
this.extraCLIOptions = extraCLIOptions;
}

public String getName() {
Expand Down Expand Up @@ -118,4 +125,12 @@ public boolean isP2pEnabled() {
public boolean isBootnodeEligible() {
return bootnodeEligible;
}

public List<String> getPlugins() {
return plugins;
}

public List<String> getExtraCLIOptions() {
return extraCLIOptions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

public class PantheonFactoryConfigurationBuilder {
Expand All @@ -45,6 +47,8 @@ public class PantheonFactoryConfigurationBuilder {
private Boolean p2pEnabled = true;
private boolean discoveryEnabled = true;
private boolean bootnodeEligible = true;
private List<String> plugins = new ArrayList<>();
private List<String> extraCLIOptions = new ArrayList<>();

public PantheonFactoryConfigurationBuilder name(final String name) {
this.name = name;
Expand Down Expand Up @@ -169,6 +173,18 @@ public PantheonFactoryConfigurationBuilder discoveryEnabled(final boolean discov
return this;
}

public PantheonFactoryConfigurationBuilder plugins(final List<String> plugins) {
this.plugins.clear();
this.plugins.addAll(plugins);
return this;
}

public PantheonFactoryConfigurationBuilder extraCLIOptions(final List<String> extraCLIOptions) {
this.extraCLIOptions.clear();
this.extraCLIOptions.addAll(extraCLIOptions);
return this;
}

public PantheonFactoryConfiguration build() {
return new PantheonFactoryConfiguration(
name,
Expand All @@ -183,6 +199,8 @@ public PantheonFactoryConfiguration build() {
genesisConfigProvider,
p2pEnabled,
discoveryEnabled,
bootnodeEligible);
bootnodeEligible,
plugins,
extraCLIOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ PantheonNode create(final PantheonFactoryConfiguration config) throws IOExceptio
config.getGenesisConfigProvider(),
config.isP2pEnabled(),
config.isDiscoveryEnabled(),
config.isBootnodeEligible());
config.isBootnodeEligible(),
config.getPlugins(),
config.getExtraCLIOptions());
}

public PantheonNode createMinerNode(final String name) throws IOException {
Expand Down Expand Up @@ -173,6 +175,17 @@ public PantheonNode createArchiveNodeWithRpcDisabled(final String name) throws I
return create(new PantheonFactoryConfigurationBuilder().name(name).build());
}

public PantheonNode createPluginsNode(
final String name, final List<String> plugins, final List<String> extraCLIOptions)
throws IOException {
return create(
new PantheonFactoryConfigurationBuilder()
.name(name)
.plugins(plugins)
.extraCLIOptions(extraCLIOptions)
.build());
}

public PantheonNode createArchiveNodeWithRpcApis(
final String name, final RpcApi... enabledRpcApis) throws IOException {
final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2019 ConsenSys AG.
*
* 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.
*/
package tech.pegasys.pantheon.tests.acceptance.plugins;

import static org.assertj.core.api.Assertions.assertThat;

import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import org.awaitility.Awaitility;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

public class PluginsAcceptanceTest extends AcceptanceTestBase {
private PantheonNode node;

// context: https://en.wikipedia.org/wiki/The_Magic_Words_are_Squeamish_Ossifrage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 😄

private static final String MAGIC_WORDS = "Squemish Ossifrage";

@Before
public void setUp() throws Exception {
node =
pantheon.createPluginsNode(
"node1",
Collections.singletonList("testPlugin"),
Collections.singletonList("--Xtest-option=" + MAGIC_WORDS));
cluster.start(node);
}

@Test
public void shouldRegister() throws IOException {
final Path registrationFile = node.homeDirectory().resolve("plugins/testPlugin.registered");
waitForFile(registrationFile);

// this assert is false as CLI will not be parsed at this point
assertThat(Files.readAllLines(registrationFile).stream().anyMatch(s -> s.contains(MAGIC_WORDS)))
.isFalse();
}

@Test
public void shouldStart() throws IOException {
final Path registrationFile = node.homeDirectory().resolve("plugins/testPlugin.started");
waitForFile(registrationFile);

// this assert is true as CLI will be parsed at this point
assertThat(Files.readAllLines(registrationFile).stream().anyMatch(s -> s.contains(MAGIC_WORDS)))
.isTrue();
}

@Test
@Ignore("No way to do a graceful shutdown of Pantheon at the moment.")
public void shouldStop() {
cluster.stopNode(node);
waitForFile(node.homeDirectory().resolve("plugins/testPlugin.stopped"));
}

private void waitForFile(final Path path) {
final File file = path.toFile();
Awaitility.waitAtMost(30, TimeUnit.SECONDS)
.until(
() -> {
if (file.exists()) {
try (final Stream<String> s = Files.lines(path)) {
return s.count() > 0;
}
} else {
return false;
}
});
}
}
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ allprojects {
target '*.gradle'
greclipse().configFile(rootProject.file('gradle/formatter.properties'))
endWithNewline()
paddedCell()
}

// Below this line are currently only license header tasks
Expand Down
3 changes: 2 additions & 1 deletion pantheon/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
implementation project(':consensus:clique')
implementation project(':consensus:ibft')
implementation project(':consensus:ibftlegacy')
implementation project(':enclave')
implementation project(':ethereum:blockcreation')
implementation project(':ethereum:core')
implementation project(':ethereum:eth')
Expand All @@ -40,8 +41,8 @@ dependencies {
implementation project(':ethereum:permissioning')
implementation project(':ethereum:p2p')
implementation project(':ethereum:rlp')
implementation project(':plugins')
implementation project(':metrics:core')
implementation project(':enclave')
implementation project(':services:kvstore')

implementation 'com.graphql-java:graphql-java'
Expand Down
Loading