Skip to content

Commit

Permalink
Merge "FABN-844 NodeSDK local peer discovery"
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew-coleman authored and Gerrit Code Review committed Sep 19, 2018
2 parents 119aaa7 + 5c34936 commit cdd6291
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 16 deletions.
6 changes: 5 additions & 1 deletion docs/tutorials/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ orderers to send the signed endorsements.
* `channel.refresh()` - The channel will be refreshed with new service discovery
results, add new peers, orderers, and MSPs. The call will use the service discovery
settings as provided on the `channel.initialize()` call.
* `getDiscoveryResults` - The channel will cache the results of the last query
* `channel.getDiscoveryResults()` - The channel will cache the results of the last query
made to the service discovery and make the results available. The call will use
the `discovery-cache-life` setting to determine if the results should be refreshed.
if the results need to be refreshed, the `channel.refresh()` will be called
internally to fetch new service discovery results. The call is used by the
`DiscoveryEndorsementHandler` as it starts to determine the target peers.
* `client.queryPeers()` - A client object will be able to query a target peer
using the discovery service to provide a list of peer endpoints and associated
organizations active on the network at the time of the query. see {@link Client#queryPeers}


#### New configuration settings
* `initialize-with-discovery` - boolean - When the applications calls for the
Expand Down
11 changes: 9 additions & 2 deletions fabric-client/lib/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ const Channel = class {
}

_buildDiscoveryEndorsementPlan(discovery_results, plan_id, msps, options){
const method = '_buildDiscoveryPeers';
const method = '_buildDiscoveryEndorsementPlan';
logger.debug('%s - build endorsement plan for %s', method, plan_id);

const endorsement_plan = discovery_results.endorsement_plans[0];
Expand Down Expand Up @@ -1099,6 +1099,8 @@ const Channel = class {
* should be included in the discovery query.
* @property {boolean} local - Optional. To indicate that the local endpoints
* should be included in the discovery query.
* @property {boolean} useAdmin - Optional. To indicate that the admin identity
* should be used to make the discovery request
*/

/**
Expand All @@ -1116,8 +1118,13 @@ const Channel = class {
if (!request) {
request = {};
}

let useAdmin = true; //default
if(typeof request.useAdmin === 'boolean') {
useAdmin = request.useAdmin;
}
const target_peer = this._getTargetForDiscovery(request.target);
const signer = this._clientContext._getSigningIdentity(true); //use the admin if assigned
const signer = this._clientContext._getSigningIdentity(useAdmin); //use the admin if assigned
const discovery_request = new _discoveryProto.Request();

const authentication = new _discoveryProto.AuthInfo();
Expand Down
74 changes: 74 additions & 0 deletions fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,80 @@ const Client = class extends BaseClient {
return results;
}

/**
* @typedef {Object} PeerQueryRequest
* @property {Peer | string} target - The {@link Peer} object or peer name to
* use for the service discovery request
* @property {boolean} useAdmin - Optional. Indicates that the admin credentials
* should be used in making this call to the peer. An administrative
* identity must have been loaded by a connection profile or by
* using the 'setAdminSigningIdentity' method.
*/

/**
* @typedef {Object} PeerQueryResponse
* @property {Object} peers_by_org
* @example
{
"peers_by_org": {
"Org1MSP": {
"peers":[
{"mspid":"Org1MSP", "endpoint":"peer0.org1.example.com:7051"}
]
},
"Org2MSP": {
"peers":[
{"mspid":"Org2MSP","endpoint":"peer0.org2.example.com:8051"}
]
}
}
}
*/

/**
* Queries the target peer for a list of {@link Peer} objects of all peers
* known by the target peer.
*
* @param {PeerQueryRequest} request - The request parameters.
* @returns {PeerQueryResponse} The list of peers
*/
async queryPeers(request) {
const method = 'queryPeers';
logger.debug('%s - start', method);

let targets = null;
if (!request && !request.target) {
throw Error('Peer is required');
} else {
targets = this.getTargetPeers(request.target);
if(!targets && !targets[0]) {
throw Error('Peer not found');
}
}

try {
const discover_request = {
target: targets[0],
local: true,
config: false, //config only for channel queries
useAdmin: request.useAdmin
};

// create dummy channel just to use the dicovery code
// since channel does not exist only the local query will work
const channel = new Channel('discover-peers', this);
const results = {};

const discovery_results = await channel._discover(discover_request);

return discovery_results;
}
catch(error) {
logger.error(error);
throw Error('Failed to discover local peers ::'+ error.toString());
}
}

/**
* @typedef {Object} ChannelQueryResponse
* @property {ChannelInfo[]} channels
Expand Down
61 changes: 48 additions & 13 deletions test/integration/discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ const fs = require('fs');
const path = require('path');

const testUtil = require('../unit/util.js');
const e2eUtils = require('../integration/e2e/e2eUtils.js');

test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
test('\n\n***** D I S C O V E R Y *****\n\n', async (t) => {

// this will use the connection profile to set up the client
const client_org1 = await testUtil.getClientForOrg(t, 'org1');
Expand Down Expand Up @@ -96,6 +95,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
interests: [{chaincodes: [{name:first_chaincode_name}]}],
config: true
});
t.comment('Found first test information ::' + JSON.stringify(results));

const ledger_height = 3;
t.equals(results.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID');
Expand All @@ -108,11 +108,12 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
if(results.endorsement_plans[0].groups.G0.peers[0].endpoint.includes('example.com:')) t.pass('Checking plan peer endpoint');
else t.fail('Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
testLayoutQuantities(t, results.endorsement_plans[0].layouts);
} else {
t.fail('MISSING group results');
}
Expand All @@ -124,6 +125,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
interests: [{chaincodes: [{name:first_chaincode_name}]}],
config: true
});
t.comment('Found second test information ::' + JSON.stringify(results));

t.equals(results.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID');
t.equals(results.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID');
Expand All @@ -136,11 +138,12 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].chaincode, first_chaincode_name, 'Checking plan id');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
if(results.endorsement_plans[0].groups.G0.peers[0].endpoint.includes('example.com:')) t.pass('Checking plan peer endpoint');
else t.fail('Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
testLayoutQuantities(t, results.endorsement_plans[0].layouts);
} else {
t.fail('MISSING group results');
}
Expand All @@ -150,6 +153,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
interests: [{chaincodes: [{name:first_chaincode_name}]}],
config: true
});
t.comment('Found third test information ::' + JSON.stringify(results));

t.equals(results.msps.OrdererMSP.id, 'OrdererMSP', 'Checking MSP ID');
t.equals(results.msps.Org1MSP.id, 'Org1MSP', 'Checking MSP ID');
Expand All @@ -161,15 +165,32 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking peer chaincode name');
t.equals(results.peers_by_org.Org1MSP.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking peer chaincode version');
if(results.endorsement_plans[0].groups.G0) {
t.equals(results.endorsement_plans[0].groups.G0.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking plan peer endpoint');
if(results.endorsement_plans[0].groups.G0.peers[0].endpoint.includes('example.com:')) t.pass('Checking plan peer endpoint');
else t.fail('Checking plan peer endpoint');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].ledger_height.low, ledger_height, 'Checking plan peer ledger_height');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].name, first_chaincode_name, 'Checking plan peer chaincode name');
t.equals(results.endorsement_plans[0].groups.G0.peers[0].chaincodes[0].version, first_chaincode_ver, 'Checking plan peer chaincode version');
t.equals(results.endorsement_plans[0].layouts[0].G0, 1, 'Checking layout quantities_by_group');
testLayoutQuantities(t, results.endorsement_plans[0].layouts);
} else {
t.fail('MISSING group results');
}

// check that we are able to make a query for the local peers
const queryPeerRequest = {
target: peer_org1,
useAdmin: true,
asLocalhost: true
};

results = await client_org1.queryPeers(queryPeerRequest);

t.comment('Found local peer information ::' + JSON.stringify(results));

t.equals(results.peers_by_org.Org1MSP.peers[0].endpoint, 'peer0.org1.example.com:7051', 'Checking org1 peer endpoint');
t.equals(results.peers_by_org.Org2MSP.peers[0].endpoint, 'peer0.org2.example.com:8051', 'Checking org2 peer endpoint');


// clean up
channel_org1.removePeer(peer_org1);
channel_org1._use_discovery = false;

Expand All @@ -186,7 +207,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {

// check orgs ... actually gets names from the msps loaded
const orgs = channel_org1.getOrganizations();
for(let index in orgs) {
for(const index in orgs) {
const org = orgs[index].id;
if(org === 'Org1MSP' || org === 'Org2MSP' || org === 'OrdererMSP') {
t.pass('Checking call to get organizations on the channel after using the discovery service for ' + org);
Expand All @@ -211,6 +232,8 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
await testUtil.queryChannelAsAdmin(t, client_org1, channel_org1, tx_id_string, null, first_chaincode_name);

const discovered_peers = channel_org1.getPeers();
t.equals(discovered_peers.length, 2, 'Checking the size of discovered peers');

const force_target_request = {
chaincodeId: first_chaincode_name,
target: 'peer0.org1.example.com'
Expand All @@ -219,7 +242,7 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {

t.pass('***** Invokes and Queries complete *****');

let tx_id = client_org1.newTransactionID(true);
const tx_id = client_org1.newTransactionID(true);
tx_id_string = tx_id.getTransactionID();
request = {
chaincodeId : 'first',
Expand Down Expand Up @@ -294,6 +317,18 @@ test('\n\n***** D I S C O V E R Y *****\n\n', async function(t) {
t.end();
});

function testLayoutQuantities(t, layouts) {
for(const layout of layouts) {
if(layout['G0']) {
t.equals(layout.G0, 1, 'Checking layout quantities_by_group');
} else if(layout['G1']) {
t.equals(layout.G1, 1, 'Checking layout quantities_by_group');
} else {
t.fail('Layout quantities_by_group not found');
}
}
}

async function installChaincode(t, client, channel, peer, chaincode_id, chaincode_ver) {
const chaincode_path = path.resolve(__dirname, '../fixtures/src/node_cc/example_cc');

Expand Down Expand Up @@ -412,9 +447,9 @@ async function createUpdateChannel(t, create, file, channel_name, client_org1, c
// now we have enough signatures...

// get an admin based transaction
let tx_id = client_org1.newTransactionID(true);
const tx_id = client_org1.newTransactionID(true);

let request = {
const request = {
config: config,
signatures : signatures,
name : channel_name,
Expand Down Expand Up @@ -467,7 +502,7 @@ async function joinChannel(t, channel_name, peer, orderer, client) {
txId : tx_id
};

let join_results = await channel.joinChannel(request, 30000);
const join_results = await channel.joinChannel(request, 30000);
if(join_results && join_results[0] && join_results[0].response && join_results[0].response.status == 200) {
t.pass('Successfully joined channnel on org');
} else {
Expand Down

0 comments on commit cdd6291

Please sign in to comment.