CORE-10259: Update template to use CSDE plugin (#35)

* updated csde to use plugin

* update csde block

* update network file

* allign to Kotlin csde

* CORE-10259: Update template to use CSDE plugin

---------

Co-authored-by: mattbradburyr3 <matthew.bradbury@r3.com>
This commit is contained in:
Tony Lawson 2023-04-11 17:25:31 +01:00 committed by GitHub
parent a043d8dcd1
commit 2346e34494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 56 additions and 1614 deletions

3
.gitignore vendored
View File

@ -86,3 +86,6 @@ workspace/**
#*.pfx #*.pfx
#CPIFileStatus*.json #CPIFileStatus*.json
#GroupPolicy.json #GroupPolicy.json
# Ignore temporary data files
*.dat

View File

@ -6,7 +6,7 @@ plugins {
id 'org.jetbrains.kotlin.plugin.jpa' id 'org.jetbrains.kotlin.plugin.jpa'
id 'java' id 'java'
id 'maven-publish' id 'maven-publish'
id 'csde' id 'net.corda.plugins.csde'
} }
allprojects { allprojects {
@ -15,6 +15,23 @@ allprojects {
def javaVersion = VERSION_11 def javaVersion = VERSION_11
// Configure the CSDE
csde {
cordaClusterURL = "https://localhost:8888"
networkConfigFile = "config/static-network-config.json"
corDappCpiName = "MyCorDapp"
notaryCpiName = "NotaryServer"
cordaRpcUser = "admin"
cordaRpcPasswd ="admin"
csdeWorkspaceDir = "workspace"
notaryVersion = cordaNotaryPluginsVersion
combinedWorkerVersion = "5.0.0.0-Gecko1.0"
postgresJdbcVersion = "42.4.3"
cordaDbContainerName = "CSDEpostgresql"
cordaBinDir = "${System.getProperty("user.home")}/.corda/corda5"
cordaCliBinDir = "${System.getProperty("user.home")}/.corda/cli"
}
// Declare the set of Java compiler options we need to build a CorDapp. // Declare the set of Java compiler options we need to build a CorDapp.
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
// -parameters - Needed for reflection and serialization to work correctly. // -parameters - Needed for reflection and serialization to work correctly.
@ -25,9 +42,23 @@ allprojects {
repositories { repositories {
// All dependencies are held in Maven Central // All dependencies are held in Maven Central
mavenLocal()
mavenCentral() mavenCentral()
// R3 Internal repositories for dev
// Repository provides Corda 5 binaries that implement Corda-API.
// These will be made publicly available.
// Final location to be decided.
// Repository subject to change
maven { maven {
url = "$artifactoryContextUrl/" url = "$artifactoryContextUrl/corda-os-maven"
authentication {
basic(BasicAuthentication)
}
credentials {
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
} }
} }

View File

@ -1,29 +0,0 @@
plugins {
id 'groovy-gradle-plugin'
id 'java'
}
def constants = new Properties()
file("$rootDir/../gradle.properties").withInputStream { InputStream input -> constants.load(input) }
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
repositories {
mavenCentral()
mavenLocal()
maven {
url = "$artifactoryContextUrl/"
}
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation "com.konghq:unirest-java:$unirestVersion"
implementation "com.konghq:unirest-objectmapper-jackson:$unirestVersion"
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jacksonVersion"
implementation "net.corda:corda-base:$cordaApiVersion"
}

View File

@ -1,4 +0,0 @@
jacksonVersion = 2.13.4
unirestVersion=3.13.10
cordaApiVersion=5.0.0.665-Gecko1.0

View File

@ -1 +0,0 @@
// File intentionally left blank

View File

@ -1,259 +0,0 @@
// Note, IntelliJ does not recognise the imported Java Classes, hence they are
// highlighted in Red. However, they are recognised in the gradle compilation.
// todo: look at the declaration of the script variables, can they be combined with the declaration of the Project Context
// todo: investigate adding corda-cli to the class path then executing it directly - might not work as gradle has to set up the jar file, so its not their when you start.
// Todo: write a test flow runner helper function??
// todo: rename deployCPIsHelper
// todo: add proper logging, rather than reading Stdout
// todo: add test corda running/live task
// todo: add a test to check docker is running and display error if not + halt start corda
// todo: add a clean corda task.
// todo: fix logging level and make it configurable.
import com.r3.csde.CordaLifeCycleHelper
import com.r3.csde.ProjectContext
import com.r3.csde.DeployCPIsHelper
import com.r3.csde.BuildCPIsHelper
import com.r3.csde.ProjectUtils
import com.r3.csde.CordaStatusQueries
import com.r3.csde.VNodesHelper
import com.r3.csde.NetworkConfig
plugins {
id 'java-library'
id 'groovy'
id 'java'
}
configurations {
combinedWorker{
canBeConsumed = false
canBeResolved= true
}
myPostgresJDBC {
canBeConsumed = false
canBeResolved = true
}
notaryServerCPB {
canBeConsumed = false
canBeResolved = true
}
}
// Dependencies for supporting tools
dependencies {
combinedWorker "net.corda:corda-combined-worker:$combinedWorkerVersion"
myPostgresJDBC "org.postgresql:postgresql:$postgresqlVersion"
notaryServerCPB("com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-server:$cordaNotaryPluginsVersion") {
artifact {
classifier = 'package'
extension = 'cpb'
}
}
}
// task groupings
def cordaGroup = 'csde-corda' // corda lifecycle tasks
def cordappGroup = 'csde-cordapp' // tasks to build and deploy corDapps
def queriesGroup = 'csde-queries' // tasks which query corda status
def supportingGroup = 'supporting' // tasks which should be hidden from the csde user
def cordaBinDir = System.getenv("CSDE_CORDA_BIN") ?: System.getProperty('user.home') + "/.corda/corda5"
def cordaCliBinDir = System.getenv("CSDE_CORDA_CLI") ?:System.getProperty('user.home') + "/.corda/cli"
def cordaJDBCDir = cordaBinDir + "/jdbcDrivers"
def cordaNotaryServerDir = cordaBinDir + "/notaryserver"
def signingCertAlias="gradle-plugin-default-key"
// Get error if this is not a autotyped object
// def signingCertFName = "$rootDir/config/gradle-plugin-default-key.pem"
def signingCertFName = rootDir.toString() + "/config/gradle-plugin-default-key.pem"
def keystoreAlias = "my-signing-key"
def keystoreFName = devEnvWorkspace + "/signingkeys.pfx"
def keystoreCertFName = devEnvWorkspace + "/signingkey1.pem"
def combiWorkerPidCacheFile = devEnvWorkspace + "/CordaPID.dat"
// todo: can we rely on the build directory always being /workflow/build? aslo, is the
// workflow directory the correct place to build the cpb to. shoudl it be the main build directory.
def workflowBuildDir = rootDir.toString() + "/workflows/build"
// todo: Need to read things from cordapp plugin - the cordapp names will be changed by the user
def appCpiName = 'cpi name' // !!! this must match with the cpi name in /config/static-network-config.json
def notaryCpiName = 'CSDE Notary Server CPI'
// todo: there should be a better way to set up these project context variables.
def projectContext = new ProjectContext(project,
cordaClusterURL.toString(),
cordaRpcUser,
cordaRpcPasswd,
devEnvWorkspace,
// todo: why is this not obtained in the groovy def's abouve - its inconsistent.
new String("${System.getProperty("java.home")}/bin"),
dbContainerName,
cordaJDBCDir,
combiWorkerPidCacheFile,
signingCertAlias,
signingCertFName,
keystoreAlias,
keystoreFName,
keystoreCertFName,
appCpiName,
notaryCpiName,
devEnvWorkspace,
cordaCliBinDir,
cordaNotaryServerDir,
workflowBuildDir,
cordaNotaryPluginsVersion
)
def networkConfig = new NetworkConfig("config/static-network-config.json")
def utils = new ProjectUtils()
// Initiate workspace folder
tasks.register('projInit') {
group = supportingGroup
doLast {
mkdir devEnvWorkspace
}
}
// CordaLifeCycle tasks
def cordaLifeCycle = new CordaLifeCycleHelper(projectContext)
tasks.register("startCorda") {
group = cordaGroup
dependsOn('getDevCordaLite', 'getPostgresJDBC')
doLast {
mkdir devEnvWorkspace
cordaLifeCycle.startCorda()
}
}
tasks.register("stopCorda") {
group = cordaGroup
doLast {
cordaLifeCycle.stopCorda()
}
}
tasks.register("stopAndCleanCorda") {
group = cordaGroup
dependsOn('clean')
doLast {
try {
cordaLifeCycle.stopCorda()
} catch (Exception ignore) {
println("ignoring exception: ${ignore.toString()}")
}
delete devEnvWorkspace
}
}
tasks.register("getPostgresJDBC") {
group = supportingGroup
doLast {
copy {
from configurations.myPostgresJDBC
into "$cordaJDBCDir"
}
}
}
tasks.register("getDevCordaLite", Copy) {
group = supportingGroup
from configurations.combinedWorker
into cordaBinDir
}
// Corda status queries
def cordaStatusQueries = new CordaStatusQueries(projectContext)
tasks.register('listVNodes') {
group = queriesGroup
doLast {
cordaStatusQueries.listVNodes()
}
}
tasks.register('listCPIs') {
group = queriesGroup
doLast {
cordaStatusQueries.listCPIs()
}
}
// Build CPI tasks
def buildCPIsHelper = new BuildCPIsHelper(projectContext, networkConfig)
tasks.register("1-createGroupPolicy") {
group = cordappGroup
dependsOn('projInit')
doLast {
buildCPIsHelper.createGroupPolicy()
}
}
tasks.register("getNotaryServerCPB", Copy) {
group = supportingGroup
from configurations.notaryServerCPB
into cordaNotaryServerDir
}
tasks.register('2-createKeystore') {
group = cordappGroup
dependsOn('projInit')
doLast {
buildCPIsHelper.createKeyStore()
}
}
tasks.register('3-buildCPIs') {
group = cordappGroup
def dependsOnTasks = subprojects.collect {it.tasks.findByName("build") }
dependsOnTasks.add('1-createGroupPolicy')
dependsOnTasks.add('2-createKeystore')
dependsOnTasks.add('getNotaryServerCPB')
dependsOn dependsOnTasks
doLast{
buildCPIsHelper.buildCPIs()
}
}
// deploy CPI tasks
def deployCPIsHelper = new DeployCPIsHelper(projectContext)
tasks.register('4-deployCPIs') {
group = cordappGroup
dependsOn('3-buildCPIs')
doLast {
deployCPIsHelper.deployCPIs()
}
}
// Setup VNodes tasks
def vNodesHelper = new VNodesHelper(projectContext, networkConfig )
tasks.register('5-vNodeSetup') {
group = cordappGroup
dependsOn('4-deployCPIs')
doLast {
vNodesHelper.vNodesSetup()
}
}

View File

@ -1,270 +0,0 @@
package com.r3.csde;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
public class BuildCPIsHelper {
public ProjectContext pc;
public ProjectUtils utils;
public NetworkConfig config;
public BuildCPIsHelper(ProjectContext _pc, NetworkConfig _config) {
pc = _pc;
utils = new ProjectUtils(pc);
config = _config;
}
public void createGroupPolicy() throws IOException {
File groupPolicyFile = new File(String.format("%s/GroupPolicy.json", pc.devEnvWorkspace));
File devnetFile = new File(pc.project.getRootDir() + "/" + config.getConfigFilePath());
if (!groupPolicyFile.exists() || groupPolicyFile.lastModified() < devnetFile.lastModified()) {
pc.out.println("createGroupPolicy: Creating a GroupPolicy");
List<String> configX500Ids = config.getX500Names();
LinkedList<String> commandList = new LinkedList<>();
commandList.add(String.format("%s/java", pc.javaBinDir));
commandList.add(String.format("-Dpf4j.pluginsDir=%s/plugins/", pc.cordaCliBinDir));
commandList.add("-jar");
commandList.add(String.format("%s/corda-cli.jar", pc.cordaCliBinDir));
commandList.add("mgm");
commandList.add("groupPolicy");
for (String id : configX500Ids) {
commandList.add("--name");
commandList.add(id);
}
commandList.add("--endpoint-protocol=1");
commandList.add("--endpoint=http://localhost:1080");
ProcessBuilder pb = new ProcessBuilder(commandList);
pb.redirectErrorStream(true);
Process proc = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
// todo add exception catching
FileWriter fileWriter = new FileWriter(groupPolicyFile);
String line;
while (( line = reader.readLine()) != null){
fileWriter.write(line + "\n");
}
fileWriter.close();
} else {
pc.out.println("createPolicyTask: everything up to date; nothing to do.");
}
}
public void createKeyStore() throws IOException, InterruptedException {
File keystoreFile = new File(pc.keystoreFName);
if(!keystoreFile.exists()) {
pc.out.println("createKeystore: Create a keystore");
generateKeyPair();
addDefaultSigningKey();
exportCert();
} else {
pc.out.println("createKeystore: keystore already created; nothing to do.");
}
}
private void generateKeyPair() throws IOException, InterruptedException {
LinkedList<String> cmdArray = new LinkedList<>();
cmdArray.add(pc.javaBinDir + "/keytool");
cmdArray.add("-genkeypair");
cmdArray.add("-alias");
cmdArray.add(pc.keystoreAlias);
cmdArray.add("-keystore");
cmdArray.add(pc.keystoreFName);
cmdArray.add("-storepass");
cmdArray.add("keystore password");
cmdArray.add("-dname");
cmdArray.add("CN=CPI Example - My Signing Key, O=CorpOrgCorp, L=London, C=GB");
cmdArray.add("-keyalg");
cmdArray.add("RSA");
cmdArray.add("-storetype");
cmdArray.add("pkcs12");
cmdArray.add("-validity");
cmdArray.add("4000");
ProcessBuilder pb = new ProcessBuilder(cmdArray);
pb.redirectErrorStream(true);
Process proc = pb.start();
proc.waitFor();
}
private void addDefaultSigningKey() throws IOException, InterruptedException {
LinkedList<String> cmdArray = new LinkedList<>();
cmdArray.add(pc.javaBinDir + "/keytool");
cmdArray.add("-importcert");
cmdArray.add("-keystore");
cmdArray.add(pc.keystoreFName);
cmdArray.add("-storepass");
cmdArray.add("keystore password");
cmdArray.add("-noprompt");
cmdArray.add("-alias");
cmdArray.add(pc.signingCertAlias);
cmdArray.add("-file");
cmdArray.add(pc.signingCertFName);
ProcessBuilder pb = new ProcessBuilder(cmdArray);
pb.redirectErrorStream(true);
Process proc = pb.start();
proc.waitFor();
}
private void exportCert() throws IOException, InterruptedException {
LinkedList<String> cmdArray = new LinkedList<>();
cmdArray.add(pc.javaBinDir + "/keytool");
cmdArray.add("-exportcert");
cmdArray.add("-rfc");
cmdArray.add("-alias");
cmdArray.add(pc.keystoreAlias);
cmdArray.add("-keystore");
cmdArray.add(pc.keystoreFName);
cmdArray.add("-storepass");
cmdArray.add("keystore password");
cmdArray.add("-file");
cmdArray.add(pc.keystoreCertFName);
ProcessBuilder pb = new ProcessBuilder(cmdArray);
pb.redirectErrorStream(true);
Process proc = pb.start();
proc.waitFor();
}
public void buildCPIs() throws IOException, InterruptedException, CsdeException {
createCorDappCPI();
createNotaryCPI();
}
private void createCorDappCPI() throws IOException, InterruptedException, CsdeException {
String appCPIFilePath = pc.workflowBuildDir + "/" +
pc.project.getRootProject().getName() + "-" +
pc.project.getVersion() + ".cpi";
File appCPIFile = new File(appCPIFilePath);
appCPIFile.delete();
File srcDir = new File(pc.workflowBuildDir + "/libs");
File[] appCPBs = srcDir.listFiles(( x , name ) -> name.endsWith(".cpb"));
if (appCPBs == null) throw new CsdeException("Expecting exactly one CPB but no CPB found.");
if (appCPBs.length != 1) throw new CsdeException("Expecting exactly one CPB but more than one found.");
pc.out.println("appCpbs:");
pc.out.println(appCPBs[0].getAbsolutePath());
LinkedList<String> commandList = new LinkedList<>();
commandList.add(String.format("%s/java", pc.javaBinDir));
commandList.add(String.format("-Dpf4j.pluginsDir=%s/plugins/", pc.cordaCliBinDir));
commandList.add("-jar");
commandList.add(String.format("%s/corda-cli.jar", pc.cordaCliBinDir));
commandList.add("package");
commandList.add("create-cpi");
commandList.add("--cpb");
commandList.add(appCPBs[0].getAbsolutePath());
commandList.add("--group-policy");
commandList.add(pc.devEnvWorkspace + "/GroupPolicy.json");
commandList.add("--cpi-name");
commandList.add(pc.appCPIName);
commandList.add("--cpi-version");
commandList.add(pc.project.getVersion().toString());
commandList.add("--file");
commandList.add(appCPIFilePath);
commandList.add("--keystore");
commandList.add(pc.devEnvWorkspace + "/signingkeys.pfx");
commandList.add("--storepass");
commandList.add("keystore password");
commandList.add("--key");
commandList.add("my-signing-key"); // todo: should be passed as context property
ProcessBuilder pb = new ProcessBuilder(commandList);
pb.redirectErrorStream(true);
Process proc = pb.start();
proc.waitFor();
//Get CPI packaging errors
if (proc.getErrorStream().available() > 0) {
proc.getErrorStream().transferTo(pc.out);
}
}
private void createNotaryCPI() throws CsdeException, IOException, InterruptedException {
String notaryCPIFilePath = pc.workflowBuildDir + "/" +
pc.notaryCPIName.replace(' ', '-').toLowerCase() + "-" +
pc.project.getVersion() + ".cpi";
File notaryCPIFile = new File(notaryCPIFilePath);
notaryCPIFile.delete();
File srcDir = new File(pc.cordaNotaryServiceDir);
File[] notaryCPBs = srcDir.listFiles(( x , name ) -> name.endsWith(".cpb") && name.contains(pc.cordaNotaryPluginsVersion));
if (notaryCPBs == null) throw new CsdeException("Expecting exactly one notary CPB but no CPB found.");
if (notaryCPBs.length != 1) throw new CsdeException("Expecting exactly one notary CPB but more than one found.");
pc.out.println("notaryCpbs:");
pc.out.println(notaryCPBs[0]);
LinkedList<String> commandList = new LinkedList<>();
commandList.add(String.format("%s/java", pc.javaBinDir));
commandList.add(String.format("-Dpf4j.pluginsDir=%s/plugins/", pc.cordaCliBinDir));
commandList.add("-jar");
commandList.add(String.format("%s/corda-cli.jar", pc.cordaCliBinDir));
commandList.add("package");
commandList.add("create-cpi");
commandList.add("--cpb");
commandList.add(notaryCPBs[0].getAbsolutePath());
commandList.add("--group-policy");
commandList.add(pc.devEnvWorkspace + "/GroupPolicy.json");
commandList.add("--cpi-name");
commandList.add(pc.notaryCPIName);
commandList.add("--cpi-version");
commandList.add(pc.project.getVersion().toString());
commandList.add("--file");
commandList.add(notaryCPIFilePath);
commandList.add("--keystore");
commandList.add(pc.devEnvWorkspace + "/signingkeys.pfx");
commandList.add("--storepass");
commandList.add("keystore password");
commandList.add("--key");
commandList.add("my-signing-key");
ProcessBuilder pb = new ProcessBuilder(commandList);
pb.redirectErrorStream(true);
Process proc = pb.start();
proc.waitFor();
}
// todo: this might be needed for improved logging
private void printCmdArray(LinkedList<String> cmdArray) {
for (int i = 0; i < cmdArray.size(); i++) {
pc.out.print(cmdArray.get(i) + " ");
}
}
}

View File

@ -1,97 +0,0 @@
package com.r3.csde;
import kong.unirest.Unirest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Scanner;
/**
* Manages Bringing corda up, testing for liveness and taking corda down
*/
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
public class CordaLifeCycleHelper {
ProjectContext pc;
ProjectUtils utils;
public CordaLifeCycleHelper(ProjectContext _pc) {
pc = _pc;
utils = new ProjectUtils(pc);
Unirest.config().verifySsl(false);
}
public void startCorda() throws IOException, CsdeException {
File cordaPIDFile = new File(pc.cordaPidCache);
if (cordaPIDFile.exists()) {
throw new CsdeException("Cannot start the Combined worker. Cached process ID file " + cordaPIDFile + " existing. Was the combined worker already started?");
}
PrintStream pidStore = new PrintStream(new FileOutputStream(cordaPIDFile));
File combinedWorkerJar = pc.project.getConfigurations().getByName("combinedWorker").getSingleFile();
// Manual version of the command to start postgres (for reference):
// docker run -d --rm -p5432:5432 --name CSDEpostgresql -e POSTGRES_DB=cordacluster -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password postgres:latest
new ProcessBuilder(
"docker",
"run", "-d", "--rm",
"-p", "5432:5432",
"--name", pc.dbContainerName,
"-e", "POSTGRES_DB=cordacluster",
"-e", "POSTGRES_USER=postgres",
"-e", "POSTGRES_PASSWORD=password",
"postgres:latest").start();
// todo: we should poll for readiness not wait 10 seconds, see https://r3-cev.atlassian.net/browse/CORE-11626
utils.rpcWait(10000);
ProcessBuilder procBuild = new ProcessBuilder(pc.javaBinDir + "/java",
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005",
"-DsecurityMangerEnabled=false",
"-Dlog4j.configurationFile=" + pc.project.getRootDir() + "/config/log4j2.xml",
"-Dco.paralleluniverse.fibers.verifyInstrumentation=true",
"-jar",
combinedWorkerJar.toString(),
"--instance-id=0",
"-mbus.busType=DATABASE",
"-spassphrase=password",
"-ssalt=salt",
"-ddatabase.user=user",
"-ddatabase.pass=password",
"-ddatabase.jdbc.url=jdbc:postgresql://localhost:5432/cordacluster",
"-ddatabase.jdbc.directory="+pc.JDBCDir);
procBuild.redirectErrorStream(true);
Process proc = procBuild.start();
pidStore.print(proc.pid());
pc.out.println("Corda Process-id="+proc.pid());
proc.getInputStream().transferTo(pc.out);
// todo: we should poll for readiness before completing the startCorda task, see https://r3-cev.atlassian.net/browse/CORE-11625
}
public void stopCorda() throws IOException, CsdeException {
File cordaPIDFile = new File(pc.cordaPidCache);
if(cordaPIDFile.exists()) {
Scanner sc = new Scanner(cordaPIDFile);
long pid = sc.nextLong();
pc.out.println("pid to kill=" + pid);
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
new ProcessBuilder("Powershell", "-Command", "Stop-Process", "-Id", Long.toString(pid), "-PassThru").start();
} else {
new ProcessBuilder("kill", "-9", Long.toString(pid)).start();
}
Process proc = new ProcessBuilder("docker", "stop", pc.dbContainerName).start();
cordaPIDFile.delete();
}
else {
throw new CsdeException("Cannot stop the Combined worker. Cached process ID file " + pc.cordaPidCache + " missing. Was the combined worker not started?");
}
}
}

