asset-transfer-basic CI improvements (#998)

* Return exit(1) if application-java fails

If application-java fails, return an exit(1) code,
so that callers such as Github Actions CI can detect the failure.

Signed-off-by: David Enyeart <enyeart@us.ibm.com>

* asset-transfer-basic CI improvements

- Application failure should result in CI failure
- Automatically remove wallet from prior runs
- Fix chaincode name issues, allow chaincode name to be passed from CI
- Fix appUser collisions (duplicate registration failures)
- Fix key create collisions across apps (in cases where same chaincode is used for multiple apps)

Signed-off-by: David Enyeart <enyeart@us.ibm.com>

---------

Signed-off-by: David Enyeart <enyeart@us.ibm.com>
This commit is contained in:
Dave Enyeart 2023-03-07 04:43:10 -05:00 committed by GitHub
parent 199e290f5e
commit 488e74ed08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 83 additions and 61 deletions

View file

@ -24,7 +24,10 @@ func main() {
log.Fatalf("Error setting DISCOVERY_AS_LOCALHOST environment variable: %v", err) log.Fatalf("Error setting DISCOVERY_AS_LOCALHOST environment variable: %v", err)
} }
wallet, err := gateway.NewFileSystemWallet("wallet") walletPath := "wallet"
// remove any existing wallet from prior runs
os.RemoveAll(walletPath)
wallet, err := gateway.NewFileSystemWallet(walletPath)
if err != nil { if err != nil {
log.Fatalf("Failed to create wallet: %v", err) log.Fatalf("Failed to create wallet: %v", err)
} }
@ -55,23 +58,23 @@ func main() {
} }
defer gw.Close() defer gw.Close()
channelName := "mychannel" channelName := "mychannel"
if cname := os.Getenv("CHANNEL_NAME"); cname != "" { if cname := os.Getenv("CHANNEL_NAME"); cname != "" {
channelName = cname channelName = cname
} }
log.Println("--> Connecting to channel", channelName) log.Println("--> Connecting to channel", channelName)
network, err := gw.GetNetwork(channelName) network, err := gw.GetNetwork(channelName)
if err != nil { if err != nil {
log.Fatalf("Failed to get network: %v", err) log.Fatalf("Failed to get network: %v", err)
} }
chaincodeName := "basic" chaincodeName := "basic"
if ccname := os.Getenv("CHAINCODE_NAME"); ccname != "" { if ccname := os.Getenv("CHAINCODE_NAME"); ccname != "" {
chaincodeName = ccname chaincodeName = ccname
} }
log.Println("--> Using chaincode", chaincodeName) log.Println("--> Using chaincode", chaincodeName)
contract := network.GetContract(chaincodeName) contract := network.GetContract(chaincodeName)
log.Println("--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger") log.Println("--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger")
@ -89,14 +92,14 @@ func main() {
log.Println(string(result)) log.Println(string(result))
log.Println("--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments") log.Println("--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments")
result, err = contract.SubmitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300") result, err = contract.SubmitTransaction("CreateAsset", "asset113", "yellow", "5", "Tom", "1300")
if err != nil { if err != nil {
log.Fatalf("Failed to Submit transaction: %v", err) log.Fatalf("Failed to Submit transaction: %v", err)
} }
log.Println(string(result)) log.Println(string(result))
log.Println("--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID") log.Println("--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID")
result, err = contract.EvaluateTransaction("ReadAsset", "asset13") result, err = contract.EvaluateTransaction("ReadAsset", "asset113")
if err != nil { if err != nil {
log.Fatalf("Failed to evaluate transaction: %v\n", err) log.Fatalf("Failed to evaluate transaction: %v\n", err)
} }

View file

@ -20,6 +20,9 @@ import org.hyperledger.fabric.gateway.Wallets;
public class App { public class App {
private static final String CHANNEL_NAME = System.getenv().getOrDefault("CHANNEL_NAME", "mychannel");
private static final String CHAINCODE_NAME = System.getenv().getOrDefault("CHAINCODE_NAME", "basic");
static { static {
System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true"); System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
} }
@ -33,7 +36,7 @@ public class App {
Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml"); Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml");
Gateway.Builder builder = Gateway.createBuilder(); Gateway.Builder builder = Gateway.createBuilder();
builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true); builder.identity(wallet, "javaAppUser").networkConfig(networkConfigPath).discovery(true);
return builder.connect(); return builder.connect();
} }
@ -50,8 +53,8 @@ public class App {
try (Gateway gateway = connect()) { try (Gateway gateway = connect()) {
// get the network and contract // get the network and contract
Network network = gateway.getNetwork("mychannel"); Network network = gateway.getNetwork(CHANNEL_NAME);
Contract contract = network.getContract("basic"); Contract contract = network.getContract(CHAINCODE_NAME);
byte[] result; byte[] result;
@ -63,14 +66,14 @@ public class App {
System.out.println("Evaluate Transaction: GetAllAssets, result: " + new String(result)); System.out.println("Evaluate Transaction: GetAllAssets, result: " + new String(result));
System.out.println("\n"); System.out.println("\n");
System.out.println("Submit Transaction: CreateAsset asset13"); System.out.println("Submit Transaction: CreateAsset asset213");
// CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300 // CreateAsset creates an asset with ID asset213, color yellow, owner Tom, size 5 and appraisedValue of 1300
contract.submitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300"); contract.submitTransaction("CreateAsset", "asset213", "yellow", "5", "Tom", "1300");
System.out.println("\n"); System.out.println("\n");
System.out.println("Evaluate Transaction: ReadAsset asset13"); System.out.println("Evaluate Transaction: ReadAsset asset213");
// ReadAsset returns an asset with given assetID // ReadAsset returns an asset with given assetID
result = contract.evaluateTransaction("ReadAsset", "asset13"); result = contract.evaluateTransaction("ReadAsset", "asset213");
System.out.println("result: " + new String(result)); System.out.println("result: " + new String(result));
System.out.println("\n"); System.out.println("\n");
@ -110,6 +113,7 @@ public class App {
} }
catch(Exception e){ catch(Exception e){
System.err.println(e); System.err.println(e);
System.exit(1);
} }
} }

View file

@ -9,6 +9,9 @@ package application.java;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Properties; import java.util.Properties;
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.hyperledger.fabric.gateway.Wallet; import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallets; import org.hyperledger.fabric.gateway.Wallets;
import org.hyperledger.fabric.gateway.Identities; import org.hyperledger.fabric.gateway.Identities;
@ -32,6 +35,9 @@ public class EnrollAdmin {
CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite(); CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
caClient.setCryptoSuite(cryptoSuite); caClient.setCryptoSuite(cryptoSuite);
// Delete wallet if it exists from prior runs
FileUtils.deleteDirectory(new File("wallet"));
// Create a wallet for managing identities // Create a wallet for managing identities
Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));

