From 2161c2b9562f7857564de4e199f97179b12e4fc4 Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Tue, 24 Oct 2017 07:50:12 -0400 Subject: [PATCH] [FAB-6679] NodeSDK - CCP timeouts Add support for the connection profile's timeout settings. Change-Id: I6ced5401c0389cab2841d38d198b249f6cfcb5e3 Signed-off-by: Bret Harrison --- fabric-client/lib/Client.js | 4 +- fabric-client/lib/impl/NetworkConfig_1_0.js | 73 ++++++++++++++++----- test/fixtures/org1.yaml | 36 +++++++--- test/fixtures/org2.yaml | 36 +++++++--- test/integration/e2e/e2eUtils.js | 2 +- test/integration/e2e/join-channel.js | 3 +- test/integration/memory.js | 4 +- test/integration/network-config.js | 9 ++- test/unit/network-config.js | 30 ++++++++- 9 files changed, 155 insertions(+), 42 deletions(-) diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index fa6aa38321..5b3ab12150 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -879,8 +879,8 @@ var Client = class extends BaseClient { /** * @typedef {Object} ChaincodeInstallRequest - * @property {Peer[]} targets - Optional. An array of Peer objects that the - * chaincode will be installed on. When excluded, the peers assigned + * @property {Peer[]} targets - Optional. An array of Peer objects where the + * chaincode will be installed. When excluded, the peers assigned * to this client's organization will be used as defined in the * network configuration. If the 'channels' property is included, * the target peers will be based the peers defined in the channels. diff --git a/fabric-client/lib/impl/NetworkConfig_1_0.js b/fabric-client/lib/impl/NetworkConfig_1_0.js index cc14a081c5..5e00f235ea 100644 --- a/fabric-client/lib/impl/NetworkConfig_1_0.js +++ b/fabric-client/lib/impl/NetworkConfig_1_0.js @@ -30,23 +30,29 @@ var CertificateAuthority = require('../CertificateAuthority.js'); var FabricCA = require('fabric-ca-client'); var logger = utils.getLogger('NetworkConfig101.js'); -var CHANNELS_CONFIG = 'channels'; -var ORGS_CONFIG = 'organizations'; -var PEERS_CONFIG = 'peers'; -var ORDERERS_CONFIG = 'orderers'; -var CAS_CONFIG = 'certificateAuthorities'; -var TLS_CACERTS = 'tlsCACerts'; -var ADMIN_PRIVATE_KEY = 'adminPrivateKey'; -var ADMIN_CERT = 'signedCert'; -var GRPC_CONNECTION_OPTIONS = 'grpcOptions'; -var HTTP_CONNECTION_OPTIONS = 'httpOptions'; -var URL = 'url'; -var EVENT_URL = 'eventUrl'; -var NAME = 'name'; -var CANAME = 'caName'; -var PEM = 'pem'; -var PATH = 'path'; -var REGISTRAR = 'registrar'; +const CHANNELS_CONFIG = 'channels'; +const ORGS_CONFIG = 'organizations'; +const PEERS_CONFIG = 'peers'; +const ORDERERS_CONFIG = 'orderers'; +const CAS_CONFIG = 'certificateAuthorities'; +const TLS_CACERTS = 'tlsCACerts'; +const ADMIN_PRIVATE_KEY = 'adminPrivateKey'; +const ADMIN_CERT = 'signedCert'; +const GRPC_CONNECTION_OPTIONS = 'grpcOptions'; +const HTTP_CONNECTION_OPTIONS = 'httpOptions'; +const URL = 'url'; +const EVENT_URL = 'eventUrl'; +const NAME = 'name'; +const CANAME = 'caName'; +const PEM = 'pem'; +const PATH = 'path'; +const REGISTRAR = 'registrar'; +const ENDORSER = 1; +const ORDERER = 2; +const EVENTHUB = 3; +const EVENTREG = 4; +const TYPES = ['unknown', 'endorser', 'orderer', 'eventHub', 'eventReg']; +const REQUEST_TIMEOUT = 'request-timeout'; var ROLES = Constants.NetworkConfig.ROLES; /** @@ -170,6 +176,7 @@ var NetworkConfig_1_0 = class { let opts = {}; opts.pem = getTLSCACert(peer_config); Object.assign(opts, peer_config[GRPC_CONNECTION_OPTIONS]); + this.addTimeout(opts, ENDORSER); peer = new Peer(peer_config[URL], opts); peer.setName(name); if(channel_org) { @@ -185,6 +192,36 @@ var NetworkConfig_1_0 = class { return peer; } + addTimeout(opts, type) { + var method = 'addTimeout'; + if(opts && opts[REQUEST_TIMEOUT]) { + logger.debug('%s - request-timeout exist',method); + return; + } + if(opts && this.hasClient() && + this._network_config.client.connection && + this._network_config.client.connection.timeout) { + let timeouts = this._network_config.client.connection.timeout; + let timeout = ''; + if(type === ENDORSER && timeouts.peer && timeouts.peer.endorser) { + timeout = timeouts.peer.endorser; + } else if(type === ORDERER && timeouts.orderer) { + timeout = timeouts.orderer; + } else if(type === EVENTHUB && timeouts.peer && timeouts.peer.eventHub) { + timeout = timeouts.peer.eventHub; + } else if(type === EVENTREG && timeouts.peer && timeouts.peer.eventReg) { + timeout = timeouts.peer.eventReg; + } + + if(!isNaN(timeout)) { + timeout = timeout * 1000; + opts[REQUEST_TIMEOUT] = timeout; + } else { + logger.warn('%s - timeout value is not a number for the %s : %s',method, TYPES[type], timeout); + } + } + } + getEventHub(name) { var method = 'getEventHub'; logger.debug('%s - name %s',method, name); @@ -195,6 +232,7 @@ var NetworkConfig_1_0 = class { let opts = {}; opts.pem = getTLSCACert(peer_config); Object.assign(opts, peer_config[GRPC_CONNECTION_OPTIONS]); + this.addTimeout(opts, EVENTREG); event_hub = new EventHub(this._client_context); event_hub.setPeerAddr(peer_config[EVENT_URL], opts); } @@ -213,6 +251,7 @@ var NetworkConfig_1_0 = class { let opts = {}; opts.pem = getTLSCACert(orderer_config); Object.assign(opts, orderer_config[GRPC_CONNECTION_OPTIONS]); + this.addTimeout(opts, ORDERER); orderer = new Orderer(orderer_config[URL], opts); orderer.setName(orderer_config[NAME]); } diff --git a/test/fixtures/org1.yaml b/test/fixtures/org1.yaml index 44e81f8900..107964c2e5 100644 --- a/test/fixtures/org1.yaml +++ b/test/fixtures/org1.yaml @@ -1,13 +1,13 @@ --- # # The network connection profile provides client applications the information about the target -# blockchain network that are necessary for the applications to interact with it. These are all +# blockchain network that are necessary for the applications to interact with it. These are all # knowledge that must be acquired from out-of-band sources. This file provides such a source. # name: "global-trade-network" # -# Describes the type of the backend, "hl-fabric" for Hyperledger Fabric, "hl-stl" for Hyperledger +# Describes the type of the backend, "hl-fabric" for Hyperledger Fabric, "hl-stl" for Hyperledger # Sawtooth Lake, etc. # type: "hl-fabric@^1.0.0" @@ -22,19 +22,39 @@ client: # defined under "organizations" organization: Org1 - # Since the node.js SDK supports pluggable KV stores, the properties under "credentialStore" + # set connection timeouts for the peer and orderer for the client + connection: + timeout: + peer: + # the timeout in seconds to be used on requests to a peer, + # for example 'sendTransactionProposal' + endorser: 120 + # the timeout in seconds to be used by applications when waiting for an + # event to occur. This time should be used in a javascript timer object + # that will cancel the event registration with the event hub instance. + eventHub: 60 + # the timeout in seconds to be used when setting up the connection + # with the peer's event hub. If the peer does not acknowledge the + # connection within the time, the application will be notified over the + # error callback if provided. + eventReg: 3 + # the timeout in seconds to be used on request to the orderer, + # for example + orderer: 30 + + # Since the node.js SDK supports pluggable KV stores, the properties under "credentialStore" # are implementation specific credentialStore: - # Specific to FileKeyValueStore.js. Can be others if using an alternative impl. For instance, + # Specific to FileKeyValueStore.js. Can be others if using an alternative impl. For instance, # CouchDBKeyValueStore.js would require an object here for properties like url, db name, etc. path: "/tmp/hfc-kvs/org1" - # Specific to the CryptoSuite implementation. Software-based implementations like - # CryptoSuite_ECDSA_AES.js requires a key store. PKCS#11 based implementations does + # Specific to the CryptoSuite implementation. Software-based implementations like + # CryptoSuite_ECDSA_AES.js requires a key store. PKCS#11 based implementations does # not. cryptoStore: - # Specific to the underlying KeyValueStore that backs the crypto key store. + # Specific to the underlying KeyValueStore that backs the crypto key store. path: "/tmp/hfc-cvs/org1" # Specific to Composer environment - wallet: wallet-name \ No newline at end of file + wallet: wallet-name diff --git a/test/fixtures/org2.yaml b/test/fixtures/org2.yaml index 832eb1507e..64c47cde7d 100644 --- a/test/fixtures/org2.yaml +++ b/test/fixtures/org2.yaml @@ -1,13 +1,13 @@ --- # # The network connection profile provides client applications the information about the target -# blockchain network that are necessary for the applications to interact with it. These are all +# blockchain network that are necessary for the applications to interact with it. These are all # knowledge that must be acquired from out-of-band sources. This file provides such a source. # name: "global-trade-network" # -# Describes the type of the backend, "hl-fabric" for Hyperledger Fabric, "hl-stl" for Hyperledger +# Describes the type of the backend, "hl-fabric" for Hyperledger Fabric, "hl-stl" for Hyperledger # Sawtooth Lake, etc. # type: "hl-fabric@^1.0.0" @@ -22,19 +22,39 @@ client: # defined under "organizations" organization: Org2 - # Since the node.js SDK supports pluggable KV stores, the properties under "credentialStore" + # set connection timeouts for the peer and orderer for the client + connection: + timeout: + peer: + # the timeout in seconds to be used on requests to a peer, + # for example 'sendTransactionProposal' + endorser: 120 + # the timeout in seconds to be used by applications when waiting for an + # event to occur. This time should be used in a javascript timer object + # that will cancel the event registration with the event hub instance. + eventHub: 60 + # the timeout in seconds to be used when setting up the connection + # with the peer's event hub. If the peer does not acknowledge the + # connection within the time, the application will be notified over the + # error callback if provided. + eventReg: 3 + # the timeout in seconds to be used on request to the orderer, + # for example + orderer: 30 + + # Since the node.js SDK supports pluggable KV stores, the properties under "credentialStore" # are implementation specific credentialStore: - # Specific to FileKeyValueStore.js. Can be others if using an alternative impl. For instance, + # Specific to FileKeyValueStore.js. Can be others if using an alternative impl. For instance, # CouchDBKeyValueStore.js would require an object here for properties like url, db name, etc. path: "/tmp/hfc-kvs/org2" - # Specific to the CryptoSuite implementation. Software-based implementations like - # CryptoSuite_ECDSA_AES.js requires a key store. PKCS#11 based implementations does + # Specific to the CryptoSuite implementation. Software-based implementations like + # CryptoSuite_ECDSA_AES.js requires a key store. PKCS#11 based implementations does # not. cryptoStore: - # Specific to the underlying KeyValueStore that backs the crypto key store. + # Specific to the underlying KeyValueStore that backs the crypto key store. path: "/tmp/hfc-cvs/org2" # Specific to Composer environment - wallet: wallet-name \ No newline at end of file + wallet: wallet-name diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index a2ad3d5ee1..51dc562a4d 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -378,7 +378,7 @@ function instantiateChaincode(userOrg, chaincode_path, version, language, upgrad resolve(); } }, (err) => { - t.fail('The was a problem with the instantiate event '+err); + t.fail('There was a problem with the instantiate event '+err); clearTimeout(handle); eh.unregisterTxEvent(deployId); }); diff --git a/test/integration/e2e/join-channel.js b/test/integration/e2e/join-channel.js index d98b98b1da..b2c060ebf2 100644 --- a/test/integration/e2e/join-channel.js +++ b/test/integration/e2e/join-channel.js @@ -196,7 +196,7 @@ function joinChannel(org, t) { block : genesis_block, txId : tx_id }; - let sendPromise = channel.joinChannel(request); + let sendPromise = channel.joinChannel(request, 30000); return Promise.all([sendPromise].concat(eventPromises)); }, (err) => { t.fail('Failed to enroll user \'admin\' due to error: ' + err.stack ? err.stack : err); @@ -215,4 +215,3 @@ function joinChannel(org, t) { t.fail('Failed to join channel due to error: ' + err.stack ? err.stack : err); }); } - diff --git a/test/integration/memory.js b/test/integration/memory.js index 277ec0d430..7173a356b7 100644 --- a/test/integration/memory.js +++ b/test/integration/memory.js @@ -216,7 +216,7 @@ async function createChannel(t) { txId : tx_id }; - results = await channel.joinChannel(request); //admin from org2 + results = await channel.joinChannel(request, 30000); //admin from org2 logger.debug(util.format('Join Channel R E S P O N S E using default targets: %j', results)); // first of the results should not have good status as submitter does not have permission @@ -252,7 +252,7 @@ async function createChannel(t) { txId : tx_id }; - results = await channel.joinChannel(request); //logged in as org1 + results = await channel.joinChannel(request, 30000); //logged in as org1 logger.debug(util.format('Join Channel R E S P O N S E for a string target: %j', results)); if(results && results[0] && results[0].response && results[0].response.status == 200) { diff --git a/test/integration/network-config.js b/test/integration/network-config.js index cf202d3ae0..327d0b413f 100644 --- a/test/integration/network-config.js +++ b/test/integration/network-config.js @@ -422,7 +422,7 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) { //targets - Letting default to all endorsing peers defined on the channel in the network configuration }; - return channel.sendTransactionProposal(request, 3000); //logged in as org1 user + return channel.sendTransactionProposal(request); //logged in as org1 user }).then((results) => { var proposalResponses = results[0]; var proposal = results[1]; @@ -439,6 +439,13 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) { one_good = true; } else { t.fail('transaction proposal was bad'); + if( proposal_response.response && proposal_response.response.status) { + t.comment(' response status:' + proposal_response.response.status + + ' message:' + proposal_response.response.message); + } else { + t.fail('transaction response was unknown' ); + logger.error('transaction response was unknown %s', proposal_response) + } } all_good = all_good & one_good; } diff --git a/test/unit/network-config.js b/test/unit/network-config.js index db6272e529..ff9d3b45ae 100644 --- a/test/unit/network-config.js +++ b/test/unit/network-config.js @@ -126,10 +126,38 @@ test('\n\n ** configuration testing **\n\n', function (t) { t.equals(1, peers.length, ' Check to see that we got 1 peer'); t.equals('grpcs://localhost:7051', peers[0].getUrl(), ' Check to see if we got the right peer for org1 that is endorsing and on the channel'); + let opts = {somesetting : 4}; + client._network_config.addTimeout(opts,1); + t.equals(opts['somesetting'], 4, 'check that existing settings are still there'); + t.equals(opts['request-timeout'], 120000, 'check that endorser timeout was added'); + opts = {}; + client._network_config.addTimeout(opts,2); + t.equals(opts['request-timeout'], 30000, 'check that orderer timeout was added'); + opts = {}; + client._network_config.addTimeout(opts,3); + t.equals(opts['request-timeout'], 60000, 'check that eventHub timeout was added'); + opts = {}; + client._network_config.addTimeout(opts,4); + t.equals(opts['request-timeout'], 3000, 'check that eventReg timeout was added'); + opts = {}; + opts['request-timeout'] = 5000; + client._network_config.addTimeout(opts,4); + t.equals(opts['request-timeout'], 5000, 'check that timeout did not change'); + client._network_config._network_config.client.connection.timeout.peer.eventHub = '2s'; + opts = {}; + client._network_config.addTimeout(opts,3); + t.equals(opts['request-timeout'], undefined, 'check that timeout did not change'); + + let peer = client._network_config.getPeer('peer0.org1.example.com'); + t.equals(peer._options['request-timeout'],120000, ' check that we get this peer endorser timeout set'); + let orderer = client._network_config.getOrderer('orderer.example.com'); + t.equals(orderer._options['request-timeout'],30000, ' check that we get this orderer timeout set'); + let eventHub = client._network_config.getEventHub('peer0.org1.example.com'); + t.equals(eventHub._ep._options['request-timeout'],3000, ' check that we get this eventHub timeout set'); }, null, - '2 Should be able to instantiate a new instance of "Channel" with the definition in the network configuration' + '2 Should be able to run a number of test without error' ); t.doesNotThrow(