View File

@ -1,117 +0,0 @@
package com.r3.csde;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import kong.unirest.json.JSONArray;
import kong.unirest.json.JSONObject;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
public class CordaStatusQueries {
ProjectContext pc;
public CordaStatusQueries(ProjectContext _pc) {
pc = _pc;
}
public HttpResponse<JsonNode> getCpiInfo() {
Unirest.config().verifySsl(false);
return Unirest.get(pc.baseURL + "/api/v1/cpi/")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
}
public HttpResponse<JsonNode> getVNodeInfo() {
Unirest.config().verifySsl(false);
return Unirest.get(pc.baseURL + "/api/v1/virtualnode/")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
}
// cpiName, cpiVersion
public void listCPIs() {
HttpResponse<JsonNode> cpiResponse = getCpiInfo();
JSONArray cpisJson = (JSONArray) cpiResponse.getBody().getObject().get("cpis");
List<List<String>> lines = new LinkedList<>();
for (Object o : cpisJson) {
if (o instanceof JSONObject) {
JSONObject idObj = ((JSONObject) o).getJSONObject("id");
String cpiName = idObj.get("cpiName").toString();
String cpiVersion = idObj.get("cpiVersion").toString();
lines.add(Arrays.asList(cpiName, cpiVersion));
}
}
List<String> title = Arrays.asList("CPI Name", "CPI Version");
List<Integer> titleSizes = Arrays.asList(40, 20);
printTable(titleSizes, title, lines);
}
public void listVNodesVerbose() {
HttpResponse<JsonNode> vnodeResponse = getVNodeInfo();
pc.out.println("VNodes:\n" + vnodeResponse.getBody().toPrettyString());
}
// x500Name, shortHash, cpiName
public void listVNodes() {
HttpResponse<JsonNode> vnodeResponse = getVNodeInfo();
JSONArray virtualNodesJson = (JSONArray) vnodeResponse.getBody().getObject().get("virtualNodes");
List<List<String>> lines = new LinkedList<>();
for (Object o : virtualNodesJson) {
if (o instanceof JSONObject) {
JSONObject idObj = ((JSONObject) o).getJSONObject("holdingIdentity");
String x500Name = idObj.get("x500Name").toString();
String shortHash = idObj.get("shortHash").toString();
JSONObject cpiObj = ((JSONObject) o).getJSONObject("cpiIdentifier");
String cpiName = cpiObj.get("cpiName").toString();
lines.add(Arrays.asList(x500Name, shortHash, cpiName));
}
}
List<String> title = Arrays.asList("X500 Name", "Holding identity short hash", "CPI Name");
List<Integer> titleSizes = Arrays.asList(60, 30, 40);
printTable(titleSizes, title, lines);
}
public void printTable(List<Integer> titleSizes, List<String> title, List<List<String>> lines) {
int width = titleSizes.stream().reduce(0, Integer::sum);
String separator = "-".repeat(width + 1);
pc.out.println(separator);
pc.out.println(formatLine(titleSizes, title));
pc.out.println(separator);
for (List<String> line : lines) {
pc.out.println(formatLine(titleSizes, line));
}
pc.out.println(separator);
}
public String formatLine(List<Integer> titleSizes, List<String> line) {
StringBuilder sb = new StringBuilder();
int delta = 0;
for (int i = 0; i < titleSizes.size(); i++) {
String s = line.get(i);
sb.append("| ").append(s);
delta += titleSizes.get(i) - (2 + s.length());
if (delta > 0) {
sb.append(" ".repeat(delta));
delta = 0;
} else {
sb.append(" ");
delta -= 1;
}
}
sb.append("|");
return sb.toString();
}
}