View file

@ -38,8 +38,8 @@ public class RegisterUser {
Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
// Check to see if we've already enrolled the user. // Check to see if we've already enrolled the user.
if (wallet.get("appUser") != null) { if (wallet.get("javaAppUser") != null) {
System.out.println("An identity for the user \"appUser\" already exists in the wallet"); System.out.println("An identity for the user \"javaAppUser\" already exists in the wallet");
return; return;
} }
@ -94,14 +94,14 @@ public class RegisterUser {
}; };
// Register the user, enroll the user, and import the new identity into the wallet. // Register the user, enroll the user, and import the new identity into the wallet.
RegistrationRequest registrationRequest = new RegistrationRequest("appUser"); RegistrationRequest registrationRequest = new RegistrationRequest("javaAppUser");
registrationRequest.setAffiliation("org1.department1"); registrationRequest.setAffiliation("org1.department1");
registrationRequest.setEnrollmentID("appUser"); registrationRequest.setEnrollmentID("javaAppUser");
String enrollmentSecret = caClient.register(registrationRequest, admin); String enrollmentSecret = caClient.register(registrationRequest, admin);
Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret); Enrollment enrollment = caClient.enroll("javaAppUser", enrollmentSecret);
Identity user = Identities.newX509Identity("Org1MSP", enrollment); Identity user = Identities.newX509Identity("Org1MSP", enrollment);
wallet.put("appUser", user); wallet.put("javaAppUser", user);
System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet"); System.out.println("Successfully enrolled user \"javaAppUser\" and imported it into the wallet");
} }
} }

View file