View File

@ -1,10 +0,0 @@
package com.r3.csde;
public class CsdeException extends Exception {
public CsdeException(String message, Throwable cause) {
super(message, cause);
}
public CsdeException(String message){
super(message);
}
}

View File

@ -1,187 +0,0 @@
package com.r3.csde;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import kong.unirest.json.JSONArray;
import kong.unirest.json.JSONObject;
import kong.unirest.HttpResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_OK;
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
public class DeployCPIsHelper {
public DeployCPIsHelper() {
}
ProjectContext pc;
CordaStatusQueries queries;
ProjectUtils utils;
public DeployCPIsHelper(ProjectContext _pc) {
pc = _pc;
queries = new CordaStatusQueries(pc);
utils = new ProjectUtils(pc);
}
public void deployCPIs() throws FileNotFoundException, CsdeException{
uploadCertificate(pc.signingCertAlias, pc.signingCertFName);
uploadCertificate(pc.keystoreAlias, pc.keystoreCertFName);
// todo: make consistent with other string building code - remove String.format
String appCPILocation = String.format("%s/%s-%s.cpi",
pc.workflowBuildDir,
pc.project.getName(),
pc.project.getVersion());
deployCPI(appCPILocation, pc.appCPIName,pc.project.getVersion().toString());
String notaryCPILocation = String.format("%s/%s-%s.cpi",
pc.workflowBuildDir,
pc.notaryCPIName.replace(' ','-').toLowerCase(),
pc.project.getVersion());
deployCPI(notaryCPILocation,
pc.notaryCPIName,
pc.project.getVersion().toString(),
"-NotaryServer" );
}
public void uploadCertificate(String certAlias, String certFName) {
Unirest.config().verifySsl(false);
HttpResponse<JsonNode> uploadResponse = Unirest.put(pc.baseURL + "/api/v1/certificates/cluster/code-signer")
.field("alias", certAlias)
.field("certificate", new File(certFName))
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
pc.out.println("Certificate/key upload, alias "+certAlias+" certificate/key file "+certFName);
pc.out.println(uploadResponse.getBody().toPrettyString());
}
public void forceuploadCPI(String cpiFName) throws FileNotFoundException, CsdeException {
forceuploadCPI(cpiFName, "");
}
public void forceuploadCPI(String cpiFName, String uploadStatusQualifier) throws FileNotFoundException, CsdeException {
Unirest.config().verifySsl(false);
HttpResponse<JsonNode> jsonResponse = Unirest.post(pc.baseURL + "/api/v1/maintenance/virtualnode/forcecpiupload/")
.field("upload", new File(cpiFName))
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if(jsonResponse.getStatus() == HTTP_OK) {
String id = (String) jsonResponse.getBody().getObject().get("id");
pc.out.println("get id:\n" +id);
HttpResponse<JsonNode> statusResponse = uploadStatus(id);
if (statusResponse.getStatus() == HTTP_OK) {
PrintStream cpiUploadStatus = new PrintStream(new FileOutputStream(
pc.CPIUploadStatusFName.replace(".json", uploadStatusQualifier + ".json" )));
cpiUploadStatus.print(statusResponse.getBody());
pc.out.println("Caching CPI file upload status:\n" + statusResponse.getBody());
} else {
utils.reportError(statusResponse);
}
}
else {
utils.reportError(jsonResponse);
}
}
private boolean uploadStatusRetry(HttpResponse<JsonNode> response) {
int status = response.getStatus();
JsonNode body = response.getBody();
// Do not retry on success // todo: need to think through the possible outcomes here - what if the bodyTitle is null, it won't retry
if(status == HTTP_OK) {
// Keep retrying until we get "OK" may move through "Validating upload", "Persisting CPI"
return !(body.getObject().get("status").equals("OK"));
}
else if (status == HTTP_BAD_REQUEST){
String bodyTitle = response.getBody().getObject().getString("title");
return bodyTitle != null && bodyTitle.matches("No such requestId=[-0-9a-f]+");
}
return false;
}
public HttpResponse<JsonNode> uploadStatus(String requestId) {
HttpResponse<JsonNode> statusResponse = null;
do {
utils.rpcWait(1000);
statusResponse = Unirest
.get(pc.baseURL + "/api/v1/cpi/status/" + requestId + "/")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
pc.out.println("Upload status="+statusResponse.getStatus()+", status query response:\n"+statusResponse.getBody().toPrettyString());
}
while(uploadStatusRetry(statusResponse));
return statusResponse;
}
public void deployCPI(String cpiFName, String cpiName, String cpiVersion) throws FileNotFoundException, CsdeException {
deployCPI(cpiFName, cpiName, cpiVersion, "");
}
public void deployCPI(String cpiFName,
String cpiName,
String cpiVersion,
String uploadStatusQualifier) throws FileNotFoundException, CsdeException {
// todo: where is the primary instance declared?
Unirest.config().verifySsl(false);
HttpResponse<JsonNode> cpiResponse = queries.getCpiInfo();
JSONArray jArray = (JSONArray) cpiResponse.getBody().getObject().get("cpis");
int matches = 0;
for(Object o: jArray.toList() ) {
if(o instanceof JSONObject) {
JSONObject idObj = ((JSONObject) o).getJSONObject("id");
if((idObj.get("cpiName").toString().equals(cpiName)
&& idObj.get("cpiVersion").toString().equals(cpiVersion))) {
matches++;
}
}
}
pc.out.println("Matching CPIS="+matches);
if(matches == 0) {
HttpResponse<JsonNode> uploadResponse = Unirest.post(pc.baseURL + "/api/v1/cpi/")
.field("upload", new File(cpiFName))
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
JsonNode body = uploadResponse.getBody();
int status = uploadResponse.getStatus();
pc.out.println("Upload Status:" + status);
pc.out.println("Pretty print the body\n" + body.toPrettyString());
// We expect the id field to be a string.
if (status == HTTP_OK) {
String id = (String) body.getObject().get("id");
pc.out.println("get id:\n" + id);
HttpResponse<JsonNode> statusResponse = uploadStatus(id);
if (statusResponse.getStatus() == HTTP_OK) {
PrintStream cpiUploadStatus = new PrintStream(new FileOutputStream(
pc.CPIUploadStatusFName.replace(".json", uploadStatusQualifier + ".json" )));
cpiUploadStatus.print(statusResponse.getBody());
pc.out.println("Caching CPI file upload status:\n" + statusResponse.getBody());
} else {
utils.reportError(statusResponse);
}
} else {
utils.reportError(uploadResponse);
}
}
else {
pc.out.println("CPI already uploaded doing a 'force' upload.");
forceuploadCPI(cpiFName, uploadStatusQualifier);
}
}
}

View File

@ -1,40 +0,0 @@
package com.r3.csde;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.FileInputStream;
import java.util.List;
import java.util.stream.Collectors;
/**
* This class reads the network config from the json file and makes it available as a list of VNodes.
*/
public class NetworkConfig {
private List<VNode> vNodes;
private String configFilePath;
public NetworkConfig(String _configFilePath) throws CsdeException {
configFilePath = _configFilePath;
ObjectMapper mapper = new ObjectMapper();
try {
FileInputStream in = new FileInputStream(configFilePath);
vNodes = mapper.readValue(in, new TypeReference<List<VNode>>() {
});
} catch (Exception e) {
throw new CsdeException("Failed to read static network configuration file, with exception: " + e);
}
}
String getConfigFilePath() { return configFilePath; }
List<VNode> getVNodes() { return vNodes; }
List<String> getX500Names() {
return vNodes.stream().map(vn -> vn.getX500Name()).collect(Collectors.toList());
}
}

View File

@ -1,84 +0,0 @@
package com.r3.csde;
import org.gradle.api.Project;
import java.io.PrintStream;
import java.util.Map;
public class ProjectContext {
Project project;
String baseURL = "https://localhost:8888";
String rpcUser = "admin";
String rpcPasswd = "admin";
String workspaceDir = "workspace";
int retryWaitMs = 1000;
PrintStream out = System.out;
String CPIUploadStatusBaseName = "CPIFileStatus.json";
String NotaryCPIUploadBaseName = "CPIFileStatus-NotaryServer.json";
String CPIUploadStatusFName;
String NotaryCPIUploadStatusFName;
String javaBinDir;
String cordaPidCache = "CordaPIDCache.dat";
String dbContainerName;
String JDBCDir;
String combinedWorkerBinRe;
Map<String, String> notaryRepresentatives = null;
String signingCertAlias;
String signingCertFName;
String keystoreAlias;
String keystoreFName;
String keystoreCertFName;
String appCPIName;
String notaryCPIName;
String devEnvWorkspace;
String cordaCliBinDir;
String cordaNotaryServiceDir;
String workflowBuildDir;
String cordaNotaryPluginsVersion;
public ProjectContext (Project inProject,
String inBaseUrl,
String inRpcUser,
String inRpcPasswd,
String inWorkspaceDir,
String inJavaBinDir,
String inDbContainerName,
String inJDBCDir,
String inCordaPidCache,
String inSigningCertAlias,
String inSigningCertFName,
String inKeystoreAlias,
String inKeystoreFName,
String inKeystoreCertFName,
String inAppCPIName,
String inNotaryCPIName,
String inDevEnvWorkspace,
String inCordaCLiBinDir,
String inCordaNotaryServiceDir,
String inWorkflowBuildDir,
String inCordaNotaryPluginsVersion
) {
project = inProject;
baseURL = inBaseUrl;
rpcUser = inRpcUser;
rpcPasswd = inRpcPasswd;
workspaceDir = inWorkspaceDir;
javaBinDir = inJavaBinDir;
cordaPidCache = inCordaPidCache;
dbContainerName = inDbContainerName;
JDBCDir = inJDBCDir;
CPIUploadStatusFName = workspaceDir + "/" + CPIUploadStatusBaseName;
NotaryCPIUploadStatusFName = workspaceDir + "/" + NotaryCPIUploadBaseName;
signingCertAlias = inSigningCertAlias;
signingCertFName = inSigningCertFName;
keystoreAlias = inKeystoreAlias;
keystoreFName = inKeystoreFName;
keystoreCertFName = inKeystoreCertFName;
appCPIName = inAppCPIName;
notaryCPIName = inNotaryCPIName;
devEnvWorkspace = inDevEnvWorkspace;
cordaCliBinDir = inCordaCLiBinDir;
cordaNotaryServiceDir = inCordaNotaryServiceDir;
workflowBuildDir = inWorkflowBuildDir;
cordaNotaryPluginsVersion = inCordaNotaryPluginsVersion;
}
}