@ -12,11 +12,12 @@ const path = require('path');
const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js'); const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js'); const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js');
const channelName = 'mychannel'; const channelName = process.env.CHANNEL_NAME || 'mychannel';
const chaincodeName = 'basic'; const chaincodeName = process.env.CHAINCODE_NAME || 'basic';
const mspOrg1 = 'Org1MSP'; const mspOrg1 = 'Org1MSP';
const walletPath = path.join(__dirname, 'wallet'); const walletPath = path.join(__dirname, 'wallet');
const org1UserId = 'appUser'; const org1UserId = 'javascriptAppUser';
function prettyJSONString(inputString) { function prettyJSONString(inputString) {
return JSON.stringify(JSON.parse(inputString), null, 2); return JSON.stringify(JSON.parse(inputString), null, 2);
@ -128,14 +129,14 @@ async function main() {
// This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
// to the orderer to be committed by each of the peer's to the channel ledger. // to the orderer to be committed by each of the peer's to the channel ledger.
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments');
result = await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); result = await contract.submitTransaction('CreateAsset', 'asset313', 'yellow', '5', 'Tom', '1300');
console.log('*** Result: committed'); console.log('*** Result: committed');
if (`${result}` !== '') { if (`${result}` !== '') {
console.log(`*** Result: ${prettyJSONString(result.toString())}`); console.log(`*** Result: ${prettyJSONString(result.toString())}`);
} }
console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID');
result = await contract.evaluateTransaction('ReadAsset', 'asset13'); result = await contract.evaluateTransaction('ReadAsset', 'asset313');
console.log(`*** Result: ${prettyJSONString(result.toString())}`); console.log(`*** Result: ${prettyJSONString(result.toString())}`);
console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist'); console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist');
@ -174,7 +175,9 @@ async function main() {
} }
} catch (error) { } catch (error) {
console.error(`******** FAILED to run the application: ${error}`); console.error(`******** FAILED to run the application: ${error}`);
process.exit(1);
} }
} }
main(); main();

View file