View File

@ -1,36 +0,0 @@
package com.r3.csde;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import static java.lang.Thread.sleep;
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
public class ProjectUtils {
ProjectContext pc;
ProjectUtils(ProjectContext _pc) {
pc = _pc;
}
void rpcWait(int millis) {
try {
sleep(millis);
}
catch(InterruptedException e) {
throw new UnsupportedOperationException("Interrupts not supported.", e);
}
}
public void reportError(HttpResponse<JsonNode> response) throws CsdeException {
pc.out.println("*** *** ***");
pc.out.println("Unexpected response from Corda");
pc.out.println("Status="+ response.getStatus());
pc.out.println("*** Headers ***\n"+ response.getHeaders());
pc.out.println("*** Body ***\n"+ response.getBody());
pc.out.println("*** *** ***");
throw new CsdeException("Error: unexpected response from Corda.");
}
}

View File

@ -1,25 +0,0 @@
package com.r3.csde;
/**
* This class is a representation of a Vnode used to express the vNodes required on the network.
*/
public class VNode {
private String x500Name;
private String cpi;
private String serviceX500Name;
public VNode() { }
public String getX500Name(){ return x500Name; }
public void setX500Name(String _x500Name) { x500Name = _x500Name; }
public String getCpi() { return cpi; }
public void setCpi(String _cpi) { cpi = _cpi; }
public String getServiceX500Name() { return serviceX500Name; }
public void setServiceX500Name(String _name) { serviceX500Name = _name; }
}

View File

@ -1,288 +0,0 @@
package com.r3.csde;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.r3.csde.dtos.*;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import java.io.FileInputStream;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static java.net.HttpURLConnection.HTTP_OK;
/**
* The VNodesHelper class is used to create and register the Vnodes specified in the static-network-config.json file.
*/
public class VNodesHelper {
private ProjectContext pc;
private ProjectUtils utils;
private NetworkConfig config;
private ObjectMapper mapper;
public VNodesHelper(ProjectContext _pc, NetworkConfig _config) {
pc = _pc;
utils = new ProjectUtils(pc);
config = _config;
Unirest.config().verifySsl(false);
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* Entry point for setting up vnodes, called from the 5-vNodeSetup task in csde.gradle
*/
public void vNodesSetup() throws CsdeException {
List<VNode> requiredNodes = config.getVNodes();
createVNodes(requiredNodes);
registerVNodes(requiredNodes);
}
/**
* Creates vnodes specified in the config if they don't already exist.
* @param requiredNodes represents the list of VNodes as specified in the network Config json file (static-network-config.json)
*/
private void createVNodes(List<VNode> requiredNodes) throws CsdeException {
// Get existing Nodes.
List<VirtualNodeInfoDTO> existingVNodes = getExistingNodes();
// Check if each required vnode already exist, if not create it.
for (VNode vn : requiredNodes) {
// Match on x500 and cpi name
List<VirtualNodeInfoDTO> matches = existingVNodes
.stream()
.filter(existing ->
existing.getHoldingIdentity().getX500Name().equals( vn.getX500Name()) &&
existing.getCpiIdentifier().getCpiName().equals(vn.getCpi()))
.collect(Collectors.toList());
if (matches.size() == 0) {
createVNode(vn);
}
}
}
/**
* Gets a list of the virtual nodes which have already been created.
* @return a list of the virtual nodes which have already been created.
*/
private List<VirtualNodeInfoDTO> getExistingNodes () throws CsdeException {
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/virtualnode")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if (response.getStatus() != HTTP_OK) {
throw new CsdeException("Failed to get Existing vNodes, response status: "+ response.getStatus());
}
try {
return mapper.readValue(response.getBody().toString(), VirtualNodesDTO.class).getVirtualNodes();
} catch (Exception e){
throw new CsdeException("Failed to get Existing vNodes with exception: " + e);
}
}
/**
* Creates a vnode on the Corda cluster from the VNode info.
* @param vNode represents a virtual node using VNode Class.
*/
private void createVNode(VNode vNode) throws CsdeException {
pc.out.println("Creating virtual node for "+ vNode.getX500Name());
// Reads the current CPIFileChecksum value and checks it has been uploaded.
String cpiCheckSum = getCpiCheckSum(vNode);
if (!checkCpiUploaded(cpiCheckSum)) throw new CsdeException("Cpi " + cpiCheckSum + " not uploaded.");
// Creates the vnode on Cluster
HttpResponse<JsonNode> response = Unirest.post(pc.baseURL + "/api/v1/virtualnode")
.body("{ \"request\" : { \"cpiFileChecksum\": \"" + cpiCheckSum + "\", \"x500Name\": \"" + vNode.getX500Name() + "\" } }")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if (response.getStatus() != HTTP_OK) {
throw new CsdeException("Creation of virtual node failed with response status: " + response.getStatus());
}
}
/**
* Reads the latest CPI checksums from file.
* @param vNode represents a virtual node using VNode Class.
*/
private String getCpiCheckSum(VNode vNode) throws CsdeException {
try {
String file = (vNode.getServiceX500Name() == null) ? pc.CPIUploadStatusFName : pc.NotaryCPIUploadStatusFName;
FileInputStream in = new FileInputStream(file);
CPIFileStatusDTO statusDTO = mapper.readValue(in, CPIFileStatusDTO.class);
return statusDTO.getCpiFileChecksum().toString();
} catch (Exception e) {
throw new CsdeException("Failed to read CPI checksum from file, with error: " + e);
}
}
private boolean checkCpiUploaded(String cpiCheckSum) throws CsdeException {
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/cpi")
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if (response.getStatus() != HTTP_OK) {
throw new CsdeException("Failed to check cpis, response status: " + response.getStatus());
}
try {
GetCPIsResponseDTO cpisResponse = mapper.readValue(
response.getBody().toString(), GetCPIsResponseDTO.class);
for (CpiMetadataDTO cpi: cpisResponse.getCpis()) {
if (Objects.equals(cpi.getCpiFileChecksum(), cpiCheckSum)) {
return true;
}
}
// Returns false if no cpis were returned or the cpiCheckSum didnt' match ay results.
return false;
} catch (Exception e) {
throw new CsdeException("Failed to check cpis with exception: " + e);
}
}
/**
* Checks if the required virtual nodes have been registered and if not registers them.
* @param requiredNodes represents the list of VNodes as specified in the network Config json file (static-network-config.json)
*/
private void registerVNodes(List<VNode> requiredNodes) throws CsdeException {
// There appears to be a delay between the successful post /virtualnodes synchronous call and the
// vnodes being returned in the GET /virtualnodes call. Putting a thread wait here as a quick fix
// as this will move to async mechanism post beta2.
utils.rpcWait(2000);
List<VirtualNodeInfoDTO> existingVNodes = getExistingNodes();
for (VNode vn: requiredNodes) {
// Match on x500 and cpi name
List<VirtualNodeInfoDTO> matches = existingVNodes
.stream()
.filter(existing ->
existing.getHoldingIdentity().getX500Name().equals( vn.getX500Name()) &&
existing.getCpiIdentifier().getCpiName().equals(vn.getCpi()))
.collect(Collectors.toList());
if (matches.size() == 0) {
throw new CsdeException("Registration failed because virtual node for '" + vn.getX500Name() + "' not found.");
} else if (matches.size() >1 ) {
throw new CsdeException(("Registration failed because more than virtual node for '" + vn.getX500Name() + "'"));
}
String shortHash = matches.get(0).getHoldingIdentity().getShortHash();
if (!checkVNodeIsRegistered(shortHash)) {
registerVnode(vn, shortHash);
}
}
}
/**
* Registers a virtual node.
* @param vnode represents the vnode to be registered.
* @param shortHash the shortHash of the virtual node to register.
*/
private void registerVnode(VNode vnode, String shortHash) throws CsdeException {
pc.out.println("Registering vNode: " + vnode.getX500Name() + " with shortHash: " + shortHash);
// Configure the registration body (notary vs non notary)
String registrationBody;
if (vnode.getServiceX500Name() == null) {
registrationBody =
"{ \"action\" : \"requestJoin\"," +
" \"context\" : {" +
" \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\" } }";
} else {
registrationBody =
"{ \"action\" : \"requestJoin\"," +
" \"context\" : {" +
" \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\", " +
" \"corda.roles.0\" : \"notary\", " +
" \"corda.notary.service.name\" : \"" + vnode.getServiceX500Name() + "\", " +
" \"corda.notary.service.plugin\" : \"net.corda.notary.NonValidatingNotary\" } }";
}
HttpResponse<JsonNode> response = Unirest.post(pc.baseURL + "/api/v1/membership/" + shortHash)
.body(registrationBody)
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if (response.getStatus() == HTTP_OK) {
pc.out.println("Membership requested for node " + shortHash);
} else {
throw new CsdeException("Failed to register virtual node " + shortHash + ", response status: " + response.getStatus() );
}
// wait until Vnode registered
pollForRegistration(shortHash, 30000, 1000);
}
/**
* Checks if a virtual node with given shortHash has been registered
* @param shortHash shortHash of the node which is being checked.
* @return returns true if the vnode is registered
*/
private boolean checkVNodeIsRegistered(String shortHash) throws CsdeException {
// Queries registration status for vnode.
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/membership/" + shortHash)
.basicAuth(pc.rpcUser, pc.rpcPasswd)
.asJson();
if (response.getStatus() != HTTP_OK)
throw new CsdeException("Failed to check registration status for virtual node '" + shortHash +
"' response status: " + response.getStatus());
try {
// If the response body is not empty check all previous requests for an "APPROVED"
if (!response.getBody().getArray().isEmpty()) {
List<RegistrationRequestProgressDTO> requests = mapper.readValue(
response.getBody().toString(), new TypeReference<>() {
});
for (RegistrationRequestProgressDTO request : requests) {
if (Objects.equals(request.getRegistrationStatus(), "APPROVED")) {
return true;
}
}
}
// Returns false if array was empty or "APPROVED" wasn't found
return false;
} catch (Exception e) {
throw new CsdeException("Failed to check registration status for " + shortHash +
" with exception " + e);
}
}
/**
* Polls for registration of a vnode
* @param shortHash short hash of the node which is being poled for
* @param duration the number of milliseconds before the pollign times out
* @param cooldown the number of milliseconds in between poll attempts.
*/
private void pollForRegistration(String shortHash, int duration, int cooldown) throws CsdeException {
int timer = 0;
while (timer < duration ) {
if (checkVNodeIsRegistered(shortHash)){
pc.out.println("Vnode " + shortHash +" registered.");
return;
}
utils.rpcWait(cooldown);
timer += cooldown;
}
throw new CsdeException("Vnode " + shortHash + " failed to register in " + duration + " milliseconds");
}
}