@ -3,8 +3,8 @@
"version": "1.0.0", "version": "1.0.0",
"description": "Asset-transfer-basic application implemented in JavaScript", "description": "Asset-transfer-basic application implemented in JavaScript",
"engines": { "engines": {
"node": ">=12", "node": ">=14.14",
"npm": ">=5" "npm": ">=6"
}, },
"scripts": { "scripts": {
"lint": "eslint *.js", "lint": "eslint *.js",

View file

@ -53,13 +53,13 @@ const transactionsToSend: TransactionToSendFormat[] = [
{ {
request: 'submit', request: 'submit',
txName: 'CreateAsset', txName: 'CreateAsset',
txArgs: ['asset13', 'yellow', '5', 'Tom', '1300'], txArgs: ['asset513', 'yellow', '5', 'Tom', '1300'],
description: '\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments', description: '\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments',
}, },
{ {
request: 'evaluate', request: 'evaluate',
txName: 'ReadAsset', txName: 'ReadAsset',
txArgs: ['asset13'], txArgs: ['asset513'],
description: '\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID', description: '\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID',
}, },
{ {

View file

@ -5,8 +5,8 @@
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
"engines": { "engines": {
"node": ">=12", "node": ">=14.14",
"npm": ">=5" "npm": ">=6"
}, },
"scripts": { "scripts": {
"lint": "tslint -c tslint.json 'src/**/*.ts'", "lint": "tslint -c tslint.json 'src/**/*.ts'",
@ -24,9 +24,9 @@
"fabric-network": "^2.2.4" "fabric-network": "^2.2.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^12.20.55", "@types/node": "^14.17.32",
"tslint": "^5.11.0", "tslint": "^5.11.0",
"typescript": "^3.1.6" "typescript": "~4.9.4"
}, },
"nyc": { "nyc": {
"extension": [ "extension": [

View file

@ -8,11 +8,12 @@ import * as path from 'path';
import { buildCCPOrg1, buildWallet, prettyJSONString } from './utils//AppUtil'; import { buildCCPOrg1, buildWallet, prettyJSONString } from './utils//AppUtil';
import { buildCAClient, enrollAdmin, registerAndEnrollUser } from './utils/CAUtil'; import { buildCAClient, enrollAdmin, registerAndEnrollUser } from './utils/CAUtil';
const channelName = 'mychannel'; const channelName = process.env.CHANNEL_NAME || 'mychannel';
const chaincodeName = 'basic'; const chaincodeName = process.env.CHAINCODE_NAME || 'basic';
const mspOrg1 = 'Org1MSP'; const mspOrg1 = 'Org1MSP';
const walletPath = path.join(__dirname, 'wallet'); const walletPath = path.join(__dirname, 'wallet');
const org1UserId = 'appUser'; const org1UserId = 'typescriptAppUser';
// pre-requisites: // pre-requisites:
// - fabric-sample two organization test-network setup with two peers, ordering service, // - fabric-sample two organization test-network setup with two peers, ordering service,
@ -121,11 +122,11 @@ async function main() {
// This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
// to the orderer to be committed by each of the peer's to the channel ledger. // to the orderer to be committed by each of the peer's to the channel ledger.
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments');
await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); await contract.submitTransaction('CreateAsset', 'asset413', 'yellow', '5', 'Tom', '1300');
console.log('*** Result: committed'); console.log('*** Result: committed');
console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID');
result = await contract.evaluateTransaction('ReadAsset', 'asset13'); result = await contract.evaluateTransaction('ReadAsset', 'asset413');
console.log(`*** Result: ${prettyJSONString(result.toString())}`); console.log(`*** Result: ${prettyJSONString(result.toString())}`);
console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist'); console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist');
@ -164,6 +165,7 @@ async function main() {
} }
} catch (error) { } catch (error) {
console.error(`******** FAILED to run the application: ${error}`); console.error(`******** FAILED to run the application: ${error}`);
process.exit(1);
} }
} }

View file

@ -46,6 +46,10 @@ const buildWallet = async (walletPath: string): Promise<Wallet> => {
// Create a new wallet : Note that wallet is for managing identities. // Create a new wallet : Note that wallet is for managing identities.
let wallet: Wallet; let wallet: Wallet;
if (walletPath) { if (walletPath) {
// remove any pre-existing wallet from prior runs
fs.rmSync(walletPath, { recursive: true, force: true });
wallet = await Wallets.newFileSystemWallet(walletPath); wallet = await Wallets.newFileSystemWallet(walletPath);
console.log(`Built a file system wallet at ${walletPath}`); console.log(`Built a file system wallet at ${walletPath}`);
} else { } else {

View file

@ -37,7 +37,7 @@ createNetwork
# Run Go application # Run Go application
print "Initializing Go application" print "Initializing Go application"
export CHAINCODE_NAME=basic_go export CHAINCODE_NAME=basic_${CHAINCODE_LANGUAGE}_for_go_app
deployChaincode deployChaincode
pushd ../asset-transfer-basic/application-go pushd ../asset-transfer-basic/application-go
print "Executing AssetTransfer.go" print "Executing AssetTransfer.go"
@ -46,7 +46,7 @@ popd
# Run Java application # Run Java application
print "Initializing Java application" print "Initializing Java application"
export CHAINCODE_NAME=basic_java export CHAINCODE_NAME=basic_${CHAINCODE_LANGUAGE}_for_java_app
deployChaincode deployChaincode
pushd ../asset-transfer-basic/application-java pushd ../asset-transfer-basic/application-java
print "Executing Gradle Run" print "Executing Gradle Run"
@ -55,7 +55,7 @@ popd
# Run Javascript application # Run Javascript application
print "Initializing Javascript application" print "Initializing Javascript application"
export CHAINCODE_NAME=basic_javascript export CHAINCODE_NAME=basic_${CHAINCODE_LANGUAGE}_for_javascript_app
deployChaincode deployChaincode
pushd ../asset-transfer-basic/application-javascript pushd ../asset-transfer-basic/application-javascript
npm install npm install
@ -65,7 +65,7 @@ popd
# Run typescript application # Run typescript application
print "Initializing Typescript application" print "Initializing Typescript application"
export CHAINCODE_NAME=basic_typescript export CHAINCODE_NAME=basic_${CHAINCODE_LANGUAGE}_for_typescript_app
deployChaincode deployChaincode
pushd ../asset-transfer-basic/application-typescript pushd ../asset-transfer-basic/application-typescript
npm install npm install

View file

@ -76,7 +76,7 @@ nginx:
#!/bin/bash #!/bin/bash
kubectl apply -k https://github.com/hyperledger-labs/fabric-operator.git/config/ingress/{{ cluster_runtime }} kubectl apply -k https://github.com/hyperledger-labs/fabric-operator.git/config/ingress/{{ cluster_runtime }}
sleep 10 sleep 20
kubectl wait --namespace ingress-nginx \ kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \ --for=condition=ready pod \