View File

@ -1,16 +0,0 @@
package com.r3.csde.dtos;
public class CPIFileStatusDTO {
private String status;
private String cpiFileChecksum;
public CPIFileStatusDTO() {}
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getCpiFileChecksum() { return cpiFileChecksum; }
public void setCpiFileChecksum(String cpiFileChecksum) { this.cpiFileChecksum = cpiFileChecksum; }
}

View File

@ -1,23 +0,0 @@
package com.r3.csde.dtos;
public class CpiIdentifierDTO {
// Note, these DTOs don't cover all returned values, just the ones required for CSDE
private String cpiName;
private String cpiVersion;
private String signerSummaryHash;
public CpiIdentifierDTO() { }
public String getCpiName() { return cpiName; }
public void setCpiName(String cpiName) { this.cpiName = cpiName; }
public String getCpiVersion() { return cpiVersion; }
public void setCpiVersion(String cpiVersion) { this.cpiVersion = cpiVersion; }
public String getSignerSummaryHash() { return signerSummaryHash; }
public void setSignerSummaryHash(String signerSummaryHash) { this.signerSummaryHash = signerSummaryHash; }
}

View File

@ -1,17 +0,0 @@
package com.r3.csde.dtos;
public class CpiMetadataDTO {
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
private String cpiFileChecksum;
private CpiIdentifierDTO id;
public CpiMetadataDTO() {}
public String getCpiFileChecksum() { return cpiFileChecksum; }
public void setCpiFileChecksum(String cpiFileChecksum) { this.cpiFileChecksum = cpiFileChecksum; }
public CpiIdentifierDTO getId() { return id; }
public void setId(CpiIdentifierDTO id) { this.id = id; }
}

View File

@ -1,14 +0,0 @@
package com.r3.csde.dtos;
import java.util.List;
public class GetCPIsResponseDTO {
private List<CpiMetadataDTO> cpis;
public GetCPIsResponseDTO() {}
public List<CpiMetadataDTO> getCpis() { return cpis; }
public void setCpis(List<CpiMetadataDTO> cpis) { this.cpis = cpis; }
}

View File

@ -1,27 +0,0 @@
package com.r3.csde.dtos;
public class HoldingIdentityDTO {
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
private String fullHash;
private String groupId;
private String shortHash;
private String x500Name;
public HoldingIdentityDTO() {}
public String getFullHash() { return fullHash; }
public void setFullHash(String fullHash) { this.fullHash = fullHash; }
public String getGroupId() { return groupId; }
public void setGroupID(String groupID) { this.groupId = groupId; }
public String getShortHash() { return shortHash; }
public void setShortHash(String shortHash) { this.shortHash = shortHash; }
public String getX500Name() { return x500Name; }
public void setX500Name(String x500Name) { this.x500Name = x500Name; }
}

View File

@ -1,17 +0,0 @@
package com.r3.csde.dtos;
public class RegistrationRequestProgressDTO {
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
private String registrationStatus;
private String reason;
public RegistrationRequestProgressDTO() {}
public String getRegistrationStatus() { return registrationStatus; }
public void setRegistrationStatus(String registrationStatus) { this.registrationStatus = registrationStatus; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
}

View File

@ -1,17 +0,0 @@
package com.r3.csde.dtos;
public class VirtualNodeInfoDTO {
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
private HoldingIdentityDTO holdingIdentity;
private CpiIdentifierDTO cpiIdentifier;
public VirtualNodeInfoDTO() {}
public HoldingIdentityDTO getHoldingIdentity() { return holdingIdentity; }
public void setHoldingIdentity(HoldingIdentityDTO holdingIdentity) { this.holdingIdentity = holdingIdentity; }
public CpiIdentifierDTO getCpiIdentifier() { return cpiIdentifier; }
public void setCpiIdentifier(CpiIdentifierDTO cpiIdentifier) { this.cpiIdentifier = cpiIdentifier; }
}

View File

@ -1,14 +0,0 @@
package com.r3.csde.dtos;
import java.util.List;
public class VirtualNodesDTO {
private List<VirtualNodeInfoDTO> virtualNodes;
public VirtualNodesDTO() {}
public List<VirtualNodeInfoDTO> getVirtualNodes() { return virtualNodes; }
public void setVirtualNodes(List<VirtualNodeInfoDTO> virtualNodes) { this.virtualNodes = virtualNodes; }
}

View File

@ -1,23 +1,23 @@
[ [
{ {
"x500Name" : "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB", "x500Name" : "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB",
"cpi" : "cpi name" "cpi" : "MyCorDapp"
}, },
{ {
"x500Name" : "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", "x500Name" : "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
"cpi" : "cpi name" "cpi" : "MyCorDapp"
}, },
{ {
"x500Name" : "CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB", "x500Name" : "CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB",
"cpi" : "cpi name" "cpi" : "MyCorDapp"
}, },
{ {
"x500Name" : "CN=Dave, OU=Test Dept, O=R3, L=London, C=GB", "x500Name" : "CN=Dave, OU=Test Dept, O=R3, L=London, C=GB",
"cpi" : "cpi name" "cpi" : "MyCorDapp"
}, },
{ {
"x500Name" : "CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB", "x500Name" : "CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB",
"cpi" : "CSDE Notary Server CPI", "cpi" : "NotaryServer",
"serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB" "serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB"
} }
] ]

View File

@ -4,10 +4,6 @@ kotlin.code.style=official
# This needs to match the version supported by the Corda Cluster the CorDapp will run on. # This needs to match the version supported by the Corda Cluster the CorDapp will run on.
cordaApiVersion=5.0.0.665-Gecko1.0 cordaApiVersion=5.0.0.665-Gecko1.0
# Settings For Development Utilities
combinedWorkerVersion=5.0.0.0-Gecko1.0
simulatorVersion=5.0.0.0-Gecko1.0
# Specify the version of the notary plugins to use. # Specify the version of the notary plugins to use.
# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version. # Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version.
cordaNotaryPluginsVersion=5.0.0.0-Gecko1.0 cordaNotaryPluginsVersion=5.0.0.0-Gecko1.0
@ -15,6 +11,9 @@ cordaNotaryPluginsVersion=5.0.0.0-Gecko1.0
# Specify the version of the cordapp-cpb and cordapp-cpk plugins # Specify the version of the cordapp-cpb and cordapp-cpk plugins
cordaPluginsVersion=7.0.1 cordaPluginsVersion=7.0.1
# Specify the version of the CSDE gradle plugin to use
csdePluginVersion=1.0.+
# For the time being this just needs to be set to a dummy value. # For the time being this just needs to be set to a dummy value.
platformVersion = 999 platformVersion = 999
@ -31,15 +30,7 @@ mockitoKotlinVersion=4.0.0
mockitoVersion=4.6.1 mockitoVersion=4.6.1
hamcrestVersion=2.2 hamcrestVersion=2.2
postgresqlVersion=42.4.3
cordaClusterURL=https://localhost:8888
cordaRpcUser=admin
cordaRpcPasswd=admin
devEnvWorkspace=workspace
dbContainerName=CSDEpostgresql
# R3 internal repository # R3 internal repository
#S3 version for HC and RC versions published by CD/CD team # Use this version when pointing to artefacts in artifactory that have not been published to S3
artifactoryContextUrl=https://staging.download.corda.net/maven/20ede3c6-29c0-11ed-966d-b7c36748b9f6-Gecko-RC03/ artifactoryContextUrl=https://software.r3.com/artifactory
#artifactoryContextUrl=https://staging.download.corda.net/maven/20ede3c6-29c0-11ed-966d-b7c36748b9f6-Gecko-RC02/

View File

@ -3,8 +3,16 @@ pluginManagement {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
mavenCentral() mavenCentral()
mavenLocal()
maven { maven {
url = "$artifactoryContextUrl/" url = "$artifactoryContextUrl/corda-os-maven"
authentication {
basic(BasicAuthentication)
}
credentials {
username = settings.ext.find('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = settings.ext.find('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
} }
} }
@ -14,6 +22,7 @@ pluginManagement {
id 'net.corda.plugins.cordapp-cpk2' version cordaPluginsVersion id 'net.corda.plugins.cordapp-cpk2' version cordaPluginsVersion
id 'net.corda.plugins.cordapp-cpb2' version cordaPluginsVersion id 'net.corda.plugins.cordapp-cpb2' version cordaPluginsVersion
id 'net.corda.cordapp.cordapp-configuration' version cordaApiVersion id 'net.corda.cordapp.cordapp-configuration' version cordaApiVersion
id 'net.corda.plugins.csde' version csdePluginVersion
id 'org.jetbrains.kotlin.jvm' version kotlinVersion id 'org.jetbrains.kotlin.jvm' version kotlinVersion
id 'org.jetbrains.kotlin.plugin.jpa' version kotlinVersion id 'org.jetbrains.kotlin.plugin.jpa' version kotlinVersion
id 'org.jetbrains.kotlin.plugin.allopen' version kotlinVersion id 'org.jetbrains.kotlin.plugin.allopen' version kotlinVersion